mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-25 12:46:53 +01:00
*Update to new libfat
*Rearranged the libs location a bit
This commit is contained in:
parent
297eb8cd55
commit
dbe694cedf
@ -2,8 +2,8 @@
|
|||||||
<app version="1">
|
<app version="1">
|
||||||
<name> USB Loader GX</name>
|
<name> USB Loader GX</name>
|
||||||
<coder>USB Loader GX Team</coder>
|
<coder>USB Loader GX Team</coder>
|
||||||
<version>1.0 r987</version>
|
<version>1.0 r988</version>
|
||||||
<release_date>201010011503</release_date>
|
<release_date>201010011518</release_date>
|
||||||
<short_description>Loads games from USB-devices</short_description>
|
<short_description>Loads games from USB-devices</short_description>
|
||||||
<long_description>USB Loader GX is a libwiigui based USB iso loader with a wii-like GUI. You can install games to your HDDs and boot them with shorter loading times.
|
<long_description>USB Loader GX is a libwiigui based USB iso loader with a wii-like GUI. You can install games to your HDDs and boot them with shorter loading times.
|
||||||
The interactive GUI is completely controllable with WiiMote, Classic Controller or GC Controller.
|
The interactive GUI is completely controllable with WiiMote, Classic Controller or GC Controller.
|
||||||
|
6
Makefile
6
Makefile
@ -22,7 +22,9 @@ SOURCES := source \
|
|||||||
source/fonts \
|
source/fonts \
|
||||||
source/sounds \
|
source/sounds \
|
||||||
source/system \
|
source/system \
|
||||||
source/libwbfs \
|
source/libs/libwbfs \
|
||||||
|
source/libs/libfat \
|
||||||
|
source/libs/libntfs \
|
||||||
source/language \
|
source/language \
|
||||||
source/mload \
|
source/mload \
|
||||||
source/mload/modules \
|
source/mload/modules \
|
||||||
@ -38,9 +40,7 @@ SOURCES := source \
|
|||||||
source/homebrewboot \
|
source/homebrewboot \
|
||||||
source/themes \
|
source/themes \
|
||||||
source/menu \
|
source/menu \
|
||||||
source/libfat \
|
|
||||||
source/memory \
|
source/memory \
|
||||||
source/libntfs \
|
|
||||||
source/FileOperations \
|
source/FileOperations \
|
||||||
source/ImageOperations \
|
source/ImageOperations \
|
||||||
source/utils \
|
source/utils \
|
||||||
|
File diff suppressed because one or more lines are too long
2
gui.pnps
2
gui.pnps
@ -1 +1 @@
|
|||||||
<pd><ViewState><e p="gui\source\mload" x="false"></e><e p="gui\source\settings" x="false"></e><e p="gui\source\utils" x="false"></e><e p="gui\source\images" x="false"></e><e p="gui\source\libfat" x="false"></e><e p="gui\source\prompts" x="false"></e><e p="gui\source\banner" x="false"></e><e p="gui\source\cheats" x="false"></e><e p="gui\source\libntfs" x="false"></e><e p="gui\source\network" x="true"></e><e p="gui\source\fonts" x="false"></e><e p="gui\source\menu" x="false"></e><e p="gui\source\sounds" x="false"></e><e p="gui\source\system" x="false"></e><e p="gui\source\wad" x="false"></e><e p="gui" x="true"></e><e p="gui\source\FileOperations" x="false"></e><e p="gui\source\homebrewboot" x="false"></e><e p="gui\source\language" x="false"></e><e p="gui\source" x="true"></e><e p="gui\source\libwbfs" x="false"></e><e p="gui\source\libwiigui" x="false"></e><e p="gui\source\patches" x="false"></e><e p="gui\source\themes" x="false"></e><e p="gui\source\ImageOperations" x="false"></e><e p="gui\source\memory" x="false"></e><e p="gui\source\usbloader" x="false"></e><e p="gui\source\xml" x="false"></e></ViewState></pd>
|
<pd><ViewState><e p="gui\source\mload" x="false"></e><e p="gui\source\settings" x="false"></e><e p="gui\source\utils" x="false"></e><e p="gui\source\images" x="false"></e><e p="gui\source\prompts" x="false"></e><e p="gui\source\banner" x="false"></e><e p="gui\source\cheats" x="false"></e><e p="gui\source\network" x="true"></e><e p="gui\source\fonts" x="false"></e><e p="gui\source\libs" x="false"></e><e p="gui\source\menu" x="false"></e><e p="gui\source\sounds" x="false"></e><e p="gui\source\system" x="false"></e><e p="gui\source\wad" x="false"></e><e p="gui" x="true"></e><e p="gui\source\FileOperations" x="false"></e><e p="gui\source\homebrewboot" x="false"></e><e p="gui\source\language" x="false"></e><e p="gui\source" x="true"></e><e p="gui\source\libwiigui" x="false"></e><e p="gui\source\patches" x="false"></e><e p="gui\source\themes" x="false"></e><e p="gui\source\ImageOperations" x="false"></e><e p="gui\source\memory" x="false"></e><e p="gui\source\usbloader" x="false"></e><e p="gui\source\xml" x="false"></e></ViewState></pd>
|
@ -20,7 +20,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "libfat/fat.h"
|
#include <fat.h>
|
||||||
|
|
||||||
#include "MD5.h"
|
#include "MD5.h"
|
||||||
#include "openingbnr.h"
|
#include "openingbnr.h"
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "usbloader/disc.h"
|
#include "usbloader/disc.h"
|
||||||
#include "usbloader/wbfs.h"
|
#include "usbloader/wbfs.h"
|
||||||
#include "prompts/PromptWindows.h"
|
#include "prompts/PromptWindows.h"
|
||||||
#include "libwbfs/libwbfs.h"
|
#include "libs/libwbfs/libwbfs.h"
|
||||||
#include "language/gettext.h"
|
#include "language/gettext.h"
|
||||||
#include "bannersound.h"
|
#include "bannersound.h"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "libfat/fat.h"
|
#include <fat.h>
|
||||||
#include "libwiigui/gui.h"
|
#include "libwiigui/gui.h"
|
||||||
#include "libwiigui/gui_customoptionbrowser.h"
|
#include "libwiigui/gui_customoptionbrowser.h"
|
||||||
#include "prompts/PromptWindows.h"
|
#include "prompts/PromptWindows.h"
|
||||||
|
@ -5,12 +5,13 @@
|
|||||||
#include <ogc/usbstorage.h>
|
#include <ogc/usbstorage.h>
|
||||||
#include <sdcard/wiisd_io.h>
|
#include <sdcard/wiisd_io.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
#include <fat.h>
|
||||||
|
#include <ntfs.h>
|
||||||
|
|
||||||
#include "usbloader/usbstorage2.h"
|
#include "usbloader/usbstorage2.h"
|
||||||
#include "usbloader/sdhc.h"
|
#include "usbloader/sdhc.h"
|
||||||
#include "usbloader/wbfs.h"
|
#include "usbloader/wbfs.h"
|
||||||
#include "libntfs/ntfs.h"
|
#include "fatmounter.h"
|
||||||
#include "libfat/fat.h"
|
|
||||||
#include "gecko.h"
|
#include "gecko.h"
|
||||||
|
|
||||||
//these are the only stable and speed is good
|
//these are the only stable and speed is good
|
||||||
@ -46,26 +47,28 @@ sec_t fs_ntfs_sec = 0;
|
|||||||
int USBDevice_Init()
|
int USBDevice_Init()
|
||||||
{
|
{
|
||||||
//closing all open Files write back the cache and then shutdown em!
|
//closing all open Files write back the cache and then shutdown em!
|
||||||
fatUnmount("USB:/");
|
USBDevice_deInit();
|
||||||
//right now mounts first FAT-partition
|
//right now mounts first FAT-partition
|
||||||
|
|
||||||
if (!fatMount("USB", &__io_usbstorage2, 0, CACHE, SECTORS))
|
if (!fatMount("USB", &__io_usbstorage2, 0, CACHE, SECTORS))
|
||||||
{
|
return -1;
|
||||||
// libogc usbstorage
|
|
||||||
if(!fatMount("USB", &__io_usbstorage, 0, CACHE, SECTORS))
|
if(!fatMount("USB", &__io_usbstorage, 0, CACHE, SECTORS))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
fat_usb_mount = 1;
|
fat_usb_mount = 1;
|
||||||
fat_usb_sec = _FAT_startSector;
|
fat_usb_sec = _FAT_startSector;
|
||||||
|
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void USBDevice_deInit()
|
void USBDevice_deInit()
|
||||||
{
|
{
|
||||||
//closing all open Files write back the cache and then shutdown em!
|
//closing all open Files write back the cache and then shutdown em!
|
||||||
fatUnmount("USB:/");
|
fatUnmount("USB:/");
|
||||||
|
__io_usbstorage.shutdown();
|
||||||
|
__io_usbstorage2.shutdown();
|
||||||
|
|
||||||
fat_usb_mount = 0;
|
fat_usb_mount = 0;
|
||||||
fat_usb_sec = 0;
|
fat_usb_sec = 0;
|
||||||
@ -109,7 +112,7 @@ int isInserted(const char *path)
|
|||||||
int SDCard_Init()
|
int SDCard_Init()
|
||||||
{
|
{
|
||||||
//closing all open Files write back the cache and then shutdown em!
|
//closing all open Files write back the cache and then shutdown em!
|
||||||
fatUnmount("SD:/");
|
SDCard_deInit();
|
||||||
|
|
||||||
//right now mounts first FAT-partition
|
//right now mounts first FAT-partition
|
||||||
if (fatMount("SD", &__io_wiisd, 0, CACHE, SECTORS))
|
if (fatMount("SD", &__io_wiisd, 0, CACHE, SECTORS))
|
||||||
@ -118,7 +121,10 @@ int SDCard_Init()
|
|||||||
fat_sd_sec = _FAT_startSector;
|
fat_sd_sec = _FAT_startSector;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else if (fatMount("SD", &__io_sdhc, 0, CACHE, SDHC_SECTOR_SIZE))
|
|
||||||
|
__io_wiisd.shutdown();
|
||||||
|
|
||||||
|
if (fatMount("SD", &__io_sdhc, 0, CACHE, SECTORS))
|
||||||
{
|
{
|
||||||
fat_sd_mount = MOUNT_SDHC;
|
fat_sd_mount = MOUNT_SDHC;
|
||||||
fat_sd_sec = _FAT_startSector;
|
fat_sd_sec = _FAT_startSector;
|
||||||
@ -131,6 +137,8 @@ int SDCard_Init()
|
|||||||
void SDCard_deInit()
|
void SDCard_deInit()
|
||||||
{
|
{
|
||||||
fatUnmount("SD:/");
|
fatUnmount("SD:/");
|
||||||
|
__io_wiisd.shutdown();
|
||||||
|
__io_sdhc.shutdown();
|
||||||
|
|
||||||
fat_sd_mount = MOUNT_NONE;
|
fat_sd_mount = MOUNT_NONE;
|
||||||
fat_sd_sec = 0;
|
fat_sd_sec = 0;
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
bit_ops.h
|
|
||||||
Functions for dealing with conversion of data between types
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __BIT_OPS_H
|
|
||||||
#define __BIT_OPS_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
Functions to deal with little endian values stored in uint8_t arrays
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
static inline uint16_t u8array_to_u16(const uint8_t* item, int offset)
|
|
||||||
{
|
|
||||||
return (item[offset] | (item[offset + 1] << 8));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t u8array_to_u32(const uint8_t* item, int offset)
|
|
||||||
{
|
|
||||||
return (item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void u16_to_u8array(uint8_t* item, int offset, uint16_t value)
|
|
||||||
{
|
|
||||||
item[offset] = (uint8_t) value;
|
|
||||||
item[offset + 1] = (uint8_t) (value >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void u32_to_u8array(uint8_t* item, int offset, uint32_t value)
|
|
||||||
{
|
|
||||||
item[offset] = (uint8_t) value;
|
|
||||||
item[offset + 1] = (uint8_t) (value >> 8);
|
|
||||||
item[offset + 2] = (uint8_t) (value >> 16);
|
|
||||||
item[offset + 3] = (uint8_t) (value >> 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _BIT_OPS_H
|
|
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
cache.h
|
|
||||||
The cache is not visible to the user. It should be flushed
|
|
||||||
when any file is closed or changes are made to the filesystem.
|
|
||||||
|
|
||||||
This cache implements a least-used-page replacement policy. This will
|
|
||||||
distribute sectors evenly over the pages, so if less than the maximum
|
|
||||||
pages are used at once, they should all eventually remain in the cache.
|
|
||||||
This also has the benefit of throwing out old sectors, so as not to keep
|
|
||||||
too many stale pages around.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CACHE_H
|
|
||||||
#define _CACHE_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "disc.h"
|
|
||||||
|
|
||||||
#define PAGE_SECTORS 64
|
|
||||||
#define CACHE_PAGE_SIZE (BYTES_PER_READ * PAGE_SECTORS)
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
sec_t sector;
|
|
||||||
unsigned int count;
|
|
||||||
unsigned int last_access;
|
|
||||||
bool dirty;
|
|
||||||
uint8_t* cache;
|
|
||||||
} CACHE_ENTRY;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
const DISC_INTERFACE* disc;
|
|
||||||
sec_t endOfPartition;
|
|
||||||
unsigned int numberOfPages;
|
|
||||||
unsigned int sectorsPerPage;
|
|
||||||
CACHE_ENTRY* cacheEntries;
|
|
||||||
} CACHE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read data from a sector in the cache
|
|
||||||
If the sector is not in the cache, it will be swapped in
|
|
||||||
offset is the position to start reading from
|
|
||||||
size is the amount of data to read
|
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_readPartialSector(CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size);
|
|
||||||
|
|
||||||
bool _FAT_cache_readLittleEndianValue(CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write data to a sector in the cache
|
|
||||||
If the sector is not in the cache, it will be swapped in.
|
|
||||||
When the sector is swapped out, the data will be written to the disc
|
|
||||||
offset is the position to start writing to
|
|
||||||
size is the amount of data to write
|
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_writePartialSector(CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
|
||||||
|
|
||||||
bool _FAT_cache_writeLittleEndianValue(CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset,
|
|
||||||
int num_bytes);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write data to a sector in the cache, zeroing the sector first
|
|
||||||
If the sector is not in the cache, it will be swapped in.
|
|
||||||
When the sector is swapped out, the data will be written to the disc
|
|
||||||
offset is the position to start writing to
|
|
||||||
size is the amount of data to write
|
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_eraseWritePartialSector(CACHE* cache, const void* buffer, sec_t sector, unsigned int offset,
|
|
||||||
size_t size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read several sectors from the cache
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_readSectors(CACHE* cache, sec_t sector, sec_t numSectors, void* buffer);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read a full sector from the cache
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_cache_readSector(CACHE* cache, void* buffer, sec_t sector)
|
|
||||||
{
|
|
||||||
return _FAT_cache_readPartialSector(cache, buffer, sector, 0, BYTES_PER_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write a full sector to the cache
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_cache_writeSector(CACHE* cache, const void* buffer, sec_t sector)
|
|
||||||
{
|
|
||||||
return _FAT_cache_writePartialSector(cache, buffer, sector, 0, BYTES_PER_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_cache_writeSectors(CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write any dirty sectors back to disc and clear out the contents of the cache
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_flush(CACHE* cache);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Clear out the contents of the cache without writing any dirty sectors first
|
|
||||||
*/
|
|
||||||
void _FAT_cache_invalidate(CACHE* cache);
|
|
||||||
|
|
||||||
CACHE* _FAT_cache_constructor(unsigned int numberOfPages, unsigned int sectorsPerPage,
|
|
||||||
const DISC_INTERFACE* discInterface, sec_t endOfPartition);
|
|
||||||
|
|
||||||
void _FAT_cache_destructor(CACHE* cache);
|
|
||||||
|
|
||||||
#endif // _CACHE_H
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
common.h
|
|
||||||
Common definitions and included files for the FATlib
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __COMMON_H
|
|
||||||
#define __COMMON_H
|
|
||||||
|
|
||||||
#define BYTES_PER_READ 512
|
|
||||||
#include "fat.h"
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
// Platform specific includes
|
|
||||||
#include <gctypes.h>
|
|
||||||
#include <ogc/disc_io.h>
|
|
||||||
#include <gccore.h>
|
|
||||||
// Platform specific options
|
|
||||||
#define DEFAULT_CACHE_PAGES 4
|
|
||||||
#define DEFAULT_SECTORS_PAGE 64
|
|
||||||
#define USE_LWP_LOCK
|
|
||||||
#define USE_RTC_TIME
|
|
||||||
|
|
||||||
#endif // _COMMON_H
|
|
File diff suppressed because it is too large
Load Diff
@ -1,181 +0,0 @@
|
|||||||
/*
|
|
||||||
directory.h
|
|
||||||
Reading, writing and manipulation of the directory structure on
|
|
||||||
a FAT partition
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __DIRECTORY_H
|
|
||||||
#define __DIRECTORY_H
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "partition.h"
|
|
||||||
|
|
||||||
#define DIR_ENTRY_DATA_SIZE 0x20
|
|
||||||
#define MAX_LFN_LENGTH 256
|
|
||||||
#define MAX_FILENAME_LENGTH 768 // 256 UCS-2 characters encoded into UTF-8 can use up to 768 UTF-8 chars
|
|
||||||
#define MAX_ALIAS_LENGTH 13
|
|
||||||
#define LFN_ENTRY_LENGTH 13
|
|
||||||
#define ALIAS_ENTRY_LENGTH 11
|
|
||||||
#define MAX_ALIAS_EXT_LENGTH 3
|
|
||||||
#define MAX_ALIAS_PRI_LENGTH 8
|
|
||||||
#define MAX_NUMERIC_TAIL 999999
|
|
||||||
#define FAT16_ROOT_DIR_CLUSTER 0
|
|
||||||
|
|
||||||
#define DIR_SEPARATOR '/'
|
|
||||||
|
|
||||||
// File attributes
|
|
||||||
#define ATTRIB_ARCH 0x20 // Archive
|
|
||||||
#define ATTRIB_DIR 0x10 // Directory
|
|
||||||
#define ATTRIB_LFN 0x0F // Long file name
|
|
||||||
#define ATTRIB_VOL 0x08 // Volume
|
|
||||||
#define ATTRIB_SYS 0x04 // System
|
|
||||||
#define ATTRIB_HID 0x02 // Hidden
|
|
||||||
#define ATTRIB_RO 0x01 // Read only
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
FT_DIRECTORY, FT_FILE
|
|
||||||
} FILE_TYPE;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint32_t cluster;
|
|
||||||
sec_t sector;
|
|
||||||
int32_t offset;
|
|
||||||
} DIR_ENTRY_POSITION;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint8_t entryData[DIR_ENTRY_DATA_SIZE];
|
|
||||||
DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
|
||||||
DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry
|
|
||||||
char filename[MAX_FILENAME_LENGTH];
|
|
||||||
} DIR_ENTRY;
|
|
||||||
|
|
||||||
// Directory entry offsets
|
|
||||||
enum DIR_ENTRY_offset
|
|
||||||
{
|
|
||||||
DIR_ENTRY_name = 0x00,
|
|
||||||
DIR_ENTRY_extension = 0x08,
|
|
||||||
DIR_ENTRY_attributes = 0x0B,
|
|
||||||
DIR_ENTRY_reserved = 0x0C,
|
|
||||||
DIR_ENTRY_cTime_ms = 0x0D,
|
|
||||||
DIR_ENTRY_cTime = 0x0E,
|
|
||||||
DIR_ENTRY_cDate = 0x10,
|
|
||||||
DIR_ENTRY_aDate = 0x12,
|
|
||||||
DIR_ENTRY_clusterHigh = 0x14,
|
|
||||||
DIR_ENTRY_mTime = 0x16,
|
|
||||||
DIR_ENTRY_mDate = 0x18,
|
|
||||||
DIR_ENTRY_cluster = 0x1A,
|
|
||||||
DIR_ENTRY_fileSize = 0x1C
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Returns true if the file specified by entry is a directory
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_directory_isDirectory(DIR_ENTRY* entry)
|
|
||||||
{
|
|
||||||
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _FAT_directory_isWritable(DIR_ENTRY* entry)
|
|
||||||
{
|
|
||||||
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _FAT_directory_isDot(DIR_ENTRY* entry)
|
|
||||||
{
|
|
||||||
return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') || ((entry->filename[1] == '.')
|
|
||||||
&& entry->filename[2] == '\0')));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reads the first directory entry from the directory starting at dirCluster
|
|
||||||
Places result in entry
|
|
||||||
entry will be destroyed even if no directory entry is found
|
|
||||||
Returns true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool _FAT_directory_getFirstEntry(PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reads the next directory entry after the one already pointed to by entry
|
|
||||||
Places result in entry
|
|
||||||
entry will be destroyed even if no directory entry is found
|
|
||||||
Returns true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool _FAT_directory_getNextEntry(PARTITION* partition, DIR_ENTRY* entry);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Gets the directory entry corrsponding to the supplied path
|
|
||||||
entry will be destroyed even if no directory entry is found
|
|
||||||
pathEnd specifies the end of the path string, for cutting strings short if needed
|
|
||||||
specify NULL to use the full length of path
|
|
||||||
pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR
|
|
||||||
after pathEND.
|
|
||||||
Returns true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool _FAT_directory_entryFromPath(PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Changes the current directory to the one specified by path
|
|
||||||
Returns true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool _FAT_directory_chdir(PARTITION* partition, const char* path);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Removes the directory entry specified by entry
|
|
||||||
Assumes that entry is valid
|
|
||||||
Returns true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool _FAT_directory_removeEntry(PARTITION* partition, DIR_ENTRY* entry);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Add a directory entry to the directory specified by dirCluster
|
|
||||||
The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are
|
|
||||||
updated with the new directory entry position and alias.
|
|
||||||
Returns true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool _FAT_directory_addEntry(PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Get the start cluster of a file from it's entry data
|
|
||||||
*/
|
|
||||||
uint32_t _FAT_directory_entryGetCluster(PARTITION* partition, const uint8_t* entryData);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Fill in the file name and entry data of DIR_ENTRY* entry.
|
|
||||||
Assumes that the entry's dataStart and dataEnd are correct
|
|
||||||
Returns true on success, false on failure
|
|
||||||
*/
|
|
||||||
bool _FAT_directory_entryFromPosition(PARTITION* partition, DIR_ENTRY* entry);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Fill in a stat struct based on a file entry
|
|
||||||
*/
|
|
||||||
void _FAT_directory_entryStat(PARTITION* partition, DIR_ENTRY* entry, struct stat *st);
|
|
||||||
|
|
||||||
#endif // _DIRECTORY_H
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
disc.h
|
|
||||||
Interface to the low level disc functions. Used by the higher level
|
|
||||||
file system code.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
#ifndef _DISC_H
|
|
||||||
#define _DISC_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
A list of all default devices to try at startup,
|
|
||||||
terminated by a {NULL,NULL} entry.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
const char* name;
|
|
||||||
const DISC_INTERFACE* (*getInterface)(void);
|
|
||||||
} INTERFACE_ID;
|
|
||||||
extern const INTERFACE_ID _FAT_disc_interfaces[];
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check if a disc is inserted
|
|
||||||
Return true if a disc is inserted and ready, false otherwise
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_isInserted(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->isInserted();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read numSectors sectors from a disc, starting at sector.
|
|
||||||
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
|
||||||
else it is at least 1
|
|
||||||
sector is 0 or greater
|
|
||||||
buffer is a pointer to the memory to fill
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_readSectors(const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer)
|
|
||||||
{
|
|
||||||
return disc->readSectors(sector, numSectors, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write numSectors sectors to a disc, starting at sector.
|
|
||||||
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
|
||||||
else it is at least 1
|
|
||||||
sector is 0 or greater
|
|
||||||
buffer is a pointer to the memory to read from
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_writeSectors(const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors,
|
|
||||||
const void* buffer)
|
|
||||||
{
|
|
||||||
return disc->writeSectors(sector, numSectors, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reset the card back to a ready state
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_clearStatus(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->clearStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise the disc to a state ready for data reading or writing
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_startup(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Put the disc in a state ready for power down.
|
|
||||||
Complete any pending writes and disable the disc if necessary
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_shutdown(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return a 32 bit value unique to each type of interface
|
|
||||||
*/
|
|
||||||
static inline uint32_t _FAT_disc_hostType(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->ioType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return a 32 bit value that specifies the capabilities of the disc
|
|
||||||
*/
|
|
||||||
static inline uint32_t _FAT_disc_features(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->features;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _DISC_H
|
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
disc.c
|
|
||||||
Interface to the low level disc functions. Used by the higher level
|
|
||||||
file system code.
|
|
||||||
|
|
||||||
Copyright (c) 2008 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "disc_fat.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
The list of interfaces consists of a series of name/interface pairs.
|
|
||||||
The interface is returned via a simple function. This allows for
|
|
||||||
platforms where the interface has to be "assembled" before it can
|
|
||||||
be used, like DLDI on the NDS. For cases where a simple struct
|
|
||||||
is available, wrapper functions are used.
|
|
||||||
The list is terminated by a NULL/NULL entry.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ====================== Wii ====================== */
|
|
||||||
#include <sdcard/wiisd_io.h>
|
|
||||||
#include "usbloader/usbstorage2.h"
|
|
||||||
#include <sdcard/gcsd.h>
|
|
||||||
|
|
||||||
static const DISC_INTERFACE* get_io_wiisd(void)
|
|
||||||
{
|
|
||||||
return &__io_wiisd;
|
|
||||||
}
|
|
||||||
static const DISC_INTERFACE* get_io_usbstorage(void)
|
|
||||||
{
|
|
||||||
return &__io_usbstorage2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const DISC_INTERFACE* get_io_gcsda(void)
|
|
||||||
{
|
|
||||||
return &__io_gcsda;
|
|
||||||
}
|
|
||||||
static const DISC_INTERFACE* get_io_gcsdb(void)
|
|
||||||
{
|
|
||||||
return &__io_gcsdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
const INTERFACE_ID _FAT_disc_interfaces[] = { { "sd", get_io_wiisd }, { "usb", get_io_usbstorage }, { "carda",
|
|
||||||
get_io_gcsda }, { "cardb", get_io_gcsdb }, { NULL, NULL } };
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
disc.h
|
|
||||||
Interface to the low level disc functions. Used by the higher level
|
|
||||||
file system code.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
#ifndef __DISC_H
|
|
||||||
#define __DISC_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
A list of all default devices to try at startup,
|
|
||||||
terminated by a {NULL,NULL} entry.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
const char* name;
|
|
||||||
const DISC_INTERFACE* (*getInterface)(void);
|
|
||||||
} INTERFACE_ID;
|
|
||||||
extern const INTERFACE_ID _FAT_disc_interfaces[];
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check if a disc is inserted
|
|
||||||
Return true if a disc is inserted and ready, false otherwise
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_isInserted(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->isInserted();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read numSectors sectors from a disc, starting at sector.
|
|
||||||
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
|
||||||
else it is at least 1
|
|
||||||
sector is 0 or greater
|
|
||||||
buffer is a pointer to the memory to fill
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_readSectors(const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer)
|
|
||||||
{
|
|
||||||
return disc->readSectors(sector, numSectors, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write numSectors sectors to a disc, starting at sector.
|
|
||||||
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
|
||||||
else it is at least 1
|
|
||||||
sector is 0 or greater
|
|
||||||
buffer is a pointer to the memory to read from
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_writeSectors(const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors,
|
|
||||||
const void* buffer)
|
|
||||||
{
|
|
||||||
return disc->writeSectors(sector, numSectors, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reset the card back to a ready state
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_clearStatus(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->clearStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise the disc to a state ready for data reading or writing
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_startup(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Put the disc in a state ready for power down.
|
|
||||||
Complete any pending writes and disable the disc if necessary
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_disc_shutdown(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return a 32 bit value unique to each type of interface
|
|
||||||
*/
|
|
||||||
static inline uint32_t _FAT_disc_hostType(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->ioType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return a 32 bit value that specifies the capabilities of the disc
|
|
||||||
*/
|
|
||||||
static inline uint32_t _FAT_disc_features(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
return disc->features;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _DISC_H
|
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
fat.h
|
|
||||||
Simple functionality for startup, mounting and unmounting of FAT-based devices.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _LIBFAT_H
|
|
||||||
#define _LIBFAT_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <ogc/disc_io.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialise any inserted block-devices.
|
|
||||||
Add the fat device driver to the devoptab, making it available for standard file functions.
|
|
||||||
cacheSize: The number of pages to allocate for each inserted block-device
|
|
||||||
setAsDefaultDevice: if true, make this the default device driver for file operations
|
|
||||||
*/
|
|
||||||
extern bool fatInit(uint32_t cacheSize, bool setAsDefaultDevice);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
|
||||||
*/
|
|
||||||
extern bool fatInitDefault(void);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
|
||||||
You can then access the filesystem using "name:/".
|
|
||||||
This will mount the active partition or the first valid partition on the disc,
|
|
||||||
and will use a cache size optimized for the host system.
|
|
||||||
*/
|
|
||||||
extern bool fatMountSimple(const char* name, const DISC_INTERFACE* interface);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
|
||||||
You can then access the filesystem using "name:/".
|
|
||||||
If startSector = 0, it will mount the active partition of the first valid partition on
|
|
||||||
the disc. Otherwise it will try to mount the partition starting at startSector.
|
|
||||||
cacheSize specifies the number of pages to allocate for the cache.
|
|
||||||
This will not startup the disc, so you need to call interface->startup(); first.
|
|
||||||
*/
|
|
||||||
extern bool fatMount(const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize,
|
|
||||||
uint32_t SectorsPerPage);
|
|
||||||
/*
|
|
||||||
Unmount the partition specified by name.
|
|
||||||
If there are open files, it will attempt to synchronise them to disc.
|
|
||||||
*/
|
|
||||||
extern void fatUnmount(const char* name);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // _LIBFAT_H
|
|
@ -1,415 +0,0 @@
|
|||||||
/*
|
|
||||||
cache.c
|
|
||||||
The cache is not visible to the user. It should be flushed
|
|
||||||
when any file is closed or changes are made to the filesystem.
|
|
||||||
|
|
||||||
This cache implements a least-used-page replacement policy. This will
|
|
||||||
distribute sectors evenly over the pages, so if less than the maximum
|
|
||||||
pages are used at once, they should all eventually remain in the cache.
|
|
||||||
This also has the benefit of throwing out old sectors, so as not to keep
|
|
||||||
too many stale pages around.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "fat_cache.h"
|
|
||||||
#include "disc_fat.h"
|
|
||||||
|
|
||||||
#include "mem_allocate.h"
|
|
||||||
#include "bit_ops.h"
|
|
||||||
#include "file_allocation_table.h"
|
|
||||||
|
|
||||||
#define CACHE_FREE UINT_MAX
|
|
||||||
|
|
||||||
CACHE* _FAT_cache_constructor(unsigned int numberOfPages, unsigned int sectorsPerPage,
|
|
||||||
const DISC_INTERFACE* discInterface, sec_t endOfPartition)
|
|
||||||
{
|
|
||||||
CACHE* cache;
|
|
||||||
unsigned int i;
|
|
||||||
CACHE_ENTRY* cacheEntries;
|
|
||||||
|
|
||||||
if (numberOfPages < 2)
|
|
||||||
{
|
|
||||||
numberOfPages = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sectorsPerPage < 8)
|
|
||||||
{
|
|
||||||
sectorsPerPage = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache = (CACHE*) _FAT_mem_allocate(sizeof(CACHE));
|
|
||||||
if (cache == NULL)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache->disc = discInterface;
|
|
||||||
cache->endOfPartition = endOfPartition;
|
|
||||||
cache->numberOfPages = numberOfPages;
|
|
||||||
cache->sectorsPerPage = sectorsPerPage;
|
|
||||||
|
|
||||||
cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate(sizeof(CACHE_ENTRY) * numberOfPages);
|
|
||||||
if (cacheEntries == NULL)
|
|
||||||
{
|
|
||||||
_FAT_mem_free(cache);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < numberOfPages; i++)
|
|
||||||
{
|
|
||||||
cacheEntries[i].sector = CACHE_FREE;
|
|
||||||
cacheEntries[i].count = 0;
|
|
||||||
cacheEntries[i].last_access = 0;
|
|
||||||
cacheEntries[i].dirty = false;
|
|
||||||
cacheEntries[i].cache = (uint8_t*) _FAT_mem_align(sectorsPerPage * BYTES_PER_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
cache->cacheEntries = cacheEntries;
|
|
||||||
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _FAT_cache_destructor(CACHE* cache)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
// Clear out cache before destroying it
|
|
||||||
_FAT_cache_flush(cache);
|
|
||||||
|
|
||||||
// Free memory in reverse allocation order
|
|
||||||
for (i = 0; i < cache->numberOfPages; i++)
|
|
||||||
{
|
|
||||||
_FAT_mem_free(cache->cacheEntries[i].cache);
|
|
||||||
}
|
|
||||||
_FAT_mem_free(cache->cacheEntries);
|
|
||||||
_FAT_mem_free(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 accessCounter = 0;
|
|
||||||
|
|
||||||
static u32 accessTime()
|
|
||||||
{
|
|
||||||
accessCounter++;
|
|
||||||
return accessCounter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache, sec_t sector)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
|
||||||
unsigned int numberOfPages = cache->numberOfPages;
|
|
||||||
unsigned int sectorsPerPage = cache->sectorsPerPage;
|
|
||||||
|
|
||||||
bool foundFree = false;
|
|
||||||
unsigned int oldUsed = 0;
|
|
||||||
unsigned int oldAccess = UINT_MAX;
|
|
||||||
|
|
||||||
for (i = 0; i < numberOfPages; i++)
|
|
||||||
{
|
|
||||||
if (sector >= cacheEntries[i].sector && sector < (cacheEntries[i].sector + cacheEntries[i].count))
|
|
||||||
{
|
|
||||||
cacheEntries[i].last_access = accessTime();
|
|
||||||
return &(cacheEntries[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundFree == false && (cacheEntries[i].sector == CACHE_FREE || cacheEntries[i].last_access < oldAccess))
|
|
||||||
{
|
|
||||||
if (cacheEntries[i].sector == CACHE_FREE) foundFree = true;
|
|
||||||
oldUsed = i;
|
|
||||||
oldAccess = cacheEntries[i].last_access;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundFree == false && cacheEntries[oldUsed].dirty == true)
|
|
||||||
{
|
|
||||||
if (!_FAT_disc_writeSectors(cache->disc, cacheEntries[oldUsed].sector, cacheEntries[oldUsed].count,
|
|
||||||
cacheEntries[oldUsed].cache)) return NULL;
|
|
||||||
cacheEntries[oldUsed].dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sector = (sector / sectorsPerPage) * sectorsPerPage; // align base sector to page size
|
|
||||||
sec_t next_page = sector + sectorsPerPage;
|
|
||||||
if (next_page > cache->endOfPartition) next_page = cache->endOfPartition;
|
|
||||||
|
|
||||||
if (!_FAT_disc_readSectors(cache->disc, sector, next_page - sector, cacheEntries[oldUsed].cache)) return NULL;
|
|
||||||
|
|
||||||
cacheEntries[oldUsed].sector = sector;
|
|
||||||
cacheEntries[oldUsed].count = next_page - sector;
|
|
||||||
cacheEntries[oldUsed].last_access = accessTime();
|
|
||||||
|
|
||||||
return &(cacheEntries[oldUsed]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_cache_readSectors(CACHE *cache, sec_t sector, sec_t numSectors, void *buffer)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
sec_t secs_to_read;
|
|
||||||
CACHE_ENTRY *entry;
|
|
||||||
uint8_t *dest = buffer;
|
|
||||||
|
|
||||||
while (numSectors > 0)
|
|
||||||
{
|
|
||||||
entry = _FAT_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
secs_to_read = entry->count - sec;
|
|
||||||
if (secs_to_read > numSectors) secs_to_read = numSectors;
|
|
||||||
|
|
||||||
memcpy(dest, entry->cache + (sec * BYTES_PER_READ), (secs_to_read * BYTES_PER_READ));
|
|
||||||
|
|
||||||
dest += (secs_to_read * BYTES_PER_READ);
|
|
||||||
sector += secs_to_read;
|
|
||||||
numSectors -= secs_to_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reads some data from a cache page, determined by the sector number
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_readPartialSector(CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
CACHE_ENTRY *entry;
|
|
||||||
|
|
||||||
if (offset + size > BYTES_PER_READ) return false;
|
|
||||||
|
|
||||||
entry = _FAT_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
memcpy(buffer, entry->cache + ((sec * BYTES_PER_READ) + offset), size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_cache_readLittleEndianValue(CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes)
|
|
||||||
{
|
|
||||||
uint8_t buf[4];
|
|
||||||
if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
|
||||||
|
|
||||||
switch (num_bytes)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
*value = buf[0];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
*value = u8array_to_u16(buf, 0);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
*value = u8array_to_u32(buf, 0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Writes some data to a cache page, making sure it is loaded into memory first.
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_writePartialSector(CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
CACHE_ENTRY *entry;
|
|
||||||
|
|
||||||
if (offset + size > BYTES_PER_READ) return false;
|
|
||||||
|
|
||||||
entry = _FAT_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
memcpy(entry->cache + ((sec * BYTES_PER_READ) + offset), buffer, size);
|
|
||||||
|
|
||||||
entry->dirty = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_cache_writeLittleEndianValue(CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size)
|
|
||||||
{
|
|
||||||
uint8_t buf[4] = { 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
switch (size)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
buf[0] = value;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
u16_to_u8array(buf, 0, value);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
u32_to_u8array(buf, 0, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _FAT_cache_writePartialSector(cache, buf, sector, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Writes some data to a cache page, zeroing out the page first
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_eraseWritePartialSector(CACHE* cache, const void* buffer, sec_t sector, unsigned int offset,
|
|
||||||
size_t size)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
CACHE_ENTRY *entry;
|
|
||||||
|
|
||||||
if (offset + size > BYTES_PER_READ) return false;
|
|
||||||
|
|
||||||
entry = _FAT_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
memset(entry->cache + (sec * BYTES_PER_READ), 0, BYTES_PER_READ);
|
|
||||||
memcpy(entry->cache + ((sec * BYTES_PER_READ) + offset), buffer, size);
|
|
||||||
|
|
||||||
entry->dirty = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static CACHE_ENTRY* _FAT_cache_findPage(CACHE *cache, sec_t sector, sec_t count)
|
|
||||||
{
|
|
||||||
|
|
||||||
unsigned int i;
|
|
||||||
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
|
||||||
unsigned int numberOfPages = cache->numberOfPages;
|
|
||||||
CACHE_ENTRY *entry = NULL;
|
|
||||||
sec_t lowest = UINT_MAX;
|
|
||||||
|
|
||||||
for (i = 0; i < numberOfPages; i++)
|
|
||||||
{
|
|
||||||
if (cacheEntries[i].sector != CACHE_FREE)
|
|
||||||
{
|
|
||||||
bool intersect;
|
|
||||||
if (sector > cacheEntries[i].sector)
|
|
||||||
{
|
|
||||||
intersect = sector - cacheEntries[i].sector < cacheEntries[i].count;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
intersect = cacheEntries[i].sector - sector < count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersect && (cacheEntries[i].sector < lowest))
|
|
||||||
{
|
|
||||||
lowest = cacheEntries[i].sector;
|
|
||||||
entry = &cacheEntries[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_cache_writeSectors(CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
sec_t secs_to_write;
|
|
||||||
CACHE_ENTRY* entry;
|
|
||||||
const uint8_t *src = buffer;
|
|
||||||
|
|
||||||
while (numSectors > 0)
|
|
||||||
{
|
|
||||||
entry = _FAT_cache_findPage(cache, sector, numSectors);
|
|
||||||
|
|
||||||
if (entry != NULL)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (entry->sector > sector)
|
|
||||||
{
|
|
||||||
|
|
||||||
secs_to_write = entry->sector - sector;
|
|
||||||
|
|
||||||
_FAT_disc_writeSectors(cache->disc, sector, secs_to_write, src);
|
|
||||||
src += (secs_to_write * BYTES_PER_READ);
|
|
||||||
sector += secs_to_write;
|
|
||||||
numSectors -= secs_to_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
secs_to_write = entry->count - sec;
|
|
||||||
|
|
||||||
if (secs_to_write > numSectors) secs_to_write = numSectors;
|
|
||||||
|
|
||||||
memcpy(entry->cache + (sec * BYTES_PER_READ), src, (secs_to_write * BYTES_PER_READ));
|
|
||||||
|
|
||||||
src += (secs_to_write * BYTES_PER_READ);
|
|
||||||
sector += secs_to_write;
|
|
||||||
numSectors -= secs_to_write;
|
|
||||||
|
|
||||||
entry->dirty = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_FAT_disc_writeSectors(cache->disc, sector, numSectors, src);
|
|
||||||
numSectors = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Flushes all dirty pages to disc, clearing the dirty flag.
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_flush(CACHE* cache)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < cache->numberOfPages; i++)
|
|
||||||
{
|
|
||||||
if (cache->cacheEntries[i].dirty)
|
|
||||||
{
|
|
||||||
if (!_FAT_disc_writeSectors(cache->disc, cache->cacheEntries[i].sector, cache->cacheEntries[i].count,
|
|
||||||
cache->cacheEntries[i].cache))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cache->cacheEntries[i].dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _FAT_cache_invalidate(CACHE* cache)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
_FAT_cache_flush(cache);
|
|
||||||
for (i = 0; i < cache->numberOfPages; i++)
|
|
||||||
{
|
|
||||||
cache->cacheEntries[i].sector = CACHE_FREE;
|
|
||||||
cache->cacheEntries[i].last_access = 0;
|
|
||||||
cache->cacheEntries[i].count = 0;
|
|
||||||
cache->cacheEntries[i].dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
cache.h
|
|
||||||
The cache is not visible to the user. It should be flushed
|
|
||||||
when any file is closed or changes are made to the filesystem.
|
|
||||||
|
|
||||||
This cache implements a least-used-page replacement policy. This will
|
|
||||||
distribute sectors evenly over the pages, so if less than the maximum
|
|
||||||
pages are used at once, they should all eventually remain in the cache.
|
|
||||||
This also has the benefit of throwing out old sectors, so as not to keep
|
|
||||||
too many stale pages around.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __CACHE_H
|
|
||||||
#define __CACHE_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "disc_fat.h"
|
|
||||||
|
|
||||||
#define PAGE_SECTORS 64
|
|
||||||
#define CACHE_PAGE_SIZE (BYTES_PER_READ * PAGE_SECTORS)
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
sec_t sector;
|
|
||||||
unsigned int count;
|
|
||||||
unsigned int last_access;
|
|
||||||
bool dirty;
|
|
||||||
uint8_t* cache;
|
|
||||||
} CACHE_ENTRY;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
const DISC_INTERFACE* disc;
|
|
||||||
sec_t endOfPartition;
|
|
||||||
unsigned int numberOfPages;
|
|
||||||
unsigned int sectorsPerPage;
|
|
||||||
CACHE_ENTRY* cacheEntries;
|
|
||||||
} CACHE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read data from a sector in the cache
|
|
||||||
If the sector is not in the cache, it will be swapped in
|
|
||||||
offset is the position to start reading from
|
|
||||||
size is the amount of data to read
|
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_readPartialSector(CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size);
|
|
||||||
|
|
||||||
bool _FAT_cache_readLittleEndianValue(CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write data to a sector in the cache
|
|
||||||
If the sector is not in the cache, it will be swapped in.
|
|
||||||
When the sector is swapped out, the data will be written to the disc
|
|
||||||
offset is the position to start writing to
|
|
||||||
size is the amount of data to write
|
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_writePartialSector(CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
|
||||||
|
|
||||||
bool _FAT_cache_writeLittleEndianValue(CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset,
|
|
||||||
int num_bytes);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write data to a sector in the cache, zeroing the sector first
|
|
||||||
If the sector is not in the cache, it will be swapped in.
|
|
||||||
When the sector is swapped out, the data will be written to the disc
|
|
||||||
offset is the position to start writing to
|
|
||||||
size is the amount of data to write
|
|
||||||
Precondition: offset + size <= BYTES_PER_READ
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_eraseWritePartialSector(CACHE* cache, const void* buffer, sec_t sector, unsigned int offset,
|
|
||||||
size_t size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read several sectors from the cache
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_readSectors(CACHE* cache, sec_t sector, sec_t numSectors, void* buffer);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read a full sector from the cache
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_cache_readSector(CACHE* cache, void* buffer, sec_t sector)
|
|
||||||
{
|
|
||||||
return _FAT_cache_readPartialSector(cache, buffer, sector, 0, BYTES_PER_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write a full sector to the cache
|
|
||||||
*/
|
|
||||||
static inline bool _FAT_cache_writeSector(CACHE* cache, const void* buffer, sec_t sector)
|
|
||||||
{
|
|
||||||
return _FAT_cache_writePartialSector(cache, buffer, sector, 0, BYTES_PER_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _FAT_cache_writeSectors(CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Write any dirty sectors back to disc and clear out the contents of the cache
|
|
||||||
*/
|
|
||||||
bool _FAT_cache_flush(CACHE* cache);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Clear out the contents of the cache without writing any dirty sectors first
|
|
||||||
*/
|
|
||||||
void _FAT_cache_invalidate(CACHE* cache);
|
|
||||||
|
|
||||||
CACHE* _FAT_cache_constructor(unsigned int numberOfPages, unsigned int sectorsPerPage,
|
|
||||||
const DISC_INTERFACE* discInterface, sec_t endOfPartition);
|
|
||||||
|
|
||||||
void _FAT_cache_destructor(CACHE* cache);
|
|
||||||
|
|
||||||
#endif // _CACHE_H
|
|
@ -1,676 +0,0 @@
|
|||||||
/*
|
|
||||||
fatdir.c
|
|
||||||
|
|
||||||
Functions used by the newlib disc stubs to interface with
|
|
||||||
this library
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/dir.h>
|
|
||||||
|
|
||||||
#include "fatdir.h"
|
|
||||||
|
|
||||||
#include "fat_cache.h"
|
|
||||||
#include "file_allocation_table.h"
|
|
||||||
#include "partition.h"
|
|
||||||
#include "directory.h"
|
|
||||||
#include "bit_ops.h"
|
|
||||||
#include "filetime.h"
|
|
||||||
#include "lock.h"
|
|
||||||
|
|
||||||
int _FAT_stat_r(struct _reent *r, const char *path, struct stat *st)
|
|
||||||
{
|
|
||||||
PARTITION* partition = NULL;
|
|
||||||
DIR_ENTRY dirEntry;
|
|
||||||
|
|
||||||
// Get the partition this file is on
|
|
||||||
partition = _FAT_partition_getPartitionFromPath(path);
|
|
||||||
if (partition == NULL)
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the path pointer to the start of the actual path
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
path = strchr(path, ':') + 1;
|
|
||||||
}
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_lock(&partition->lock);
|
|
||||||
|
|
||||||
// Search for the file on the disc
|
|
||||||
if (!_FAT_directory_entryFromPath(partition, &dirEntry, path, NULL))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill in the stat struct
|
|
||||||
_FAT_directory_entryStat(partition, &dirEntry, st);
|
|
||||||
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_link_r(struct _reent *r, const char *existing, const char *newLink)
|
|
||||||
{
|
|
||||||
r->_errno = ENOTSUP;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_unlink_r(struct _reent *r, const char *path)
|
|
||||||
{
|
|
||||||
PARTITION* partition = NULL;
|
|
||||||
DIR_ENTRY dirEntry;
|
|
||||||
DIR_ENTRY dirContents;
|
|
||||||
uint32_t cluster;
|
|
||||||
bool nextEntry;
|
|
||||||
bool errorOccured = false;
|
|
||||||
|
|
||||||
// Get the partition this directory is on
|
|
||||||
partition = _FAT_partition_getPartitionFromPath(path);
|
|
||||||
if (partition == NULL)
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we aren't trying to write to a read-only disc
|
|
||||||
if (partition->readOnly)
|
|
||||||
{
|
|
||||||
r->_errno = EROFS;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the path pointer to the start of the actual path
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
path = strchr(path, ':') + 1;
|
|
||||||
}
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_lock(&partition->lock);
|
|
||||||
|
|
||||||
// Search for the file on the disc
|
|
||||||
if (!_FAT_directory_entryFromPath(partition, &dirEntry, path, NULL))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cluster = _FAT_directory_entryGetCluster(partition, dirEntry.entryData);
|
|
||||||
|
|
||||||
// If this is a directory, make sure it is empty
|
|
||||||
if (_FAT_directory_isDirectory(&dirEntry))
|
|
||||||
{
|
|
||||||
nextEntry = _FAT_directory_getFirstEntry(partition, &dirContents, cluster);
|
|
||||||
|
|
||||||
while (nextEntry)
|
|
||||||
{
|
|
||||||
if (!_FAT_directory_isDot(&dirContents))
|
|
||||||
{
|
|
||||||
// The directory had something in it that isn't a reference to itself or it's parent
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EPERM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
nextEntry = _FAT_directory_getNextEntry(partition, &dirContents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_FAT_fat_isValidCluster(partition, cluster))
|
|
||||||
{
|
|
||||||
// Remove the cluster chain for this file
|
|
||||||
if (!_FAT_fat_clearLinks(partition, cluster))
|
|
||||||
{
|
|
||||||
r->_errno = EIO;
|
|
||||||
errorOccured = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the directory entry for this file
|
|
||||||
if (!_FAT_directory_removeEntry(partition, &dirEntry))
|
|
||||||
{
|
|
||||||
r->_errno = EIO;
|
|
||||||
errorOccured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush any sectors in the disc cache
|
|
||||||
if (!_FAT_cache_flush(partition->cache))
|
|
||||||
{
|
|
||||||
r->_errno = EIO;
|
|
||||||
errorOccured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
if (errorOccured)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_chdir_r(struct _reent *r, const char *path)
|
|
||||||
{
|
|
||||||
PARTITION* partition = NULL;
|
|
||||||
|
|
||||||
// Get the partition this directory is on
|
|
||||||
partition = _FAT_partition_getPartitionFromPath(path);
|
|
||||||
if (partition == NULL)
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the path pointer to the start of the actual path
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
path = strchr(path, ':') + 1;
|
|
||||||
}
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_lock(&partition->lock);
|
|
||||||
|
|
||||||
// Try changing directory
|
|
||||||
if (_FAT_directory_chdir(partition, path))
|
|
||||||
{
|
|
||||||
// Successful
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Failed
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOTDIR;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_rename_r(struct _reent *r, const char *oldName, const char *newName)
|
|
||||||
{
|
|
||||||
PARTITION* partition = NULL;
|
|
||||||
DIR_ENTRY oldDirEntry;
|
|
||||||
DIR_ENTRY newDirEntry;
|
|
||||||
const char *pathEnd;
|
|
||||||
uint32_t dirCluster;
|
|
||||||
|
|
||||||
// Get the partition this directory is on
|
|
||||||
partition = _FAT_partition_getPartitionFromPath(oldName);
|
|
||||||
if (partition == NULL)
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_lock(&partition->lock);
|
|
||||||
|
|
||||||
// Make sure the same partition is used for the old and new names
|
|
||||||
if (partition != _FAT_partition_getPartitionFromPath(newName))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EXDEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we aren't trying to write to a read-only disc
|
|
||||||
if (partition->readOnly)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EROFS;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the path pointer to the start of the actual path
|
|
||||||
if (strchr(oldName, ':') != NULL)
|
|
||||||
{
|
|
||||||
oldName = strchr(oldName, ':') + 1;
|
|
||||||
}
|
|
||||||
if (strchr(oldName, ':') != NULL)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (strchr(newName, ':') != NULL)
|
|
||||||
{
|
|
||||||
newName = strchr(newName, ':') + 1;
|
|
||||||
}
|
|
||||||
if (strchr(newName, ':') != NULL)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for the file on the disc
|
|
||||||
if (!_FAT_directory_entryFromPath(partition, &oldDirEntry, oldName, NULL))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure there is no existing file / directory with the new name
|
|
||||||
if (_FAT_directory_entryFromPath(partition, &newDirEntry, newName, NULL))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EEXIST;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the new file entry
|
|
||||||
// Get the directory it has to go in
|
|
||||||
pathEnd = strrchr(newName, DIR_SEPARATOR);
|
|
||||||
if (pathEnd == NULL)
|
|
||||||
{
|
|
||||||
// No path was specified
|
|
||||||
dirCluster = partition->cwdCluster;
|
|
||||||
pathEnd = newName;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Path was specified -- get the right dirCluster
|
|
||||||
// Recycling newDirEntry, since it needs to be recreated anyway
|
|
||||||
if (!_FAT_directory_entryFromPath(partition, &newDirEntry, newName, pathEnd) || !_FAT_directory_isDirectory(
|
|
||||||
&newDirEntry))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOTDIR;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
dirCluster = _FAT_directory_entryGetCluster(partition, newDirEntry.entryData);
|
|
||||||
// Move the pathEnd past the last DIR_SEPARATOR
|
|
||||||
pathEnd += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the entry data
|
|
||||||
memcpy(&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY));
|
|
||||||
|
|
||||||
// Set the new name
|
|
||||||
strncpy(newDirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
|
|
||||||
|
|
||||||
// Write the new entry
|
|
||||||
if (!_FAT_directory_addEntry(partition, &newDirEntry, dirCluster))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOSPC;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the old entry
|
|
||||||
if (!_FAT_directory_removeEntry(partition, &oldDirEntry))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EIO;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush any sectors in the disc cache
|
|
||||||
if (!_FAT_cache_flush(partition->cache))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EIO;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_mkdir_r(struct _reent *r, const char *path, int mode)
|
|
||||||
{
|
|
||||||
PARTITION* partition = NULL;
|
|
||||||
bool fileExists;
|
|
||||||
DIR_ENTRY dirEntry;
|
|
||||||
const char* pathEnd;
|
|
||||||
uint32_t parentCluster, dirCluster;
|
|
||||||
uint8_t newEntryData[DIR_ENTRY_DATA_SIZE];
|
|
||||||
|
|
||||||
partition = _FAT_partition_getPartitionFromPath(path);
|
|
||||||
if (partition == NULL)
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the path pointer to the start of the actual path
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
path = strchr(path, ':') + 1;
|
|
||||||
}
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_lock(&partition->lock);
|
|
||||||
|
|
||||||
// Search for the file/directory on the disc
|
|
||||||
fileExists = _FAT_directory_entryFromPath(partition, &dirEntry, path, NULL);
|
|
||||||
|
|
||||||
// Make sure it doesn't exist
|
|
||||||
if (fileExists)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EEXIST;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partition->readOnly)
|
|
||||||
{
|
|
||||||
// We can't write to a read-only partition
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EROFS;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the directory it has to go in
|
|
||||||
pathEnd = strrchr(path, DIR_SEPARATOR);
|
|
||||||
if (pathEnd == NULL)
|
|
||||||
{
|
|
||||||
// No path was specified
|
|
||||||
parentCluster = partition->cwdCluster;
|
|
||||||
pathEnd = path;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Path was specified -- get the right parentCluster
|
|
||||||
// Recycling dirEntry, since it needs to be recreated anyway
|
|
||||||
if (!_FAT_directory_entryFromPath(partition, &dirEntry, path, pathEnd)
|
|
||||||
|| !_FAT_directory_isDirectory(&dirEntry))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOTDIR;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
parentCluster = _FAT_directory_entryGetCluster(partition, dirEntry.entryData);
|
|
||||||
// Move the pathEnd past the last DIR_SEPARATOR
|
|
||||||
pathEnd += 1;
|
|
||||||
}
|
|
||||||
// Create the entry data
|
|
||||||
strncpy(dirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
|
|
||||||
memset(dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE);
|
|
||||||
|
|
||||||
// Set the creation time and date
|
|
||||||
dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0;
|
|
||||||
u16_to_u8array(dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC());
|
|
||||||
u16_to_u8array(dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
|
|
||||||
u16_to_u8array(dirEntry.entryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC());
|
|
||||||
u16_to_u8array(dirEntry.entryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC());
|
|
||||||
u16_to_u8array(dirEntry.entryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
|
|
||||||
|
|
||||||
// Set the directory attribute
|
|
||||||
dirEntry.entryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
|
|
||||||
|
|
||||||
// Get a cluster for the new directory
|
|
||||||
dirCluster = _FAT_fat_linkFreeClusterCleared(partition, CLUSTER_FREE);
|
|
||||||
if (!_FAT_fat_isValidCluster(partition, dirCluster))
|
|
||||||
{
|
|
||||||
// No space left on disc for the cluster
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOSPC;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
u16_to_u8array(dirEntry.entryData, DIR_ENTRY_cluster, dirCluster);
|
|
||||||
u16_to_u8array(dirEntry.entryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
|
|
||||||
|
|
||||||
// Write the new directory's entry to it's parent
|
|
||||||
if (!_FAT_directory_addEntry(partition, &dirEntry, parentCluster))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = ENOSPC;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the dot entry within the directory
|
|
||||||
memset(newEntryData, 0, DIR_ENTRY_DATA_SIZE);
|
|
||||||
memset(newEntryData, ' ', 11);
|
|
||||||
newEntryData[DIR_ENTRY_name] = '.';
|
|
||||||
newEntryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
|
|
||||||
u16_to_u8array(newEntryData, DIR_ENTRY_cluster, dirCluster);
|
|
||||||
u16_to_u8array(newEntryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
|
|
||||||
|
|
||||||
// Write it to the directory, erasing that sector in the process
|
|
||||||
_FAT_cache_eraseWritePartialSector(partition->cache, newEntryData, _FAT_fat_clusterToSector(partition, dirCluster),
|
|
||||||
0, DIR_ENTRY_DATA_SIZE);
|
|
||||||
|
|
||||||
// Create the double dot entry within the directory
|
|
||||||
|
|
||||||
// if ParentDir == Rootdir then ".."" always link to Cluster 0
|
|
||||||
if (parentCluster == partition->rootDirCluster) parentCluster = FAT16_ROOT_DIR_CLUSTER;
|
|
||||||
|
|
||||||
newEntryData[DIR_ENTRY_name + 1] = '.';
|
|
||||||
u16_to_u8array(newEntryData, DIR_ENTRY_cluster, parentCluster);
|
|
||||||
u16_to_u8array(newEntryData, DIR_ENTRY_clusterHigh, parentCluster >> 16);
|
|
||||||
|
|
||||||
// Write it to the directory
|
|
||||||
_FAT_cache_writePartialSector(partition->cache, newEntryData, _FAT_fat_clusterToSector(partition, dirCluster),
|
|
||||||
DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
|
|
||||||
|
|
||||||
// Flush any sectors in the disc cache
|
|
||||||
if (!_FAT_cache_flush(partition->cache))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
r->_errno = EIO;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_statvfs_r(struct _reent *r, const char *path, struct statvfs *buf)
|
|
||||||
{
|
|
||||||
PARTITION* partition = NULL;
|
|
||||||
unsigned int freeClusterCount;
|
|
||||||
|
|
||||||
// Get the partition of the requested path
|
|
||||||
partition = _FAT_partition_getPartitionFromPath(path);
|
|
||||||
if (partition == NULL)
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_lock(&partition->lock);
|
|
||||||
|
|
||||||
freeClusterCount = _FAT_fat_freeClusterCount(partition);
|
|
||||||
|
|
||||||
// FAT clusters = POSIX blocks
|
|
||||||
buf->f_bsize = partition->bytesPerCluster; // File system block size.
|
|
||||||
buf->f_frsize = partition->bytesPerCluster; // Fundamental file system block size.
|
|
||||||
|
|
||||||
buf->f_blocks = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of blocks on file system in units of f_frsize.
|
|
||||||
buf->f_bfree = freeClusterCount; // Total number of free blocks.
|
|
||||||
buf->f_bavail = freeClusterCount; // Number of free blocks available to non-privileged process.
|
|
||||||
|
|
||||||
// Treat requests for info on inodes as clusters
|
|
||||||
buf->f_files = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of file serial numbers.
|
|
||||||
buf->f_ffree = freeClusterCount; // Total number of free file serial numbers.
|
|
||||||
buf->f_favail = freeClusterCount; // Number of file serial numbers available to non-privileged process.
|
|
||||||
|
|
||||||
// File system ID. 32bit ioType value
|
|
||||||
buf->f_fsid = _FAT_disc_hostType(partition->disc);
|
|
||||||
|
|
||||||
// Bit mask of f_flag values.
|
|
||||||
buf->f_flag = ST_NOSUID /* No support for ST_ISUID and ST_ISGID file mode bits */
|
|
||||||
| (partition->readOnly ? ST_RDONLY /* Read only file system */: 0);
|
|
||||||
// Maximum filename length.
|
|
||||||
buf->f_namemax = MAX_FILENAME_LENGTH;
|
|
||||||
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path)
|
|
||||||
{
|
|
||||||
DIR_ENTRY dirEntry;
|
|
||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
|
||||||
bool fileExists;
|
|
||||||
|
|
||||||
state->partition = _FAT_partition_getPartitionFromPath(path);
|
|
||||||
if (state->partition == NULL)
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the path pointer to the start of the actual path
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
path = strchr(path, ':') + 1;
|
|
||||||
}
|
|
||||||
if (strchr(path, ':') != NULL)
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_lock(&state->partition->lock);
|
|
||||||
|
|
||||||
// Get the start cluster of the directory
|
|
||||||
fileExists = _FAT_directory_entryFromPath(state->partition, &dirEntry, path, NULL);
|
|
||||||
|
|
||||||
if (!fileExists)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
r->_errno = ENOENT;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it is a directory
|
|
||||||
if (!_FAT_directory_isDirectory(&dirEntry))
|
|
||||||
{
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
r->_errno = ENOTDIR;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the start cluster for use when resetting the directory data
|
|
||||||
state->startCluster = _FAT_directory_entryGetCluster(state->partition, dirEntry.entryData);
|
|
||||||
|
|
||||||
// Get the first entry for use with a call to dirnext
|
|
||||||
state->validEntry = _FAT_directory_getFirstEntry(state->partition, &(state->currentEntry), state->startCluster);
|
|
||||||
|
|
||||||
// We are now using this entry
|
|
||||||
state->inUse = true;
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
return (DIR_ITER*) state;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_dirreset_r(struct _reent *r, DIR_ITER *dirState)
|
|
||||||
{
|
|
||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
|
||||||
|
|
||||||
_FAT_lock(&state->partition->lock);
|
|
||||||
|
|
||||||
// Make sure we are still using this entry
|
|
||||||
if (!state->inUse)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
r->_errno = EBADF;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first entry for use with a call to dirnext
|
|
||||||
state->validEntry = _FAT_directory_getFirstEntry(state->partition, &(state->currentEntry), state->startCluster);
|
|
||||||
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat)
|
|
||||||
{
|
|
||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
|
||||||
|
|
||||||
_FAT_lock(&state->partition->lock);
|
|
||||||
|
|
||||||
// Make sure we are still using this entry
|
|
||||||
if (!state->inUse)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
r->_errno = EBADF;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure there is another file to report on
|
|
||||||
if (!state->validEntry)
|
|
||||||
{
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
r->_errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the filename
|
|
||||||
strncpy(filename, state->currentEntry.filename, MAX_FILENAME_LENGTH);
|
|
||||||
// Get the stats, if requested
|
|
||||||
if (filestat != NULL)
|
|
||||||
{
|
|
||||||
_FAT_directory_entryStat(state->partition, &(state->currentEntry), filestat);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for the next entry for use next time
|
|
||||||
state->validEntry = _FAT_directory_getNextEntry(state->partition, &(state->currentEntry));
|
|
||||||
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _FAT_dirclose_r(struct _reent *r, DIR_ITER *dirState)
|
|
||||||
{
|
|
||||||
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
|
||||||
|
|
||||||
// We are no longer using this entry
|
|
||||||
_FAT_lock(&state->partition->lock);
|
|
||||||
state->inUse = false;
|
|
||||||
_FAT_unlock(&state->partition->lock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
fatdir.h
|
|
||||||
|
|
||||||
Functions used by the newlib disc stubs to interface with
|
|
||||||
this library
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __FATDIR_H
|
|
||||||
#define __FATDIR_H
|
|
||||||
|
|
||||||
#include <sys/reent.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/statvfs.h>
|
|
||||||
#include <sys/iosupport.h>
|
|
||||||
#include "common.h"
|
|
||||||
#include "directory.h"
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
PARTITION* partition;
|
|
||||||
DIR_ENTRY currentEntry;
|
|
||||||
uint32_t startCluster;
|
|
||||||
bool inUse;
|
|
||||||
bool validEntry;
|
|
||||||
} DIR_STATE_STRUCT;
|
|
||||||
|
|
||||||
extern int _FAT_stat_r(struct _reent *r, const char *path, struct stat *st);
|
|
||||||
|
|
||||||
extern int _FAT_link_r(struct _reent *r, const char *existing, const char *newLink);
|
|
||||||
|
|
||||||
extern int _FAT_unlink_r(struct _reent *r, const char *name);
|
|
||||||
|
|
||||||
extern int _FAT_chdir_r(struct _reent *r, const char *name);
|
|
||||||
|
|
||||||
extern int _FAT_rename_r(struct _reent *r, const char *oldName, const char *newName);
|
|
||||||
|
|
||||||
extern int _FAT_mkdir_r(struct _reent *r, const char *path, int mode);
|
|
||||||
|
|
||||||
extern int _FAT_statvfs_r(struct _reent *r, const char *path, struct statvfs *buf);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Directory iterator functions
|
|
||||||
*/
|
|
||||||
extern DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path);
|
|
||||||
extern int _FAT_dirreset_r(struct _reent *r, DIR_ITER *dirState);
|
|
||||||
extern int _FAT_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
|
||||||
extern int _FAT_dirclose_r(struct _reent *r, DIR_ITER *dirState);
|
|
||||||
|
|
||||||
#endif // _FATDIR_H
|
|
File diff suppressed because it is too large
Load Diff
@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
fatfile.h
|
|
||||||
|
|
||||||
Functions used by the newlib disc stubs to interface with
|
|
||||||
this library
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _FATFILE_H
|
|
||||||
#define _FATFILE_H
|
|
||||||
|
|
||||||
#include <sys/reent.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "partition.h"
|
|
||||||
#include "directory.h"
|
|
||||||
|
|
||||||
#define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
u32 cluster;
|
|
||||||
sec_t sector;
|
|
||||||
s32 byte;
|
|
||||||
} FILE_POSITION;
|
|
||||||
|
|
||||||
struct _FILE_STRUCT;
|
|
||||||
|
|
||||||
struct _FILE_STRUCT
|
|
||||||
{
|
|
||||||
uint32_t filesize;
|
|
||||||
uint32_t startCluster;
|
|
||||||
uint32_t currentPosition;
|
|
||||||
FILE_POSITION rwPosition;
|
|
||||||
FILE_POSITION appendPosition;
|
|
||||||
DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
|
||||||
DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry
|
|
||||||
PARTITION* partition;
|
|
||||||
struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files
|
|
||||||
struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files
|
|
||||||
bool read;
|
|
||||||
bool write;
|
|
||||||
bool append;
|
|
||||||
bool inUse;
|
|
||||||
bool modified;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _FILE_STRUCT FILE_STRUCT;
|
|
||||||
|
|
||||||
extern int _FAT_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
|
||||||
|
|
||||||
extern int _FAT_close_r(struct _reent *r, int fd);
|
|
||||||
|
|
||||||
extern ssize_t _FAT_write_r(struct _reent *r, int fd, const char *ptr, size_t len);
|
|
||||||
|
|
||||||
extern ssize_t _FAT_read_r(struct _reent *r, int fd, char *ptr, size_t len);
|
|
||||||
|
|
||||||
extern off_t _FAT_seek_r(struct _reent *r, int fd, off_t pos, int dir);
|
|
||||||
|
|
||||||
extern int _FAT_fstat_r(struct _reent *r, int fd, struct stat *st);
|
|
||||||
|
|
||||||
extern int _FAT_stat_r(struct _reent *r, const char *path, struct stat *st);
|
|
||||||
|
|
||||||
extern int _FAT_link_r(struct _reent *r, const char *existing, const char *newLink);
|
|
||||||
|
|
||||||
extern int _FAT_unlink_r(struct _reent *r, const char *name);
|
|
||||||
|
|
||||||
extern int _FAT_chdir_r(struct _reent *r, const char *name);
|
|
||||||
|
|
||||||
extern int _FAT_rename_r(struct _reent *r, const char *oldName, const char *newName);
|
|
||||||
|
|
||||||
extern int _FAT_ftruncate_r(struct _reent *r, int fd, off_t len);
|
|
||||||
|
|
||||||
extern int _FAT_fsync_r(struct _reent *r, int fd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Synchronizes the file data to disc.
|
|
||||||
Does no locking of its own -- lock the partition before calling.
|
|
||||||
Returns 0 on success, an error code on failure.
|
|
||||||
*/
|
|
||||||
extern int _FAT_syncToDisc(FILE_STRUCT* file);
|
|
||||||
|
|
||||||
#endif // _FATFILE_H
|
|
@ -1,417 +0,0 @@
|
|||||||
/*
|
|
||||||
file_allocation_table.c
|
|
||||||
Reading, writing and manipulation of the FAT structure on
|
|
||||||
a FAT partition
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "file_allocation_table.h"
|
|
||||||
#include "partition.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
Gets the cluster linked from input cluster
|
|
||||||
*/
|
|
||||||
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster)
|
|
||||||
{
|
|
||||||
uint32_t nextCluster = CLUSTER_FREE;
|
|
||||||
sec_t sector;
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
if (cluster == CLUSTER_FREE)
|
|
||||||
{
|
|
||||||
return CLUSTER_FREE;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (partition->filesysType)
|
|
||||||
{
|
|
||||||
case FS_UNKNOWN:
|
|
||||||
return CLUSTER_ERROR;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FS_FAT12:
|
|
||||||
{
|
|
||||||
u32 nextCluster_h;
|
|
||||||
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
|
|
||||||
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
|
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue(partition->cache, &nextCluster, sector, offset, sizeof(u8));
|
|
||||||
|
|
||||||
offset++;
|
|
||||||
|
|
||||||
if (offset >= BYTES_PER_READ)
|
|
||||||
{
|
|
||||||
offset = 0;
|
|
||||||
sector++;
|
|
||||||
}
|
|
||||||
nextCluster_h = 0;
|
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue(partition->cache, &nextCluster_h, sector, offset, sizeof(u8));
|
|
||||||
nextCluster |= (nextCluster_h << 8);
|
|
||||||
|
|
||||||
if (cluster & 0x01)
|
|
||||||
{
|
|
||||||
nextCluster = nextCluster >> 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nextCluster &= 0x0FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextCluster >= 0x0FF7)
|
|
||||||
{
|
|
||||||
nextCluster = CLUSTER_EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FS_FAT16:
|
|
||||||
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
|
|
||||||
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
|
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue(partition->cache, &nextCluster, sector, offset, sizeof(u16));
|
|
||||||
|
|
||||||
if (nextCluster >= 0xFFF7)
|
|
||||||
{
|
|
||||||
nextCluster = CLUSTER_EOF;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FS_FAT32:
|
|
||||||
sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
|
|
||||||
offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
|
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue(partition->cache, &nextCluster, sector, offset, sizeof(u32));
|
|
||||||
|
|
||||||
if (nextCluster >= 0x0FFFFFF7)
|
|
||||||
{
|
|
||||||
nextCluster = CLUSTER_EOF;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return CLUSTER_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nextCluster;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
writes value into the correct offset within a partition's FAT, based
|
|
||||||
on the cluster number.
|
|
||||||
*/
|
|
||||||
static bool _FAT_fat_writeFatEntry(PARTITION* partition, uint32_t cluster, uint32_t value)
|
|
||||||
{
|
|
||||||
sec_t sector;
|
|
||||||
int offset;
|
|
||||||
uint32_t oldValue;
|
|
||||||
|
|
||||||
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (partition->filesysType)
|
|
||||||
{
|
|
||||||
case FS_UNKNOWN:
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FS_FAT12:
|
|
||||||
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
|
|
||||||
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
|
|
||||||
|
|
||||||
if (cluster & 0x01)
|
|
||||||
{
|
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue(partition->cache, &oldValue, sector, offset, sizeof(u8));
|
|
||||||
|
|
||||||
value = (value << 4) | (oldValue & 0x0F);
|
|
||||||
|
|
||||||
_FAT_cache_writeLittleEndianValue(partition->cache, value & 0xFF, sector, offset, sizeof(u8));
|
|
||||||
|
|
||||||
offset++;
|
|
||||||
if (offset >= BYTES_PER_READ)
|
|
||||||
{
|
|
||||||
offset = 0;
|
|
||||||
sector++;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_cache_writeLittleEndianValue(partition->cache, (value >> 8) & 0xFF, sector, offset, sizeof(u8));
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
_FAT_cache_writeLittleEndianValue(partition->cache, value, sector, offset, sizeof(u8));
|
|
||||||
|
|
||||||
offset++;
|
|
||||||
if (offset >= BYTES_PER_READ)
|
|
||||||
{
|
|
||||||
offset = 0;
|
|
||||||
sector++;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_cache_readLittleEndianValue(partition->cache, &oldValue, sector, offset, sizeof(u8));
|
|
||||||
|
|
||||||
value = ((value >> 8) & 0x0F) | (oldValue & 0xF0);
|
|
||||||
|
|
||||||
_FAT_cache_writeLittleEndianValue(partition->cache, value, sector, offset, sizeof(u8));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FS_FAT16:
|
|
||||||
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
|
|
||||||
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
|
|
||||||
|
|
||||||
_FAT_cache_writeLittleEndianValue(partition->cache, value, sector, offset, sizeof(u16));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FS_FAT32:
|
|
||||||
sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
|
|
||||||
offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
|
|
||||||
|
|
||||||
_FAT_cache_writeLittleEndianValue(partition->cache, value, sector, offset, sizeof(u32));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
gets the first available free cluster, sets it
|
|
||||||
to end of file, links the input cluster to it then returns the
|
|
||||||
cluster number
|
|
||||||
If an error occurs, return CLUSTER_ERROR
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster)
|
|
||||||
{
|
|
||||||
uint32_t firstFree;
|
|
||||||
uint32_t curLink;
|
|
||||||
uint32_t lastCluster;
|
|
||||||
bool loopedAroundFAT = false;
|
|
||||||
|
|
||||||
lastCluster = partition->fat.lastCluster;
|
|
||||||
|
|
||||||
if (cluster > lastCluster)
|
|
||||||
{
|
|
||||||
return CLUSTER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the cluster already has a link, and return it if so
|
|
||||||
curLink = _FAT_fat_nextCluster(partition, cluster);
|
|
||||||
if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster))
|
|
||||||
{
|
|
||||||
return curLink; // Return the current link - don't allocate a new one
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a free cluster
|
|
||||||
firstFree = partition->fat.firstFree;
|
|
||||||
// Start at first valid cluster
|
|
||||||
if (firstFree < CLUSTER_FIRST)
|
|
||||||
{
|
|
||||||
firstFree = CLUSTER_FIRST;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search until a free cluster is found
|
|
||||||
while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE)
|
|
||||||
{
|
|
||||||
firstFree++;
|
|
||||||
if (firstFree > lastCluster)
|
|
||||||
{
|
|
||||||
if (loopedAroundFAT)
|
|
||||||
{
|
|
||||||
// If couldn't get a free cluster then return an error
|
|
||||||
partition->fat.firstFree = firstFree;
|
|
||||||
return CLUSTER_ERROR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Try looping back to the beginning of the FAT
|
|
||||||
// This was suggested by loopy
|
|
||||||
firstFree = CLUSTER_FIRST;
|
|
||||||
loopedAroundFAT = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
partition->fat.firstFree = firstFree;
|
|
||||||
|
|
||||||
if ((cluster >= CLUSTER_FIRST) && (cluster < lastCluster))
|
|
||||||
{
|
|
||||||
// Update the linked from FAT entry
|
|
||||||
_FAT_fat_writeFatEntry(partition, cluster, firstFree);
|
|
||||||
}
|
|
||||||
// Create the linked to FAT entry
|
|
||||||
_FAT_fat_writeFatEntry(partition, firstFree, CLUSTER_EOF);
|
|
||||||
|
|
||||||
return firstFree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
gets the first available free cluster, sets it
|
|
||||||
to end of file, links the input cluster to it, clears the new
|
|
||||||
cluster to 0 valued bytes, then returns the cluster number
|
|
||||||
If an error occurs, return CLUSTER_ERROR
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
uint32_t _FAT_fat_linkFreeClusterCleared(PARTITION* partition, uint32_t cluster)
|
|
||||||
{
|
|
||||||
uint32_t newCluster;
|
|
||||||
uint32_t i;
|
|
||||||
uint8_t emptySector[BYTES_PER_READ];
|
|
||||||
|
|
||||||
// Link the cluster
|
|
||||||
newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
|
|
||||||
|
|
||||||
if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR)
|
|
||||||
{
|
|
||||||
return CLUSTER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear all the sectors within the cluster
|
|
||||||
memset(emptySector, 0, BYTES_PER_READ);
|
|
||||||
for (i = 0; i < partition->sectorsPerCluster; i++)
|
|
||||||
{
|
|
||||||
_FAT_cache_writeSectors(partition->cache, _FAT_fat_clusterToSector(partition, newCluster) + i, 1, emptySector);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCluster;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FAT_fat_clearLinks
|
|
||||||
frees any cluster used by a file
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
bool _FAT_fat_clearLinks(PARTITION* partition, uint32_t cluster)
|
|
||||||
{
|
|
||||||
uint32_t nextCluster;
|
|
||||||
|
|
||||||
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */)) return false;
|
|
||||||
|
|
||||||
// If this clears up more space in the FAT before the current free pointer, move it backwards
|
|
||||||
if (cluster < partition->fat.firstFree)
|
|
||||||
{
|
|
||||||
partition->fat.firstFree = cluster;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE) && (cluster != CLUSTER_ERROR))
|
|
||||||
{
|
|
||||||
// Store next cluster before erasing the link
|
|
||||||
nextCluster = _FAT_fat_nextCluster(partition, cluster);
|
|
||||||
|
|
||||||
// Erase the link
|
|
||||||
_FAT_fat_writeFatEntry(partition, cluster, CLUSTER_FREE);
|
|
||||||
|
|
||||||
// Move onto next cluster
|
|
||||||
cluster = nextCluster;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FAT_fat_trimChain
|
|
||||||
Drop all clusters past the chainLength.
|
|
||||||
If chainLength is 0, all clusters are dropped.
|
|
||||||
If chainLength is 1, the first cluster is kept and the rest are
|
|
||||||
dropped, and so on.
|
|
||||||
Return the last cluster left in the chain.
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
uint32_t _FAT_fat_trimChain(PARTITION* partition, uint32_t startCluster, unsigned int chainLength)
|
|
||||||
{
|
|
||||||
uint32_t nextCluster;
|
|
||||||
|
|
||||||
if (chainLength == 0)
|
|
||||||
{
|
|
||||||
// Drop the entire chain
|
|
||||||
_FAT_fat_clearLinks(partition, startCluster);
|
|
||||||
return CLUSTER_FREE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Find the last cluster in the chain, and the one after it
|
|
||||||
chainLength--;
|
|
||||||
nextCluster = _FAT_fat_nextCluster(partition, startCluster);
|
|
||||||
while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF))
|
|
||||||
{
|
|
||||||
chainLength--;
|
|
||||||
startCluster = nextCluster;
|
|
||||||
nextCluster = _FAT_fat_nextCluster(partition, startCluster);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop all clusters after the last in the chain
|
|
||||||
if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF)
|
|
||||||
{
|
|
||||||
_FAT_fat_clearLinks(partition, nextCluster);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the last cluster in the chain as the end of the file
|
|
||||||
_FAT_fat_writeFatEntry(partition, startCluster, CLUSTER_EOF);
|
|
||||||
|
|
||||||
return startCluster;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FAT_fat_lastCluster
|
|
||||||
Trace the cluster links until the last one is found
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
uint32_t _FAT_fat_lastCluster(PARTITION* partition, uint32_t cluster)
|
|
||||||
{
|
|
||||||
while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster)
|
|
||||||
!= CLUSTER_EOF))
|
|
||||||
{
|
|
||||||
cluster = _FAT_fat_nextCluster(partition, cluster);
|
|
||||||
}
|
|
||||||
return cluster;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
_FAT_fat_freeClusterCount
|
|
||||||
Return the number of free clusters available
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
unsigned int _FAT_fat_freeClusterCount(PARTITION* partition)
|
|
||||||
{
|
|
||||||
unsigned int count = 0;
|
|
||||||
uint32_t curCluster;
|
|
||||||
|
|
||||||
for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++)
|
|
||||||
{
|
|
||||||
if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
file_allocation_table.h
|
|
||||||
Reading, writing and manipulation of the FAT structure on
|
|
||||||
a FAT partition
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __FAT_H
|
|
||||||
#define __FAT_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "partition.h"
|
|
||||||
|
|
||||||
#define CLUSTER_EOF_16 0xFFFF
|
|
||||||
#define CLUSTER_EOF 0x0FFFFFFF
|
|
||||||
#define CLUSTER_FREE 0x00000000
|
|
||||||
#define CLUSTER_ROOT 0x00000000
|
|
||||||
#define CLUSTER_FIRST 0x00000002
|
|
||||||
#define CLUSTER_ERROR 0xFFFFFFFF
|
|
||||||
|
|
||||||
#define CLUSTERS_PER_FAT12 4085
|
|
||||||
#define CLUSTERS_PER_FAT16 65525
|
|
||||||
|
|
||||||
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster);
|
|
||||||
|
|
||||||
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster);
|
|
||||||
uint32_t _FAT_fat_linkFreeClusterCleared(PARTITION* partition, uint32_t cluster);
|
|
||||||
|
|
||||||
bool _FAT_fat_clearLinks(PARTITION* partition, uint32_t cluster);
|
|
||||||
|
|
||||||
uint32_t _FAT_fat_trimChain(PARTITION* partition, uint32_t startCluster, unsigned int chainLength);
|
|
||||||
|
|
||||||
uint32_t _FAT_fat_lastCluster(PARTITION* partition, uint32_t cluster);
|
|
||||||
|
|
||||||
unsigned int _FAT_fat_freeClusterCount(PARTITION* partition);
|
|
||||||
|
|
||||||
static inline sec_t _FAT_fat_clusterToSector(PARTITION* partition, uint32_t cluster)
|
|
||||||
{
|
|
||||||
return (cluster >= CLUSTER_FIRST) ? ((cluster - CLUSTER_FIRST) * (sec_t) partition->sectorsPerCluster)
|
|
||||||
+ partition->dataStart : partition->rootDirStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _FAT_fat_isValidCluster(PARTITION* partition, uint32_t cluster)
|
|
||||||
{
|
|
||||||
return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _FAT_H
|
|
@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
filetime.c
|
|
||||||
Conversion of file time and date values to various other types
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
#include "filetime.h"
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#define MAX_HOUR 23
|
|
||||||
#define MAX_MINUTE 59
|
|
||||||
#define MAX_SECOND 59
|
|
||||||
|
|
||||||
#define MAX_MONTH 11
|
|
||||||
#define MIN_MONTH 0
|
|
||||||
#define MAX_DAY 31
|
|
||||||
#define MIN_DAY 1
|
|
||||||
|
|
||||||
uint16_t _FAT_filetime_getTimeFromRTC(void)
|
|
||||||
{
|
|
||||||
#ifdef USE_RTC_TIME
|
|
||||||
struct tm timeParts;
|
|
||||||
time_t epochTime;
|
|
||||||
|
|
||||||
if (time(&epochTime) == (time_t) -1)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
localtime_r(&epochTime, &timeParts);
|
|
||||||
|
|
||||||
// Check that the values are all in range.
|
|
||||||
// If they are not, return 0 (no timestamp)
|
|
||||||
if ((timeParts.tm_hour < 0) || (timeParts.tm_hour > MAX_HOUR)) return 0;
|
|
||||||
if ((timeParts.tm_min < 0) || (timeParts.tm_min > MAX_MINUTE)) return 0;
|
|
||||||
if ((timeParts.tm_sec < 0) || (timeParts.tm_sec > MAX_SECOND)) return 0;
|
|
||||||
|
|
||||||
return (((timeParts.tm_hour & 0x1F) << 11) | ((timeParts.tm_min & 0x3F) << 5) | ((timeParts.tm_sec >> 1) & 0x1F));
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t _FAT_filetime_getDateFromRTC(void)
|
|
||||||
{
|
|
||||||
#ifdef USE_RTC_TIME
|
|
||||||
struct tm timeParts;
|
|
||||||
time_t epochTime;
|
|
||||||
|
|
||||||
if (time(&epochTime) == (time_t) -1)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
localtime_r(&epochTime, &timeParts);
|
|
||||||
|
|
||||||
if ((timeParts.tm_mon < MIN_MONTH) || (timeParts.tm_mon > MAX_MONTH)) return 0;
|
|
||||||
if ((timeParts.tm_mday < MIN_DAY) || (timeParts.tm_mday > MAX_DAY)) return 0;
|
|
||||||
|
|
||||||
return ((((timeParts.tm_year - 80) & 0x7F) << 9) | // Adjust for MS-FAT base year (1980 vs 1900 for tm_year)
|
|
||||||
(((timeParts.tm_mon + 1) & 0xF) << 5) | (timeParts.tm_mday & 0x1F));
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t _FAT_filetime_to_time_t(uint16_t t, uint16_t d)
|
|
||||||
{
|
|
||||||
struct tm timeParts;
|
|
||||||
|
|
||||||
timeParts.tm_hour = t >> 11;
|
|
||||||
timeParts.tm_min = (t >> 5) & 0x3F;
|
|
||||||
timeParts.tm_sec = (t & 0x1F) << 1;
|
|
||||||
|
|
||||||
timeParts.tm_mday = d & 0x1F;
|
|
||||||
timeParts.tm_mon = ((d >> 5) & 0x0F) - 1;
|
|
||||||
timeParts.tm_year = (d >> 9) + 80;
|
|
||||||
|
|
||||||
timeParts.tm_isdst = 0;
|
|
||||||
|
|
||||||
return mktime(&timeParts);
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
/*
|
|
||||||
libfat.c
|
|
||||||
Simple functionality for startup, mounting and unmounting of FAT-based devices.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/iosupport.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "partition.h"
|
|
||||||
#include "fatfile.h"
|
|
||||||
#include "fatdir.h"
|
|
||||||
#include "lock.h"
|
|
||||||
#include "mem_allocate.h"
|
|
||||||
#include "disc_fat.h"
|
|
||||||
|
|
||||||
static const devoptab_t dotab_fat = { "fat", sizeof(FILE_STRUCT), _FAT_open_r, _FAT_close_r, _FAT_write_r, _FAT_read_r,
|
|
||||||
_FAT_seek_r, _FAT_fstat_r, _FAT_stat_r, _FAT_link_r, _FAT_unlink_r, _FAT_chdir_r, _FAT_rename_r, _FAT_mkdir_r,
|
|
||||||
sizeof(DIR_STATE_STRUCT), _FAT_diropen_r, _FAT_dirreset_r, _FAT_dirnext_r, _FAT_dirclose_r, _FAT_statvfs_r,
|
|
||||||
_FAT_ftruncate_r, _FAT_fsync_r, NULL /* Device data */
|
|
||||||
};
|
|
||||||
|
|
||||||
bool fatMount(const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize,
|
|
||||||
uint32_t SectorsPerPage)
|
|
||||||
{
|
|
||||||
PARTITION* partition;
|
|
||||||
devoptab_t* devops;
|
|
||||||
char* nameCopy;
|
|
||||||
|
|
||||||
if (!interface->startup()) return false;
|
|
||||||
|
|
||||||
if (!interface->isInserted())
|
|
||||||
{
|
|
||||||
interface->shutdown();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
devops = _FAT_mem_allocate(sizeof(devoptab_t) + strlen(name) + 1);
|
|
||||||
if (!devops)
|
|
||||||
{
|
|
||||||
interface->shutdown();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Use the space allocated at the end of the devoptab struct for storing the name
|
|
||||||
nameCopy = (char*) (devops + 1);
|
|
||||||
|
|
||||||
// Initialize the file system
|
|
||||||
partition = _FAT_partition_constructor(interface, cacheSize, SectorsPerPage, startSector);
|
|
||||||
if (!partition)
|
|
||||||
{
|
|
||||||
_FAT_mem_free(devops);
|
|
||||||
interface->shutdown();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an entry for this device to the devoptab table
|
|
||||||
memcpy(devops, &dotab_fat, sizeof(dotab_fat));
|
|
||||||
strcpy(nameCopy, name);
|
|
||||||
devops->name = nameCopy;
|
|
||||||
devops->deviceData = partition;
|
|
||||||
|
|
||||||
AddDevice(devops);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fatMountSimple(const char* name, const DISC_INTERFACE* interface)
|
|
||||||
{
|
|
||||||
return fatMount(name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fatUnmount(const char* name)
|
|
||||||
{
|
|
||||||
devoptab_t *devops;
|
|
||||||
PARTITION* partition;
|
|
||||||
const DISC_INTERFACE *disc;
|
|
||||||
|
|
||||||
devops = (devoptab_t*) GetDeviceOpTab(name);
|
|
||||||
if (!devops)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform a quick check to make sure we're dealing with a libfat controlled device
|
|
||||||
if (devops->open_r != dotab_fat.open_r)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RemoveDevice(name) == -1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
partition = (PARTITION*) devops->deviceData;
|
|
||||||
disc = partition->disc;
|
|
||||||
_FAT_partition_destructor(partition);
|
|
||||||
_FAT_mem_free(devops);
|
|
||||||
disc->shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fatInit(uint32_t cacheSize, bool setAsDefaultDevice)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int defaultDevice = -1;
|
|
||||||
const DISC_INTERFACE *disc;
|
|
||||||
|
|
||||||
for (i = 0; _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; i++)
|
|
||||||
{
|
|
||||||
disc = _FAT_disc_interfaces[i].getInterface();
|
|
||||||
if (fatMount(_FAT_disc_interfaces[i].name, disc, 0, cacheSize, DEFAULT_SECTORS_PAGE))
|
|
||||||
{
|
|
||||||
// The first device to successfully mount is set as the default
|
|
||||||
if (defaultDevice < 0)
|
|
||||||
{
|
|
||||||
defaultDevice = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultDevice < 0)
|
|
||||||
{
|
|
||||||
// None of our devices mounted
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setAsDefaultDevice)
|
|
||||||
{
|
|
||||||
char filePath[MAXPATHLEN * 2];
|
|
||||||
strcpy(filePath, _FAT_disc_interfaces[defaultDevice].name);
|
|
||||||
strcat(filePath, ":/");
|
|
||||||
#ifdef ARGV_MAGIC
|
|
||||||
if (__system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr(__system_argv->argv[0], '/')
|
|
||||||
!= NULL)
|
|
||||||
{
|
|
||||||
// Check the app's path against each of our mounted devices, to see
|
|
||||||
// if we can support it. If so, change to that path.
|
|
||||||
for (i = 0; _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; i++)
|
|
||||||
{
|
|
||||||
if (!strncasecmp(__system_argv->argv[0], _FAT_disc_interfaces[i].name, strlen(
|
|
||||||
_FAT_disc_interfaces[i].name)))
|
|
||||||
{
|
|
||||||
char *lastSlash;
|
|
||||||
strcpy(filePath, __system_argv->argv[0]);
|
|
||||||
lastSlash = strrchr(filePath, '/');
|
|
||||||
|
|
||||||
if (NULL != lastSlash)
|
|
||||||
{
|
|
||||||
if (*(lastSlash - 1) == ':') lastSlash++;
|
|
||||||
*lastSlash = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
chdir(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fatInitDefault(void)
|
|
||||||
{
|
|
||||||
return fatInit(DEFAULT_CACHE_PAGES, true);
|
|
||||||
}
|
|
||||||
|
|
@ -1,352 +0,0 @@
|
|||||||
/*
|
|
||||||
partition.c
|
|
||||||
Functions for mounting and dismounting partitions
|
|
||||||
on various block devices.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "partition.h"
|
|
||||||
#include "bit_ops.h"
|
|
||||||
#include "file_allocation_table.h"
|
|
||||||
#include "directory.h"
|
|
||||||
#include "mem_allocate.h"
|
|
||||||
#include "fatfile.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
sec_t _FAT_startSector;
|
|
||||||
|
|
||||||
/*
|
|
||||||
This device name, as known by devkitPro toolchains
|
|
||||||
*/
|
|
||||||
const char* DEVICE_NAME = "fat";
|
|
||||||
|
|
||||||
/*
|
|
||||||
Data offsets
|
|
||||||
*/
|
|
||||||
|
|
||||||
// BIOS Parameter Block offsets
|
|
||||||
enum BPB
|
|
||||||
{
|
|
||||||
BPB_jmpBoot = 0x00, BPB_OEMName = 0x03,
|
|
||||||
// BIOS Parameter Block
|
|
||||||
BPB_bytesPerSector = 0x0B,
|
|
||||||
BPB_sectorsPerCluster = 0x0D,
|
|
||||||
BPB_reservedSectors = 0x0E,
|
|
||||||
BPB_numFATs = 0x10,
|
|
||||||
BPB_rootEntries = 0x11,
|
|
||||||
BPB_numSectorsSmall = 0x13,
|
|
||||||
BPB_mediaDesc = 0x15,
|
|
||||||
BPB_sectorsPerFAT = 0x16,
|
|
||||||
BPB_sectorsPerTrk = 0x18,
|
|
||||||
BPB_numHeads = 0x1A,
|
|
||||||
BPB_numHiddenSectors = 0x1C,
|
|
||||||
BPB_numSectors = 0x20,
|
|
||||||
// Ext BIOS Parameter Block for FAT16
|
|
||||||
BPB_FAT16_driveNumber = 0x24,
|
|
||||||
BPB_FAT16_reserved1 = 0x25,
|
|
||||||
BPB_FAT16_extBootSig = 0x26,
|
|
||||||
BPB_FAT16_volumeID = 0x27,
|
|
||||||
BPB_FAT16_volumeLabel = 0x2B,
|
|
||||||
BPB_FAT16_fileSysType = 0x36,
|
|
||||||
// Bootcode
|
|
||||||
BPB_FAT16_bootCode = 0x3E,
|
|
||||||
// FAT32 extended block
|
|
||||||
BPB_FAT32_sectorsPerFAT32 = 0x24,
|
|
||||||
BPB_FAT32_extFlags = 0x28,
|
|
||||||
BPB_FAT32_fsVer = 0x2A,
|
|
||||||
BPB_FAT32_rootClus = 0x2C,
|
|
||||||
BPB_FAT32_fsInfo = 0x30,
|
|
||||||
BPB_FAT32_bkBootSec = 0x32,
|
|
||||||
// Ext BIOS Parameter Block for FAT32
|
|
||||||
BPB_FAT32_driveNumber = 0x40,
|
|
||||||
BPB_FAT32_reserved1 = 0x41,
|
|
||||||
BPB_FAT32_extBootSig = 0x42,
|
|
||||||
BPB_FAT32_volumeID = 0x43,
|
|
||||||
BPB_FAT32_volumeLabel = 0x47,
|
|
||||||
BPB_FAT32_fileSysType = 0x52,
|
|
||||||
// Bootcode
|
|
||||||
BPB_FAT32_bootCode = 0x5A,
|
|
||||||
BPB_bootSig_55 = 0x1FE,
|
|
||||||
BPB_bootSig_AA = 0x1FF
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char FAT_SIG[3] = { 'F', 'A', 'T' };
|
|
||||||
|
|
||||||
sec_t FindFirstValidPartition(const DISC_INTERFACE* disc)
|
|
||||||
{
|
|
||||||
uint8_t part_table[16 * 4];
|
|
||||||
uint8_t *ptr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
uint8_t sectorBuffer[BYTES_PER_READ] = { 0 };
|
|
||||||
|
|
||||||
// Read first sector of disc
|
|
||||||
if (!_FAT_disc_readSectors(disc, 0, 1, sectorBuffer))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(part_table, sectorBuffer + 0x1BE, 16 * 4);
|
|
||||||
ptr = part_table;
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++, ptr += 16)
|
|
||||||
{
|
|
||||||
sec_t part_lba = u8array_to_u32(ptr, 0x8);
|
|
||||||
|
|
||||||
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || !memcmp(sectorBuffer
|
|
||||||
+ BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
|
||||||
{
|
|
||||||
return part_lba;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ptr[4] == 0) continue;
|
|
||||||
|
|
||||||
if (ptr[4] == 0x0F)
|
|
||||||
{
|
|
||||||
sec_t part_lba2 = part_lba;
|
|
||||||
sec_t next_lba2 = 0;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
for (n = 0; n < 8; n++) // max 8 logic partitions
|
|
||||||
{
|
|
||||||
if (!_FAT_disc_readSectors(disc, part_lba + next_lba2, 1, sectorBuffer)) return 0;
|
|
||||||
|
|
||||||
part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6);
|
|
||||||
next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6);
|
|
||||||
|
|
||||||
if (!_FAT_disc_readSectors(disc, part_lba2, 1, sectorBuffer)) return 0;
|
|
||||||
|
|
||||||
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || !memcmp(sectorBuffer
|
|
||||||
+ BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
|
||||||
{
|
|
||||||
return part_lba2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next_lba2 == 0) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_FAT_disc_readSectors(disc, part_lba, 1, sectorBuffer)) return 0;
|
|
||||||
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || !memcmp(sectorBuffer
|
|
||||||
+ BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
|
||||||
{
|
|
||||||
return part_lba;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PARTITION* _FAT_partition_constructor(const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage,
|
|
||||||
sec_t startSector)
|
|
||||||
{
|
|
||||||
PARTITION* partition;
|
|
||||||
uint8_t sectorBuffer[BYTES_PER_READ] = { 0 };
|
|
||||||
|
|
||||||
// Read first sector of disc
|
|
||||||
if (!_FAT_disc_readSectors(disc, startSector, 1, sectorBuffer))
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it is a valid MBR or boot sector
|
|
||||||
if ((sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA))
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startSector != 0)
|
|
||||||
{
|
|
||||||
// We're told where to start the partition, so just accept it
|
|
||||||
}
|
|
||||||
else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
|
||||||
{
|
|
||||||
// Check if there is a FAT string, which indicates this is a boot sector
|
|
||||||
startSector = 0;
|
|
||||||
}
|
|
||||||
else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
|
||||||
{
|
|
||||||
// Check for FAT32
|
|
||||||
startSector = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
startSector = FindFirstValidPartition(disc);
|
|
||||||
if (!_FAT_disc_readSectors(disc, startSector, 1, sectorBuffer))
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now verify that this is indeed a FAT partition
|
|
||||||
if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) && memcmp(sectorBuffer
|
|
||||||
+ BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check again for the last two cases to make sure that we really have a FAT filesystem here
|
|
||||||
// and won't corrupt any data
|
|
||||||
if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, "FAT", 3) != 0 && memcmp(sectorBuffer + BPB_FAT32_fileSysType,
|
|
||||||
"FAT32", 5) != 0)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
partition = (PARTITION*) _FAT_mem_allocate(sizeof(PARTITION));
|
|
||||||
if (partition == NULL)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FAT_startSector = startSector;
|
|
||||||
|
|
||||||
// Init the partition lock
|
|
||||||
_FAT_lock_init(&partition->lock);
|
|
||||||
|
|
||||||
// Set partition's disc interface
|
|
||||||
partition->disc = disc;
|
|
||||||
|
|
||||||
// Store required information about the file system
|
|
||||||
partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
|
|
||||||
if (partition->fat.sectorsPerFat == 0)
|
|
||||||
{
|
|
||||||
partition->fat.sectorsPerFat = u8array_to_u32(sectorBuffer, BPB_FAT32_sectorsPerFAT32);
|
|
||||||
}
|
|
||||||
|
|
||||||
partition->numberOfSectors = u8array_to_u16(sectorBuffer, BPB_numSectorsSmall);
|
|
||||||
if (partition->numberOfSectors == 0)
|
|
||||||
{
|
|
||||||
partition->numberOfSectors = u8array_to_u32(sectorBuffer, BPB_numSectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
|
|
||||||
partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer,
|
|
||||||
BPB_bytesPerSector) / BYTES_PER_READ;
|
|
||||||
partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
|
|
||||||
partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
|
|
||||||
|
|
||||||
partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
|
|
||||||
partition->dataStart = partition->rootDirStart + ((u8array_to_u16(sectorBuffer, BPB_rootEntries)
|
|
||||||
* DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
|
|
||||||
|
|
||||||
partition->totalSize = ((uint64_t) partition->numberOfSectors - (partition->dataStart - startSector))
|
|
||||||
* (uint64_t) partition->bytesPerSector;
|
|
||||||
|
|
||||||
// Store info about FAT
|
|
||||||
uint32_t clusterCount = (partition->numberOfSectors - (uint32_t) (partition->dataStart - startSector))
|
|
||||||
/ partition->sectorsPerCluster;
|
|
||||||
partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1;
|
|
||||||
partition->fat.firstFree = CLUSTER_FIRST;
|
|
||||||
|
|
||||||
if (clusterCount < CLUSTERS_PER_FAT12)
|
|
||||||
{
|
|
||||||
partition->filesysType = FS_FAT12; // FAT12 volume
|
|
||||||
}
|
|
||||||
else if (clusterCount < CLUSTERS_PER_FAT16)
|
|
||||||
{
|
|
||||||
partition->filesysType = FS_FAT16; // FAT16 volume
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
partition->filesysType = FS_FAT32; // FAT32 volume
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partition->filesysType != FS_FAT32)
|
|
||||||
{
|
|
||||||
partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Set up for the FAT32 way
|
|
||||||
partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
|
|
||||||
// Check if FAT mirroring is enabled
|
|
||||||
if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80))
|
|
||||||
{
|
|
||||||
// Use the active FAT
|
|
||||||
partition->fat.fatStart = partition->fat.fatStart + (partition->fat.sectorsPerFat
|
|
||||||
* (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a cache to use
|
|
||||||
partition->cache = _FAT_cache_constructor(cacheSize, sectorsPerPage, partition->disc, startSector
|
|
||||||
+ partition->numberOfSectors);
|
|
||||||
|
|
||||||
// Set current directory to the root
|
|
||||||
partition->cwdCluster = partition->rootDirCluster;
|
|
||||||
|
|
||||||
// Check if this disc is writable, and set the readOnly property appropriately
|
|
||||||
partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
|
|
||||||
|
|
||||||
// There are currently no open files on this partition
|
|
||||||
partition->openFileCount = 0;
|
|
||||||
partition->firstOpenFile = NULL;
|
|
||||||
|
|
||||||
return partition;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _FAT_partition_destructor(PARTITION* partition)
|
|
||||||
{
|
|
||||||
FILE_STRUCT* nextFile;
|
|
||||||
|
|
||||||
_FAT_lock(&partition->lock);
|
|
||||||
|
|
||||||
// Synchronize open files
|
|
||||||
nextFile = partition->firstOpenFile;
|
|
||||||
while (nextFile)
|
|
||||||
{
|
|
||||||
_FAT_syncToDisc(nextFile);
|
|
||||||
nextFile = nextFile->nextOpenFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free memory used by the cache, writing it to disc at the same time
|
|
||||||
_FAT_cache_destructor(partition->cache);
|
|
||||||
|
|
||||||
// Unlock the partition and destroy the lock
|
|
||||||
_FAT_unlock(&partition->lock);
|
|
||||||
_FAT_lock_deinit(&partition->lock);
|
|
||||||
|
|
||||||
// Free memory used by the partition
|
|
||||||
_FAT_mem_free(partition);
|
|
||||||
}
|
|
||||||
|
|
||||||
PARTITION* _FAT_partition_getPartitionFromPath(const char* path)
|
|
||||||
{
|
|
||||||
const devoptab_t *devops;
|
|
||||||
|
|
||||||
devops = GetDeviceOpTab(path);
|
|
||||||
|
|
||||||
if (!devops)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (PARTITION*) devops->deviceData;
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
partition.h
|
|
||||||
Functions for mounting and dismounting partitions
|
|
||||||
on various block devices.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __PARTITION_H
|
|
||||||
#define __PARTITION_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "fat_cache.h"
|
|
||||||
#include "lock.h"
|
|
||||||
|
|
||||||
// Device name
|
|
||||||
extern const char* DEVICE_NAME;
|
|
||||||
|
|
||||||
// Filesystem type
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32
|
|
||||||
} FS_TYPE;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
sec_t fatStart;
|
|
||||||
uint32_t sectorsPerFat;
|
|
||||||
uint32_t lastCluster;
|
|
||||||
uint32_t firstFree;
|
|
||||||
} FAT;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
const DISC_INTERFACE* disc;
|
|
||||||
CACHE* cache;
|
|
||||||
// Info about the partition
|
|
||||||
FS_TYPE filesysType;
|
|
||||||
uint64_t totalSize;
|
|
||||||
sec_t rootDirStart;
|
|
||||||
uint32_t rootDirCluster;
|
|
||||||
uint32_t numberOfSectors;
|
|
||||||
sec_t dataStart;
|
|
||||||
uint32_t bytesPerSector;
|
|
||||||
uint32_t sectorsPerCluster;
|
|
||||||
uint32_t bytesPerCluster;
|
|
||||||
FAT fat;
|
|
||||||
// Values that may change after construction
|
|
||||||
uint32_t cwdCluster; // Current working directory cluster
|
|
||||||
int openFileCount;
|
|
||||||
struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files
|
|
||||||
mutex_t lock; // A lock for partition operations
|
|
||||||
bool readOnly; // If this is set, then do not try writing to the disc
|
|
||||||
} PARTITION;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Mount the supplied device and return a pointer to the struct necessary to use it
|
|
||||||
*/
|
|
||||||
PARTITION* _FAT_partition_constructor(const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage,
|
|
||||||
sec_t startSector);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Dismount the device and free all structures used.
|
|
||||||
Will also attempt to synchronise all open files to disc.
|
|
||||||
*/
|
|
||||||
void _FAT_partition_destructor(PARTITION* partition);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Return the partition specified in a path, as taken from the devoptab.
|
|
||||||
*/
|
|
||||||
PARTITION* _FAT_partition_getPartitionFromPath(const char* path);
|
|
||||||
|
|
||||||
#endif // _PARTITION_H
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,316 +0,0 @@
|
|||||||
/**
|
|
||||||
* attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS
|
|
||||||
* project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2005 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
|
||||||
* Copyright (c) 2006 Szabolcs Szakacsits
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "layout.h"
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "attrlist.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "unistr.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_attrlist_need - check whether inode need attribute list
|
|
||||||
* @ni: opened ntfs inode for which perform check
|
|
||||||
*
|
|
||||||
* Check whether all are attributes belong to one MFT record, in that case
|
|
||||||
* attribute list is not needed.
|
|
||||||
*
|
|
||||||
* Return 1 if inode need attribute list, 0 if not, -1 on error with errno set
|
|
||||||
* to the error code. If function succeed errno set to 0. The following error
|
|
||||||
* codes are defined:
|
|
||||||
* EINVAL - Invalid arguments passed to function or attribute haven't got
|
|
||||||
* attribute list.
|
|
||||||
*/
|
|
||||||
int ntfs_attrlist_need(ntfs_inode *ni)
|
|
||||||
{
|
|
||||||
ATTR_LIST_ENTRY *ale;
|
|
||||||
|
|
||||||
if (!ni)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Invalid arguments.\n");
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
|
|
||||||
|
|
||||||
if (!NInoAttrList(ni))
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Inode haven't got attribute list.\n");
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ni->attr_list)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Corrupt in-memory struct.\n");
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
ale = (ATTR_LIST_ENTRY *) ni->attr_list;
|
|
||||||
while ((u8*) ale < ni->attr_list + ni->attr_list_size)
|
|
||||||
{
|
|
||||||
if (MREF_LE(ale->mft_reference) != ni->mft_no) return 1;
|
|
||||||
ale = (ATTR_LIST_ENTRY *) ((u8*) ale + le16_to_cpu(ale->length));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
|
||||||
* @ni: opened ntfs inode, which contains that attribute
|
|
||||||
* @attr: attribute record to add to attribute list
|
|
||||||
*
|
|
||||||
* Return 0 on success and -1 on error with errno set to the error code. The
|
|
||||||
* following error codes are defined:
|
|
||||||
* EINVAL - Invalid arguments passed to function.
|
|
||||||
* ENOMEM - Not enough memory to allocate necessary buffers.
|
|
||||||
* EIO - I/O error occurred or damaged filesystem.
|
|
||||||
* EEXIST - Such attribute already present in attribute list.
|
|
||||||
*/
|
|
||||||
int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
|
|
||||||
{
|
|
||||||
ATTR_LIST_ENTRY *ale;
|
|
||||||
MFT_REF mref;
|
|
||||||
ntfs_attr *na = NULL;
|
|
||||||
ntfs_attr_search_ctx *ctx;
|
|
||||||
u8 *new_al;
|
|
||||||
int entry_len, entry_offset, err;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
|
|
||||||
(long long) ni->mft_no,
|
|
||||||
(unsigned) le32_to_cpu(attr->type));
|
|
||||||
|
|
||||||
if (!ni || !attr)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Invalid arguments.\n");
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
|
|
||||||
|
|
||||||
if (ni->nr_extents == -1) ni = ni->base_ni;
|
|
||||||
|
|
||||||
if (!NInoAttrList(ni))
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Attribute list isn't present.\n");
|
|
||||||
errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine size and allocate memory for new attribute list. */
|
|
||||||
entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * attr->name_length + 7) & ~7;
|
|
||||||
new_al = ntfs_calloc(ni->attr_list_size + entry_len);
|
|
||||||
if (!new_al) return -1;
|
|
||||||
|
|
||||||
/* Find place for the new entry. */
|
|
||||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
|
||||||
if (!ctx)
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) ((u8*) attr + le16_to_cpu(attr->name_offset))
|
|
||||||
: AT_UNNAMED, attr->name_length, CASE_SENSITIVE, (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : 0,
|
|
||||||
(attr->non_resident) ? NULL : ((u8*) attr + le16_to_cpu(attr->value_offset)), (attr->non_resident) ?
|
|
||||||
0
|
|
||||||
: le32_to_cpu(attr->value_length), ctx))
|
|
||||||
{
|
|
||||||
/* Found some extent, check it to be before new extent. */
|
|
||||||
if (ctx->al_entry->lowest_vcn == attr->lowest_vcn)
|
|
||||||
{
|
|
||||||
err = EEXIST;
|
|
||||||
ntfs_log_trace("Such attribute already present in the "
|
|
||||||
"attribute list.\n");
|
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
/* Add new entry after this extent. */
|
|
||||||
ale = (ATTR_LIST_ENTRY*) ((u8*) ctx->al_entry + le16_to_cpu(ctx->al_entry->length));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Check for real errors. */
|
|
||||||
if (errno != ENOENT)
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
ntfs_log_trace("Attribute lookup failed.\n");
|
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
/* No previous extents found. */
|
|
||||||
ale = ctx->al_entry;
|
|
||||||
}
|
|
||||||
/* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
|
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
|
||||||
|
|
||||||
/* Determine new entry offset. */
|
|
||||||
entry_offset = ((u8 *) ale - ni->attr_list);
|
|
||||||
/* Set pointer to new entry. */
|
|
||||||
ale = (ATTR_LIST_ENTRY *) (new_al + entry_offset);
|
|
||||||
/* Zero it to fix valgrind warning. */
|
|
||||||
memset(ale, 0, entry_len);
|
|
||||||
/* Form new entry. */
|
|
||||||
ale->type = attr->type;
|
|
||||||
ale->length = cpu_to_le16(entry_len);
|
|
||||||
ale->name_length = attr->name_length;
|
|
||||||
ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
|
|
||||||
if (attr->non_resident)
|
|
||||||
ale->lowest_vcn = attr->lowest_vcn;
|
|
||||||
else ale->lowest_vcn = 0;
|
|
||||||
ale->mft_reference = mref;
|
|
||||||
ale->instance = attr->instance;
|
|
||||||
memcpy(ale->name, (u8 *) attr + le16_to_cpu(attr->name_offset), attr->name_length * sizeof(ntfschar));
|
|
||||||
|
|
||||||
/* Resize $ATTRIBUTE_LIST to new length. */
|
|
||||||
na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
|
||||||
if (!na)
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len))
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy entries from old attribute list to new. */
|
|
||||||
memcpy(new_al, ni->attr_list, entry_offset);
|
|
||||||
memcpy(new_al + entry_offset + entry_len, ni->attr_list + entry_offset, ni->attr_list_size - entry_offset);
|
|
||||||
|
|
||||||
/* Set new runlist. */
|
|
||||||
free(ni->attr_list);
|
|
||||||
ni->attr_list = new_al;
|
|
||||||
ni->attr_list_size = ni->attr_list_size + entry_len;
|
|
||||||
NInoAttrListSetDirty(ni);
|
|
||||||
/* Done! */
|
|
||||||
ntfs_attr_close(na);
|
|
||||||
return 0;
|
|
||||||
err_out: if (na) ntfs_attr_close(na);
|
|
||||||
free(new_al);
|
|
||||||
errno = err;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_attrlist_entry_rm - remove an attribute list attribute entry
|
|
||||||
* @ctx: attribute search context describing the attribute list entry
|
|
||||||
*
|
|
||||||
* Remove the attribute list entry @ctx->al_entry from the attribute list.
|
|
||||||
*
|
|
||||||
* Return 0 on success and -1 on error with errno set to the error code.
|
|
||||||
*/
|
|
||||||
int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
|
|
||||||
{
|
|
||||||
u8 *new_al;
|
|
||||||
int new_al_len;
|
|
||||||
ntfs_inode *base_ni;
|
|
||||||
ntfs_attr *na;
|
|
||||||
ATTR_LIST_ENTRY *ale;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!ctx || !ctx->ntfs_ino || !ctx->al_entry)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Invalid arguments.\n");
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->base_ntfs_ino)
|
|
||||||
base_ni = ctx->base_ntfs_ino;
|
|
||||||
else base_ni = ctx->ntfs_ino;
|
|
||||||
ale = ctx->al_entry;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
|
|
||||||
(long long) ctx->ntfs_ino->mft_no,
|
|
||||||
(unsigned) le32_to_cpu(ctx->al_entry->type),
|
|
||||||
(long long) le64_to_cpu(ctx->al_entry->lowest_vcn));
|
|
||||||
|
|
||||||
if (!NInoAttrList(base_ni))
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Attribute list isn't present.\n");
|
|
||||||
errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate memory for new attribute list. */
|
|
||||||
new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
|
|
||||||
new_al = ntfs_calloc(new_al_len);
|
|
||||||
if (!new_al) return -1;
|
|
||||||
|
|
||||||
/* Reisze $ATTRIBUTE_LIST to new length. */
|
|
||||||
na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
|
||||||
if (!na)
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
if (ntfs_attr_truncate(na, new_al_len))
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy entries from old attribute list to new. */
|
|
||||||
memcpy(new_al, base_ni->attr_list, (u8*) ale - base_ni->attr_list);
|
|
||||||
memcpy(new_al + ((u8*) ale - base_ni->attr_list), (u8*) ale + le16_to_cpu(
|
|
||||||
ale->length), new_al_len - ((u8*) ale - base_ni->attr_list));
|
|
||||||
|
|
||||||
/* Set new runlist. */
|
|
||||||
free(base_ni->attr_list);
|
|
||||||
base_ni->attr_list = new_al;
|
|
||||||
base_ni->attr_list_size = new_al_len;
|
|
||||||
NInoAttrListSetDirty(base_ni);
|
|
||||||
/* Done! */
|
|
||||||
ntfs_attr_close(na);
|
|
||||||
return 0;
|
|
||||||
err_out: if (na) ntfs_attr_close(na);
|
|
||||||
free(new_al);
|
|
||||||
errno = err;
|
|
||||||
return -1;
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
bit_ops.h
|
|
||||||
Functions for dealing with conversion of data between types
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _BIT_OPS_H
|
|
||||||
#define _BIT_OPS_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------
|
|
||||||
Functions to deal with little endian values stored in uint8_t arrays
|
|
||||||
-----------------------------------------------------------------*/
|
|
||||||
static inline uint16_t u8array_to_u16(const uint8_t* item, int offset)
|
|
||||||
{
|
|
||||||
return (item[offset] | (item[offset + 1] << 8));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t u8array_to_u32(const uint8_t* item, int offset)
|
|
||||||
{
|
|
||||||
return (item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void u16_to_u8array(uint8_t* item, int offset, uint16_t value)
|
|
||||||
{
|
|
||||||
item[offset] = (uint8_t) value;
|
|
||||||
item[offset + 1] = (uint8_t) (value >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void u32_to_u8array(uint8_t* item, int offset, uint32_t value)
|
|
||||||
{
|
|
||||||
item[offset] = (uint8_t) value;
|
|
||||||
item[offset + 1] = (uint8_t) (value >> 8);
|
|
||||||
item[offset + 2] = (uint8_t) (value >> 16);
|
|
||||||
item[offset + 3] = (uint8_t) (value >> 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _BIT_OPS_H
|
|
@ -1,296 +0,0 @@
|
|||||||
/**
|
|
||||||
* bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2006 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2004-2005 Richard Russon
|
|
||||||
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STDIO_H
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "bitmap.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_bit_set - set a bit in a field of bits
|
|
||||||
* @bitmap: field of bits
|
|
||||||
* @bit: bit to set
|
|
||||||
* @new_value: value to set bit to (0 or 1)
|
|
||||||
*
|
|
||||||
* Set the bit @bit in the @bitmap to @new_value. Ignore all errors.
|
|
||||||
*/
|
|
||||||
void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
|
||||||
{
|
|
||||||
if (!bitmap || new_value > 1) return;
|
|
||||||
if (!new_value)
|
|
||||||
bitmap[bit >> 3] &= ~(1 << (bit & 7));
|
|
||||||
else bitmap[bit >> 3] |= (1 << (bit & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_bit_get - get value of a bit in a field of bits
|
|
||||||
* @bitmap: field of bits
|
|
||||||
* @bit: bit to get
|
|
||||||
*
|
|
||||||
* Get and return the value of the bit @bit in @bitmap (0 or 1).
|
|
||||||
* Return -1 on error.
|
|
||||||
*/
|
|
||||||
char ntfs_bit_get(const u8 *bitmap, const u64 bit)
|
|
||||||
{
|
|
||||||
if (!bitmap) return -1;
|
|
||||||
return (bitmap[bit >> 3] >> (bit & 7)) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_bit_get_and_set - get value of a bit in a field of bits and set it
|
|
||||||
* @bitmap: field of bits
|
|
||||||
* @bit: bit to get/set
|
|
||||||
* @new_value: value to set bit to (0 or 1)
|
|
||||||
*
|
|
||||||
* Return the value of the bit @bit and set it to @new_value (0 or 1).
|
|
||||||
* Return -1 on error.
|
|
||||||
*/
|
|
||||||
char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
|
||||||
{
|
|
||||||
register u8 old_bit, shift;
|
|
||||||
|
|
||||||
if (!bitmap || new_value > 1) return -1;
|
|
||||||
shift = bit & 7;
|
|
||||||
old_bit = (bitmap[bit >> 3] >> shift) & 1;
|
|
||||||
if (new_value != old_bit) bitmap[bit >> 3] ^= 1 << shift;
|
|
||||||
return old_bit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
|
|
||||||
* @na: attribute containing the bitmap
|
|
||||||
* @start_bit: first bit to set
|
|
||||||
* @count: number of bits to set
|
|
||||||
* @value: value to set the bits to (i.e. 0 or 1)
|
|
||||||
*
|
|
||||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
|
||||||
* attribute @na to @value, where @value is either 0 or 1.
|
|
||||||
*
|
|
||||||
* On success return 0 and on error return -1 with errno set to the error code.
|
|
||||||
*/
|
|
||||||
static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, s64 count, int value)
|
|
||||||
{
|
|
||||||
s64 bufsize, br;
|
|
||||||
u8 *buf, *lastbyte_buf;
|
|
||||||
int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1;
|
|
||||||
|
|
||||||
if (!na || start_bit < 0 || count < 0)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)",
|
|
||||||
__FUNCTION__, na, (long long)start_bit, (long long)count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bit = start_bit & 7;
|
|
||||||
if (bit)
|
|
||||||
firstbyte = 1;
|
|
||||||
else firstbyte = 0;
|
|
||||||
|
|
||||||
/* Calculate the required buffer size in bytes, capping it at 8kiB. */
|
|
||||||
bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
|
|
||||||
if (bufsize > 8192) bufsize = 8192;
|
|
||||||
|
|
||||||
buf = ntfs_malloc(bufsize);
|
|
||||||
if (!buf) return -1;
|
|
||||||
|
|
||||||
/* Depending on @value, zero or set all bits in the allocated buffer. */
|
|
||||||
memset(buf, value ? 0xff : 0, bufsize);
|
|
||||||
|
|
||||||
/* If there is a first partial byte... */
|
|
||||||
if (bit)
|
|
||||||
{
|
|
||||||
/* read it in... */
|
|
||||||
br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
|
|
||||||
if (br != 1)
|
|
||||||
{
|
|
||||||
if (br >= 0) errno = EIO;
|
|
||||||
goto free_err_out;
|
|
||||||
}
|
|
||||||
/* and set or clear the appropriate bits in it. */
|
|
||||||
while ((bit & 7) && count--)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
*buf |= 1 << bit++;
|
|
||||||
else *buf &= ~(1 << bit++);
|
|
||||||
}
|
|
||||||
/* Update @start_bit to the new position. */
|
|
||||||
start_bit = (start_bit + 7) & ~7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loop until @count reaches zero. */
|
|
||||||
lastbyte = 0;
|
|
||||||
lastbyte_buf = NULL;
|
|
||||||
bit = count & 7;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
/* If there is a last partial byte... */
|
|
||||||
if (count > 0 && bit)
|
|
||||||
{
|
|
||||||
lastbyte_pos = ((count + 7) >> 3) + firstbyte;
|
|
||||||
if (!lastbyte_pos)
|
|
||||||
{
|
|
||||||
// FIXME: Eeek! BUG!
|
|
||||||
ntfs_log_error("Lastbyte is zero. Leaving "
|
|
||||||
"inconsistent metadata.\n");
|
|
||||||
errno = EIO;
|
|
||||||
goto free_err_out;
|
|
||||||
}
|
|
||||||
/* and it is in the currently loaded bitmap window... */
|
|
||||||
if (lastbyte_pos <= bufsize)
|
|
||||||
{
|
|
||||||
lastbyte_buf = buf + lastbyte_pos - 1;
|
|
||||||
|
|
||||||
/* read the byte in... */
|
|
||||||
br = ntfs_attr_pread(na, (start_bit + count) >> 3, 1, lastbyte_buf);
|
|
||||||
if (br != 1)
|
|
||||||
{
|
|
||||||
// FIXME: Eeek! We need rollback! (AIA)
|
|
||||||
if (br >= 0) errno = EIO;
|
|
||||||
ntfs_log_perror("Reading of last byte "
|
|
||||||
"failed (%lld). Leaving inconsistent "
|
|
||||||
"metadata", (long long)br);
|
|
||||||
goto free_err_out;
|
|
||||||
}
|
|
||||||
/* and set/clear the appropriate bits in it. */
|
|
||||||
while (bit && count--)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
*lastbyte_buf |= 1 << --bit;
|
|
||||||
else *lastbyte_buf &= ~(1 << --bit);
|
|
||||||
}
|
|
||||||
/* We don't want to come back here... */
|
|
||||||
bit = 0;
|
|
||||||
/* We have a last byte that we have handled. */
|
|
||||||
lastbyte = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write the prepared buffer to disk. */
|
|
||||||
tmp = (start_bit >> 3) - firstbyte;
|
|
||||||
br = ntfs_attr_pwrite(na, tmp, bufsize, buf);
|
|
||||||
if (br != bufsize)
|
|
||||||
{
|
|
||||||
// FIXME: Eeek! We need rollback! (AIA)
|
|
||||||
if (br >= 0) errno = EIO;
|
|
||||||
ntfs_log_perror("Failed to write buffer to bitmap "
|
|
||||||
"(%lld != %lld). Leaving inconsistent metadata",
|
|
||||||
(long long)br, (long long)bufsize);
|
|
||||||
goto free_err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update counters. */
|
|
||||||
tmp = (bufsize - firstbyte - lastbyte) << 3;
|
|
||||||
if (firstbyte)
|
|
||||||
{
|
|
||||||
firstbyte = 0;
|
|
||||||
/*
|
|
||||||
* Re-set the partial first byte so a subsequent write
|
|
||||||
* of the buffer does not have stale, incorrect bits.
|
|
||||||
*/
|
|
||||||
*buf = value ? 0xff : 0;
|
|
||||||
}
|
|
||||||
start_bit += tmp;
|
|
||||||
count -= tmp;
|
|
||||||
if (bufsize > (tmp = (count + 7) >> 3)) bufsize = tmp;
|
|
||||||
|
|
||||||
if (lastbyte && count != 0)
|
|
||||||
{
|
|
||||||
// FIXME: Eeek! BUG!
|
|
||||||
ntfs_log_error("Last buffer but count is not zero "
|
|
||||||
"(%lld). Leaving inconsistent metadata.\n",
|
|
||||||
(long long)count);
|
|
||||||
errno = EIO;
|
|
||||||
goto free_err_out;
|
|
||||||
}
|
|
||||||
} while (count > 0);
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
free_err_out: free(buf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_bitmap_set_run - set a run of bits in a bitmap
|
|
||||||
* @na: attribute containing the bitmap
|
|
||||||
* @start_bit: first bit to set
|
|
||||||
* @count: number of bits to set
|
|
||||||
*
|
|
||||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
|
||||||
* attribute @na.
|
|
||||||
*
|
|
||||||
* On success return 0 and on error return -1 with errno set to the error code.
|
|
||||||
*/
|
|
||||||
int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ntfs_log_enter("Set from bit %lld, count %lld\n",
|
|
||||||
(long long)start_bit, (long long)count);
|
|
||||||
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1);
|
|
||||||
ntfs_log_leave("\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_bitmap_clear_run - clear a run of bits in a bitmap
|
|
||||||
* @na: attribute containing the bitmap
|
|
||||||
* @start_bit: first bit to clear
|
|
||||||
* @count: number of bits to clear
|
|
||||||
*
|
|
||||||
* Clear @count bits starting at bit @start_bit in the bitmap described by the
|
|
||||||
* attribute @na.
|
|
||||||
*
|
|
||||||
* On success return 0 and on error return -1 with errno set to the error code.
|
|
||||||
*/
|
|
||||||
int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ntfs_log_enter("Clear from bit %lld, count %lld\n",
|
|
||||||
(long long)start_bit, (long long)count);
|
|
||||||
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
|
|
||||||
ntfs_log_leave("\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
@ -1,303 +0,0 @@
|
|||||||
/**
|
|
||||||
* bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2003-2008 Szabolcs Szakacsits
|
|
||||||
* Copyright (c) 2005 Yura Pakhuchiy
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDIO_H
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "compat.h"
|
|
||||||
#include "bootsect.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "logging.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector
|
|
||||||
* @b: buffer containing putative boot sector to analyze
|
|
||||||
* @silent: if zero, output progress messages to stderr
|
|
||||||
*
|
|
||||||
* Check if the buffer @b contains a valid ntfs boot sector. The buffer @b
|
|
||||||
* must be at least 512 bytes in size.
|
|
||||||
*
|
|
||||||
* If @silent is zero, output progress messages to stderr. Otherwise, do not
|
|
||||||
* output any messages (except when configured with --enable-debug in which
|
|
||||||
* case warning/debug messages may be displayed).
|
|
||||||
*
|
|
||||||
* Return TRUE if @b contains a valid ntfs boot sector and FALSE if not.
|
|
||||||
*/
|
|
||||||
BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
|
|
||||||
{
|
|
||||||
u32 i;
|
|
||||||
BOOL ret = FALSE;
|
|
||||||
|
|
||||||
ntfs_log_debug("Beginning bootsector check.\n");
|
|
||||||
|
|
||||||
ntfs_log_debug("Checking OEMid, NTFS signature.\n");
|
|
||||||
if (b->oem_id != cpu_to_le64(0x202020205346544eULL))
|
|
||||||
{ /* "NTFS " */
|
|
||||||
ntfs_log_error("NTFS signature is missing.\n");
|
|
||||||
goto not_ntfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_debug("Checking bytes per sector.\n");
|
|
||||||
if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 || le16_to_cpu(b->bpb.bytes_per_sector) > 4096)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Unexpected bytes per sector value (%d).\n",
|
|
||||||
le16_to_cpu(b->bpb.bytes_per_sector));
|
|
||||||
goto not_ntfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_debug("Checking sectors per cluster.\n");
|
|
||||||
switch (b->bpb.sectors_per_cluster)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
case 8:
|
|
||||||
case 16:
|
|
||||||
case 32:
|
|
||||||
case 64:
|
|
||||||
case 128:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ntfs_log_error("Unexpected sectors per cluster value (%d).\n",
|
|
||||||
b->bpb.sectors_per_cluster);
|
|
||||||
goto not_ntfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_debug("Checking cluster size.\n");
|
|
||||||
i = (u32) le16_to_cpu(b->bpb.bytes_per_sector) * b->bpb.sectors_per_cluster;
|
|
||||||
if (i > 65536)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Unexpected cluster size (%d).\n", i);
|
|
||||||
goto not_ntfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_debug("Checking reserved fields are zero.\n");
|
|
||||||
if (le16_to_cpu(b->bpb.reserved_sectors) || le16_to_cpu(b->bpb.root_entries) || le16_to_cpu(b->bpb.sectors)
|
|
||||||
|| le16_to_cpu(b->bpb.sectors_per_fat) || le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Reserved fields aren't zero "
|
|
||||||
"(%d, %d, %d, %d, %d, %d).\n",
|
|
||||||
le16_to_cpu(b->bpb.reserved_sectors),
|
|
||||||
le16_to_cpu(b->bpb.root_entries),
|
|
||||||
le16_to_cpu(b->bpb.sectors),
|
|
||||||
le16_to_cpu(b->bpb.sectors_per_fat),
|
|
||||||
le32_to_cpu(b->bpb.large_sectors),
|
|
||||||
b->bpb.fats);
|
|
||||||
goto not_ntfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_debug("Checking clusters per mft record.\n");
|
|
||||||
if ((u8) b->clusters_per_mft_record < 0xe1 || (u8) b->clusters_per_mft_record > 0xf7)
|
|
||||||
{
|
|
||||||
switch (b->clusters_per_mft_record)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
case 8:
|
|
||||||
case 0x10:
|
|
||||||
case 0x20:
|
|
||||||
case 0x40:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ntfs_log_error("Unexpected clusters per mft record "
|
|
||||||
"(%d).\n", b->clusters_per_mft_record);
|
|
||||||
goto not_ntfs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_debug("Checking clusters per index block.\n");
|
|
||||||
if ((u8) b->clusters_per_index_record < 0xe1 || (u8) b->clusters_per_index_record > 0xf7)
|
|
||||||
{
|
|
||||||
switch (b->clusters_per_index_record)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
case 8:
|
|
||||||
case 0x10:
|
|
||||||
case 0x20:
|
|
||||||
case 0x40:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ntfs_log_error("Unexpected clusters per index record "
|
|
||||||
"(%d).\n", b->clusters_per_index_record);
|
|
||||||
goto not_ntfs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b->end_of_sector_marker != cpu_to_le16(0xaa55))
|
|
||||||
ntfs_log_debug("Warning: Bootsector has invalid end of sector "
|
|
||||||
"marker.\n");
|
|
||||||
|
|
||||||
ntfs_log_debug("Bootsector check completed successfully.\n");
|
|
||||||
|
|
||||||
ret = TRUE;
|
|
||||||
not_ntfs: return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *last_sector_error = "HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n"
|
|
||||||
" or it was not setup correctly (e.g. by not using mdadm --build ...),\n"
|
|
||||||
" or a wrong device is tried to be mounted,\n"
|
|
||||||
" or the partition table is corrupt (partition is smaller than NTFS),\n"
|
|
||||||
" or the NTFS boot sector is corrupt (NTFS size is not valid).\n";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector
|
|
||||||
* @vol: ntfs_volume to setup
|
|
||||||
* @bs: buffer containing ntfs boot sector to parse
|
|
||||||
*
|
|
||||||
* Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the
|
|
||||||
* obtained values.
|
|
||||||
*
|
|
||||||
* Return 0 on success or -1 on error with errno set to the error code EINVAL.
|
|
||||||
*/
|
|
||||||
int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
|
|
||||||
{
|
|
||||||
s64 sectors;
|
|
||||||
u8 sectors_per_cluster;
|
|
||||||
s8 c;
|
|
||||||
|
|
||||||
/* We return -1 with errno = EINVAL on error. */
|
|
||||||
errno = EINVAL;
|
|
||||||
|
|
||||||
vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector);
|
|
||||||
vol->sector_size_bits = ffs(vol->sector_size) - 1;
|
|
||||||
ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size);
|
|
||||||
ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits);
|
|
||||||
/*
|
|
||||||
* The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
|
|
||||||
* below or equal the number_of_clusters) really belong in the
|
|
||||||
* ntfs_boot_sector_is_ntfs but in this way we can just do this once.
|
|
||||||
*/
|
|
||||||
sectors_per_cluster = bs->bpb.sectors_per_cluster;
|
|
||||||
ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
|
|
||||||
if (sectors_per_cluster & (sectors_per_cluster - 1))
|
|
||||||
{
|
|
||||||
ntfs_log_error("sectors_per_cluster (%d) is not a power of 2."
|
|
||||||
"\n", sectors_per_cluster);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sectors = sle64_to_cpu(bs->number_of_sectors);
|
|
||||||
ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors);
|
|
||||||
if (!sectors)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Volume size is set to zero.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (vol->dev->d_ops->seek(vol->dev, (sectors - 1) << vol->sector_size_bits, SEEK_SET) == -1)
|
|
||||||
{
|
|
||||||
ntfs_log_perror("Failed to read last sector (%lld)",
|
|
||||||
(long long)sectors);
|
|
||||||
ntfs_log_error("%s", last_sector_error);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1);
|
|
||||||
|
|
||||||
vol->mft_lcn = sle64_to_cpu(bs->mft_lcn);
|
|
||||||
vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn);
|
|
||||||
ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn);
|
|
||||||
ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn);
|
|
||||||
if (vol->mft_lcn > vol->nr_clusters || vol->mftmirr_lcn > vol->nr_clusters)
|
|
||||||
{
|
|
||||||
ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is "
|
|
||||||
"greater than the number of clusters (%lld).\n",
|
|
||||||
(long long)vol->mft_lcn, (long long)vol->mftmirr_lcn,
|
|
||||||
(long long)vol->nr_clusters);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
vol->cluster_size = sectors_per_cluster * vol->sector_size;
|
|
||||||
if (vol->cluster_size & (vol->cluster_size - 1))
|
|
||||||
{
|
|
||||||
ntfs_log_error("cluster_size (%d) is not a power of 2.\n",
|
|
||||||
vol->cluster_size);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
|
|
||||||
/*
|
|
||||||
* Need to get the clusters per mft record and handle it if it is
|
|
||||||
* negative. Then calculate the mft_record_size. A value of 0x80 is
|
|
||||||
* illegal, thus signed char is actually ok!
|
|
||||||
*/
|
|
||||||
c = bs->clusters_per_mft_record;
|
|
||||||
ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size);
|
|
||||||
ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits);
|
|
||||||
ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c);
|
|
||||||
/*
|
|
||||||
* When clusters_per_mft_record is negative, it means that it is to
|
|
||||||
* be taken to be the negative base 2 logarithm of the mft_record_size
|
|
||||||
* min bytes. Then:
|
|
||||||
* mft_record_size = 2^(-clusters_per_mft_record) bytes.
|
|
||||||
*/
|
|
||||||
if (c < 0)
|
|
||||||
vol->mft_record_size = 1 << -c;
|
|
||||||
else vol->mft_record_size = c << vol->cluster_size_bits;
|
|
||||||
if (vol->mft_record_size & (vol->mft_record_size - 1))
|
|
||||||
{
|
|
||||||
ntfs_log_error("mft_record_size (%d) is not a power of 2.\n",
|
|
||||||
vol->mft_record_size);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
|
|
||||||
ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size);
|
|
||||||
ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
|
|
||||||
/* Same as above for INDX record. */
|
|
||||||
c = bs->clusters_per_index_record;
|
|
||||||
ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c);
|
|
||||||
if (c < 0)
|
|
||||||
vol->indx_record_size = 1 << -c;
|
|
||||||
else vol->indx_record_size = c << vol->cluster_size_bits;
|
|
||||||
vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1;
|
|
||||||
ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size);
|
|
||||||
ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits);
|
|
||||||
/*
|
|
||||||
* Work out the size of the MFT mirror in number of mft records. If the
|
|
||||||
* cluster size is less than or equal to the size taken by four mft
|
|
||||||
* records, the mft mirror stores the first four mft records. If the
|
|
||||||
* cluster size is bigger than the size taken by four mft records, the
|
|
||||||
* mft mirror contains as many mft records as will fit into one
|
|
||||||
* cluster.
|
|
||||||
*/
|
|
||||||
if (vol->cluster_size <= 4 * vol->mft_record_size)
|
|
||||||
vol->mftmirr_size = 4;
|
|
||||||
else vol->mftmirr_size = vol->cluster_size / vol->mft_record_size;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,623 +0,0 @@
|
|||||||
/**
|
|
||||||
* cache.c : deal with LRU caches
|
|
||||||
*
|
|
||||||
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "security.h"
|
|
||||||
#include "cache.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "logging.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* General functions to deal with LRU caches
|
|
||||||
*
|
|
||||||
* The cached data have to be organized in a structure in which
|
|
||||||
* the first fields must follow a mandatory pattern and further
|
|
||||||
* fields may contain any fixed size data. They are stored in an
|
|
||||||
* LRU list.
|
|
||||||
*
|
|
||||||
* A compare function must be provided for finding a wanted entry
|
|
||||||
* in the cache. Another function may be provided for invalidating
|
|
||||||
* an entry to facilitate multiple invalidation.
|
|
||||||
*
|
|
||||||
* These functions never return error codes. When there is a
|
|
||||||
* shortage of memory, data is simply not cached.
|
|
||||||
* When there is a hashing bug, hashing is dropped, and sequential
|
|
||||||
* searches are used.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enter a new hash index, after a new record has been inserted
|
|
||||||
*
|
|
||||||
* Do not call when a record has been modified (with no key change)
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void inserthashindex(struct CACHE_HEADER *cache, struct CACHED_GENERIC *current)
|
|
||||||
{
|
|
||||||
int h;
|
|
||||||
struct HASH_ENTRY *link;
|
|
||||||
struct HASH_ENTRY *first;
|
|
||||||
|
|
||||||
if (cache->dohash)
|
|
||||||
{
|
|
||||||
h = cache->dohash(current);
|
|
||||||
if ((h >= 0) && (h < cache->max_hash))
|
|
||||||
{
|
|
||||||
/* get a free link and insert at top of hash list */
|
|
||||||
link = cache->free_hash;
|
|
||||||
if (link)
|
|
||||||
{
|
|
||||||
cache->free_hash = link->next;
|
|
||||||
first = cache->first_hash[h];
|
|
||||||
if (first)
|
|
||||||
link->next = first;
|
|
||||||
else link->next = NULL;
|
|
||||||
link->entry = current;
|
|
||||||
cache->first_hash[h] = link;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ntfs_log_error("No more hash entries,"
|
|
||||||
" cache %s hashing dropped\n",
|
|
||||||
cache->name);
|
|
||||||
cache->dohash = (cache_hash) NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ntfs_log_error("Illegal hash value,"
|
|
||||||
" cache %s hashing dropped\n",
|
|
||||||
cache->name);
|
|
||||||
cache->dohash = (cache_hash) NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Drop a hash index when a record is about to be deleted
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void drophashindex(struct CACHE_HEADER *cache, const struct CACHED_GENERIC *current, int hash)
|
|
||||||
{
|
|
||||||
struct HASH_ENTRY *link;
|
|
||||||
struct HASH_ENTRY *previous;
|
|
||||||
|
|
||||||
if (cache->dohash)
|
|
||||||
{
|
|
||||||
if ((hash >= 0) && (hash < cache->max_hash))
|
|
||||||
{
|
|
||||||
/* find the link and unlink */
|
|
||||||
link = cache->first_hash[hash];
|
|
||||||
previous = (struct HASH_ENTRY*) NULL;
|
|
||||||
while (link && (link->entry != current))
|
|
||||||
{
|
|
||||||
previous = link;
|
|
||||||
link = link->next;
|
|
||||||
}
|
|
||||||
if (link)
|
|
||||||
{
|
|
||||||
if (previous)
|
|
||||||
previous->next = link->next;
|
|
||||||
else cache->first_hash[hash] = link->next;
|
|
||||||
link->next = cache->free_hash;
|
|
||||||
cache->free_hash = link;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ntfs_log_error("Bad hash list,"
|
|
||||||
" cache %s hashing dropped\n",
|
|
||||||
cache->name);
|
|
||||||
cache->dohash = (cache_hash) NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ntfs_log_error("Illegal hash value,"
|
|
||||||
" cache %s hashing dropped\n",
|
|
||||||
cache->name);
|
|
||||||
cache->dohash = (cache_hash) NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fetch an entry from cache
|
|
||||||
*
|
|
||||||
* returns the cache entry, or NULL if not available
|
|
||||||
* The returned entry may be modified, but not freed
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, const struct CACHED_GENERIC *wanted,
|
|
||||||
cache_compare compare)
|
|
||||||
{
|
|
||||||
struct CACHED_GENERIC *current;
|
|
||||||
struct CACHED_GENERIC *previous;
|
|
||||||
struct HASH_ENTRY *link;
|
|
||||||
int h;
|
|
||||||
|
|
||||||
current = (struct CACHED_GENERIC*) NULL;
|
|
||||||
if (cache)
|
|
||||||
{
|
|
||||||
if (cache->dohash)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* When possible, use the hash table to
|
|
||||||
* locate the entry if present
|
|
||||||
*/
|
|
||||||
h = cache->dohash(wanted);
|
|
||||||
link = cache->first_hash[h];
|
|
||||||
while (link && compare(link->entry, wanted))
|
|
||||||
link = link->next;
|
|
||||||
if (link) current = link->entry;
|
|
||||||
}
|
|
||||||
if (!cache->dohash)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Search sequentially in LRU list if no hash table
|
|
||||||
* or if hashing has just failed
|
|
||||||
*/
|
|
||||||
current = cache->most_recent_entry;
|
|
||||||
while (current && compare(current, wanted))
|
|
||||||
{
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (current)
|
|
||||||
{
|
|
||||||
previous = current->previous;
|
|
||||||
cache->hits++;
|
|
||||||
if (previous)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* found and not at head of list, unlink from current
|
|
||||||
* position and relink as head of list
|
|
||||||
*/
|
|
||||||
previous->next = current->next;
|
|
||||||
if (current->next)
|
|
||||||
current->next->previous = current->previous;
|
|
||||||
else cache->oldest_entry = current->previous;
|
|
||||||
current->next = cache->most_recent_entry;
|
|
||||||
current->previous = (struct CACHED_GENERIC*) NULL;
|
|
||||||
cache->most_recent_entry->previous = current;
|
|
||||||
cache->most_recent_entry = current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cache->reads++;
|
|
||||||
}
|
|
||||||
return (current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enter an inode number into cache
|
|
||||||
* returns the cache entry or NULL if not possible
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, const struct CACHED_GENERIC *item,
|
|
||||||
cache_compare compare)
|
|
||||||
{
|
|
||||||
struct CACHED_GENERIC *current;
|
|
||||||
struct CACHED_GENERIC *before;
|
|
||||||
struct HASH_ENTRY *link;
|
|
||||||
int h;
|
|
||||||
|
|
||||||
current = (struct CACHED_GENERIC*) NULL;
|
|
||||||
if (cache)
|
|
||||||
{
|
|
||||||
if (cache->dohash)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* When possible, use the hash table to
|
|
||||||
* find out whether the entry if present
|
|
||||||
*/
|
|
||||||
h = cache->dohash(item);
|
|
||||||
link = cache->first_hash[h];
|
|
||||||
while (link && compare(link->entry, item))
|
|
||||||
link = link->next;
|
|
||||||
if (link)
|
|
||||||
{
|
|
||||||
current = link->entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!cache->dohash)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Search sequentially in LRU list to locate the end,
|
|
||||||
* and find out whether the entry is already in list
|
|
||||||
* As we normally go to the end, no statistics is
|
|
||||||
* kept.
|
|
||||||
*/
|
|
||||||
current = cache->most_recent_entry;
|
|
||||||
while (current && compare(current, item))
|
|
||||||
{
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!current)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Not in list, get a free entry or reuse the
|
|
||||||
* last entry, and relink as head of list
|
|
||||||
* Note : we assume at least three entries, so
|
|
||||||
* before, previous and first are different when
|
|
||||||
* an entry is reused.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (cache->free_entry)
|
|
||||||
{
|
|
||||||
current = cache->free_entry;
|
|
||||||
cache->free_entry = cache->free_entry->next;
|
|
||||||
if (item->varsize)
|
|
||||||
{
|
|
||||||
current->variable = ntfs_malloc(item->varsize);
|
|
||||||
}
|
|
||||||
else current->variable = (void*) NULL;
|
|
||||||
current->varsize = item->varsize;
|
|
||||||
if (!cache->oldest_entry) cache->oldest_entry = current;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* reusing the oldest entry */
|
|
||||||
current = cache->oldest_entry;
|
|
||||||
before = current->previous;
|
|
||||||
before->next = (struct CACHED_GENERIC*) NULL;
|
|
||||||
if (cache->dohash) drophashindex(cache, current, cache->dohash(current));
|
|
||||||
if (cache->dofree) cache->dofree(current);
|
|
||||||
cache->oldest_entry = current->previous;
|
|
||||||
if (item->varsize)
|
|
||||||
{
|
|
||||||
if (current->varsize)
|
|
||||||
current->variable = realloc(current->variable, item->varsize);
|
|
||||||
else current->variable = ntfs_malloc(item->varsize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (current->varsize) free(current->variable);
|
|
||||||
current->variable = (void*) NULL;
|
|
||||||
}
|
|
||||||
current->varsize = item->varsize;
|
|
||||||
}
|
|
||||||
current->next = cache->most_recent_entry;
|
|
||||||
current->previous = (struct CACHED_GENERIC*) NULL;
|
|
||||||
if (cache->most_recent_entry) cache->most_recent_entry->previous = current;
|
|
||||||
cache->most_recent_entry = current;
|
|
||||||
memcpy(current->fixed, item->fixed, cache->fixed_size);
|
|
||||||
if (item->varsize)
|
|
||||||
{
|
|
||||||
if (current->variable)
|
|
||||||
{
|
|
||||||
memcpy(current->variable, item->variable, item->varsize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* no more memory for variable part
|
|
||||||
* recycle entry in free list
|
|
||||||
* not an error, just uncacheable
|
|
||||||
*/
|
|
||||||
cache->most_recent_entry = current->next;
|
|
||||||
current->next = cache->free_entry;
|
|
||||||
cache->free_entry = current;
|
|
||||||
current = (struct CACHED_GENERIC*) NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current->variable = (void*) NULL;
|
|
||||||
current->varsize = 0;
|
|
||||||
}
|
|
||||||
if (cache->dohash && current) inserthashindex(cache, current);
|
|
||||||
}
|
|
||||||
cache->writes++;
|
|
||||||
}
|
|
||||||
return (current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Invalidate a cache entry
|
|
||||||
* The entry is moved to the free entry list
|
|
||||||
* A specific function may be called for entry deletion
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void do_invalidate(struct CACHE_HEADER *cache, struct CACHED_GENERIC *current, int flags)
|
|
||||||
{
|
|
||||||
struct CACHED_GENERIC *previous;
|
|
||||||
|
|
||||||
previous = current->previous;
|
|
||||||
if ((flags & CACHE_FREE) && cache->dofree) cache->dofree(current);
|
|
||||||
/*
|
|
||||||
* Relink into free list
|
|
||||||
*/
|
|
||||||
if (current->next)
|
|
||||||
current->next->previous = current->previous;
|
|
||||||
else cache->oldest_entry = current->previous;
|
|
||||||
if (previous)
|
|
||||||
previous->next = current->next;
|
|
||||||
else cache->most_recent_entry = current->next;
|
|
||||||
current->next = cache->free_entry;
|
|
||||||
cache->free_entry = current;
|
|
||||||
if (current->variable) free(current->variable);
|
|
||||||
current->varsize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Invalidate entries in cache
|
|
||||||
*
|
|
||||||
* Several entries may have to be invalidated (at least for inodes
|
|
||||||
* associated to directories which have been renamed), a different
|
|
||||||
* compare function may be provided to select entries to invalidate
|
|
||||||
*
|
|
||||||
* Returns the number of deleted entries, this can be used by
|
|
||||||
* the caller to signal a cache corruption if the entry was
|
|
||||||
* supposed to be found.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_invalidate_cache(struct CACHE_HEADER *cache, const struct CACHED_GENERIC *item, cache_compare compare,
|
|
||||||
int flags)
|
|
||||||
{
|
|
||||||
struct CACHED_GENERIC *current;
|
|
||||||
struct CACHED_GENERIC *previous;
|
|
||||||
struct CACHED_GENERIC *next;
|
|
||||||
struct HASH_ENTRY *link;
|
|
||||||
int count;
|
|
||||||
int h;
|
|
||||||
|
|
||||||
current = (struct CACHED_GENERIC*) NULL;
|
|
||||||
count = 0;
|
|
||||||
if (cache)
|
|
||||||
{
|
|
||||||
if (!(flags & CACHE_NOHASH) && cache->dohash)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* When possible, use the hash table to
|
|
||||||
* find out whether the entry if present
|
|
||||||
*/
|
|
||||||
h = cache->dohash(item);
|
|
||||||
link = cache->first_hash[h];
|
|
||||||
while (link)
|
|
||||||
{
|
|
||||||
if (compare(link->entry, item))
|
|
||||||
link = link->next;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
current = link->entry;
|
|
||||||
link = link->next;
|
|
||||||
if (current)
|
|
||||||
{
|
|
||||||
drophashindex(cache, current, h);
|
|
||||||
do_invalidate(cache, current, flags);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((flags & CACHE_NOHASH) || !cache->dohash)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Search sequentially in LRU list
|
|
||||||
*/
|
|
||||||
current = cache->most_recent_entry;
|
|
||||||
previous = (struct CACHED_GENERIC*) NULL;
|
|
||||||
while (current)
|
|
||||||
{
|
|
||||||
if (!compare(current, item))
|
|
||||||
{
|
|
||||||
next = current->next;
|
|
||||||
if (cache->dohash) drophashindex(cache, current, cache->dohash(current));
|
|
||||||
do_invalidate(cache, current, flags);
|
|
||||||
current = next;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
previous = current;
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (count);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ntfs_remove_cache(struct CACHE_HEADER *cache, struct CACHED_GENERIC *item, int flags)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
if (cache)
|
|
||||||
{
|
|
||||||
if (cache->dohash) drophashindex(cache, item, cache->dohash(item));
|
|
||||||
do_invalidate(cache, item, flags);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return (count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free memory allocated to a cache
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void ntfs_free_cache(struct CACHE_HEADER *cache)
|
|
||||||
{
|
|
||||||
struct CACHED_GENERIC *entry;
|
|
||||||
|
|
||||||
if (cache)
|
|
||||||
{
|
|
||||||
for (entry = cache->most_recent_entry; entry; entry = entry->next)
|
|
||||||
{
|
|
||||||
if (cache->dofree) cache->dofree(entry);
|
|
||||||
if (entry->variable) free(entry->variable);
|
|
||||||
}
|
|
||||||
free(cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a cache
|
|
||||||
*
|
|
||||||
* Returns the cache header, or NULL if the cache could not be created
|
|
||||||
*/
|
|
||||||
|
|
||||||
static struct CACHE_HEADER *ntfs_create_cache(const char *name, cache_free dofree, cache_hash dohash,
|
|
||||||
int full_item_size, int item_count, int max_hash)
|
|
||||||
{
|
|
||||||
struct CACHE_HEADER *cache;
|
|
||||||
struct CACHED_GENERIC *pc;
|
|
||||||
struct CACHED_GENERIC *qc;
|
|
||||||
struct HASH_ENTRY *ph;
|
|
||||||
struct HASH_ENTRY *qh;
|
|
||||||
struct HASH_ENTRY **px;
|
|
||||||
size_t size;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
size = sizeof(struct CACHE_HEADER) + item_count * full_item_size;
|
|
||||||
if (max_hash) size += item_count * sizeof(struct HASH_ENTRY) + max_hash * sizeof(struct HASH_ENTRY*);
|
|
||||||
cache = (struct CACHE_HEADER*) ntfs_malloc(size);
|
|
||||||
if (cache)
|
|
||||||
{
|
|
||||||
/* header */
|
|
||||||
cache->name = name;
|
|
||||||
cache->dofree = dofree;
|
|
||||||
if (dohash && max_hash)
|
|
||||||
{
|
|
||||||
cache->dohash = dohash;
|
|
||||||
cache->max_hash = max_hash;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cache->dohash = (cache_hash) NULL;
|
|
||||||
cache->max_hash = 0;
|
|
||||||
}
|
|
||||||
cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC);
|
|
||||||
cache->reads = 0;
|
|
||||||
cache->writes = 0;
|
|
||||||
cache->hits = 0;
|
|
||||||
/* chain the data entries, and mark an invalid entry */
|
|
||||||
cache->most_recent_entry = (struct CACHED_GENERIC*) NULL;
|
|
||||||
cache->oldest_entry = (struct CACHED_GENERIC*) NULL;
|
|
||||||
cache->free_entry = &cache->entry[0];
|
|
||||||
pc = &cache->entry[0];
|
|
||||||
for (i = 0; i < (item_count - 1); i++)
|
|
||||||
{
|
|
||||||
qc = (struct CACHED_GENERIC*) ((char*) pc + full_item_size);
|
|
||||||
pc->next = qc;
|
|
||||||
pc->variable = (void*) NULL;
|
|
||||||
pc->varsize = 0;
|
|
||||||
pc = qc;
|
|
||||||
}
|
|
||||||
/* special for the last entry */
|
|
||||||
pc->next = (struct CACHED_GENERIC*) NULL;
|
|
||||||
pc->variable = (void*) NULL;
|
|
||||||
pc->varsize = 0;
|
|
||||||
|
|
||||||
if (max_hash)
|
|
||||||
{
|
|
||||||
/* chain the hash entries */
|
|
||||||
ph = (struct HASH_ENTRY*) (((char*) pc) + full_item_size);
|
|
||||||
cache->free_hash = ph;
|
|
||||||
for (i = 0; i < (item_count - 1); i++)
|
|
||||||
{
|
|
||||||
qh = &ph[1];
|
|
||||||
ph->next = qh;
|
|
||||||
ph = qh;
|
|
||||||
}
|
|
||||||
/* special for the last entry */
|
|
||||||
if (item_count)
|
|
||||||
{
|
|
||||||
ph->next = (struct HASH_ENTRY*) NULL;
|
|
||||||
}
|
|
||||||
/* create and initialize the hash indexes */
|
|
||||||
px = (struct HASH_ENTRY**) &ph[1];
|
|
||||||
cache->first_hash = px;
|
|
||||||
for (i = 0; i < max_hash; i++)
|
|
||||||
px[i] = (struct HASH_ENTRY*) NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cache->free_hash = (struct HASH_ENTRY*) NULL;
|
|
||||||
cache->first_hash = (struct HASH_ENTRY**) NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create all LRU caches
|
|
||||||
*
|
|
||||||
* No error return, if creation is not possible, cacheing will
|
|
||||||
* just be not available
|
|
||||||
*/
|
|
||||||
|
|
||||||
void ntfs_create_lru_caches(ntfs_volume *vol)
|
|
||||||
{
|
|
||||||
#if CACHE_INODE_SIZE
|
|
||||||
/* inode cache */
|
|
||||||
vol->xinode_cache = ntfs_create_cache("inode", (cache_free) NULL, ntfs_dir_inode_hash, sizeof(struct CACHED_INODE),
|
|
||||||
CACHE_INODE_SIZE, 2 * CACHE_INODE_SIZE);
|
|
||||||
#endif
|
|
||||||
#if CACHE_NIDATA_SIZE
|
|
||||||
/* idata cache */
|
|
||||||
vol->nidata_cache = ntfs_create_cache("nidata", ntfs_inode_nidata_free, ntfs_inode_nidata_hash,
|
|
||||||
sizeof(struct CACHED_NIDATA), CACHE_NIDATA_SIZE, 2 * CACHE_NIDATA_SIZE);
|
|
||||||
#endif
|
|
||||||
#if CACHE_LOOKUP_SIZE
|
|
||||||
/* lookup cache */
|
|
||||||
vol->lookup_cache = ntfs_create_cache("lookup", (cache_free) NULL, ntfs_dir_lookup_hash,
|
|
||||||
sizeof(struct CACHED_LOOKUP), CACHE_LOOKUP_SIZE, 2 * CACHE_LOOKUP_SIZE);
|
|
||||||
#endif
|
|
||||||
vol->securid_cache = ntfs_create_cache("securid", (cache_free) NULL, (cache_hash) NULL,
|
|
||||||
sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0);
|
|
||||||
#if CACHE_LEGACY_SIZE
|
|
||||||
vol->legacy_cache = ntfs_create_cache("legacy", (cache_free) NULL, (cache_hash) NULL,
|
|
||||||
sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free all LRU caches
|
|
||||||
*/
|
|
||||||
|
|
||||||
void ntfs_free_lru_caches(ntfs_volume *vol)
|
|
||||||
{
|
|
||||||
#if CACHE_INODE_SIZE
|
|
||||||
ntfs_free_cache(vol->xinode_cache);
|
|
||||||
#endif
|
|
||||||
#if CACHE_NIDATA_SIZE
|
|
||||||
ntfs_free_cache(vol->nidata_cache);
|
|
||||||
#endif
|
|
||||||
#if CACHE_LOOKUP_SIZE
|
|
||||||
ntfs_free_cache(vol->lookup_cache);
|
|
||||||
#endif
|
|
||||||
ntfs_free_cache(vol->securid_cache);
|
|
||||||
#if CACHE_LEGACY_SIZE
|
|
||||||
ntfs_free_cache(vol->legacy_cache);
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* cache.h : deal with indexed LRU caches
|
|
||||||
*
|
|
||||||
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NTFS_CACHE_H_
|
|
||||||
#define _NTFS_CACHE_H_
|
|
||||||
|
|
||||||
#include "volume.h"
|
|
||||||
|
|
||||||
struct CACHED_GENERIC
|
|
||||||
{
|
|
||||||
struct CACHED_GENERIC *next;
|
|
||||||
struct CACHED_GENERIC *previous;
|
|
||||||
void *variable;
|
|
||||||
size_t varsize;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
/* force alignment for pointers and u64 */
|
|
||||||
u64 u64align;
|
|
||||||
void *ptralign;
|
|
||||||
} fixed[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CACHED_INODE
|
|
||||||
{
|
|
||||||
struct CACHED_INODE *next;
|
|
||||||
struct CACHED_INODE *previous;
|
|
||||||
const char *pathname;
|
|
||||||
size_t varsize;
|
|
||||||
/* above fields must match "struct CACHED_GENERIC" */
|
|
||||||
u64 inum;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CACHED_NIDATA
|
|
||||||
{
|
|
||||||
struct CACHED_NIDATA *next;
|
|
||||||
struct CACHED_NIDATA *previous;
|
|
||||||
const char *pathname; /* not used */
|
|
||||||
size_t varsize; /* not used */
|
|
||||||
/* above fields must match "struct CACHED_GENERIC" */
|
|
||||||
u64 inum;
|
|
||||||
ntfs_inode *ni;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CACHED_LOOKUP
|
|
||||||
{
|
|
||||||
struct CACHED_LOOKUP *next;
|
|
||||||
struct CACHED_LOOKUP *previous;
|
|
||||||
const char *name;
|
|
||||||
size_t namesize;
|
|
||||||
/* above fields must match "struct CACHED_GENERIC" */
|
|
||||||
u64 parent;
|
|
||||||
u64 inum;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
CACHE_FREE = 1, CACHE_NOHASH = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, const struct CACHED_GENERIC *item);
|
|
||||||
typedef void (*cache_free)(const struct CACHED_GENERIC *cached);
|
|
||||||
typedef int (*cache_hash)(const struct CACHED_GENERIC *cached);
|
|
||||||
|
|
||||||
struct HASH_ENTRY
|
|
||||||
{
|
|
||||||
struct HASH_ENTRY *next;
|
|
||||||
struct CACHED_GENERIC *entry;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CACHE_HEADER
|
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
struct CACHED_GENERIC *most_recent_entry;
|
|
||||||
struct CACHED_GENERIC *oldest_entry;
|
|
||||||
struct CACHED_GENERIC *free_entry;
|
|
||||||
struct HASH_ENTRY *free_hash;
|
|
||||||
struct HASH_ENTRY **first_hash;
|
|
||||||
cache_free dofree;
|
|
||||||
cache_hash dohash;
|
|
||||||
unsigned long reads;
|
|
||||||
unsigned long writes;
|
|
||||||
unsigned long hits;
|
|
||||||
int fixed_size;
|
|
||||||
int max_hash;
|
|
||||||
struct CACHED_GENERIC entry[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* cast to generic, avoiding gcc warnings */
|
|
||||||
#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr))
|
|
||||||
|
|
||||||
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, const struct CACHED_GENERIC *wanted,
|
|
||||||
cache_compare compare);
|
|
||||||
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, const struct CACHED_GENERIC *item,
|
|
||||||
cache_compare compare);
|
|
||||||
int ntfs_invalidate_cache(struct CACHE_HEADER *cache, const struct CACHED_GENERIC *item, cache_compare compare,
|
|
||||||
int flags);
|
|
||||||
int ntfs_remove_cache(struct CACHE_HEADER *cache, struct CACHED_GENERIC *item, int flags);
|
|
||||||
|
|
||||||
void ntfs_create_lru_caches(ntfs_volume *vol);
|
|
||||||
void ntfs_free_lru_caches(ntfs_volume *vol);
|
|
||||||
|
|
||||||
#endif /* _NTFS_CACHE_H_ */
|
|
||||||
|
|
@ -1,428 +0,0 @@
|
|||||||
/*
|
|
||||||
cache.c
|
|
||||||
The cache is not visible to the user. It should be flushed
|
|
||||||
when any file is closed or changes are made to the filesystem.
|
|
||||||
|
|
||||||
This cache implements a least-used-page replacement policy. This will
|
|
||||||
distribute sectors evenly over the pages, so if less than the maximum
|
|
||||||
pages are used at once, they should all eventually remain in the cache.
|
|
||||||
This also has the benefit of throwing out old sectors, so as not to keep
|
|
||||||
too many stale pages around.
|
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
Copyright (c) 2009 shareese, rodries
|
|
||||||
Copyright (c) 2010 Dimok
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
|
||||||
other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ogc/lwp_watchdog.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "cache2.h"
|
|
||||||
#include "bit_ops.h"
|
|
||||||
#include "mem_allocate.h"
|
|
||||||
|
|
||||||
#define CACHE_FREE UINT_MAX
|
|
||||||
|
|
||||||
NTFS_CACHE* _NTFS_cache_constructor(unsigned int numberOfPages, unsigned int sectorsPerPage,
|
|
||||||
const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize)
|
|
||||||
{
|
|
||||||
NTFS_CACHE* cache;
|
|
||||||
unsigned int i;
|
|
||||||
NTFS_CACHE_ENTRY* cacheEntries;
|
|
||||||
|
|
||||||
if (numberOfPages == 0 || sectorsPerPage == 0) return NULL;
|
|
||||||
|
|
||||||
if (numberOfPages < 4)
|
|
||||||
{
|
|
||||||
numberOfPages = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sectorsPerPage < 32)
|
|
||||||
{
|
|
||||||
sectorsPerPage = 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache = (NTFS_CACHE*) ntfs_alloc(sizeof(NTFS_CACHE));
|
|
||||||
if (cache == NULL)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache->disc = discInterface;
|
|
||||||
cache->endOfPartition = endOfPartition;
|
|
||||||
cache->numberOfPages = numberOfPages;
|
|
||||||
cache->sectorsPerPage = sectorsPerPage;
|
|
||||||
cache->sectorSize = sectorSize;
|
|
||||||
|
|
||||||
cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc(sizeof(NTFS_CACHE_ENTRY) * numberOfPages);
|
|
||||||
if (cacheEntries == NULL)
|
|
||||||
{
|
|
||||||
ntfs_free(cache);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < numberOfPages; i++)
|
|
||||||
{
|
|
||||||
cacheEntries[i].sector = CACHE_FREE;
|
|
||||||
cacheEntries[i].count = 0;
|
|
||||||
cacheEntries[i].last_access = 0;
|
|
||||||
cacheEntries[i].dirty = false;
|
|
||||||
cacheEntries[i].cache = (uint8_t*) ntfs_align(sectorsPerPage * cache->sectorSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
cache->cacheEntries = cacheEntries;
|
|
||||||
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _NTFS_cache_destructor(NTFS_CACHE* cache)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
if (cache == NULL) return;
|
|
||||||
|
|
||||||
// Clear out cache before destroying it
|
|
||||||
_NTFS_cache_flush(cache);
|
|
||||||
|
|
||||||
// Free memory in reverse allocation order
|
|
||||||
for (i = 0; i < cache->numberOfPages; i++)
|
|
||||||
{
|
|
||||||
ntfs_free(cache->cacheEntries[i].cache);
|
|
||||||
}
|
|
||||||
ntfs_free(cache->cacheEntries);
|
|
||||||
ntfs_free(cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 accessCounter = 0;
|
|
||||||
|
|
||||||
static u32 accessTime()
|
|
||||||
{
|
|
||||||
accessCounter++;
|
|
||||||
return accessCounter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache, sec_t sector)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
|
||||||
unsigned int numberOfPages = cache->numberOfPages;
|
|
||||||
unsigned int sectorsPerPage = cache->sectorsPerPage;
|
|
||||||
|
|
||||||
bool foundFree = false;
|
|
||||||
unsigned int oldUsed = 0;
|
|
||||||
unsigned int oldAccess = UINT_MAX;
|
|
||||||
|
|
||||||
for (i = 0; i < numberOfPages; i++)
|
|
||||||
{
|
|
||||||
if (sector >= cacheEntries[i].sector && sector < (cacheEntries[i].sector + cacheEntries[i].count))
|
|
||||||
{
|
|
||||||
cacheEntries[i].last_access = accessTime();
|
|
||||||
return &(cacheEntries[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundFree == false && (cacheEntries[i].sector == CACHE_FREE || cacheEntries[i].last_access < oldAccess))
|
|
||||||
{
|
|
||||||
if (cacheEntries[i].sector == CACHE_FREE) foundFree = true;
|
|
||||||
oldUsed = i;
|
|
||||||
oldAccess = cacheEntries[i].last_access;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundFree == false && cacheEntries[oldUsed].dirty == true)
|
|
||||||
{
|
|
||||||
if (!cache->disc->writeSectors(cacheEntries[oldUsed].sector, cacheEntries[oldUsed].count,
|
|
||||||
cacheEntries[oldUsed].cache)) return NULL;
|
|
||||||
cacheEntries[oldUsed].dirty = false;
|
|
||||||
}
|
|
||||||
sector = (sector / sectorsPerPage) * sectorsPerPage; // align base sector to page size
|
|
||||||
sec_t next_page = sector + sectorsPerPage;
|
|
||||||
if (next_page > cache->endOfPartition) next_page = cache->endOfPartition;
|
|
||||||
|
|
||||||
if (!cache->disc->readSectors(sector, next_page - sector, cacheEntries[oldUsed].cache)) return NULL;
|
|
||||||
|
|
||||||
cacheEntries[oldUsed].sector = sector;
|
|
||||||
cacheEntries[oldUsed].count = next_page - sector;
|
|
||||||
cacheEntries[oldUsed].last_access = accessTime();
|
|
||||||
|
|
||||||
return &(cacheEntries[oldUsed]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count)
|
|
||||||
{
|
|
||||||
|
|
||||||
unsigned int i;
|
|
||||||
NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
|
||||||
unsigned int numberOfPages = cache->numberOfPages;
|
|
||||||
NTFS_CACHE_ENTRY *entry = NULL;
|
|
||||||
sec_t lowest = UINT_MAX;
|
|
||||||
|
|
||||||
for (i = 0; i < numberOfPages; i++)
|
|
||||||
{
|
|
||||||
if (cacheEntries[i].sector != CACHE_FREE)
|
|
||||||
{
|
|
||||||
bool intersect;
|
|
||||||
if (sector > cacheEntries[i].sector)
|
|
||||||
{
|
|
||||||
intersect = sector - cacheEntries[i].sector < cacheEntries[i].count;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
intersect = cacheEntries[i].sector - sector < count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersect && (cacheEntries[i].sector < lowest))
|
|
||||||
{
|
|
||||||
lowest = cacheEntries[i].sector;
|
|
||||||
entry = &cacheEntries[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NTFS_cache_readSectors(NTFS_CACHE *cache, sec_t sector, sec_t numSectors, void *buffer)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
sec_t secs_to_read;
|
|
||||||
NTFS_CACHE_ENTRY *entry;
|
|
||||||
uint8_t *dest = buffer;
|
|
||||||
|
|
||||||
while (numSectors > 0)
|
|
||||||
{
|
|
||||||
entry = _NTFS_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
secs_to_read = entry->count - sec;
|
|
||||||
if (secs_to_read > numSectors) secs_to_read = numSectors;
|
|
||||||
|
|
||||||
memcpy(dest, entry->cache + (sec * cache->sectorSize), (secs_to_read * cache->sectorSize));
|
|
||||||
|
|
||||||
dest += (secs_to_read * cache->sectorSize);
|
|
||||||
sector += secs_to_read;
|
|
||||||
numSectors -= secs_to_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reads some data from a cache page, determined by the sector number
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool _NTFS_cache_readPartialSector(NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
NTFS_CACHE_ENTRY *entry;
|
|
||||||
|
|
||||||
if (offset + size > cache->sectorSize) return false;
|
|
||||||
|
|
||||||
entry = _NTFS_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
memcpy(buffer, entry->cache + ((sec * cache->sectorSize) + offset), size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NTFS_cache_readLittleEndianValue(NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset,
|
|
||||||
int num_bytes)
|
|
||||||
{
|
|
||||||
uint8_t buf[4];
|
|
||||||
if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
|
||||||
|
|
||||||
switch (num_bytes)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
*value = buf[0];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
*value = u8array_to_u16(buf, 0);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
*value = u8array_to_u32(buf, 0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Writes some data to a cache page, making sure it is loaded into memory first.
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool _NTFS_cache_writePartialSector(NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset,
|
|
||||||
size_t size)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
NTFS_CACHE_ENTRY *entry;
|
|
||||||
|
|
||||||
if (offset + size > cache->sectorSize) return false;
|
|
||||||
|
|
||||||
entry = _NTFS_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
memcpy(entry->cache + ((sec * cache->sectorSize) + offset), buffer, size);
|
|
||||||
|
|
||||||
entry->dirty = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NTFS_cache_writeLittleEndianValue(NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset,
|
|
||||||
int size)
|
|
||||||
{
|
|
||||||
uint8_t buf[4] = { 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
switch (size)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
buf[0] = value;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
u16_to_u8array(buf, 0, value);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
u32_to_u8array(buf, 0, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Writes some data to a cache page, zeroing out the page first
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool _NTFS_cache_eraseWritePartialSector(NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset,
|
|
||||||
size_t size)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
NTFS_CACHE_ENTRY *entry;
|
|
||||||
|
|
||||||
if (offset + size > cache->sectorSize) return false;
|
|
||||||
|
|
||||||
entry = _NTFS_cache_getPage(cache, sector);
|
|
||||||
if (entry == NULL) return false;
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
memset(entry->cache + (sec * cache->sectorSize), 0, cache->sectorSize);
|
|
||||||
memcpy(entry->cache + ((sec * cache->sectorSize) + offset), buffer, size);
|
|
||||||
|
|
||||||
entry->dirty = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _NTFS_cache_writeSectors(NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer)
|
|
||||||
{
|
|
||||||
sec_t sec;
|
|
||||||
sec_t secs_to_write;
|
|
||||||
NTFS_CACHE_ENTRY* entry;
|
|
||||||
const uint8_t *src = buffer;
|
|
||||||
|
|
||||||
while (numSectors > 0)
|
|
||||||
{
|
|
||||||
entry = _NTFS_cache_findPage(cache, sector, numSectors);
|
|
||||||
|
|
||||||
if (entry != NULL)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (entry->sector > sector)
|
|
||||||
{
|
|
||||||
|
|
||||||
secs_to_write = entry->sector - sector;
|
|
||||||
|
|
||||||
cache->disc->writeSectors(sector, secs_to_write, src);
|
|
||||||
src += (secs_to_write * cache->sectorSize);
|
|
||||||
sector += secs_to_write;
|
|
||||||
numSectors -= secs_to_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
sec = sector - entry->sector;
|
|
||||||
secs_to_write = entry->count - sec;
|
|
||||||
|
|
||||||
if (secs_to_write > numSectors) secs_to_write = numSectors;
|
|
||||||
|
|
||||||
memcpy(entry->cache + (sec * cache->sectorSize), src, (secs_to_write * cache->sectorSize));
|
|
||||||
|
|
||||||
src += (secs_to_write * cache->sectorSize);
|
|
||||||
sector += secs_to_write;
|
|
||||||
numSectors -= secs_to_write;
|
|
||||||
|
|
||||||
entry->dirty = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cache->disc->writeSectors(sector, numSectors, src);
|
|
||||||
numSectors = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Flushes all dirty pages to disc, clearing the dirty flag.
|
|
||||||
*/
|
|
||||||
bool _NTFS_cache_flush(NTFS_CACHE* cache)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
if (cache == NULL) return true;
|
|
||||||
|
|
||||||
for (i = 0; i < cache->numberOfPages; i++)
|
|
||||||
{
|
|
||||||
if (cache->cacheEntries[i].dirty)
|
|
||||||
{
|
|
||||||
if (!cache->disc->writeSectors(cache->cacheEntries[i].sector, cache->cacheEntries[i].count,
|
|
||||||
cache->cacheEntries[i].cache))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cache->cacheEntries[i].dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _NTFS_cache_invalidate(NTFS_CACHE* cache)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
if (cache == NULL) return;
|
|
||||||
|
|
||||||
_NTFS_cache_flush(cache);
|
|
||||||
for (i = 0; i < cache->numberOfPages; i++)
|
|
||||||
{
|
|
||||||
cache->cacheEntries[i].sector = CACHE_FREE;
|
|
||||||
cache->cacheEntries[i].last_access = 0;
|
|
||||||
cache->cacheEntries[i].count = 0;
|
|
||||||
cache->cacheEntries[i].dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,270 +0,0 @@
|
|||||||
/**
|
|
||||||
* collate.c - NTFS collation handling. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2005 Yura Pakhuchiy
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "index.h"
|
|
||||||
#include "collate.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "unistr.h"
|
|
||||||
#include "logging.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_collate_binary - Which of two binary objects should be listed first
|
|
||||||
* @vol: unused
|
|
||||||
* @data1:
|
|
||||||
* @data1_len:
|
|
||||||
* @data2:
|
|
||||||
* @data2_len:
|
|
||||||
*
|
|
||||||
* Description...
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
*/
|
|
||||||
static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)), const void *data1, const int data1_len, const void *data2,
|
|
||||||
const int data2_len)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
rc = memcmp(data1, data2, min(data1_len, data2_len));
|
|
||||||
if (!rc && (data1_len != data2_len))
|
|
||||||
{
|
|
||||||
if (data1_len < data2_len)
|
|
||||||
rc = -1;
|
|
||||||
else rc = 1;
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_collate_ntofs_ulong - Which of two long ints should be listed first
|
|
||||||
* @vol: unused
|
|
||||||
* @data1:
|
|
||||||
* @data1_len:
|
|
||||||
* @data2:
|
|
||||||
* @data2_len:
|
|
||||||
*
|
|
||||||
* Description...
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
*/
|
|
||||||
static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), const void *data1, const int data1_len, const void *data2,
|
|
||||||
const int data2_len)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
u32 d1, d2;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
if (data1_len != data2_len || data1_len != 4)
|
|
||||||
{
|
|
||||||
ntfs_log_error("data1_len or/and data2_len not equal to 4.\n");
|
|
||||||
return NTFS_COLLATION_ERROR;
|
|
||||||
}
|
|
||||||
d1 = le32_to_cpup(data1);
|
|
||||||
d2 = le32_to_cpup(data2);
|
|
||||||
if (d1 < d2)
|
|
||||||
rc = -1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (d1 == d2)
|
|
||||||
rc = 0;
|
|
||||||
else rc = 1;
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first
|
|
||||||
*
|
|
||||||
* Returns: -1, 0 or 1 depending of how the arrays compare
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)), const void *data1, const int data1_len, const void *data2,
|
|
||||||
const int data2_len)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
int len;
|
|
||||||
const le32 *p1, *p2;
|
|
||||||
u32 d1, d2;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3))
|
|
||||||
{
|
|
||||||
ntfs_log_error("data1_len or data2_len not valid\n");
|
|
||||||
return NTFS_COLLATION_ERROR;
|
|
||||||
}
|
|
||||||
p1 = (const le32*) data1;
|
|
||||||
p2 = (const le32*) data2;
|
|
||||||
len = data1_len;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
d1 = le32_to_cpup(p1);
|
|
||||||
p1++;
|
|
||||||
d2 = le32_to_cpup(p2);
|
|
||||||
p2++;
|
|
||||||
} while ((d1 == d2) && ((len -= 4) > 0));
|
|
||||||
if (d1 < d2)
|
|
||||||
rc = -1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (d1 == d2)
|
|
||||||
rc = 0;
|
|
||||||
else rc = 1;
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_collate_ntofs_security_hash - Which of two security descriptors
|
|
||||||
* should be listed first
|
|
||||||
* @vol: unused
|
|
||||||
* @data1:
|
|
||||||
* @data1_len:
|
|
||||||
* @data2:
|
|
||||||
* @data2_len:
|
|
||||||
*
|
|
||||||
* JPA compare two security hash keys made of two unsigned le32
|
|
||||||
*
|
|
||||||
* Returns: -1, 0 or 1 depending of how the keys compare
|
|
||||||
*/
|
|
||||||
static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)), const void *data1, const int data1_len,
|
|
||||||
const void *data2, const int data2_len)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
u32 d1, d2;
|
|
||||||
const le32 *p1, *p2;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
if (data1_len != data2_len || data1_len != 8)
|
|
||||||
{
|
|
||||||
ntfs_log_error("data1_len or/and data2_len not equal to 8.\n");
|
|
||||||
return NTFS_COLLATION_ERROR;
|
|
||||||
}
|
|
||||||
p1 = (const le32*) data1;
|
|
||||||
p2 = (const le32*) data2;
|
|
||||||
d1 = le32_to_cpup(p1);
|
|
||||||
d2 = le32_to_cpup(p2);
|
|
||||||
if (d1 < d2)
|
|
||||||
rc = -1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (d1 > d2)
|
|
||||||
rc = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
p1++;
|
|
||||||
p2++;
|
|
||||||
d1 = le32_to_cpup(p1);
|
|
||||||
d2 = le32_to_cpup(p2);
|
|
||||||
if (d1 < d2)
|
|
||||||
rc = -1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (d1 > d2)
|
|
||||||
rc = 1;
|
|
||||||
else rc = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_collate_file_name - Which of two filenames should be listed first
|
|
||||||
* @vol:
|
|
||||||
* @data1:
|
|
||||||
* @data1_len: unused
|
|
||||||
* @data2:
|
|
||||||
* @data2_len: unused
|
|
||||||
*
|
|
||||||
* Description...
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
*/
|
|
||||||
static int ntfs_collate_file_name(ntfs_volume *vol, const void *data1, const int data1_len __attribute__((unused)), const void *data2,
|
|
||||||
const int data2_len __attribute__((unused)))
|
|
||||||
{
|
|
||||||
const FILE_NAME_ATTR *file_name_attr1;
|
|
||||||
const FILE_NAME_ATTR *file_name_attr2;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
file_name_attr1 = (const FILE_NAME_ATTR*) data1;
|
|
||||||
file_name_attr2 = (const FILE_NAME_ATTR*) data2;
|
|
||||||
rc = ntfs_names_full_collate((ntfschar*) &file_name_attr1->file_name, file_name_attr1->file_name_length,
|
|
||||||
(ntfschar*) &file_name_attr2->file_name, file_name_attr2->file_name_length, CASE_SENSITIVE, vol->upcase,
|
|
||||||
vol->upcase_len);
|
|
||||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get a pointer to appropriate collation function.
|
|
||||||
*
|
|
||||||
* Returns NULL if the needed function is not implemented
|
|
||||||
*/
|
|
||||||
|
|
||||||
COLLATE ntfs_get_collate_function(COLLATION_RULES cr)
|
|
||||||
{
|
|
||||||
COLLATE collate;
|
|
||||||
|
|
||||||
switch (cr)
|
|
||||||
{
|
|
||||||
case COLLATION_BINARY:
|
|
||||||
collate = ntfs_collate_binary;
|
|
||||||
break;
|
|
||||||
case COLLATION_FILE_NAME:
|
|
||||||
collate = ntfs_collate_file_name;
|
|
||||||
break;
|
|
||||||
case COLLATION_NTOFS_SECURITY_HASH:
|
|
||||||
collate = ntfs_collate_ntofs_security_hash;
|
|
||||||
break;
|
|
||||||
case COLLATION_NTOFS_ULONG:
|
|
||||||
collate = ntfs_collate_ntofs_ulong;
|
|
||||||
break;
|
|
||||||
case COLLATION_NTOFS_ULONGS:
|
|
||||||
collate = ntfs_collate_ntofs_ulongs;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errno = EOPNOTSUPP;
|
|
||||||
collate = (COLLATE) NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (collate);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
2687
source/libntfs/dir.c
2687
source/libntfs/dir.c
File diff suppressed because it is too large
Load Diff
@ -1,506 +0,0 @@
|
|||||||
/**
|
|
||||||
* efs.c - Limited processing of encrypted files
|
|
||||||
*
|
|
||||||
* This module is part of ntfs-3g library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Martin Bene
|
|
||||||
* Copyright (c) 2009-2010 Jean-Pierre Andre
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SETXATTR
|
|
||||||
#include <sys/xattr.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_SYSMACROS_H
|
|
||||||
#include <sys/sysmacros.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "inode.h"
|
|
||||||
#include "dir.h"
|
|
||||||
#include "efs.h"
|
|
||||||
#include "index.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "efs.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
|
||||||
|
|
||||||
static ntfschar logged_utility_stream_name[] =
|
|
||||||
{
|
|
||||||
const_cpu_to_le16('$'),
|
|
||||||
const_cpu_to_le16('E'),
|
|
||||||
const_cpu_to_le16('F'),
|
|
||||||
const_cpu_to_le16('S'),
|
|
||||||
const_cpu_to_le16(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the ntfs EFS info into an extended attribute
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size)
|
|
||||||
{
|
|
||||||
EFS_ATTR_HEADER *efs_info;
|
|
||||||
s64 attr_size = 0;
|
|
||||||
|
|
||||||
if (ni)
|
|
||||||
{
|
|
||||||
if (ni->flags & FILE_ATTR_ENCRYPTED)
|
|
||||||
{
|
|
||||||
efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni,
|
|
||||||
AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0,
|
|
||||||
&attr_size);
|
|
||||||
if (efs_info
|
|
||||||
&& (le32_to_cpu(efs_info->length) == attr_size))
|
|
||||||
{
|
|
||||||
if (attr_size <= (s64)size)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
memcpy(value,efs_info,attr_size);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = EFAULT;
|
|
||||||
attr_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (size)
|
|
||||||
{
|
|
||||||
errno = ERANGE;
|
|
||||||
attr_size = 0;
|
|
||||||
}
|
|
||||||
free (efs_info);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (efs_info)
|
|
||||||
{
|
|
||||||
free(efs_info);
|
|
||||||
ntfs_log_error("Bad efs_info for inode %lld\n",
|
|
||||||
(long long)ni->mft_no);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ntfs_log_error("Could not get efsinfo"
|
|
||||||
" for inode %lld\n",
|
|
||||||
(long long)ni->mft_no);
|
|
||||||
}
|
|
||||||
errno = EIO;
|
|
||||||
attr_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = ENODATA;
|
|
||||||
ntfs_log_trace("Inode %lld is not encrypted\n",
|
|
||||||
(long long)ni->mft_no);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (attr_size ? (int)attr_size : -errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fix all encrypted AT_DATA attributes of an inode
|
|
||||||
*
|
|
||||||
* The fix may require making an attribute non resident, which
|
|
||||||
* requires more space in the MFT record, and may cause some
|
|
||||||
* attribute to be expelled and the full record to be reorganized.
|
|
||||||
* When this happens, the search for data attributes has to be
|
|
||||||
* reinitialized.
|
|
||||||
*
|
|
||||||
* Returns zero if successful.
|
|
||||||
* -1 if there is a problem.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int fixup_loop(ntfs_inode *ni)
|
|
||||||
{
|
|
||||||
ntfs_attr_search_ctx *ctx;
|
|
||||||
ntfs_attr *na;
|
|
||||||
ATTR_RECORD *a;
|
|
||||||
BOOL restart;
|
|
||||||
BOOL first;
|
|
||||||
int cnt;
|
|
||||||
int maxcnt;
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
maxcnt = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
restart = FALSE;
|
|
||||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
|
||||||
if (!ctx)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Failed to get ctx for efs\n");
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
cnt = 0;
|
|
||||||
while (!restart && !res
|
|
||||||
&& !ntfs_attr_lookup(AT_DATA, NULL, 0,
|
|
||||||
CASE_SENSITIVE, 0, NULL, 0, ctx))
|
|
||||||
{
|
|
||||||
cnt++;
|
|
||||||
a = ctx->attr;
|
|
||||||
na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
|
|
||||||
(ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
|
|
||||||
a->name_length);
|
|
||||||
if (!na)
|
|
||||||
{
|
|
||||||
ntfs_log_error("can't open DATA Attribute\n");
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED))
|
|
||||||
{
|
|
||||||
if (!NAttrNonResident(na)
|
|
||||||
&& ntfs_attr_make_non_resident(na, ctx))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* ntfs_attr_make_non_resident fails if there
|
|
||||||
* is not enough space in the MFT record.
|
|
||||||
* When this happens, force making non-resident
|
|
||||||
* so that some other attribute is expelled.
|
|
||||||
*/
|
|
||||||
if (ntfs_attr_force_non_resident(na))
|
|
||||||
{
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* make sure there is some progress */
|
|
||||||
if (cnt <= maxcnt)
|
|
||||||
{
|
|
||||||
errno = EIO;
|
|
||||||
ntfs_log_error("Multiple failure"
|
|
||||||
" making non resident\n");
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
|
||||||
ctx = (ntfs_attr_search_ctx*)NULL;
|
|
||||||
restart = TRUE;
|
|
||||||
maxcnt = cnt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!restart && !res
|
|
||||||
&& ntfs_efs_fixup_attribute(ctx, na))
|
|
||||||
{
|
|
||||||
ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (na)
|
|
||||||
ntfs_attr_close(na);
|
|
||||||
}
|
|
||||||
first = FALSE;
|
|
||||||
}while (restart && !res);
|
|
||||||
if (ctx)
|
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the efs data from an extended attribute
|
|
||||||
* Warning : the new data is not checked
|
|
||||||
* Returns 0, or -1 if there is a problem
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
|
|
||||||
int flags)
|
|
||||||
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
int written;
|
|
||||||
ntfs_attr *na;
|
|
||||||
const EFS_ATTR_HEADER *info_header;
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
if (ni && value && size)
|
|
||||||
{
|
|
||||||
if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED))
|
|
||||||
{
|
|
||||||
if (ni->flags & FILE_ATTR_ENCRYPTED)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Inode %lld already encrypted\n",
|
|
||||||
(long long)ni->mft_no);
|
|
||||||
errno = EEXIST;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Possible problem : if encrypted file was
|
|
||||||
* restored in a compressed directory, it was
|
|
||||||
* restored as compressed.
|
|
||||||
* TODO : decompress first.
|
|
||||||
*/
|
|
||||||
ntfs_log_error("Inode %lld cannot be encrypted and compressed\n",
|
|
||||||
(long long)ni->mft_no);
|
|
||||||
errno = EIO;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
info_header = (const EFS_ATTR_HEADER*)value;
|
|
||||||
/* make sure we get a likely efsinfo */
|
|
||||||
if (le32_to_cpu(info_header->length) != size)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM,
|
|
||||||
(ntfschar*)NULL,0))
|
|
||||||
{
|
|
||||||
if (!(flags & XATTR_REPLACE))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* no logged_utility_stream attribute : add one,
|
|
||||||
* apparently, this does not feed the new value in
|
|
||||||
*/
|
|
||||||
res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM,
|
|
||||||
logged_utility_stream_name,4,
|
|
||||||
(u8*)NULL,(s64)size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = ENODATA;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = EEXIST;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
if (!res)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* open and update the existing efs data
|
|
||||||
*/
|
|
||||||
na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM,
|
|
||||||
logged_utility_stream_name, 4);
|
|
||||||
if (na)
|
|
||||||
{
|
|
||||||
/* resize attribute */
|
|
||||||
res = ntfs_attr_truncate(na, (s64)size);
|
|
||||||
/* overwrite value if any */
|
|
||||||
if (!res && value)
|
|
||||||
{
|
|
||||||
written = (int)ntfs_attr_pwrite(na,
|
|
||||||
(s64)0, (s64)size, value);
|
|
||||||
if (written != (s64)size)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Failed to "
|
|
||||||
"update efs data\n");
|
|
||||||
errno = EIO;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ntfs_attr_close(na);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
if (!res)
|
|
||||||
{
|
|
||||||
/* Don't handle AT_DATA Attribute(s) if inode is a directory */
|
|
||||||
if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))
|
|
||||||
{
|
|
||||||
/* iterate over AT_DATA attributes */
|
|
||||||
/* set encrypted flag, truncate attribute to match padding bytes */
|
|
||||||
|
|
||||||
if (fixup_loop(ni))
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
ni->flags |= FILE_ATTR_ENCRYPTED;
|
|
||||||
NInoSetDirty(ni);
|
|
||||||
NInoFileNameSetDirty(ni);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
return (res ? -1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fixup raw encrypted AT_DATA Attribute
|
|
||||||
* read padding length from last two bytes
|
|
||||||
* truncate attribute, make non-resident,
|
|
||||||
* set data size to match padding length
|
|
||||||
* set ATTR_IS_ENCRYPTED flag on attribute
|
|
||||||
*
|
|
||||||
* Return 0 if successful
|
|
||||||
* -1 if failed (errno tells why)
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
|
|
||||||
{
|
|
||||||
u64 newsize;
|
|
||||||
u64 oldsize;
|
|
||||||
le16 appended_bytes;
|
|
||||||
u16 padding_length;
|
|
||||||
ntfs_inode *ni;
|
|
||||||
BOOL close_ctx = FALSE;
|
|
||||||
|
|
||||||
if (!na)
|
|
||||||
{
|
|
||||||
ntfs_log_error("no na specified for efs_fixup_attribute\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
if (!ctx)
|
|
||||||
{
|
|
||||||
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
|
|
||||||
if (!ctx)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Failed to get ctx for efs\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
close_ctx = TRUE;
|
|
||||||
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
|
|
||||||
CASE_SENSITIVE, 0, NULL, 0, ctx))
|
|
||||||
{
|
|
||||||
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!NAttrNonResident(na))
|
|
||||||
{
|
|
||||||
ntfs_log_error("Cannot make non resident"
|
|
||||||
" when a context has been allocated\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* no extra bytes are added to void attributes */
|
|
||||||
oldsize = na->data_size;
|
|
||||||
if (oldsize)
|
|
||||||
{
|
|
||||||
/* make sure size is valid for a raw encrypted stream */
|
|
||||||
if ((oldsize & 511) != 2)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Bad raw encrypted stream\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
/* read padding length from last two bytes of attribute */
|
|
||||||
if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Error reading padding length\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
padding_length = le16_to_cpu(appended_bytes);
|
|
||||||
if (padding_length > 511 || padding_length > na->data_size-2)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
ntfs_log_error("invalid padding length %d for data_size %lld\n",
|
|
||||||
padding_length, (long long)oldsize);
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
newsize = oldsize - padding_length - 2;
|
|
||||||
/*
|
|
||||||
* truncate attribute to possibly free clusters allocated
|
|
||||||
* for the last two bytes, but do not truncate to new size
|
|
||||||
* to avoid losing useful data
|
|
||||||
*/
|
|
||||||
if (ntfs_attr_truncate(na, oldsize - 2))
|
|
||||||
{
|
|
||||||
ntfs_log_error("Error truncating attribute\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
newsize = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Encrypted AT_DATA Attributes MUST be non-resident
|
|
||||||
* This has to be done after the attribute is resized, as
|
|
||||||
* resizing down to zero may cause the attribute to be made
|
|
||||||
* resident.
|
|
||||||
*/
|
|
||||||
if (!NAttrNonResident(na)
|
|
||||||
&& ntfs_attr_make_non_resident(na, ctx))
|
|
||||||
{
|
|
||||||
if (!close_ctx
|
|
||||||
|| ntfs_attr_force_non_resident(na))
|
|
||||||
{
|
|
||||||
ntfs_log_error("Error making DATA attribute non-resident\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* must reinitialize context after forcing
|
|
||||||
* non-resident. We need a context for updating
|
|
||||||
* the state, and at this point, we are sure
|
|
||||||
* the context is not used elsewhere.
|
|
||||||
*/
|
|
||||||
ntfs_attr_reinit_search_ctx(ctx);
|
|
||||||
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
|
|
||||||
CASE_SENSITIVE, 0, NULL, 0, ctx))
|
|
||||||
{
|
|
||||||
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ni = na->ni;
|
|
||||||
if (!na->name_len)
|
|
||||||
{
|
|
||||||
ni->data_size = newsize;
|
|
||||||
ni->allocated_size = na->allocated_size;
|
|
||||||
}
|
|
||||||
NInoSetDirty(ni);
|
|
||||||
NInoFileNameSetDirty(ni);
|
|
||||||
|
|
||||||
ctx->attr->data_size = cpu_to_le64(newsize);
|
|
||||||
if (le64_to_cpu(ctx->attr->initialized_size) > newsize)
|
|
||||||
ctx->attr->initialized_size = ctx->attr->data_size;
|
|
||||||
ctx->attr->flags |= ATTR_IS_ENCRYPTED;
|
|
||||||
if (close_ctx)
|
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
err_out:
|
|
||||||
if (close_ctx && ctx)
|
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* HAVE_SETXATTR */
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* gekko_io.h - Platform specifics for device io.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _GEKKO_IO_H
|
|
||||||
#define _GEKKO_IO_H
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "cache2.h"
|
|
||||||
#include <gccore.h>
|
|
||||||
#include <ogc/disc_io.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gekko_fd - Gekko device driver descriptor
|
|
||||||
*/
|
|
||||||
typedef struct _gekko_fd
|
|
||||||
{
|
|
||||||
const DISC_INTERFACE* interface; /* Device disc interface */
|
|
||||||
sec_t startSector; /* LBA of partition start */
|
|
||||||
sec_t hiddenSectors; /* LBA offset to true partition start (as described by boot sector) */
|
|
||||||
u16 sectorSize; /* Device sector size (in bytes) */
|
|
||||||
u64 sectorCount; /* Total number of sectors in partition */
|
|
||||||
u64 pos; /* Current position within the partition (in bytes) */
|
|
||||||
u64 len; /* Total length of partition (in bytes) */
|
|
||||||
ino_t ino; /* Device identifier */
|
|
||||||
NTFS_CACHE *cache; /* Cache */
|
|
||||||
u32 cachePageCount; /* The number of pages in the cache */
|
|
||||||
u32 cachePageSize; /* The number of sectors per cache page */
|
|
||||||
} gekko_fd;
|
|
||||||
|
|
||||||
/* Forward declarations */
|
|
||||||
struct ntfs_device_operations;
|
|
||||||
|
|
||||||
/* Gekko device driver i/o operations */
|
|
||||||
extern struct ntfs_device_operations ntfs_device_gekko_io_ops;
|
|
||||||
|
|
||||||
#endif /* _GEKKO_IO_H */
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,781 +0,0 @@
|
|||||||
/**
|
|
||||||
* lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2004 Yura Pakhuchiy
|
|
||||||
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
|
||||||
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STDIO_H
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "bitmap.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "runlist.h"
|
|
||||||
#include "volume.h"
|
|
||||||
#include "lcnalloc.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Plenty possibilities for big optimizations all over in the cluster
|
|
||||||
* allocation, however at the moment the dominant bottleneck (~ 90%) is
|
|
||||||
* the update of the mapping pairs which converges to the cubic Faulhaber's
|
|
||||||
* formula as the function of the number of extents (fragments, runs).
|
|
||||||
*/
|
|
||||||
#define NTFS_LCNALLOC_BSIZE 4096
|
|
||||||
#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
ZONE_MFT = 1, ZONE_DATA1 = 2, ZONE_DATA2 = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc);
|
|
||||||
|
|
||||||
if (tc >= end)
|
|
||||||
*pos = start;
|
|
||||||
else if (tc >= start) *pos = tc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone);
|
|
||||||
|
|
||||||
if (zone == ZONE_MFT)
|
|
||||||
ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end, &vol->mft_zone_pos, tc);
|
|
||||||
else if (zone == ZONE_DATA1)
|
|
||||||
ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters, &vol->data1_zone_pos, tc);
|
|
||||||
else /* zone == ZONE_DATA2 */
|
|
||||||
ntfs_cluster_set_zone_pos(0, vol->mft_zone_start, &vol->data2_zone_pos, tc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unmark full zones when a cluster has been freed in a full zone
|
|
||||||
*
|
|
||||||
* Next allocation will reuse the freed cluster
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void update_full_status(ntfs_volume *vol, LCN lcn)
|
|
||||||
{
|
|
||||||
if (lcn >= vol->mft_zone_end)
|
|
||||||
{
|
|
||||||
if (vol->full_zones & ZONE_DATA1)
|
|
||||||
{
|
|
||||||
ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn);
|
|
||||||
vol->full_zones &= ~ZONE_DATA1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (lcn < vol->mft_zone_start)
|
|
||||||
{
|
|
||||||
if (vol->full_zones & ZONE_DATA2)
|
|
||||||
{
|
|
||||||
ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn);
|
|
||||||
vol->full_zones &= ~ZONE_DATA2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (vol->full_zones & ZONE_MFT)
|
|
||||||
{
|
|
||||||
ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn);
|
|
||||||
vol->full_zones &= ~ZONE_MFT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static s64 max_empty_bit_range(unsigned char *buf, int size)
|
|
||||||
{
|
|
||||||
int i, j, run = 0;
|
|
||||||
int max_range = 0;
|
|
||||||
s64 start_pos = -1;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering\n");
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < size)
|
|
||||||
{
|
|
||||||
switch (*buf)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
do
|
|
||||||
{
|
|
||||||
buf++;
|
|
||||||
run += 8;
|
|
||||||
i++;
|
|
||||||
} while ((i < size) && !*buf);
|
|
||||||
break;
|
|
||||||
case 255:
|
|
||||||
if (run > max_range)
|
|
||||||
{
|
|
||||||
max_range = run;
|
|
||||||
start_pos = (s64) i * 8 - run;
|
|
||||||
}
|
|
||||||
run = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
buf++;
|
|
||||||
i++;
|
|
||||||
} while ((i < size) && (*buf == 255));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
|
|
||||||
int bit = *buf & (1 << j);
|
|
||||||
|
|
||||||
if (bit)
|
|
||||||
{
|
|
||||||
if (run > max_range)
|
|
||||||
{
|
|
||||||
max_range = run;
|
|
||||||
start_pos = (s64) i * 8 + (j - run);
|
|
||||||
}
|
|
||||||
run = 0;
|
|
||||||
}
|
|
||||||
else run++;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
buf++;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (run > max_range) start_pos = (s64) i * 8 - run;
|
|
||||||
|
|
||||||
return start_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, u8 *writeback)
|
|
||||||
{
|
|
||||||
s64 written;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering\n");
|
|
||||||
|
|
||||||
if (!*writeback) return 0;
|
|
||||||
|
|
||||||
*writeback = 0;
|
|
||||||
|
|
||||||
written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b);
|
|
||||||
if (written != size)
|
|
||||||
{
|
|
||||||
if (!written) errno = EIO;
|
|
||||||
ntfs_log_perror("Bitmap write error (%lld, %lld)",
|
|
||||||
(long long)pos, (long long)size);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_cluster_alloc - allocate clusters on an ntfs volume
|
|
||||||
* @vol: mounted ntfs volume on which to allocate the clusters
|
|
||||||
* @start_vcn: vcn to use for the first allocated cluster
|
|
||||||
* @count: number of clusters to allocate
|
|
||||||
* @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
|
|
||||||
* @zone: zone from which to allocate the clusters
|
|
||||||
*
|
|
||||||
* Allocate @count clusters preferably starting at cluster @start_lcn or at the
|
|
||||||
* current allocator position if @start_lcn is -1, on the mounted ntfs volume
|
|
||||||
* @vol. @zone is either DATA_ZONE for allocation of normal clusters and
|
|
||||||
* MFT_ZONE for allocation of clusters for the master file table, i.e. the
|
|
||||||
* $MFT/$DATA attribute.
|
|
||||||
*
|
|
||||||
* On success return a runlist describing the allocated cluster(s).
|
|
||||||
*
|
|
||||||
* On error return NULL with errno set to the error code.
|
|
||||||
*
|
|
||||||
* Notes on the allocation algorithm
|
|
||||||
* =================================
|
|
||||||
*
|
|
||||||
* There are two data zones. First is the area between the end of the mft zone
|
|
||||||
* and the end of the volume, and second is the area between the start of the
|
|
||||||
* volume and the start of the mft zone. On unmodified/standard NTFS 1.x
|
|
||||||
* volumes, the second data zone doesn't exist due to the mft zone being
|
|
||||||
* expanded to cover the start of the volume in order to reserve space for the
|
|
||||||
* mft bitmap attribute.
|
|
||||||
*
|
|
||||||
* The complexity stems from the need of implementing the mft vs data zoned
|
|
||||||
* approach and from the fact that we have access to the lcn bitmap via up to
|
|
||||||
* NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over
|
|
||||||
* boundaries of two buffers. Further, the fact that the allocator allows for
|
|
||||||
* caller supplied hints as to the location of where allocation should begin
|
|
||||||
* and the fact that the allocator keeps track of where in the data zones the
|
|
||||||
* next natural allocation should occur, contribute to the complexity of the
|
|
||||||
* function. But it should all be worthwhile, because this allocator:
|
|
||||||
* 1) implements MFT zone reservation
|
|
||||||
* 2) causes reduction in fragmentation.
|
|
||||||
* The code is not optimized for speed.
|
|
||||||
*/
|
|
||||||
runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, LCN start_lcn,
|
|
||||||
const NTFS_CLUSTER_ALLOCATION_ZONES zone)
|
|
||||||
{
|
|
||||||
LCN zone_start, zone_end; /* current search range */
|
|
||||||
LCN last_read_pos, lcn;
|
|
||||||
LCN bmp_pos; /* current bit position inside the bitmap */
|
|
||||||
LCN prev_lcn = 0, prev_run_len = 0;
|
|
||||||
s64 clusters, br;
|
|
||||||
runlist *rl = NULL, *trl;
|
|
||||||
u8 *buf, *byte, bit, writeback;
|
|
||||||
u8 pass = 1; /* 1: inside zone; 2: start of zone */
|
|
||||||
u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */
|
|
||||||
u8 done_zones = 0;
|
|
||||||
u8 has_guess, used_zone_pos;
|
|
||||||
int err = 0, rlpos, rlsize, buf_size;
|
|
||||||
|
|
||||||
ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, "
|
|
||||||
"zone = %s_ZONE.\n", (long long)count, (long long)
|
|
||||||
start_lcn, zone == MFT_ZONE ? "MFT" : "DATA");
|
|
||||||
|
|
||||||
if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || (s8) zone < FIRST_ZONE || zone > LAST_ZONE)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld",
|
|
||||||
__FUNCTION__, (long long)start_vcn,
|
|
||||||
(long long)count, (long long)start_lcn);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return empty runlist if @count == 0 */
|
|
||||||
if (!count)
|
|
||||||
{
|
|
||||||
rl = ntfs_malloc(0x1000);
|
|
||||||
if (rl)
|
|
||||||
{
|
|
||||||
rl[0].vcn = start_vcn;
|
|
||||||
rl[0].lcn = LCN_RL_NOT_MAPPED;
|
|
||||||
rl[0].length = 0;
|
|
||||||
}
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE);
|
|
||||||
if (!buf) goto out;
|
|
||||||
/*
|
|
||||||
* If no @start_lcn was requested, use the current zone
|
|
||||||
* position otherwise use the requested @start_lcn.
|
|
||||||
*/
|
|
||||||
has_guess = 1;
|
|
||||||
zone_start = start_lcn;
|
|
||||||
|
|
||||||
if (zone_start < 0)
|
|
||||||
{
|
|
||||||
if (zone == DATA_ZONE)
|
|
||||||
zone_start = vol->data1_zone_pos;
|
|
||||||
else zone_start = vol->mft_zone_pos;
|
|
||||||
has_guess = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
used_zone_pos = has_guess ? 0 : 1;
|
|
||||||
|
|
||||||
if (!zone_start || zone_start == vol->mft_zone_start || zone_start == vol->mft_zone_end) pass = 2;
|
|
||||||
|
|
||||||
if (zone_start < vol->mft_zone_start)
|
|
||||||
{
|
|
||||||
zone_end = vol->mft_zone_start;
|
|
||||||
search_zone = ZONE_DATA2;
|
|
||||||
}
|
|
||||||
else if (zone_start < vol->mft_zone_end)
|
|
||||||
{
|
|
||||||
zone_end = vol->mft_zone_end;
|
|
||||||
search_zone = ZONE_MFT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
zone_end = vol->nr_clusters;
|
|
||||||
search_zone = ZONE_DATA1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bmp_pos = zone_start;
|
|
||||||
|
|
||||||
/* Loop until all clusters are allocated. */
|
|
||||||
clusters = count;
|
|
||||||
rlpos = rlsize = 0;
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* check whether we have exhausted the current zone */
|
|
||||||
if (search_zone & vol->full_zones) goto zone_pass_done;
|
|
||||||
last_read_pos = bmp_pos >> 3;
|
|
||||||
br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, NTFS_LCNALLOC_BSIZE, buf);
|
|
||||||
if (br <= 0)
|
|
||||||
{
|
|
||||||
if (!br) goto zone_pass_done;
|
|
||||||
err = errno;
|
|
||||||
ntfs_log_perror("Reading $BITMAP failed");
|
|
||||||
goto err_ret;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* We might have read less than NTFS_LCNALLOC_BSIZE bytes
|
|
||||||
* if we are close to the end of the attribute.
|
|
||||||
*/
|
|
||||||
buf_size = (int) br << 3;
|
|
||||||
lcn = bmp_pos & 7;
|
|
||||||
bmp_pos &= ~7;
|
|
||||||
writeback = 0;
|
|
||||||
|
|
||||||
while (lcn < buf_size)
|
|
||||||
{
|
|
||||||
byte = buf + (lcn >> 3);
|
|
||||||
bit = 1 << (lcn & 7);
|
|
||||||
if (has_guess)
|
|
||||||
{
|
|
||||||
if (*byte & bit)
|
|
||||||
{
|
|
||||||
has_guess = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lcn = max_empty_bit_range(buf, br);
|
|
||||||
if (lcn < 0) break;
|
|
||||||
has_guess = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First free bit is at lcn + bmp_pos. */
|
|
||||||
|
|
||||||
/* Reallocate memory if necessary. */
|
|
||||||
if ((rlpos + 2) * (int) sizeof(runlist) >= rlsize)
|
|
||||||
{
|
|
||||||
rlsize += 4096;
|
|
||||||
trl = realloc(rl, rlsize);
|
|
||||||
if (!trl)
|
|
||||||
{
|
|
||||||
err = ENOMEM;
|
|
||||||
ntfs_log_perror("realloc() failed");
|
|
||||||
goto wb_err_ret;
|
|
||||||
}
|
|
||||||
rl = trl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate the bitmap bit. */
|
|
||||||
*byte |= bit;
|
|
||||||
writeback = 1;
|
|
||||||
if (vol->free_clusters <= 0)
|
|
||||||
ntfs_log_error("Non-positive free clusters "
|
|
||||||
"(%lld)!\n",
|
|
||||||
(long long)vol->free_clusters);
|
|
||||||
else vol->free_clusters--;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Coalesce with previous run if adjacent LCNs.
|
|
||||||
* Otherwise, append a new run.
|
|
||||||
*/
|
|
||||||
if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos)
|
|
||||||
{
|
|
||||||
ntfs_log_debug("Cluster coalesce: prev_lcn: "
|
|
||||||
"%lld lcn: %lld bmp_pos: %lld "
|
|
||||||
"prev_run_len: %lld\n",
|
|
||||||
(long long)prev_lcn,
|
|
||||||
(long long)lcn, (long long)bmp_pos,
|
|
||||||
(long long)prev_run_len);
|
|
||||||
rl[rlpos - 1].length = ++prev_run_len;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (rlpos)
|
|
||||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + prev_run_len;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rl[rlpos].vcn = start_vcn;
|
|
||||||
ntfs_log_debug("Start_vcn: %lld\n",
|
|
||||||
(long long)start_vcn);
|
|
||||||
}
|
|
||||||
|
|
||||||
rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;
|
|
||||||
rl[rlpos].length = prev_run_len = 1;
|
|
||||||
rlpos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n",
|
|
||||||
(long long)rl[rlpos - 1].vcn,
|
|
||||||
(long long)rl[rlpos - 1].lcn,
|
|
||||||
(long long)rl[rlpos - 1].length);
|
|
||||||
/* Done? */
|
|
||||||
if (!--clusters)
|
|
||||||
{
|
|
||||||
if (used_zone_pos) ntfs_cluster_update_zone_pos(vol, search_zone, lcn + bmp_pos + 1
|
|
||||||
+ NTFS_LCNALLOC_SKIP);
|
|
||||||
goto done_ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
lcn++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback))
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
goto err_ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!used_zone_pos)
|
|
||||||
{
|
|
||||||
|
|
||||||
used_zone_pos = 1;
|
|
||||||
|
|
||||||
if (search_zone == ZONE_MFT)
|
|
||||||
zone_start = vol->mft_zone_pos;
|
|
||||||
else if (search_zone == ZONE_DATA1)
|
|
||||||
zone_start = vol->data1_zone_pos;
|
|
||||||
else zone_start = vol->data2_zone_pos;
|
|
||||||
|
|
||||||
if (!zone_start || zone_start == vol->mft_zone_start || zone_start == vol->mft_zone_end) pass = 2;
|
|
||||||
bmp_pos = zone_start;
|
|
||||||
}
|
|
||||||
else bmp_pos += buf_size;
|
|
||||||
|
|
||||||
if (bmp_pos < zone_end) continue;
|
|
||||||
|
|
||||||
zone_pass_done:
|
|
||||||
ntfs_log_trace("Finished current zone pass(%i).\n", pass);
|
|
||||||
if (pass == 1)
|
|
||||||
{
|
|
||||||
pass = 2;
|
|
||||||
zone_end = zone_start;
|
|
||||||
|
|
||||||
if (search_zone == ZONE_MFT)
|
|
||||||
zone_start = vol->mft_zone_start;
|
|
||||||
else if (search_zone == ZONE_DATA1)
|
|
||||||
zone_start = vol->mft_zone_end;
|
|
||||||
else zone_start = 0;
|
|
||||||
|
|
||||||
/* Sanity check. */
|
|
||||||
if (zone_end < zone_start) zone_end = zone_start;
|
|
||||||
|
|
||||||
bmp_pos = zone_start;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* pass == 2 */
|
|
||||||
done_zones_check: done_zones |= search_zone;
|
|
||||||
vol->full_zones |= search_zone;
|
|
||||||
if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2))
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Switching zone.\n");
|
|
||||||
pass = 1;
|
|
||||||
if (rlpos)
|
|
||||||
{
|
|
||||||
LCN tc = rl[rlpos - 1].lcn + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP;
|
|
||||||
|
|
||||||
if (used_zone_pos) ntfs_cluster_update_zone_pos(vol, search_zone, tc);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (search_zone)
|
|
||||||
{
|
|
||||||
case ZONE_MFT:
|
|
||||||
ntfs_log_trace("Zone switch: mft -> data1\n");
|
|
||||||
switch_to_data1_zone: search_zone = ZONE_DATA1;
|
|
||||||
zone_start = vol->data1_zone_pos;
|
|
||||||
zone_end = vol->nr_clusters;
|
|
||||||
if (zone_start == vol->mft_zone_end) pass = 2;
|
|
||||||
break;
|
|
||||||
case ZONE_DATA1:
|
|
||||||
ntfs_log_trace("Zone switch: data1 -> data2\n");
|
|
||||||
search_zone = ZONE_DATA2;
|
|
||||||
zone_start = vol->data2_zone_pos;
|
|
||||||
zone_end = vol->mft_zone_start;
|
|
||||||
if (!zone_start) pass = 2;
|
|
||||||
break;
|
|
||||||
case ZONE_DATA2:
|
|
||||||
if (!(done_zones & ZONE_DATA1))
|
|
||||||
{
|
|
||||||
ntfs_log_trace("data2 -> data1\n");
|
|
||||||
goto switch_to_data1_zone;
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Zone switch: data2 -> mft\n");
|
|
||||||
search_zone = ZONE_MFT;
|
|
||||||
zone_start = vol->mft_zone_pos;
|
|
||||||
zone_end = vol->mft_zone_end;
|
|
||||||
if (zone_start == vol->mft_zone_start) pass = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bmp_pos = zone_start;
|
|
||||||
|
|
||||||
if (zone_start == zone_end)
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Empty zone, skipped.\n");
|
|
||||||
goto done_zones_check;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_trace("All zones are finished, no space on device.\n");
|
|
||||||
err = ENOSPC;
|
|
||||||
goto err_ret;
|
|
||||||
}
|
|
||||||
done_ret:
|
|
||||||
ntfs_log_debug("At done_ret.\n");
|
|
||||||
/* Add runlist terminator element. */
|
|
||||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
|
||||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
|
||||||
rl[rlpos].length = 0;
|
|
||||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback))
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
goto err_ret;
|
|
||||||
}
|
|
||||||
done_err_ret: free(buf);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
errno = err;
|
|
||||||
ntfs_log_perror("Failed to allocate clusters");
|
|
||||||
rl = NULL;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
ntfs_log_leave("\n");
|
|
||||||
return rl;
|
|
||||||
|
|
||||||
wb_err_ret:
|
|
||||||
ntfs_log_trace("At wb_err_ret.\n");
|
|
||||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) err = errno;
|
|
||||||
err_ret:
|
|
||||||
ntfs_log_trace("At err_ret.\n");
|
|
||||||
if (rl)
|
|
||||||
{
|
|
||||||
/* Add runlist terminator element. */
|
|
||||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
|
||||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
|
||||||
rl[rlpos].length = 0;
|
|
||||||
ntfs_debug_runlist_dump(rl);
|
|
||||||
ntfs_cluster_free_from_rl(vol, rl);
|
|
||||||
free(rl);
|
|
||||||
rl = NULL;
|
|
||||||
}
|
|
||||||
goto done_err_ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_cluster_free_from_rl - free clusters from runlist
|
|
||||||
* @vol: mounted ntfs volume on which to free the clusters
|
|
||||||
* @rl: runlist from which deallocate clusters
|
|
||||||
*
|
|
||||||
* On success return 0 and on error return -1 with errno set to the error code.
|
|
||||||
*/
|
|
||||||
int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
|
|
||||||
{
|
|
||||||
s64 nr_freed = 0;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
|
|
||||||
for (; rl->length; rl++)
|
|
||||||
{
|
|
||||||
|
|
||||||
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
|
||||||
(long long)rl->lcn, (long long)rl->length);
|
|
||||||
|
|
||||||
if (rl->lcn >= 0)
|
|
||||||
{
|
|
||||||
update_full_status(vol, rl->lcn);
|
|
||||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, rl->length))
|
|
||||||
{
|
|
||||||
ntfs_log_perror("Cluster deallocation failed "
|
|
||||||
"(%lld, %lld)",
|
|
||||||
(long long)rl->lcn,
|
|
||||||
(long long)rl->length);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
nr_freed += rl->length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
out: vol->free_clusters += nr_freed;
|
|
||||||
if (vol->free_clusters > vol->nr_clusters) ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
|
||||||
(long long)vol->free_clusters,
|
|
||||||
(long long)vol->nr_clusters);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Basic cluster run free
|
|
||||||
* Returns 0 if successful
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count)
|
|
||||||
{
|
|
||||||
s64 nr_freed = 0;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
|
||||||
(long long)lcn, (long long)count);
|
|
||||||
|
|
||||||
if (lcn >= 0)
|
|
||||||
{
|
|
||||||
update_full_status(vol, lcn);
|
|
||||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, count))
|
|
||||||
{
|
|
||||||
ntfs_log_perror("Cluster deallocation failed "
|
|
||||||
"(%lld, %lld)",
|
|
||||||
(long long)lcn,
|
|
||||||
(long long)count);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
nr_freed += count;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
out: vol->free_clusters += nr_freed;
|
|
||||||
if (vol->free_clusters > vol->nr_clusters) ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
|
||||||
(long long)vol->free_clusters,
|
|
||||||
(long long)vol->nr_clusters);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_cluster_free - free clusters on an ntfs volume
|
|
||||||
* @vol: mounted ntfs volume on which to free the clusters
|
|
||||||
* @na: attribute whose runlist describes the clusters to free
|
|
||||||
* @start_vcn: vcn in @rl at which to start freeing clusters
|
|
||||||
* @count: number of clusters to free or -1 for all clusters
|
|
||||||
*
|
|
||||||
* Free @count clusters starting at the cluster @start_vcn in the runlist
|
|
||||||
* described by the attribute @na from the mounted ntfs volume @vol.
|
|
||||||
*
|
|
||||||
* If @count is -1, all clusters from @start_vcn to the end of the runlist
|
|
||||||
* are deallocated.
|
|
||||||
*
|
|
||||||
* On success return the number of deallocated clusters (not counting sparse
|
|
||||||
* clusters) and on error return -1 with errno set to the error code.
|
|
||||||
*/
|
|
||||||
int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count)
|
|
||||||
{
|
|
||||||
runlist *rl;
|
|
||||||
s64 delta, to_free, nr_freed = 0;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || (count < 0 && count != -1))
|
|
||||||
{
|
|
||||||
ntfs_log_trace("Invalid arguments!\n");
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, "
|
|
||||||
"vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no,
|
|
||||||
na->type, (long long)count, (long long)start_vcn);
|
|
||||||
|
|
||||||
rl = ntfs_attr_find_vcn(na, start_vcn);
|
|
||||||
if (!rl)
|
|
||||||
{
|
|
||||||
if (errno == ENOENT) ret = 0;
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rl->lcn < 0 && rl->lcn != LCN_HOLE)
|
|
||||||
{
|
|
||||||
errno = EIO;
|
|
||||||
ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__,
|
|
||||||
(long long)rl->lcn);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the starting cluster inside the run that needs freeing. */
|
|
||||||
delta = start_vcn - rl->vcn;
|
|
||||||
|
|
||||||
/* The number of clusters in this run that need freeing. */
|
|
||||||
to_free = rl->length - delta;
|
|
||||||
if (count >= 0 && to_free > count) to_free = count;
|
|
||||||
|
|
||||||
if (rl->lcn != LCN_HOLE)
|
|
||||||
{
|
|
||||||
/* Do the actual freeing of the clusters in this run. */
|
|
||||||
update_full_status(vol, rl->lcn + delta);
|
|
||||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, to_free)) goto leave;
|
|
||||||
nr_freed = to_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go to the next run and adjust the number of clusters left to free. */
|
|
||||||
++rl;
|
|
||||||
if (count >= 0) count -= to_free;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Loop over the remaining runs, using @count as a capping value, and
|
|
||||||
* free them.
|
|
||||||
*/
|
|
||||||
for (; rl->length && count != 0; ++rl)
|
|
||||||
{
|
|
||||||
// FIXME: Need to try ntfs_attr_map_runlist() for attribute
|
|
||||||
// list support! (AIA)
|
|
||||||
if (rl->lcn < 0 && rl->lcn != LCN_HOLE)
|
|
||||||
{
|
|
||||||
// FIXME: Eeek! We need rollback! (AIA)
|
|
||||||
errno = EIO;
|
|
||||||
ntfs_log_perror("%s: Invalid lcn (%lli)",
|
|
||||||
__FUNCTION__, (long long)rl->lcn);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The number of clusters in this run that need freeing. */
|
|
||||||
to_free = rl->length;
|
|
||||||
if (count >= 0 && to_free > count) to_free = count;
|
|
||||||
|
|
||||||
if (rl->lcn != LCN_HOLE)
|
|
||||||
{
|
|
||||||
update_full_status(vol, rl->lcn);
|
|
||||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, to_free))
|
|
||||||
{
|
|
||||||
// FIXME: Eeek! We need rollback! (AIA)
|
|
||||||
ntfs_log_perror("%s: Clearing bitmap run failed",
|
|
||||||
__FUNCTION__);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
nr_freed += to_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count >= 0) count -= to_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count != -1 && count != 0)
|
|
||||||
{
|
|
||||||
// FIXME: Eeek! BUG()
|
|
||||||
errno = EIO;
|
|
||||||
ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__,
|
|
||||||
(long long)count);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = nr_freed;
|
|
||||||
out: vol->free_clusters += nr_freed;
|
|
||||||
if (vol->free_clusters > vol->nr_clusters) ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
|
||||||
(long long)vol->free_clusters,
|
|
||||||
(long long)vol->nr_clusters);
|
|
||||||
leave:
|
|
||||||
ntfs_log_leave("\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
@ -1,735 +0,0 @@
|
|||||||
/**
|
|
||||||
* logfile.c - NTFS journal handling. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2002-2005 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2005 Yura Pakhuchiy
|
|
||||||
* Copyright (c) 2005-2009 Szabolcs Szakacsits
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "logfile.h"
|
|
||||||
#include "volume.h"
|
|
||||||
#include "mst.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_check_restart_page_header - check the page header for consistency
|
|
||||||
* @rp: restart page header to check
|
|
||||||
* @pos: position in logfile at which the restart page header resides
|
|
||||||
*
|
|
||||||
* Check the restart page header @rp for consistency and return TRUE if it is
|
|
||||||
* consistent and FALSE otherwise.
|
|
||||||
*
|
|
||||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
|
||||||
* require the full restart page.
|
|
||||||
*/
|
|
||||||
static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
|
|
||||||
{
|
|
||||||
u32 logfile_system_page_size, logfile_log_page_size;
|
|
||||||
u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
|
|
||||||
BOOL have_usa = TRUE;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
/*
|
|
||||||
* If the system or log page sizes are smaller than the ntfs block size
|
|
||||||
* or either is not a power of 2 we cannot handle this log file.
|
|
||||||
*/
|
|
||||||
logfile_system_page_size = le32_to_cpu(rp->system_page_size);
|
|
||||||
logfile_log_page_size = le32_to_cpu(rp->log_page_size);
|
|
||||||
if (logfile_system_page_size < NTFS_BLOCK_SIZE || logfile_log_page_size < NTFS_BLOCK_SIZE
|
|
||||||
|| logfile_system_page_size & (logfile_system_page_size - 1) || logfile_log_page_size
|
|
||||||
& (logfile_log_page_size - 1))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile uses unsupported page size.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* We must be either at !pos (1st restart page) or at pos = system page
|
|
||||||
* size (2nd restart page).
|
|
||||||
*/
|
|
||||||
if (pos && pos != logfile_system_page_size)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Found restart area in incorrect "
|
|
||||||
"position in $LogFile.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* We only know how to handle version 1.1. */
|
|
||||||
if (sle16_to_cpu(rp->major_ver) != 1 || sle16_to_cpu(rp->minor_ver) != 1)
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile version %i.%i is not "
|
|
||||||
"supported. (This driver supports version "
|
|
||||||
"1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver),
|
|
||||||
(int)sle16_to_cpu(rp->minor_ver));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* If chkdsk has been run the restart page may not be protected by an
|
|
||||||
* update sequence array.
|
|
||||||
*/
|
|
||||||
if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count))
|
|
||||||
{
|
|
||||||
have_usa = FALSE;
|
|
||||||
goto skip_usa_checks;
|
|
||||||
}
|
|
||||||
/* Verify the size of the update sequence array. */
|
|
||||||
usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
|
|
||||||
if (usa_count != le16_to_cpu(rp->usa_count))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart page specifies "
|
|
||||||
"inconsistent update sequence array count.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* Verify the position of the update sequence array. */
|
|
||||||
usa_ofs = le16_to_cpu(rp->usa_ofs);
|
|
||||||
usa_end = usa_ofs + usa_count * sizeof(u16);
|
|
||||||
if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || usa_end > NTFS_BLOCK_SIZE - sizeof(u16))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart page specifies "
|
|
||||||
"inconsistent update sequence array offset.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
skip_usa_checks:
|
|
||||||
/*
|
|
||||||
* Verify the position of the restart area. It must be:
|
|
||||||
* - aligned to 8-byte boundary,
|
|
||||||
* - after the update sequence array, and
|
|
||||||
* - within the system page size.
|
|
||||||
*/
|
|
||||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
|
||||||
if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : ra_ofs < sizeof(RESTART_PAGE_HEADER)) || ra_ofs
|
|
||||||
> logfile_system_page_size)
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart page specifies "
|
|
||||||
"inconsistent restart area offset.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
|
|
||||||
* set.
|
|
||||||
*/
|
|
||||||
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart page is not modified "
|
|
||||||
"by chkdsk but a chkdsk LSN is specified.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done.\n");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_check_restart_area - check the restart area for consistency
|
|
||||||
* @rp: restart page whose restart area to check
|
|
||||||
*
|
|
||||||
* Check the restart area of the restart page @rp for consistency and return
|
|
||||||
* TRUE if it is consistent and FALSE otherwise.
|
|
||||||
*
|
|
||||||
* This function assumes that the restart page header has already been
|
|
||||||
* consistency checked.
|
|
||||||
*
|
|
||||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
|
||||||
* require the full restart page.
|
|
||||||
*/
|
|
||||||
static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
|
|
||||||
{
|
|
||||||
u64 file_size;
|
|
||||||
RESTART_AREA *ra;
|
|
||||||
u16 ra_ofs, ra_len, ca_ofs;
|
|
||||||
u8 fs_bits;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
|
||||||
ra = (RESTART_AREA*) ((u8*) rp + ra_ofs);
|
|
||||||
/*
|
|
||||||
* Everything before ra->file_size must be before the first word
|
|
||||||
* protected by an update sequence number. This ensures that it is
|
|
||||||
* safe to access ra->client_array_offset.
|
|
||||||
*/
|
|
||||||
if (ra_ofs + offsetof(RESTART_AREA, file_size) > NTFS_BLOCK_SIZE - sizeof(u16))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart area specifies "
|
|
||||||
"inconsistent file offset.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Now that we can access ra->client_array_offset, make sure everything
|
|
||||||
* up to the log client array is before the first word protected by an
|
|
||||||
* update sequence number. This ensures we can access all of the
|
|
||||||
* restart area elements safely. Also, the client array offset must be
|
|
||||||
* aligned to an 8-byte boundary.
|
|
||||||
*/
|
|
||||||
ca_ofs = le16_to_cpu(ra->client_array_offset);
|
|
||||||
if (((ca_ofs + 7) & ~7) != ca_ofs || ra_ofs + ca_ofs > (u16) (NTFS_BLOCK_SIZE - sizeof(u16)))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart area specifies "
|
|
||||||
"inconsistent client array offset.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* The restart area must end within the system page size both when
|
|
||||||
* calculated manually and as specified by ra->restart_area_length.
|
|
||||||
* Also, the calculated length must not exceed the specified length.
|
|
||||||
*/
|
|
||||||
ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * sizeof(LOG_CLIENT_RECORD);
|
|
||||||
if ((u32) (ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || (u32) (ra_ofs
|
|
||||||
+ le16_to_cpu(ra->restart_area_length)) > le32_to_cpu(rp->system_page_size) || ra_len
|
|
||||||
> le16_to_cpu(ra->restart_area_length))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart area is out of bounds "
|
|
||||||
"of the system page size specified by the "
|
|
||||||
"restart page header and/or the specified "
|
|
||||||
"restart area length is inconsistent.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* The ra->client_free_list and ra->client_in_use_list must be either
|
|
||||||
* LOGFILE_NO_CLIENT or less than ra->log_clients or they are
|
|
||||||
* overflowing the client array.
|
|
||||||
*/
|
|
||||||
if ((ra->client_free_list != LOGFILE_NO_CLIENT && le16_to_cpu(ra->client_free_list) >= le16_to_cpu(ra->log_clients))
|
|
||||||
|| (ra->client_in_use_list != LOGFILE_NO_CLIENT && le16_to_cpu(ra->client_in_use_list)
|
|
||||||
>= le16_to_cpu(ra->log_clients)))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart area specifies "
|
|
||||||
"overflowing client free and/or in use lists.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Check ra->seq_number_bits against ra->file_size for consistency.
|
|
||||||
* We cannot just use ffs() because the file size is not a power of 2.
|
|
||||||
*/
|
|
||||||
file_size = (u64) sle64_to_cpu(ra->file_size);
|
|
||||||
fs_bits = 0;
|
|
||||||
while (file_size)
|
|
||||||
{
|
|
||||||
file_size >>= 1;
|
|
||||||
fs_bits++;
|
|
||||||
}
|
|
||||||
if (le32_to_cpu(ra->seq_number_bits) != (u32) (67 - fs_bits))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart area specifies "
|
|
||||||
"inconsistent sequence number bits.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* The log record header length must be a multiple of 8. */
|
|
||||||
if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != le16_to_cpu(ra->log_record_header_length))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart area specifies "
|
|
||||||
"inconsistent log record header length.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* Ditto for the log page data offset. */
|
|
||||||
if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != le16_to_cpu(ra->log_page_data_offset))
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile restart area specifies "
|
|
||||||
"inconsistent log page data offset.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done.\n");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_check_log_client_array - check the log client array for consistency
|
|
||||||
* @rp: restart page whose log client array to check
|
|
||||||
*
|
|
||||||
* Check the log client array of the restart page @rp for consistency and
|
|
||||||
* return TRUE if it is consistent and FALSE otherwise.
|
|
||||||
*
|
|
||||||
* This function assumes that the restart page header and the restart area have
|
|
||||||
* already been consistency checked.
|
|
||||||
*
|
|
||||||
* Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
|
|
||||||
* function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
|
|
||||||
* restart page and the page must be multi sector transfer deprotected.
|
|
||||||
*/
|
|
||||||
static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
|
|
||||||
{
|
|
||||||
RESTART_AREA *ra;
|
|
||||||
LOG_CLIENT_RECORD *ca, *cr;
|
|
||||||
u16 nr_clients, idx;
|
|
||||||
BOOL in_free_list, idx_is_first;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
ra = (RESTART_AREA*) ((u8*) rp + le16_to_cpu(rp->restart_area_offset));
|
|
||||||
ca = (LOG_CLIENT_RECORD*) ((u8*) ra + le16_to_cpu(ra->client_array_offset));
|
|
||||||
/*
|
|
||||||
* Check the ra->client_free_list first and then check the
|
|
||||||
* ra->client_in_use_list. Check each of the log client records in
|
|
||||||
* each of the lists and check that the array does not overflow the
|
|
||||||
* ra->log_clients value. Also keep track of the number of records
|
|
||||||
* visited as there cannot be more than ra->log_clients records and
|
|
||||||
* that way we detect eventual loops in within a list.
|
|
||||||
*/
|
|
||||||
nr_clients = le16_to_cpu(ra->log_clients);
|
|
||||||
idx = le16_to_cpu(ra->client_free_list);
|
|
||||||
in_free_list = TRUE;
|
|
||||||
check_list: for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, idx
|
|
||||||
= le16_to_cpu(cr->next_client))
|
|
||||||
{
|
|
||||||
if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) goto err_out;
|
|
||||||
/* Set @cr to the current log client record. */
|
|
||||||
cr = ca + idx;
|
|
||||||
/* The first log client record must not have a prev_client. */
|
|
||||||
if (idx_is_first)
|
|
||||||
{
|
|
||||||
if (cr->prev_client != LOGFILE_NO_CLIENT) goto err_out;
|
|
||||||
idx_is_first = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Switch to and check the in use list if we just did the free list. */
|
|
||||||
if (in_free_list)
|
|
||||||
{
|
|
||||||
in_free_list = FALSE;
|
|
||||||
idx = le16_to_cpu(ra->client_in_use_list);
|
|
||||||
goto check_list;
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done.\n");
|
|
||||||
return TRUE;
|
|
||||||
err_out: ntfs_log_error("$LogFile log client array is corrupt.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_check_and_load_restart_page - check the restart page for consistency
|
|
||||||
* @log_na: opened ntfs attribute for journal $LogFile
|
|
||||||
* @rp: restart page to check
|
|
||||||
* @pos: position in @log_na at which the restart page resides
|
|
||||||
* @wrp: [OUT] copy of the multi sector transfer deprotected restart page
|
|
||||||
* @lsn: [OUT] set to the current logfile lsn on success
|
|
||||||
*
|
|
||||||
* Check the restart page @rp for consistency and return 0 if it is consistent
|
|
||||||
* and errno otherwise. The restart page may have been modified by chkdsk in
|
|
||||||
* which case its magic is CHKD instead of RSTR.
|
|
||||||
*
|
|
||||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
|
||||||
* require the full restart page.
|
|
||||||
*
|
|
||||||
* If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
|
|
||||||
* copy of the complete multi sector transfer deprotected page. On failure,
|
|
||||||
* *@wrp is undefined.
|
|
||||||
*
|
|
||||||
* Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
|
|
||||||
* logfile lsn according to this restart page. On failure, *@lsn is undefined.
|
|
||||||
*
|
|
||||||
* The following error codes are defined:
|
|
||||||
* EINVAL - The restart page is inconsistent.
|
|
||||||
* ENOMEM - Not enough memory to load the restart page.
|
|
||||||
* EIO - Failed to reading from $LogFile.
|
|
||||||
*/
|
|
||||||
static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp, s64 pos,
|
|
||||||
RESTART_PAGE_HEADER **wrp, LSN *lsn)
|
|
||||||
{
|
|
||||||
RESTART_AREA *ra;
|
|
||||||
RESTART_PAGE_HEADER *trp;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
/* Check the restart page header for consistency. */
|
|
||||||
if (!ntfs_check_restart_page_header(rp, pos))
|
|
||||||
{
|
|
||||||
/* Error output already done inside the function. */
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
/* Check the restart area for consistency. */
|
|
||||||
if (!ntfs_check_restart_area(rp))
|
|
||||||
{
|
|
||||||
/* Error output already done inside the function. */
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
ra = (RESTART_AREA*) ((u8*) rp + le16_to_cpu(rp->restart_area_offset));
|
|
||||||
/*
|
|
||||||
* Allocate a buffer to store the whole restart page so we can multi
|
|
||||||
* sector transfer deprotect it.
|
|
||||||
*/
|
|
||||||
trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
|
|
||||||
if (!trp) return errno;
|
|
||||||
/*
|
|
||||||
* Read the whole of the restart page into the buffer. If it fits
|
|
||||||
* completely inside @rp, just copy it from there. Otherwise read it
|
|
||||||
* from disk.
|
|
||||||
*/
|
|
||||||
if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
|
|
||||||
memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
|
|
||||||
else if (ntfs_attr_pread(log_na, pos, le32_to_cpu(rp->system_page_size), trp) != le32_to_cpu(rp->system_page_size))
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
ntfs_log_error("Failed to read whole restart page into the "
|
|
||||||
"buffer.\n");
|
|
||||||
if (err != ENOMEM) err = EIO;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Perform the multi sector transfer deprotection on the buffer if the
|
|
||||||
* restart page is protected.
|
|
||||||
*/
|
|
||||||
if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) && ntfs_mst_post_read_fixup(
|
|
||||||
(NTFS_RECORD*) trp, le32_to_cpu(rp->system_page_size)))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* A multi sector tranfer error was detected. We only need to
|
|
||||||
* abort if the restart page contents exceed the multi sector
|
|
||||||
* transfer fixup of the first sector.
|
|
||||||
*/
|
|
||||||
if (le16_to_cpu(rp->restart_area_offset) + le16_to_cpu(ra->restart_area_length) > NTFS_BLOCK_SIZE
|
|
||||||
- (int) sizeof(u16))
|
|
||||||
{
|
|
||||||
ntfs_log_error("Multi sector transfer error "
|
|
||||||
"detected in $LogFile restart page.\n");
|
|
||||||
err = EINVAL;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* If the restart page is modified by chkdsk or there are no active
|
|
||||||
* logfile clients, the logfile is consistent. Otherwise, need to
|
|
||||||
* check the log client records for consistency, too.
|
|
||||||
*/
|
|
||||||
err = 0;
|
|
||||||
if (ntfs_is_rstr_record(rp->magic) && ra->client_in_use_list != LOGFILE_NO_CLIENT)
|
|
||||||
{
|
|
||||||
if (!ntfs_check_log_client_array(trp))
|
|
||||||
{
|
|
||||||
err = EINVAL;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lsn)
|
|
||||||
{
|
|
||||||
if (ntfs_is_rstr_record(rp->magic))
|
|
||||||
*lsn = sle64_to_cpu(ra->current_lsn);
|
|
||||||
else /* if (ntfs_is_chkd_record(rp->magic)) */
|
|
||||||
*lsn = sle64_to_cpu(rp->chkdsk_lsn);
|
|
||||||
}
|
|
||||||
ntfs_log_trace("Done.\n");
|
|
||||||
if (wrp)
|
|
||||||
*wrp = trp;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err_out: free(trp);
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_check_logfile - check in the journal if the volume is consistent
|
|
||||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
|
||||||
* @rp: [OUT] on success this is a copy of the current restart page
|
|
||||||
*
|
|
||||||
* Check the $LogFile journal for consistency and return TRUE if it is
|
|
||||||
* consistent and FALSE if not. On success, the current restart page is
|
|
||||||
* returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
|
|
||||||
*
|
|
||||||
* At present we only check the two restart pages and ignore the log record
|
|
||||||
* pages.
|
|
||||||
*
|
|
||||||
* Note that the MstProtected flag is not set on the $LogFile inode and hence
|
|
||||||
* when reading pages they are not deprotected. This is because we do not know
|
|
||||||
* if the $LogFile was created on a system with a different page size to ours
|
|
||||||
* yet and mst deprotection would fail if our page size is smaller.
|
|
||||||
*/
|
|
||||||
BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
|
|
||||||
{
|
|
||||||
s64 size, pos;
|
|
||||||
LSN rstr1_lsn, rstr2_lsn;
|
|
||||||
ntfs_volume *vol = log_na->ni->vol;
|
|
||||||
u8 *kaddr = NULL;
|
|
||||||
RESTART_PAGE_HEADER *rstr1_ph = NULL;
|
|
||||||
RESTART_PAGE_HEADER *rstr2_ph = NULL;
|
|
||||||
int log_page_size, log_page_mask, err;
|
|
||||||
BOOL logfile_is_empty = TRUE;
|
|
||||||
u8 log_page_bits;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
/* An empty $LogFile must have been clean before it got emptied. */
|
|
||||||
if (NVolLogFileEmpty(vol)) goto is_empty;
|
|
||||||
size = log_na->data_size;
|
|
||||||
/* Make sure the file doesn't exceed the maximum allowed size. */
|
|
||||||
if (size > (s64) MaxLogFileSize) size = MaxLogFileSize;
|
|
||||||
log_page_size = DefaultLogPageSize;
|
|
||||||
log_page_mask = log_page_size - 1;
|
|
||||||
/*
|
|
||||||
* Use generic_ffs() instead of ffs() to enable the compiler to
|
|
||||||
* optimize log_page_size and log_page_bits into constants.
|
|
||||||
*/
|
|
||||||
log_page_bits = ffs(log_page_size) - 1;
|
|
||||||
size &= ~(log_page_size - 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensure the log file is big enough to store at least the two restart
|
|
||||||
* pages and the minimum number of log record pages.
|
|
||||||
*/
|
|
||||||
if (size < log_page_size * 2 || (size - log_page_size * 2) >> log_page_bits < MinLogRecordPages)
|
|
||||||
{
|
|
||||||
ntfs_log_error("$LogFile is too small.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* Allocate memory for restart page. */
|
|
||||||
kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
|
|
||||||
if (!kaddr) return FALSE;
|
|
||||||
/*
|
|
||||||
* Read through the file looking for a restart page. Since the restart
|
|
||||||
* page header is at the beginning of a page we only need to search at
|
|
||||||
* what could be the beginning of a page (for each page size) rather
|
|
||||||
* than scanning the whole file byte by byte. If all potential places
|
|
||||||
* contain empty and uninitialized records, the log file can be assumed
|
|
||||||
* to be empty.
|
|
||||||
*/
|
|
||||||
for (pos = 0; pos < size; pos <<= 1)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Read first NTFS_BLOCK_SIZE bytes of potential restart page.
|
|
||||||
*/
|
|
||||||
if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != NTFS_BLOCK_SIZE)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
|
|
||||||
"bytes of potential restart page.\n");
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A non-empty block means the logfile is not empty while an
|
|
||||||
* empty block after a non-empty block has been encountered
|
|
||||||
* means we are done.
|
|
||||||
*/
|
|
||||||
if (!ntfs_is_empty_recordp((le32*)kaddr))
|
|
||||||
logfile_is_empty = FALSE;
|
|
||||||
else if (!logfile_is_empty) break;
|
|
||||||
/*
|
|
||||||
* A log record page means there cannot be a restart page after
|
|
||||||
* this so no need to continue searching.
|
|
||||||
*/
|
|
||||||
if (ntfs_is_rcrd_recordp((le32*)kaddr)) break;
|
|
||||||
/* If not a (modified by chkdsk) restart page, continue. */
|
|
||||||
if (!ntfs_is_rstr_recordp((le32*)kaddr) && !ntfs_is_chkd_recordp((le32*)kaddr))
|
|
||||||
{
|
|
||||||
if (!pos) pos = NTFS_BLOCK_SIZE >> 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Check the (modified by chkdsk) restart page for consistency
|
|
||||||
* and get a copy of the complete multi sector transfer
|
|
||||||
* deprotected restart page.
|
|
||||||
*/
|
|
||||||
err = ntfs_check_and_load_restart_page(log_na, (RESTART_PAGE_HEADER*) kaddr, pos, !rstr1_ph ? &rstr1_ph
|
|
||||||
: &rstr2_ph, !rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
|
|
||||||
if (!err)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If we have now found the first (modified by chkdsk)
|
|
||||||
* restart page, continue looking for the second one.
|
|
||||||
*/
|
|
||||||
if (!pos)
|
|
||||||
{
|
|
||||||
pos = NTFS_BLOCK_SIZE >> 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* We have now found the second (modified by chkdsk)
|
|
||||||
* restart page, so we can stop looking.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Error output already done inside the function. Note, we do
|
|
||||||
* not abort if the restart page was invalid as we might still
|
|
||||||
* find a valid one further in the file.
|
|
||||||
*/
|
|
||||||
if (err != EINVAL) goto err_out;
|
|
||||||
/* Continue looking. */
|
|
||||||
if (!pos) pos = NTFS_BLOCK_SIZE >> 1;
|
|
||||||
}
|
|
||||||
if (kaddr)
|
|
||||||
{
|
|
||||||
free(kaddr);
|
|
||||||
kaddr = NULL;
|
|
||||||
}
|
|
||||||
if (logfile_is_empty)
|
|
||||||
{
|
|
||||||
NVolSetLogFileEmpty(vol);
|
|
||||||
is_empty:
|
|
||||||
ntfs_log_trace("Done. ($LogFile is empty.)\n");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
if (!rstr1_ph)
|
|
||||||
{
|
|
||||||
if (rstr2_ph) ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
|
|
||||||
ntfs_log_error("Did not find any restart pages in "
|
|
||||||
"$LogFile and it was not empty.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* If both restart pages were found, use the more recent one. */
|
|
||||||
if (rstr2_ph)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If the second restart area is more recent, switch to it.
|
|
||||||
* Otherwise just throw it away.
|
|
||||||
*/
|
|
||||||
if (rstr2_lsn > rstr1_lsn)
|
|
||||||
{
|
|
||||||
ntfs_log_debug("Using second restart page as it is more "
|
|
||||||
"recent.\n");
|
|
||||||
free(rstr1_ph);
|
|
||||||
rstr1_ph = rstr2_ph;
|
|
||||||
/* rstr1_lsn = rstr2_lsn; */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ntfs_log_debug("Using first restart page as it is more "
|
|
||||||
"recent.\n");
|
|
||||||
free(rstr2_ph);
|
|
||||||
}
|
|
||||||
rstr2_ph = NULL;
|
|
||||||
}
|
|
||||||
/* All consistency checks passed. */
|
|
||||||
if (rp)
|
|
||||||
*rp = rstr1_ph;
|
|
||||||
else free(rstr1_ph);
|
|
||||||
ntfs_log_trace("Done.\n");
|
|
||||||
return TRUE;
|
|
||||||
err_out: free(kaddr);
|
|
||||||
free(rstr1_ph);
|
|
||||||
free(rstr2_ph);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_is_logfile_clean - check in the journal if the volume is clean
|
|
||||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
|
||||||
* @rp: copy of the current restart page
|
|
||||||
*
|
|
||||||
* Analyze the $LogFile journal and return TRUE if it indicates the volume was
|
|
||||||
* shutdown cleanly and FALSE if not.
|
|
||||||
*
|
|
||||||
* At present we only look at the two restart pages and ignore the log record
|
|
||||||
* pages. This is a little bit crude in that there will be a very small number
|
|
||||||
* of cases where we think that a volume is dirty when in fact it is clean.
|
|
||||||
* This should only affect volumes that have not been shutdown cleanly but did
|
|
||||||
* not have any pending, non-check-pointed i/o, i.e. they were completely idle
|
|
||||||
* at least for the five seconds preceding the unclean shutdown.
|
|
||||||
*
|
|
||||||
* This function assumes that the $LogFile journal has already been consistency
|
|
||||||
* checked by a call to ntfs_check_logfile() and in particular if the $LogFile
|
|
||||||
* is empty this function requires that NVolLogFileEmpty() is true otherwise an
|
|
||||||
* empty volume will be reported as dirty.
|
|
||||||
*/
|
|
||||||
BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
|
|
||||||
{
|
|
||||||
RESTART_AREA *ra;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
/* An empty $LogFile must have been clean before it got emptied. */
|
|
||||||
if (NVolLogFileEmpty(log_na->ni->vol))
|
|
||||||
{
|
|
||||||
ntfs_log_trace("$LogFile is empty\n");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
if (!rp)
|
|
||||||
{
|
|
||||||
ntfs_log_error("Restart page header is NULL\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!ntfs_is_rstr_record(rp->magic) && !ntfs_is_chkd_record(rp->magic))
|
|
||||||
{
|
|
||||||
ntfs_log_error("Restart page buffer is invalid\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ra = (RESTART_AREA*) ((u8*) rp + le16_to_cpu(rp->restart_area_offset));
|
|
||||||
/*
|
|
||||||
* If the $LogFile has active clients, i.e. it is open, and we do not
|
|
||||||
* have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
|
|
||||||
* we assume there was an unclean shutdown.
|
|
||||||
*/
|
|
||||||
if (ra->client_in_use_list != LOGFILE_NO_CLIENT && !(ra->flags & RESTART_VOLUME_IS_CLEAN))
|
|
||||||
{
|
|
||||||
ntfs_log_error("The disk contains an unclean file system (%d, "
|
|
||||||
"%d).\n", le16_to_cpu(ra->client_in_use_list),
|
|
||||||
le16_to_cpu(ra->flags));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
/* $LogFile indicates a clean shutdown. */
|
|
||||||
ntfs_log_trace("$LogFile indicates a clean shutdown\n");
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_empty_logfile - empty the contents of the $LogFile journal
|
|
||||||
* @na: ntfs attribute of journal $LogFile to empty
|
|
||||||
*
|
|
||||||
* Empty the contents of the $LogFile journal @na and return 0 on success and
|
|
||||||
* -1 on error.
|
|
||||||
*
|
|
||||||
* This function assumes that the $LogFile journal has already been consistency
|
|
||||||
* checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
|
|
||||||
* has been used to ensure that the $LogFile is clean.
|
|
||||||
*/
|
|
||||||
int ntfs_empty_logfile(ntfs_attr *na)
|
|
||||||
{
|
|
||||||
s64 pos, count;
|
|
||||||
char buf[NTFS_BUF_SIZE];
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering.\n");
|
|
||||||
|
|
||||||
if (NVolLogFileEmpty(na->ni->vol)) return 0;
|
|
||||||
|
|
||||||
if (!NAttrNonResident(na))
|
|
||||||
{
|
|
||||||
errno = EIO;
|
|
||||||
ntfs_log_perror("Resident $LogFile $DATA attribute");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(buf, -1, NTFS_BUF_SIZE);
|
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
while ((count = na->data_size - pos) > 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (count > NTFS_BUF_SIZE) count = NTFS_BUF_SIZE;
|
|
||||||
|
|
||||||
count = ntfs_attr_pwrite(na, pos, count, buf);
|
|
||||||
if (count <= 0)
|
|
||||||
{
|
|
||||||
ntfs_log_perror("Failed to reset $LogFile");
|
|
||||||
if (count != -1) errno = EIO;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
pos += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
NVolSetLogFileEmpty(na->ni->vol);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,437 +0,0 @@
|
|||||||
/*
|
|
||||||
* logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2000-2005 Anton Altaparmakov
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NTFS_LOGFILE_H
|
|
||||||
#define _NTFS_LOGFILE_H
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "endians.h"
|
|
||||||
#include "layout.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Journal ($LogFile) organization:
|
|
||||||
*
|
|
||||||
* Two restart areas present in the first two pages (restart pages, one restart
|
|
||||||
* area in each page). When the volume is dismounted they should be identical,
|
|
||||||
* except for the update sequence array which usually has a different update
|
|
||||||
* sequence number.
|
|
||||||
*
|
|
||||||
* These are followed by log records organized in pages headed by a log record
|
|
||||||
* header going up to log file size. Not all pages contain log records when a
|
|
||||||
* volume is first formatted, but as the volume ages, all records will be used.
|
|
||||||
* When the log file fills up, the records at the beginning are purged (by
|
|
||||||
* modifying the oldest_lsn to a higher value presumably) and writing begins
|
|
||||||
* at the beginning of the file. Effectively, the log file is viewed as a
|
|
||||||
* circular entity.
|
|
||||||
*
|
|
||||||
* NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept
|
|
||||||
* versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We
|
|
||||||
* probably only want to support 1.1 as this seems to be the current version
|
|
||||||
* and we don't know how that differs from the older versions. The only
|
|
||||||
* exception is if the journal is clean as marked by the two restart pages
|
|
||||||
* then it doesn't matter whether we are on an earlier version. We can just
|
|
||||||
* reinitialize the logfile and start again with version 1.1.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Some $LogFile related constants. */
|
|
||||||
#define MaxLogFileSize 0x100000000ULL
|
|
||||||
#define DefaultLogPageSize 4096
|
|
||||||
#define MinLogRecordPages 48
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct RESTART_PAGE_HEADER - Log file restart page header.
|
|
||||||
*
|
|
||||||
* Begins the restart area.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
/*Ofs*/
|
|
||||||
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
|
||||||
/* 0*/
|
|
||||||
NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */
|
|
||||||
/* 4*/
|
|
||||||
le16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
|
||||||
When creating, set this to be immediately
|
|
||||||
after this header structure (without any
|
|
||||||
alignment). */
|
|
||||||
/* 6*/
|
|
||||||
le16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
|
||||||
|
|
||||||
/* 8*/
|
|
||||||
leLSN chkdsk_lsn; /* The last log file sequence number found by
|
|
||||||
chkdsk. Only used when the magic is changed
|
|
||||||
to "CHKD". Otherwise this is zero. */
|
|
||||||
/* 16*/
|
|
||||||
le32 system_page_size; /* Byte size of system pages when the log file
|
|
||||||
was created, has to be >= 512 and a power of
|
|
||||||
2. Use this to calculate the required size
|
|
||||||
of the usa (usa_count) and add it to usa_ofs.
|
|
||||||
Then verify that the result is less than the
|
|
||||||
value of the restart_area_offset. */
|
|
||||||
/* 20*/
|
|
||||||
le32 log_page_size; /* Byte size of log file pages, has to be >=
|
|
||||||
512 and a power of 2. The default is 4096
|
|
||||||
and is used when the system page size is
|
|
||||||
between 4096 and 8192. Otherwise this is
|
|
||||||
set to the system page size instead. */
|
|
||||||
/* 24*/
|
|
||||||
le16 restart_area_offset;/* Byte offset from the start of this header to
|
|
||||||
the RESTART_AREA. Value has to be aligned
|
|
||||||
to 8-byte boundary. When creating, set this
|
|
||||||
to be after the usa. */
|
|
||||||
/* 26*/
|
|
||||||
sle16 minor_ver; /* Log file minor version. Only check if major
|
|
||||||
version is 1. */
|
|
||||||
/* 28*/
|
|
||||||
sle16 major_ver; /* Log file major version. We only support
|
|
||||||
version 1.1. */
|
|
||||||
/* sizeof() = 30 (0x1e) bytes */
|
|
||||||
}__attribute__((__packed__)) RESTART_PAGE_HEADER;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constant for the log client indices meaning that there are no client records
|
|
||||||
* in this particular client array. Also inside the client records themselves,
|
|
||||||
* this means that there are no client records preceding or following this one.
|
|
||||||
*/
|
|
||||||
#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff)
|
|
||||||
#define LOGFILE_NO_CLIENT_CPU 0xffff
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These are the so far known RESTART_AREA_* flags (16-bit) which contain
|
|
||||||
* information about the log file in which they are present.
|
|
||||||
*/
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002), RESTART_SPACE_FILLER = 0xffff,
|
|
||||||
/* gcc: Force enum bit width to 16. */
|
|
||||||
}__attribute__((__packed__));
|
|
||||||
|
|
||||||
typedef le16 RESTART_AREA_FLAGS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct RESTART_AREA - Log file restart area record.
|
|
||||||
*
|
|
||||||
* The offset of this record is found by adding the offset of the
|
|
||||||
* RESTART_PAGE_HEADER to the restart_area_offset value found in it.
|
|
||||||
* See notes at restart_area_offset above.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
/*Ofs*/
|
|
||||||
/* 0*/
|
|
||||||
leLSN current_lsn; /* The current, i.e. last LSN inside the log
|
|
||||||
when the restart area was last written.
|
|
||||||
This happens often but what is the interval?
|
|
||||||
Is it just fixed time or is it every time a
|
|
||||||
check point is written or something else?
|
|
||||||
On create set to 0. */
|
|
||||||
/* 8*/
|
|
||||||
le16 log_clients; /* Number of log client records in the array of
|
|
||||||
log client records which follows this
|
|
||||||
restart area. Must be 1. */
|
|
||||||
/* 10*/
|
|
||||||
le16 client_free_list; /* The index of the first free log client record
|
|
||||||
in the array of log client records.
|
|
||||||
LOGFILE_NO_CLIENT means that there are no
|
|
||||||
free log client records in the array.
|
|
||||||
If != LOGFILE_NO_CLIENT, check that
|
|
||||||
log_clients > client_free_list. On Win2k
|
|
||||||
and presumably earlier, on a clean volume
|
|
||||||
this is != LOGFILE_NO_CLIENT, and it should
|
|
||||||
be 0, i.e. the first (and only) client
|
|
||||||
record is free and thus the logfile is
|
|
||||||
closed and hence clean. A dirty volume
|
|
||||||
would have left the logfile open and hence
|
|
||||||
this would be LOGFILE_NO_CLIENT. On WinXP
|
|
||||||
and presumably later, the logfile is always
|
|
||||||
open, even on clean shutdown so this should
|
|
||||||
always be LOGFILE_NO_CLIENT. */
|
|
||||||
/* 12*/
|
|
||||||
le16 client_in_use_list;/* The index of the first in-use log client
|
|
||||||
record in the array of log client records.
|
|
||||||
LOGFILE_NO_CLIENT means that there are no
|
|
||||||
in-use log client records in the array. If
|
|
||||||
!= LOGFILE_NO_CLIENT check that log_clients
|
|
||||||
> client_in_use_list. On Win2k and
|
|
||||||
presumably earlier, on a clean volume this
|
|
||||||
is LOGFILE_NO_CLIENT, i.e. there are no
|
|
||||||
client records in use and thus the logfile
|
|
||||||
is closed and hence clean. A dirty volume
|
|
||||||
would have left the logfile open and hence
|
|
||||||
this would be != LOGFILE_NO_CLIENT, and it
|
|
||||||
should be 0, i.e. the first (and only)
|
|
||||||
client record is in use. On WinXP and
|
|
||||||
presumably later, the logfile is always
|
|
||||||
open, even on clean shutdown so this should
|
|
||||||
always be 0. */
|
|
||||||
/* 14*/
|
|
||||||
RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k
|
|
||||||
and presumably earlier this is always 0. On
|
|
||||||
WinXP and presumably later, if the logfile
|
|
||||||
was shutdown cleanly, the second bit,
|
|
||||||
RESTART_VOLUME_IS_CLEAN, is set. This bit
|
|
||||||
is cleared when the volume is mounted by
|
|
||||||
WinXP and set when the volume is dismounted,
|
|
||||||
thus if the logfile is dirty, this bit is
|
|
||||||
clear. Thus we don't need to check the
|
|
||||||
Windows version to determine if the logfile
|
|
||||||
is clean. Instead if the logfile is closed,
|
|
||||||
we know it must be clean. If it is open and
|
|
||||||
this bit is set, we also know it must be
|
|
||||||
clean. If on the other hand the logfile is
|
|
||||||
open and this bit is clear, we can be almost
|
|
||||||
certain that the logfile is dirty. */
|
|
||||||
/* 16*/
|
|
||||||
le32 seq_number_bits; /* How many bits to use for the sequence
|
|
||||||
number. This is calculated as 67 - the
|
|
||||||
number of bits required to store the logfile
|
|
||||||
size in bytes and this can be used in with
|
|
||||||
the specified file_size as a consistency
|
|
||||||
check. */
|
|
||||||
/* 20*/
|
|
||||||
le16 restart_area_length;/* Length of the restart area including the
|
|
||||||
client array. Following checks required if
|
|
||||||
version matches. Otherwise, skip them.
|
|
||||||
restart_area_offset + restart_area_length
|
|
||||||
has to be <= system_page_size. Also,
|
|
||||||
restart_area_length has to be >=
|
|
||||||
client_array_offset + (log_clients *
|
|
||||||
sizeof(log client record)). */
|
|
||||||
/* 22*/
|
|
||||||
le16 client_array_offset;/* Offset from the start of this record to
|
|
||||||
the first log client record if versions are
|
|
||||||
matched. When creating, set this to be
|
|
||||||
after this restart area structure, aligned
|
|
||||||
to 8-bytes boundary. If the versions do not
|
|
||||||
match, this is ignored and the offset is
|
|
||||||
assumed to be (sizeof(RESTART_AREA) + 7) &
|
|
||||||
~7, i.e. rounded up to first 8-byte
|
|
||||||
boundary. Either way, client_array_offset
|
|
||||||
has to be aligned to an 8-byte boundary.
|
|
||||||
Also, restart_area_offset +
|
|
||||||
client_array_offset has to be <= 510.
|
|
||||||
Finally, client_array_offset + (log_clients
|
|
||||||
* sizeof(log client record)) has to be <=
|
|
||||||
system_page_size. On Win2k and presumably
|
|
||||||
earlier, this is 0x30, i.e. immediately
|
|
||||||
following this record. On WinXP and
|
|
||||||
presumably later, this is 0x40, i.e. there
|
|
||||||
are 16 extra bytes between this record and
|
|
||||||
the client array. This probably means that
|
|
||||||
the RESTART_AREA record is actually bigger
|
|
||||||
in WinXP and later. */
|
|
||||||
/* 24*/
|
|
||||||
sle64 file_size; /* Usable byte size of the log file. If the
|
|
||||||
restart_area_offset + the offset of the
|
|
||||||
file_size are > 510 then corruption has
|
|
||||||
occurred. This is the very first check when
|
|
||||||
starting with the restart_area as if it
|
|
||||||
fails it means that some of the above values
|
|
||||||
will be corrupted by the multi sector
|
|
||||||
transfer protection. The file_size has to
|
|
||||||
be rounded down to be a multiple of the
|
|
||||||
log_page_size in the RESTART_PAGE_HEADER and
|
|
||||||
then it has to be at least big enough to
|
|
||||||
store the two restart pages and 48 (0x30)
|
|
||||||
log record pages. */
|
|
||||||
/* 32*/
|
|
||||||
le32 last_lsn_data_length;/* Length of data of last LSN, not including
|
|
||||||
the log record header. On create set to
|
|
||||||
0. */
|
|
||||||
/* 36*/
|
|
||||||
le16 log_record_header_length;/* Byte size of the log record header.
|
|
||||||
If the version matches then check that the
|
|
||||||
value of log_record_header_length is a
|
|
||||||
multiple of 8, i.e.
|
|
||||||
(log_record_header_length + 7) & ~7 ==
|
|
||||||
log_record_header_length. When creating set
|
|
||||||
it to sizeof(LOG_RECORD_HEADER), aligned to
|
|
||||||
8 bytes. */
|
|
||||||
/* 38*/
|
|
||||||
le16 log_page_data_offset;/* Offset to the start of data in a log record
|
|
||||||
page. Must be a multiple of 8. On create
|
|
||||||
set it to immediately after the update
|
|
||||||
sequence array of the log record page. */
|
|
||||||
/* 40*/
|
|
||||||
le32 restart_log_open_count;/* A counter that gets incremented every
|
|
||||||
time the logfile is restarted which happens
|
|
||||||
at mount time when the logfile is opened.
|
|
||||||
When creating set to a random value. Win2k
|
|
||||||
sets it to the low 32 bits of the current
|
|
||||||
system time in NTFS format (see time.h). */
|
|
||||||
/* 44*/
|
|
||||||
le32 reserved; /* Reserved/alignment to 8-byte boundary. */
|
|
||||||
/* sizeof() = 48 (0x30) bytes */
|
|
||||||
}__attribute__((__packed__)) RESTART_AREA;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct LOG_CLIENT_RECORD - Log client record.
|
|
||||||
*
|
|
||||||
* The offset of this record is found by adding the offset of the
|
|
||||||
* RESTART_AREA to the client_array_offset value found in it.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
/*Ofs*/
|
|
||||||
/* 0*/
|
|
||||||
leLSN oldest_lsn; /* Oldest LSN needed by this client. On create
|
|
||||||
set to 0. */
|
|
||||||
/* 8*/
|
|
||||||
leLSN client_restart_lsn;/* LSN at which this client needs to restart
|
|
||||||
the volume, i.e. the current position within
|
|
||||||
the log file. At present, if clean this
|
|
||||||
should = current_lsn in restart area but it
|
|
||||||
probably also = current_lsn when dirty most
|
|
||||||
of the time. At create set to 0. */
|
|
||||||
/* 16*/
|
|
||||||
le16 prev_client; /* The offset to the previous log client record
|
|
||||||
in the array of log client records.
|
|
||||||
LOGFILE_NO_CLIENT means there is no previous
|
|
||||||
client record, i.e. this is the first one.
|
|
||||||
This is always LOGFILE_NO_CLIENT. */
|
|
||||||
/* 18*/
|
|
||||||
le16 next_client; /* The offset to the next log client record in
|
|
||||||
the array of log client records.
|
|
||||||
LOGFILE_NO_CLIENT means there are no next
|
|
||||||
client records, i.e. this is the last one.
|
|
||||||
This is always LOGFILE_NO_CLIENT. */
|
|
||||||
/* 20*/
|
|
||||||
le16 seq_number; /* On Win2k and presumably earlier, this is set
|
|
||||||
to zero every time the logfile is restarted
|
|
||||||
and it is incremented when the logfile is
|
|
||||||
closed at dismount time. Thus it is 0 when
|
|
||||||
dirty and 1 when clean. On WinXP and
|
|
||||||
presumably later, this is always 0. */
|
|
||||||
/* 22*/
|
|
||||||
u8 reserved[6]; /* Reserved/alignment. */
|
|
||||||
/* 28*/
|
|
||||||
le32 client_name_length;/* Length of client name in bytes. Should
|
|
||||||
always be 8. */
|
|
||||||
/* 32*/
|
|
||||||
ntfschar client_name[64];/* Name of the client in Unicode. Should
|
|
||||||
always be "NTFS" with the remaining bytes
|
|
||||||
set to 0. */
|
|
||||||
/* sizeof() = 160 (0xa0) bytes */
|
|
||||||
}__attribute__((__packed__)) LOG_CLIENT_RECORD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct RECORD_PAGE_HEADER - Log page record page header.
|
|
||||||
*
|
|
||||||
* Each log page begins with this header and is followed by several LOG_RECORD
|
|
||||||
* structures, starting at offset 0x40 (the size of this structure and the
|
|
||||||
* following update sequence array and then aligned to 8 byte boundary, but is
|
|
||||||
* this specified anywhere?).
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
|
||||||
NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */
|
|
||||||
u16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
|
||||||
When creating, set this to be immediately
|
|
||||||
after this header structure (without any
|
|
||||||
alignment). */
|
|
||||||
u16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
LSN last_lsn;
|
|
||||||
s64 file_offset;
|
|
||||||
}__attribute__((__packed__)) copy;
|
|
||||||
u32 flags;
|
|
||||||
u16 page_count;
|
|
||||||
u16 page_position;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u16 next_record_offset;
|
|
||||||
u8 reserved[6];
|
|
||||||
LSN last_end_lsn;
|
|
||||||
}__attribute__((__packed__)) packed;
|
|
||||||
}__attribute__((__packed__)) header;
|
|
||||||
}__attribute__((__packed__)) RECORD_PAGE_HEADER;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records.
|
|
||||||
*
|
|
||||||
* (Or is it log record pages?)
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */
|
|
||||||
LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff,
|
|
||||||
/* This has nothing to do with the log record. It is only so
|
|
||||||
gcc knows to make the flags 16-bit. */
|
|
||||||
}__attribute__((__packed__)) LOG_RECORD_FLAGS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct LOG_CLIENT_ID - The log client id structure identifying a log client.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
u16 seq_number;
|
|
||||||
u16 client_index;
|
|
||||||
}__attribute__((__packed__)) LOG_CLIENT_ID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct LOG_RECORD - Log record header.
|
|
||||||
*
|
|
||||||
* Each log record seems to have a constant size of 0x70 bytes.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
LSN this_lsn;
|
|
||||||
LSN client_previous_lsn;
|
|
||||||
LSN client_undo_next_lsn;
|
|
||||||
u32 client_data_length;
|
|
||||||
LOG_CLIENT_ID client_id;
|
|
||||||
u32 record_type;
|
|
||||||
u32 transaction_id;
|
|
||||||
u16 flags;
|
|
||||||
u16 reserved_or_alignment[3];
|
|
||||||
/* Now are at ofs 0x30 into struct. */
|
|
||||||
u16 redo_operation;
|
|
||||||
u16 undo_operation;
|
|
||||||
u16 redo_offset;
|
|
||||||
u16 redo_length;
|
|
||||||
u16 undo_offset;
|
|
||||||
u16 undo_length;
|
|
||||||
u16 target_attribute;
|
|
||||||
u16 lcns_to_follow; /* Number of lcn_list entries
|
|
||||||
following this entry. */
|
|
||||||
/* Now at ofs 0x40. */
|
|
||||||
u16 record_offset;
|
|
||||||
u16 attribute_offset;
|
|
||||||
u32 alignment_or_reserved;
|
|
||||||
VCN target_vcn;
|
|
||||||
/* Now at ofs 0x50. */
|
|
||||||
struct
|
|
||||||
{ /* Only present if lcns_to_follow
|
|
||||||
is not 0. */
|
|
||||||
LCN lcn;
|
|
||||||
}__attribute__((__packed__)) lcn_list[0];
|
|
||||||
}__attribute__((__packed__)) LOG_RECORD;
|
|
||||||
|
|
||||||
extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp);
|
|
||||||
extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp);
|
|
||||||
extern int ntfs_empty_logfile(ntfs_attr *na);
|
|
||||||
|
|
||||||
#endif /* defined _NTFS_LOGFILE_H */
|
|
1881
source/libntfs/mft.c
1881
source/libntfs/mft.c
File diff suppressed because it is too large
Load Diff
@ -1,235 +0,0 @@
|
|||||||
/**
|
|
||||||
* mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2006-2009 Szabolcs Szakacsits
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mst.h"
|
|
||||||
#include "logging.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data
|
|
||||||
* @b: pointer to the data to deprotect
|
|
||||||
* @size: size in bytes of @b
|
|
||||||
*
|
|
||||||
* Perform the necessary post read multi sector transfer fixups and detect the
|
|
||||||
* presence of incomplete multi sector transfers. - In that case, overwrite the
|
|
||||||
* magic of the ntfs record header being processed with "BAAD" (in memory only!)
|
|
||||||
* and abort processing.
|
|
||||||
*
|
|
||||||
* Return 0 on success and -1 on error, with errno set to the error code. The
|
|
||||||
* following error codes are defined:
|
|
||||||
* EINVAL Invalid arguments or invalid NTFS record in buffer @b.
|
|
||||||
* EIO Multi sector transfer error was detected. Magic of the NTFS
|
|
||||||
* record in @b will have been set to "BAAD".
|
|
||||||
*/
|
|
||||||
int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
|
|
||||||
{
|
|
||||||
u16 usa_ofs, usa_count, usn;
|
|
||||||
u16 *usa_pos, *data_pos;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering\n");
|
|
||||||
|
|
||||||
/* Setup the variables. */
|
|
||||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
|
||||||
/* Decrement usa_count to get number of fixups. */
|
|
||||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
|
||||||
/* Size and alignment checks. */
|
|
||||||
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || (u32) (usa_ofs + (usa_count * 2)) > size || (size
|
|
||||||
>> NTFS_BLOCK_SIZE_BITS) != usa_count)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d "
|
|
||||||
"usa_count: %d", __FUNCTION__, *(le32 *)b,
|
|
||||||
size, usa_ofs, usa_count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Position of usn in update sequence array. */
|
|
||||||
usa_pos = (u16*) b + usa_ofs / sizeof(u16);
|
|
||||||
/*
|
|
||||||
* The update sequence number which has to be equal to each of the
|
|
||||||
* u16 values before they are fixed up. Note no need to care for
|
|
||||||
* endianness since we are comparing and moving data for on disk
|
|
||||||
* structures which means the data is consistent. - If it is
|
|
||||||
* consistency the wrong endianness it doesn't make any difference.
|
|
||||||
*/
|
|
||||||
usn = *usa_pos;
|
|
||||||
/*
|
|
||||||
* Position in protected data of first u16 that needs fixing up.
|
|
||||||
*/
|
|
||||||
data_pos = (u16*) b + NTFS_BLOCK_SIZE / sizeof(u16) - 1;
|
|
||||||
/*
|
|
||||||
* Check for incomplete multi sector transfer(s).
|
|
||||||
*/
|
|
||||||
while (usa_count--)
|
|
||||||
{
|
|
||||||
if (*data_pos != usn)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Incomplete multi sector transfer detected! )-:
|
|
||||||
* Set the magic to "BAAD" and return failure.
|
|
||||||
* Note that magic_BAAD is already converted to le32.
|
|
||||||
*/
|
|
||||||
errno = EIO;
|
|
||||||
ntfs_log_perror("Incomplete multi-sector transfer: "
|
|
||||||
"magic: 0x%08x size: %d usa_ofs: %d usa_count:"
|
|
||||||
" %d data: %d usn: %d", *(le32 *)b, size,
|
|
||||||
usa_ofs, usa_count, *data_pos, usn);
|
|
||||||
b->magic = magic_BAAD;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
data_pos += NTFS_BLOCK_SIZE / sizeof(u16);
|
|
||||||
}
|
|
||||||
/* Re-setup the variables. */
|
|
||||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
|
||||||
data_pos = (u16*) b + NTFS_BLOCK_SIZE / sizeof(u16) - 1;
|
|
||||||
/* Fixup all sectors. */
|
|
||||||
while (usa_count--)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Increment position in usa and restore original data from
|
|
||||||
* the usa into the data buffer.
|
|
||||||
*/
|
|
||||||
*data_pos = *(++usa_pos);
|
|
||||||
/* Increment position in data as well. */
|
|
||||||
data_pos += NTFS_BLOCK_SIZE / sizeof(u16);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_mst_pre_write_fixup - apply multi sector transfer protection
|
|
||||||
* @b: pointer to the data to protect
|
|
||||||
* @size: size in bytes of @b
|
|
||||||
*
|
|
||||||
* Perform the necessary pre write multi sector transfer fixup on the data
|
|
||||||
* pointer to by @b of @size.
|
|
||||||
*
|
|
||||||
* Return 0 if fixups applied successfully or -1 if no fixups were performed
|
|
||||||
* due to errors. In that case errno i set to the error code (EINVAL).
|
|
||||||
*
|
|
||||||
* NOTE: We consider the absence / invalidity of an update sequence array to
|
|
||||||
* mean error. This means that you have to create a valid update sequence
|
|
||||||
* array header in the ntfs record before calling this function, otherwise it
|
|
||||||
* will fail (the header needs to contain the position of the update sequence
|
|
||||||
* array together with the number of elements in the array). You also need to
|
|
||||||
* initialise the update sequence number before calling this function
|
|
||||||
* otherwise a random word will be used (whatever was in the record at that
|
|
||||||
* position at that time).
|
|
||||||
*/
|
|
||||||
int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size)
|
|
||||||
{
|
|
||||||
u16 usa_ofs, usa_count, usn;
|
|
||||||
u16 *usa_pos, *data_pos;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering\n");
|
|
||||||
|
|
||||||
/* Sanity check + only fixup if it makes sense. */
|
|
||||||
if (!b || ntfs_is_baad_record(b->magic) || ntfs_is_hole_record(b->magic))
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
ntfs_log_perror("%s: bad argument", __FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Setup the variables. */
|
|
||||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
|
||||||
/* Decrement usa_count to get number of fixups. */
|
|
||||||
usa_count = le16_to_cpu(b->usa_count) - 1;
|
|
||||||
/* Size and alignment checks. */
|
|
||||||
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || (u32) (usa_ofs + (usa_count * 2)) > size || (size
|
|
||||||
>> NTFS_BLOCK_SIZE_BITS) != usa_count)
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
ntfs_log_perror("%s", __FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Position of usn in update sequence array. */
|
|
||||||
usa_pos = (u16*) ((u8*) b + usa_ofs);
|
|
||||||
/*
|
|
||||||
* Cyclically increment the update sequence number
|
|
||||||
* (skipping 0 and -1, i.e. 0xffff).
|
|
||||||
*/
|
|
||||||
usn = le16_to_cpup(usa_pos) + 1;
|
|
||||||
if (usn == 0xffff || !usn) usn = 1;
|
|
||||||
usn = cpu_to_le16(usn);
|
|
||||||
*usa_pos = usn;
|
|
||||||
/* Position in data of first u16 that needs fixing up. */
|
|
||||||
data_pos = (u16*) b + NTFS_BLOCK_SIZE / sizeof(u16) - 1;
|
|
||||||
/* Fixup all sectors. */
|
|
||||||
while (usa_count--)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Increment the position in the usa and save the
|
|
||||||
* original data from the data buffer into the usa.
|
|
||||||
*/
|
|
||||||
*(++usa_pos) = *data_pos;
|
|
||||||
/* Apply fixup to data. */
|
|
||||||
*data_pos = usn;
|
|
||||||
/* Increment position in data as well. */
|
|
||||||
data_pos += NTFS_BLOCK_SIZE / sizeof(u16);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data
|
|
||||||
* @b: pointer to the data to deprotect
|
|
||||||
*
|
|
||||||
* Perform the necessary post write multi sector transfer fixup, not checking
|
|
||||||
* for any errors, because we assume we have just used
|
|
||||||
* ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never
|
|
||||||
* have gotten here.
|
|
||||||
*/
|
|
||||||
void ntfs_mst_post_write_fixup(NTFS_RECORD *b)
|
|
||||||
{
|
|
||||||
u16 *usa_pos, *data_pos;
|
|
||||||
|
|
||||||
u16 usa_ofs = le16_to_cpu(b->usa_ofs);
|
|
||||||
u16 usa_count = le16_to_cpu(b->usa_count) - 1;
|
|
||||||
|
|
||||||
ntfs_log_trace("Entering\n");
|
|
||||||
|
|
||||||
/* Position of usn in update sequence array. */
|
|
||||||
usa_pos = (u16*) b + usa_ofs / sizeof(u16);
|
|
||||||
|
|
||||||
/* Position in protected data of first u16 that needs fixing up. */
|
|
||||||
data_pos = (u16*) b + NTFS_BLOCK_SIZE / sizeof(u16) - 1;
|
|
||||||
|
|
||||||
/* Fixup all sectors. */
|
|
||||||
while (usa_count--)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Increment position in usa and restore original data from
|
|
||||||
* the usa into the data buffer.
|
|
||||||
*/
|
|
||||||
*data_pos = *(++usa_pos);
|
|
||||||
|
|
||||||
/* Increment position in data as well. */
|
|
||||||
data_pos += NTFS_BLOCK_SIZE / sizeof(u16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
|||||||
/**
|
|
||||||
* ntfs.h - Simple functionality for startup, mounting and unmounting of NTFS-based devices.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
|
||||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _LIBNTFS_H
|
|
||||||
#define _LIBNTFS_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <gctypes.h>
|
|
||||||
#include <gccore.h>
|
|
||||||
#include <ogc/disc_io.h>
|
|
||||||
|
|
||||||
/* NTFS errno values */
|
|
||||||
#define ENOPART 3000 /* No partition was found */
|
|
||||||
#define EINVALPART 3001 /* Specified partition is invalid or not supported */
|
|
||||||
#define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount */
|
|
||||||
#define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount */
|
|
||||||
|
|
||||||
/* NTFS cache options */
|
|
||||||
#define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */
|
|
||||||
#define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */
|
|
||||||
|
|
||||||
/* NTFS mount flags */
|
|
||||||
#define NTFS_DEFAULT 0x00000000 /* Standard mount, expects a clean, non-hibernated volume */
|
|
||||||
#define NTFS_SHOW_HIDDEN_FILES 0x00000001 /* Display hidden files when enumerating directories */
|
|
||||||
#define NTFS_SHOW_SYSTEM_FILES 0x00000002 /* Display system files when enumerating directories */
|
|
||||||
#define NTFS_UPDATE_ACCESS_TIMES 0x00000004 /* Update file and directory access times */
|
|
||||||
#define NTFS_RECOVER 0x00000008 /* Reset $LogFile if dirty (i.e. from unclean disconnect) */
|
|
||||||
#define NTFS_IGNORE_HIBERFILE 0x00000010 /* Mount even if volume is hibernated */
|
|
||||||
#define NTFS_READ_ONLY 0x00000020 /* Mount in read only mode */
|
|
||||||
#define NTFS_IGNORE_CASE 0x00000040 /* Ignore case sensitivity. Everything must be and will be provided in lowercase. */
|
|
||||||
#define NTFS_SU NTFS_SHOW_HIDDEN_FILES | NTFS_SHOW_SYSTEM_FILES
|
|
||||||
#define NTFS_FORCE NTFS_RECOVER | NTFS_IGNORE_HIBERFILE
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_md - NTFS mount descriptor
|
|
||||||
*/
|
|
||||||
typedef struct _ntfs_md
|
|
||||||
{
|
|
||||||
char name[32]; /* Mount name (can be accessed as "name:/") */
|
|
||||||
const DISC_INTERFACE *interface; /* Block device containing the mounted partition */
|
|
||||||
sec_t startSector; /* Local block address to first sector of partition */
|
|
||||||
} ntfs_md;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find all NTFS partitions on a block device.
|
|
||||||
*
|
|
||||||
* @param INTERFACE The block device to search
|
|
||||||
* @param PARTITIONS (out) A pointer to receive the array of partition start sectors
|
|
||||||
*
|
|
||||||
* @return The number of entries in PARTITIONS or -1 if an error occurred (see errno)
|
|
||||||
* @note The caller is responsible for freeing PARTITIONS when finished with it
|
|
||||||
*/
|
|
||||||
extern int ntfsFindPartitions(const DISC_INTERFACE *interface, sec_t **partitions);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mount all NTFS partitions on all inserted block devices.
|
|
||||||
*
|
|
||||||
* @param MOUNTS (out) A pointer to receive the array of mount descriptors
|
|
||||||
* @param FLAGS Additional mounting flags. (see above)
|
|
||||||
*
|
|
||||||
* @return The number of entries in MOUNTS or -1 if an error occurred (see errno)
|
|
||||||
* @note The caller is responsible for freeing MOUNTS when finished with it
|
|
||||||
* @note All device caches are setup using default values (see above)
|
|
||||||
*/
|
|
||||||
extern int ntfsMountAll(ntfs_md **mounts, u32 flags);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mount all NTFS partitions on a block devices.
|
|
||||||
*
|
|
||||||
* @param INTERFACE The block device to mount.
|
|
||||||
* @param MOUNTS (out) A pointer to receive the array of mount descriptors
|
|
||||||
* @param FLAGS Additional mounting flags. (see above)
|
|
||||||
*
|
|
||||||
* @return The number of entries in MOUNTS or -1 if an error occurred (see errno)
|
|
||||||
* @note The caller is responsible for freeing MOUNTS when finished with it
|
|
||||||
* @note The device cache is setup using default values (see above)
|
|
||||||
*/
|
|
||||||
extern int ntfsMountDevice(const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mount a NTFS partition from a specific sector on a block device.
|
|
||||||
*
|
|
||||||
* @param NAME The name to mount the device under (can then be accessed as "NAME:/")
|
|
||||||
* @param INTERFACE The block device to mount
|
|
||||||
* @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions)
|
|
||||||
* @param CACHEPAGECOUNT The total number of pages in the device cache
|
|
||||||
* @param CACHEPAGESIZE The number of sectors per cache page
|
|
||||||
* @param FLAGS Additional mounting flags (see above)
|
|
||||||
*
|
|
||||||
* @return True if mount was successful, false if no partition was found or an error occurred (see errno)
|
|
||||||
* @note ntfsFindPartitions should be used first to locate the partitions start sector
|
|
||||||
*/
|
|
||||||
extern bool ntfsMount(const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount,
|
|
||||||
u32 cachePageSize, u32 flags);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unmount a NTFS partition.
|
|
||||||
*
|
|
||||||
* @param NAME The name of mount used in ntfsMountSimple() and ntfsMount()
|
|
||||||
* @param FORCE If true unmount even if the device is busy (may lead to data lose)
|
|
||||||
*/
|
|
||||||
extern void ntfsUnmount(const char *name, bool force);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the volume name of a mounted NTFS partition.
|
|
||||||
*
|
|
||||||
* @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount)
|
|
||||||
*
|
|
||||||
* @return The volumes name if successful or NULL if an error occurred (see errno)
|
|
||||||
*/
|
|
||||||
extern const char *ntfsGetVolumeName(const char *name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the volume name of a mounted NTFS partition.
|
|
||||||
*
|
|
||||||
* @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount)
|
|
||||||
* @param VOLUMENAME The new volume name
|
|
||||||
*
|
|
||||||
* @return True if mount was successful, false if an error occurred (see errno)
|
|
||||||
* @note The mount must be write-enabled else this will fail
|
|
||||||
*/
|
|
||||||
extern bool ntfsSetVolumeName(const char *name, const char *volumeName);
|
|
||||||
|
|
||||||
typedef int (*_ntfs_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count);
|
|
||||||
int _NTFS_get_fragments(const char *path, _ntfs_frag_append_t append_fragment, void *callback_data);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _LIBNTFS_H */
|
|
@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* ntfs_dir.c - devoptab directory routines for NTFS-based devices.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
|
||||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NTFSDIR_H
|
|
||||||
#define _NTFSDIR_H
|
|
||||||
|
|
||||||
#include "ntfsinternal.h"
|
|
||||||
#include <sys/reent.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_dir_entry - Directory entry
|
|
||||||
*/
|
|
||||||
typedef struct _ntfs_dir_entry
|
|
||||||
{
|
|
||||||
char *name;
|
|
||||||
u64 mref;
|
|
||||||
struct _ntfs_dir_entry *next;
|
|
||||||
} ntfs_dir_entry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_dir_state - Directory state
|
|
||||||
*/
|
|
||||||
typedef struct _ntfs_dir_state
|
|
||||||
{
|
|
||||||
ntfs_vd *vd; /* Volume this directory belongs to */
|
|
||||||
ntfs_inode *ni; /* Directory descriptor */
|
|
||||||
ntfs_dir_entry *first; /* The first entry in the directory */
|
|
||||||
ntfs_dir_entry *current; /* The current entry in the directory */
|
|
||||||
struct _ntfs_dir_state *prevOpenDir; /* The previous entry in a double-linked FILO list of open directories */
|
|
||||||
struct _ntfs_dir_state *nextOpenDir; /* The next entry in a double-linked FILO list of open directories */
|
|
||||||
} ntfs_dir_state;
|
|
||||||
|
|
||||||
/* Directory state routines */
|
|
||||||
void ntfsCloseDir(ntfs_dir_state *file);
|
|
||||||
|
|
||||||
/* Gekko devoptab directory routines for NTFS-based devices */
|
|
||||||
extern int ntfs_stat_r(struct _reent *r, const char *path, struct stat *st);
|
|
||||||
extern int ntfs_link_r(struct _reent *r, const char *existing, const char *newLink);
|
|
||||||
extern int ntfs_unlink_r(struct _reent *r, const char *name);
|
|
||||||
extern int ntfs_chdir_r(struct _reent *r, const char *name);
|
|
||||||
extern int ntfs_rename_r(struct _reent *r, const char *oldName, const char *newName);
|
|
||||||
extern int ntfs_mkdir_r(struct _reent *r, const char *path, int mode);
|
|
||||||
extern int ntfs_statvfs_r(struct _reent *r, const char *path, struct statvfs *buf);
|
|
||||||
|
|
||||||
/* Gekko devoptab directory walking routines for NTFS-based devices */
|
|
||||||
extern DIR_ITER *ntfs_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path);
|
|
||||||
extern int ntfs_dirreset_r(struct _reent *r, DIR_ITER *dirState);
|
|
||||||
extern int ntfs_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
|
||||||
extern int ntfs_dirclose_r(struct _reent *r, DIR_ITER *dirState);
|
|
||||||
|
|
||||||
#endif /* _NTFSDIR_H */
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* ntfsfile.c - devoptab file routines for NTFS-based devices.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
|
||||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NTFSFILE_H
|
|
||||||
#define _NTFSFILE_H
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ntfsinternal.h"
|
|
||||||
#include <sys/reent.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_file_state - File state
|
|
||||||
*/
|
|
||||||
typedef struct _ntfs_file_state
|
|
||||||
{
|
|
||||||
ntfs_vd *vd; /* Volume this file belongs to */
|
|
||||||
ntfs_inode *ni; /* File descriptor */
|
|
||||||
ntfs_attr *data_na; /* File data descriptor */
|
|
||||||
int flags; /* Opening flags */
|
|
||||||
bool read; /* True if allowed to read from file */
|
|
||||||
bool write; /* True if allowed to write to file */
|
|
||||||
bool append; /* True if allowed to append to file */
|
|
||||||
bool compressed; /* True if file data is compressed */
|
|
||||||
bool encrypted; /* True if file data is encryted */
|
|
||||||
off_t pos; /* Current position within the file (in bytes) */
|
|
||||||
u64 len; /* Total length of the file (in bytes) */
|
|
||||||
struct _ntfs_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */
|
|
||||||
struct _ntfs_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */
|
|
||||||
} ntfs_file_state;
|
|
||||||
|
|
||||||
/* File state routines */
|
|
||||||
void ntfsCloseFile(ntfs_file_state *file);
|
|
||||||
|
|
||||||
/* Gekko devoptab file routines for NTFS-based devices */
|
|
||||||
extern int ntfs_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
|
||||||
extern int ntfs_close_r(struct _reent *r, int fd);
|
|
||||||
extern ssize_t ntfs_write_r(struct _reent *r, int fd, const char *ptr, size_t len);
|
|
||||||
extern ssize_t ntfs_read_r(struct _reent *r, int fd, char *ptr, size_t len);
|
|
||||||
extern off_t ntfs_seek_r(struct _reent *r, int fd, off_t pos, int dir);
|
|
||||||
extern int ntfs_fstat_r(struct _reent *r, int fd, struct stat *st);
|
|
||||||
extern int ntfs_ftruncate_r(struct _reent *r, int fd, off_t len);
|
|
||||||
extern int ntfs_fsync_r(struct _reent *r, int fd);
|
|
||||||
|
|
||||||
#endif /* _NTFSFILE_H */
|
|
||||||
|
|
@ -1,617 +0,0 @@
|
|||||||
/**
|
|
||||||
* ntfsfile.c - devoptab file routines for NTFS-based devices.
|
|
||||||
* Copyright (c) 2010 Miigotu
|
|
||||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
|
||||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_FCNTL_H
|
|
||||||
#include <fcntl.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ntfsinternal.h"
|
|
||||||
#include "ntfsfile.h"
|
|
||||||
#include "ntfs.h"
|
|
||||||
|
|
||||||
#define STATE(x) ((ntfs_file_state*)x)
|
|
||||||
|
|
||||||
// for easier comparision of ntfsfile.c against ntfsfile_frag.c
|
|
||||||
// everything is included but ifdef-ed out
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
void ntfsCloseFile ( ntfs_file_state *file )
|
|
||||||
{
|
|
||||||
// Sanity check
|
|
||||||
if ( !file || !file->vd )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Special case fix ups for compressed and/or encrypted files
|
|
||||||
if ( file->compressed )
|
|
||||||
ntfs_attr_pclose( file->data_na );
|
|
||||||
#ifdef HAVE_SETXATTR
|
|
||||||
if ( file->encrypted )
|
|
||||||
ntfs_efs_fixup_attribute( NULL, file->data_na );
|
|
||||||
#endif
|
|
||||||
// Close the file data attribute (if open)
|
|
||||||
if ( file->data_na )
|
|
||||||
ntfs_attr_close( file->data_na );
|
|
||||||
|
|
||||||
// Sync the file (and its attributes) to disc
|
|
||||||
if ( file->write )
|
|
||||||
{
|
|
||||||
ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME );
|
|
||||||
ntfsSync( file->vd, file->ni );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( file->read )
|
|
||||||
ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_ATIME );
|
|
||||||
|
|
||||||
// Close the file (if open)
|
|
||||||
if ( file->ni )
|
|
||||||
ntfsCloseEntry( file->vd, file->ni );
|
|
||||||
|
|
||||||
// Reset the file state
|
|
||||||
file->ni = NULL;
|
|
||||||
file->data_na = NULL;
|
|
||||||
file->flags = 0;
|
|
||||||
file->read = false;
|
|
||||||
file->write = false;
|
|
||||||
file->append = false;
|
|
||||||
file->pos = 0;
|
|
||||||
file->len = 0;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ntfs_open_r ( struct _reent *r, void *fileStruct, const char *path, int flags, int mode )
|
|
||||||
{
|
|
||||||
ntfs_log_trace( "fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode );
|
|
||||||
|
|
||||||
ntfs_file_state* file = STATE( fileStruct );
|
|
||||||
|
|
||||||
// Get the volume descriptor for this path
|
|
||||||
file->vd = ntfsGetVolume( path );
|
|
||||||
if ( !file->vd )
|
|
||||||
{
|
|
||||||
r->_errno = ENODEV;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock
|
|
||||||
ntfsLock( file->vd );
|
|
||||||
|
|
||||||
// Determine which mode the file is opened for
|
|
||||||
file->flags = flags;
|
|
||||||
if ( ( flags & 0x03 ) == O_RDONLY )
|
|
||||||
{
|
|
||||||
file->read = true;
|
|
||||||
file->write = false;
|
|
||||||
file->append = false;
|
|
||||||
}
|
|
||||||
else if ( ( flags & 0x03 ) == O_WRONLY )
|
|
||||||
{
|
|
||||||
file->read = false;
|
|
||||||
file->write = true;
|
|
||||||
file->append = ( flags & O_APPEND );
|
|
||||||
}
|
|
||||||
else if ( ( flags & 0x03 ) == O_RDWR )
|
|
||||||
{
|
|
||||||
file->read = true;
|
|
||||||
file->write = true;
|
|
||||||
file->append = ( flags & O_APPEND );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
r->_errno = EACCES;
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try and find the file and (if found) ensure that it is not a directory
|
|
||||||
file->ni = ntfsOpenEntry( file->vd, path );
|
|
||||||
if ( file->ni && ( file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ) )
|
|
||||||
{
|
|
||||||
ntfsCloseEntry( file->vd, file->ni );
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = EISDIR;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Are we creating this file?
|
|
||||||
if ( flags & O_CREAT )
|
|
||||||
{
|
|
||||||
|
|
||||||
// The file SHOULD NOT already exist
|
|
||||||
if ( file->ni )
|
|
||||||
{
|
|
||||||
ntfsCloseEntry( file->vd, file->ni );
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = EEXIST;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file
|
|
||||||
file->ni = ntfsCreate( file->vd, path, S_IFREG, NULL );
|
|
||||||
if ( !file->ni )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check, the file should be open by now
|
|
||||||
if ( !file->ni )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = ENOENT;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the files data attribute
|
|
||||||
file->data_na = ntfs_attr_open( file->ni, AT_DATA, AT_UNNAMED, 0 );
|
|
||||||
if ( !file->data_na )
|
|
||||||
{
|
|
||||||
ntfsCloseEntry( file->vd, file->ni );
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if this files data is compressed and/or encrypted
|
|
||||||
file->compressed = NAttrCompressed( file->data_na ) || ( file->ni->flags & FILE_ATTR_COMPRESSED );
|
|
||||||
file->encrypted = NAttrEncrypted( file->data_na ) || ( file->ni->flags & FILE_ATTR_ENCRYPTED );
|
|
||||||
|
|
||||||
// We cannot read/write encrypted files
|
|
||||||
if ( file->encrypted )
|
|
||||||
{
|
|
||||||
ntfs_attr_close( file->data_na );
|
|
||||||
ntfsCloseEntry( file->vd, file->ni );
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = EACCES;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we aren't trying to write to a read-only file
|
|
||||||
if ( ( file->ni->flags & FILE_ATTR_READONLY ) && file->write )
|
|
||||||
{
|
|
||||||
ntfs_attr_close( file->data_na );
|
|
||||||
ntfsCloseEntry( file->vd, file->ni );
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = EROFS;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Truncate the file if requested
|
|
||||||
if ( ( flags & O_TRUNC ) && file->write )
|
|
||||||
{
|
|
||||||
if ( ntfs_attr_truncate( file->data_na, 0 ) )
|
|
||||||
{
|
|
||||||
ntfs_attr_close( file->data_na );
|
|
||||||
ntfsCloseEntry( file->vd, file->ni );
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = errno;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the files current position and length
|
|
||||||
file->pos = 0;
|
|
||||||
file->len = file->data_na->data_size;
|
|
||||||
|
|
||||||
ntfs_log_trace( "file->len %d\n", file->len );
|
|
||||||
|
|
||||||
// Update file times
|
|
||||||
ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_ATIME );
|
|
||||||
|
|
||||||
// Insert the file into the double-linked FILO list of open files
|
|
||||||
if ( file->vd->firstOpenFile )
|
|
||||||
{
|
|
||||||
file->nextOpenFile = file->vd->firstOpenFile;
|
|
||||||
file->vd->firstOpenFile->prevOpenFile = file;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
file->nextOpenFile = NULL;
|
|
||||||
}
|
|
||||||
file->prevOpenFile = NULL;
|
|
||||||
file->vd->firstOpenFile = file;
|
|
||||||
file->vd->openFileCount++;
|
|
||||||
|
|
||||||
// Unlock
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
|
|
||||||
return ( int )fileStruct;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ntfs_close_r ( struct _reent *r, int fd )
|
|
||||||
{
|
|
||||||
ntfs_log_trace( "fd %p\n", fd );
|
|
||||||
|
|
||||||
ntfs_file_state* file = STATE( fd );
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if ( !file || !file->vd )
|
|
||||||
{
|
|
||||||
r->_errno = EBADF;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock
|
|
||||||
ntfsLock( file->vd );
|
|
||||||
|
|
||||||
// Close the file
|
|
||||||
ntfsCloseFile( file );
|
|
||||||
|
|
||||||
// Remove the file from the double-linked FILO list of open files
|
|
||||||
file->vd->openFileCount--;
|
|
||||||
if ( file->nextOpenFile )
|
|
||||||
file->nextOpenFile->prevOpenFile = file->prevOpenFile;
|
|
||||||
if ( file->prevOpenFile )
|
|
||||||
file->prevOpenFile->nextOpenFile = file->nextOpenFile;
|
|
||||||
else
|
|
||||||
file->vd->firstOpenFile = file->nextOpenFile;
|
|
||||||
|
|
||||||
// Unlock
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t ntfs_write_r ( struct _reent *r, int fd, const char *ptr, size_t len )
|
|
||||||
{
|
|
||||||
ntfs_log_trace( "fd %p, ptr %p, len %Li\n", fd, ptr, len );
|
|
||||||
|
|
||||||
ntfs_file_state* file = STATE( fd );
|
|
||||||
ssize_t written = 0;
|
|
||||||
off_t old_pos = 0;
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if ( !file || !file->vd || !file->ni || !file->data_na )
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short circuit cases where we don't actually have to do anything
|
|
||||||
if ( !ptr || len <= 0 )
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock
|
|
||||||
ntfsLock( file->vd );
|
|
||||||
|
|
||||||
// Check that we are allowed to write to this file
|
|
||||||
if ( !file->write )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = EACCES;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are in append mode, backup the current position and move to the end of the file
|
|
||||||
if ( file->append )
|
|
||||||
{
|
|
||||||
old_pos = file->pos;
|
|
||||||
file->pos = file->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to the files data atrribute
|
|
||||||
while ( len )
|
|
||||||
{
|
|
||||||
ssize_t ret = ntfs_attr_pwrite( file->data_na, file->pos, len, ptr );
|
|
||||||
if ( ret <= 0 )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = errno;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len -= ret;
|
|
||||||
file->pos += ret;
|
|
||||||
written += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are in append mode, restore the current position to were it was prior to this write
|
|
||||||
if ( file->append )
|
|
||||||
{
|
|
||||||
file->pos = old_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the file for archiving (if we actually wrote something)
|
|
||||||
if ( written )
|
|
||||||
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
|
||||||
|
|
||||||
// Update the files data length
|
|
||||||
file->len = file->data_na->data_size;
|
|
||||||
|
|
||||||
// Unlock
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s64 ntfs_attr_getfragments(ntfs_attr *na, const s64 pos, s64 count, u64 offset, _ntfs_frag_append_t append_fragment,
|
|
||||||
void *callback_data);
|
|
||||||
|
|
||||||
int _NTFS_get_fragments(const char *path, _ntfs_frag_append_t append_fragment, void *callback_data)
|
|
||||||
{
|
|
||||||
struct _reent r;
|
|
||||||
ntfs_file_state file_st, *file = &file_st;
|
|
||||||
ssize_t read = 0;
|
|
||||||
int ret_val = -11;
|
|
||||||
|
|
||||||
// Open File
|
|
||||||
r._errno = 0;
|
|
||||||
int fd = ntfs_open_r(&r, file, path, O_RDONLY, 0);
|
|
||||||
if (fd != (int) file) return -12;
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if (!file || !file->vd || !file->ni || !file->data_na)
|
|
||||||
{
|
|
||||||
//r->_errno = EINVAL;
|
|
||||||
return -13;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
// Short circuit cases where we don't actually have to do anything
|
|
||||||
if (!ptr || len <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Lock
|
|
||||||
ntfsLock(file->vd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Check that we are allowed to read from this file
|
|
||||||
if (!file->read) {
|
|
||||||
ntfsUnlock(file->vd);
|
|
||||||
r->_errno = EACCES;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't read past the end of file
|
|
||||||
if (file->pos + len > file->len) {
|
|
||||||
r->_errno = EOVERFLOW;
|
|
||||||
len = file->len - file->pos;
|
|
||||||
ntfs_log_trace("EOVERFLOW");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
u64 offset = 0;
|
|
||||||
u64 len = file->len;
|
|
||||||
|
|
||||||
// Read from the files data attribute
|
|
||||||
while (len)
|
|
||||||
{
|
|
||||||
s64 ret = ntfs_attr_getfragments(file->data_na, file->pos, len, offset, append_fragment, callback_data);
|
|
||||||
if (ret <= 0 || ret > len)
|
|
||||||
{
|
|
||||||
ntfsUnlock(file->vd);
|
|
||||||
//r->_errno = errno;
|
|
||||||
ret_val = -14;
|
|
||||||
if (ret < 0) ret_val = ret;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
offset += ret;
|
|
||||||
len -= ret;
|
|
||||||
file->pos += ret;
|
|
||||||
read += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set file size
|
|
||||||
append_fragment(callback_data, file->len >> 9, 0, 0);
|
|
||||||
// success
|
|
||||||
ret_val = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
//ntfs_log_trace("file->pos: %d \n", (u32)file->pos);
|
|
||||||
// Update file times (if we actually read something)
|
|
||||||
if (read)
|
|
||||||
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);
|
|
||||||
*/
|
|
||||||
|
|
||||||
out:
|
|
||||||
// Unlock
|
|
||||||
ntfsUnlock(file->vd);
|
|
||||||
// Close the file
|
|
||||||
ntfs_close_r(&r, fd);
|
|
||||||
|
|
||||||
return ret_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
off_t ntfs_seek_r ( struct _reent *r, int fd, off_t pos, int dir )
|
|
||||||
{
|
|
||||||
ntfs_log_trace( "fd %p, pos %Li, dir %i\n", fd, pos, dir );
|
|
||||||
|
|
||||||
ntfs_file_state* file = STATE( fd );
|
|
||||||
off_t position = 0;
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if ( !file || !file->vd || !file->ni || !file->data_na )
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock
|
|
||||||
ntfsLock( file->vd );
|
|
||||||
|
|
||||||
// Set the files current position
|
|
||||||
switch ( dir )
|
|
||||||
{
|
|
||||||
case SEEK_SET: position = file->pos = MIN( MAX( pos, 0 ), file->len ); break;
|
|
||||||
case SEEK_CUR: position = file->pos = MIN( MAX( file->pos + pos, 0 ), file->len ); break;
|
|
||||||
case SEEK_END: position = file->pos = MIN( MAX( file->len + pos, 0 ), file->len ); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
int ntfs_fstat_r ( struct _reent *r, int fd, struct stat *st )
|
|
||||||
{
|
|
||||||
ntfs_log_trace( "fd %p\n", fd );
|
|
||||||
|
|
||||||
ntfs_file_state* file = STATE( fd );
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if ( !file || !file->vd || !file->ni || !file->data_na )
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short circuit cases were we don't actually have to do anything
|
|
||||||
if ( !st )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Get the file stats
|
|
||||||
ret = ntfsStat( file->vd, file->ni, st );
|
|
||||||
if ( ret )
|
|
||||||
r->_errno = errno;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ntfs_ftruncate_r ( struct _reent *r, int fd, off_t len )
|
|
||||||
{
|
|
||||||
ntfs_log_trace( "fd %p, len %Li\n", fd, len );
|
|
||||||
|
|
||||||
ntfs_file_state* file = STATE( fd );
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if ( !file || !file->vd || !file->ni || !file->data_na )
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock
|
|
||||||
ntfsLock( file->vd );
|
|
||||||
|
|
||||||
// Check that we are allowed to write to this file
|
|
||||||
if ( !file->write )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = EACCES;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For compressed files, only deleting and expanding contents are implemented
|
|
||||||
if ( file->compressed &&
|
|
||||||
len > 0 &&
|
|
||||||
len < file->data_na->initialized_size )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = EOPNOTSUPP;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resize the files data attribute, either by expanding or truncating
|
|
||||||
if ( file->compressed && len > file->data_na->initialized_size )
|
|
||||||
{
|
|
||||||
char zero = 0;
|
|
||||||
if ( ntfs_attr_pwrite( file->data_na, len - 1, 1, &zero ) <= 0 )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = errno;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( ntfs_attr_truncate( file->data_na, len ) )
|
|
||||||
{
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
r->_errno = errno;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the file for archiving (if we actually changed something)
|
|
||||||
if ( file->len != file->data_na->data_size )
|
|
||||||
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
|
||||||
|
|
||||||
// Update file times (if we actually changed something)
|
|
||||||
if ( file->len != file->data_na->data_size )
|
|
||||||
ntfsUpdateTimes( file->vd, file->ni, NTFS_UPDATE_AMCTIME );
|
|
||||||
|
|
||||||
// Update the files data length
|
|
||||||
file->len = file->data_na->data_size;
|
|
||||||
|
|
||||||
// Sync the file (and its attributes) to disc
|
|
||||||
ntfsSync( file->vd, file->ni );
|
|
||||||
|
|
||||||
// Unlock
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ntfs_fsync_r ( struct _reent *r, int fd )
|
|
||||||
{
|
|
||||||
ntfs_log_trace( "fd %p\n", fd );
|
|
||||||
|
|
||||||
ntfs_file_state* file = STATE( fd );
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if ( !file || !file->vd || !file->ni || !file->data_na )
|
|
||||||
{
|
|
||||||
r->_errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock
|
|
||||||
ntfsLock( file->vd );
|
|
||||||
|
|
||||||
// Sync the file (and its attributes) to disc
|
|
||||||
ret = ntfsSync( file->vd, file->ni );
|
|
||||||
if ( ret )
|
|
||||||
r->_errno = errno;
|
|
||||||
|
|
||||||
// Unlock
|
|
||||||
ntfsUnlock( file->vd );
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
|||||||
/**
|
|
||||||
* ntfsinternal.h - Internal support routines for NTFS-based devices.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
|
||||||
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NTFSINTERNAL_H
|
|
||||||
#define _NTFSINTERNAL_H
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "layout.h"
|
|
||||||
#include "device.h"
|
|
||||||
#include "volume.h"
|
|
||||||
#include "dir.h"
|
|
||||||
#include "inode.h"
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "reparse.h"
|
|
||||||
#include "security.h"
|
|
||||||
#include "efs.h"
|
|
||||||
#include "unistr.h"
|
|
||||||
|
|
||||||
#include <gccore.h>
|
|
||||||
#include <ogc/disc_io.h>
|
|
||||||
#include <sys/iosupport.h>
|
|
||||||
|
|
||||||
#define NTFS_MOUNT_PREFIX "ntfs" /* Device name prefix to use when auto-mounting */
|
|
||||||
#define NTFS_MAX_PARTITIONS 32 /* Maximum number of partitions that can be found */
|
|
||||||
#define NTFS_MAX_MOUNTS 10 /* Maximum number of mounts available at one time */
|
|
||||||
#define NTFS_MAX_SYMLINK_DEPTH 10 /* Maximum search depth when resolving symbolic links */
|
|
||||||
|
|
||||||
#define NTFS_OEM_ID cpu_to_le64(0x202020205346544eULL) /* "NTFS " */
|
|
||||||
|
|
||||||
#define MBR_SIGNATURE cpu_to_le16(0xAA55)
|
|
||||||
#define EBR_SIGNATURE cpu_to_le16(0xAA55)
|
|
||||||
|
|
||||||
#define PARTITION_STATUS_NONBOOTABLE 0x00 /* Non-bootable */
|
|
||||||
#define PARTITION_STATUS_BOOTABLE 0x80 /* Bootable (active) */
|
|
||||||
|
|
||||||
#define PARTITION_TYPE_EMPTY 0x00 /* Empty */
|
|
||||||
#define PARTITION_TYPE_DOS33_EXTENDED 0x05 /* DOS 3.3+ extended partition */
|
|
||||||
#define PARTITION_TYPE_NTFS 0x07 /* Windows NT NTFS */
|
|
||||||
#define PARTITION_TYPE_WIN95_EXTENDED 0x0F /* Windows 95 extended partition */
|
|
||||||
|
|
||||||
/* Forward declarations */
|
|
||||||
struct _ntfs_file_state;
|
|
||||||
struct _ntfs_dir_state;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PRIMARY_PARTITION - Block device partition record
|
|
||||||
*/
|
|
||||||
typedef struct _PARTITION_RECORD
|
|
||||||
{
|
|
||||||
u8 status; /* Partition status; see above */
|
|
||||||
u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */
|
|
||||||
u8 type; /* Partition type; see above */
|
|
||||||
u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */
|
|
||||||
u32 lba_start; /* Local block address to first sector of partition */
|
|
||||||
u32 block_count; /* Number of blocks in partition */
|
|
||||||
}__attribute__((__packed__)) PARTITION_RECORD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MASTER_BOOT_RECORD - Block device master boot record
|
|
||||||
*/
|
|
||||||
typedef struct _MASTER_BOOT_RECORD
|
|
||||||
{
|
|
||||||
u8 code_area[446]; /* Code area; normally empty */
|
|
||||||
PARTITION_RECORD partitions[4]; /* 4 primary partitions */
|
|
||||||
u16 signature; /* MBR signature; 0xAA55 */
|
|
||||||
}__attribute__((__packed__)) MASTER_BOOT_RECORD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EXTENDED_PARTITION - Block device extended boot record
|
|
||||||
*/
|
|
||||||
typedef struct _EXTENDED_BOOT_RECORD
|
|
||||||
{
|
|
||||||
u8 code_area[446]; /* Code area; normally empty */
|
|
||||||
PARTITION_RECORD partition; /* Primary partition */
|
|
||||||
PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */
|
|
||||||
u8 reserved[32]; /* Normally empty */
|
|
||||||
u16 signature; /* EBR signature; 0xAA55 */
|
|
||||||
}__attribute__((__packed__)) EXTENDED_BOOT_RECORD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* INTERFACE_ID - Disc interface identifier
|
|
||||||
*/
|
|
||||||
typedef struct _INTERFACE_ID
|
|
||||||
{
|
|
||||||
const char *name; /* Interface name */
|
|
||||||
const DISC_INTERFACE *interface; /* Disc interface */
|
|
||||||
} INTERFACE_ID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_atime_t - File access time update strategies
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
ATIME_ENABLED, /* Update access times */
|
|
||||||
ATIME_DISABLED
|
|
||||||
/* Don't update access times */
|
|
||||||
} ntfs_atime_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_vd - NTFS volume descriptor
|
|
||||||
*/
|
|
||||||
typedef struct _ntfs_vd
|
|
||||||
{
|
|
||||||
struct ntfs_device *dev; /* NTFS device handle */
|
|
||||||
ntfs_volume *vol; /* NTFS volume handle */
|
|
||||||
mutex_t lock; /* Volume lock mutex */
|
|
||||||
s64 id; /* Filesystem id */
|
|
||||||
u32 flags; /* Mount flags */
|
|
||||||
char name[128]; /* Volume name (cached) */
|
|
||||||
u16 uid; /* User id for entry creation */
|
|
||||||
u16 gid; /* Group id for entry creation */
|
|
||||||
u16 fmask; /* Unix style permission mask for file creation */
|
|
||||||
u16 dmask; /* Unix style permission mask for directory creation */
|
|
||||||
ntfs_atime_t atime; /* Entry access time update strategy */
|
|
||||||
bool showHiddenFiles; /* If true, show hidden files when enumerating directories */
|
|
||||||
bool showSystemFiles; /* If true, show system files when enumerating directories */
|
|
||||||
ntfs_inode *cwd_ni; /* Current directory */
|
|
||||||
struct _ntfs_dir_state *firstOpenDir; /* The start of a FILO linked list of currently opened directories */
|
|
||||||
struct _ntfs_file_state *firstOpenFile; /* The start of a FILO linked list of currently opened files */
|
|
||||||
u16 openDirCount; /* The total number of directories currently open in this volume */
|
|
||||||
u16 openFileCount; /* The total number of files currently open in this volume */
|
|
||||||
} ntfs_vd;
|
|
||||||
|
|
||||||
/* Lock volume */
|
|
||||||
static inline void ntfsLock(ntfs_vd *vd)
|
|
||||||
{
|
|
||||||
LWP_MutexLock(vd->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unlock volume */
|
|
||||||
static inline void ntfsUnlock(ntfs_vd *vd)
|
|
||||||
{
|
|
||||||
LWP_MutexUnlock(vd->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gekko device related routines */
|
|
||||||
int ntfsAddDevice(const char *name, void *deviceData);
|
|
||||||
void ntfsRemoveDevice(const char *path);
|
|
||||||
const devoptab_t *ntfsGetDevice(const char *path, bool useDefaultDevice);
|
|
||||||
const devoptab_t *ntfsGetDevOpTab(void);
|
|
||||||
const INTERFACE_ID* ntfsGetDiscInterfaces(void);
|
|
||||||
|
|
||||||
/* Miscellaneous helper/support routines */
|
|
||||||
int ntfsInitVolume(ntfs_vd *vd);
|
|
||||||
void ntfsDeinitVolume(ntfs_vd *vd);
|
|
||||||
ntfs_vd *ntfsGetVolume(const char *path);
|
|
||||||
ntfs_inode *ntfsOpenEntry(ntfs_vd *vd, const char *path);
|
|
||||||
ntfs_inode *ntfsParseEntry(ntfs_vd *vd, const char *path, int reparseLevel);
|
|
||||||
void ntfsCloseEntry(ntfs_vd *vd, ntfs_inode *ni);
|
|
||||||
ntfs_inode *ntfsCreate(ntfs_vd *vd, const char *path, mode_t type, const char *target);
|
|
||||||
int ntfsLink(ntfs_vd *vd, const char *old_path, const char *new_path);
|
|
||||||
int ntfsUnlink(ntfs_vd *vd, const char *path);
|
|
||||||
int ntfsSync(ntfs_vd *vd, ntfs_inode *ni);
|
|
||||||
int ntfsStat(ntfs_vd *vd, ntfs_inode *ni, struct stat *st);
|
|
||||||
void ntfsUpdateTimes(ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask);
|
|
||||||
|
|
||||||
const char *ntfsRealPath(const char *path);
|
|
||||||
int ntfsUnicodeToLocal(const ntfschar *ins, const int ins_len, char **outs, int outs_len);
|
|
||||||
int ntfsLocalToUnicode(const char *ins, ntfschar **outs);
|
|
||||||
|
|
||||||
#endif /* _NTFSINTERNAL_H */
|
|
@ -1,682 +0,0 @@
|
|||||||
/**
|
|
||||||
* object_id.c - Processing of object ids
|
|
||||||
*
|
|
||||||
* This module is part of ntfs-3g library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Jean-Pierre Andre
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_ERRNO_H
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_STRING_H
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_STAT_H
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SETXATTR
|
|
||||||
#include <sys/xattr.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SYS_SYSMACROS_H
|
|
||||||
#include <sys/sysmacros.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "inode.h"
|
|
||||||
#include "dir.h"
|
|
||||||
#include "volume.h"
|
|
||||||
#include "mft.h"
|
|
||||||
#include "index.h"
|
|
||||||
#include "lcnalloc.h"
|
|
||||||
#include "object_id.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Endianness considerations
|
|
||||||
*
|
|
||||||
* According to RFC 4122, GUIDs should be printed with the most
|
|
||||||
* significant byte first, and the six fields be compared individually
|
|
||||||
* for ordering. RFC 4122 does not define the internal representation.
|
|
||||||
*
|
|
||||||
* Here we always copy disk images with no endianness change,
|
|
||||||
* and, for indexing, GUIDs are compared as if they were a sequence
|
|
||||||
* of four unsigned 32 bit integers.
|
|
||||||
*
|
|
||||||
* --------------------- begin from RFC 4122 ----------------------
|
|
||||||
* Consider each field of the UUID to be an unsigned integer as shown
|
|
||||||
* in the table in section Section 4.1.2. Then, to compare a pair of
|
|
||||||
* UUIDs, arithmetically compare the corresponding fields from each
|
|
||||||
* UUID in order of significance and according to their data type.
|
|
||||||
* Two UUIDs are equal if and only if all the corresponding fields
|
|
||||||
* are equal.
|
|
||||||
*
|
|
||||||
* UUIDs, as defined in this document, can also be ordered
|
|
||||||
* lexicographically. For a pair of UUIDs, the first one follows the
|
|
||||||
* second if the most significant field in which the UUIDs differ is
|
|
||||||
* greater for the first UUID. The second precedes the first if the
|
|
||||||
* most significant field in which the UUIDs differ is greater for
|
|
||||||
* the second UUID.
|
|
||||||
*
|
|
||||||
* The fields are encoded as 16 octets, with the sizes and order of the
|
|
||||||
* fields defined above, and with each field encoded with the Most
|
|
||||||
* Significant Byte first (known as network byte order). Note that the
|
|
||||||
* field names, particularly for multiplexed fields, follow historical
|
|
||||||
* practice.
|
|
||||||
*
|
|
||||||
* 0 1 2 3
|
|
||||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
||||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
* | time_low |
|
|
||||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
* | time_mid | time_hi_and_version |
|
|
||||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
* |clk_seq_hi_res | clk_seq_low | node (0-1) |
|
|
||||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
* | node (2-5) |
|
|
||||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
*
|
|
||||||
* ---------------------- end from RFC 4122 -----------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
GUID object_id;
|
|
||||||
} OBJECT_ID_INDEX_KEY;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
le64 file_id;
|
|
||||||
GUID birth_volume_id;
|
|
||||||
GUID birth_object_id;
|
|
||||||
GUID domain_id;
|
|
||||||
} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA
|
|
||||||
|
|
||||||
struct OBJECT_ID_INDEX
|
|
||||||
{ /* index entry in $Extend/$ObjId */
|
|
||||||
INDEX_ENTRY_HEADER header;
|
|
||||||
OBJECT_ID_INDEX_KEY key;
|
|
||||||
OBJECT_ID_INDEX_DATA data;
|
|
||||||
};
|
|
||||||
|
|
||||||
static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), const_cpu_to_le16('O') };
|
|
||||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the index for a new object id
|
|
||||||
*
|
|
||||||
* Returns 0 if success
|
|
||||||
* -1 if failure, explained by errno
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo,
|
|
||||||
const OBJECT_ID_ATTR *object_id)
|
|
||||||
{
|
|
||||||
struct OBJECT_ID_INDEX indx;
|
|
||||||
u64 file_id_cpu;
|
|
||||||
le64 file_id;
|
|
||||||
le16 seqn;
|
|
||||||
|
|
||||||
seqn = ni->mrec->sequence_number;
|
|
||||||
file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn));
|
|
||||||
file_id = cpu_to_le64(file_id_cpu);
|
|
||||||
indx.header.data_offset = const_cpu_to_le16(
|
|
||||||
sizeof(INDEX_ENTRY_HEADER)
|
|
||||||
+ sizeof(OBJECT_ID_INDEX_KEY));
|
|
||||||
indx.header.data_length = const_cpu_to_le16(
|
|
||||||
sizeof(OBJECT_ID_INDEX_DATA));
|
|
||||||
indx.header.reservedV = const_cpu_to_le32(0);
|
|
||||||
indx.header.length = const_cpu_to_le16(
|
|
||||||
sizeof(struct OBJECT_ID_INDEX));
|
|
||||||
indx.header.key_length = const_cpu_to_le16(
|
|
||||||
sizeof(OBJECT_ID_INDEX_KEY));
|
|
||||||
indx.header.flags = const_cpu_to_le16(0);
|
|
||||||
indx.header.reserved = const_cpu_to_le16(0);
|
|
||||||
|
|
||||||
memcpy(&indx.key.object_id,object_id,sizeof(GUID));
|
|
||||||
|
|
||||||
indx.data.file_id = file_id;
|
|
||||||
memcpy(&indx.data.birth_volume_id,
|
|
||||||
&object_id->birth_volume_id,sizeof(GUID));
|
|
||||||
memcpy(&indx.data.birth_object_id,
|
|
||||||
&object_id->birth_object_id,sizeof(GUID));
|
|
||||||
memcpy(&indx.data.domain_id,
|
|
||||||
&object_id->domain_id,sizeof(GUID));
|
|
||||||
ntfs_index_ctx_reinit(xo);
|
|
||||||
return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* HAVE_SETXATTR */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Open the $Extend/$ObjId file and its index
|
|
||||||
*
|
|
||||||
* Return the index context if opened
|
|
||||||
* or NULL if an error occurred (errno tells why)
|
|
||||||
*
|
|
||||||
* The index has to be freed and inode closed when not needed any more.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
|
|
||||||
{
|
|
||||||
u64 inum;
|
|
||||||
ntfs_inode *ni;
|
|
||||||
ntfs_inode *dir_ni;
|
|
||||||
ntfs_index_context *xo;
|
|
||||||
|
|
||||||
/* do not use path_name_to inode - could reopen root */
|
|
||||||
dir_ni = ntfs_inode_open(vol, FILE_Extend);
|
|
||||||
ni = (ntfs_inode*) NULL;
|
|
||||||
if (dir_ni)
|
|
||||||
{
|
|
||||||
inum = ntfs_inode_lookup_by_mbsname(dir_ni, "$ObjId");
|
|
||||||
if (inum != (u64) -1) ni = ntfs_inode_open(vol, inum);
|
|
||||||
ntfs_inode_close(dir_ni);
|
|
||||||
}
|
|
||||||
if (ni)
|
|
||||||
{
|
|
||||||
xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
|
|
||||||
if (!xo)
|
|
||||||
{
|
|
||||||
ntfs_inode_close(ni);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else xo = (ntfs_index_context*) NULL;
|
|
||||||
return (xo);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Merge object_id data stored in the index into
|
|
||||||
* a full object_id struct.
|
|
||||||
*
|
|
||||||
* returns 0 if merging successful
|
|
||||||
* -1 if no data could be merged. This is generally not an error
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int merge_index_data(ntfs_inode *ni,
|
|
||||||
const OBJECT_ID_ATTR *objectid_attr,
|
|
||||||
OBJECT_ID_ATTR *full_objectid)
|
|
||||||
{
|
|
||||||
OBJECT_ID_INDEX_KEY key;
|
|
||||||
struct OBJECT_ID_INDEX *entry;
|
|
||||||
ntfs_index_context *xo;
|
|
||||||
ntfs_inode *xoni;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = -1;
|
|
||||||
xo = open_object_id_index(ni->vol);
|
|
||||||
if (xo)
|
|
||||||
{
|
|
||||||
memcpy(&key.object_id,objectid_attr,sizeof(GUID));
|
|
||||||
if (!ntfs_index_lookup(&key,
|
|
||||||
sizeof(OBJECT_ID_INDEX_KEY), xo))
|
|
||||||
{
|
|
||||||
entry = (struct OBJECT_ID_INDEX*)xo->entry;
|
|
||||||
/* make sure inode numbers match */
|
|
||||||
if (entry
|
|
||||||
&& (MREF(le64_to_cpu(entry->data.file_id))
|
|
||||||
== ni->mft_no))
|
|
||||||
{
|
|
||||||
memcpy(&full_objectid->birth_volume_id,
|
|
||||||
&entry->data.birth_volume_id,
|
|
||||||
sizeof(GUID));
|
|
||||||
memcpy(&full_objectid->birth_object_id,
|
|
||||||
&entry->data.birth_object_id,
|
|
||||||
sizeof(GUID));
|
|
||||||
memcpy(&full_objectid->domain_id,
|
|
||||||
&entry->data.domain_id,
|
|
||||||
sizeof(GUID));
|
|
||||||
res = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xoni = xo->ni;
|
|
||||||
ntfs_index_ctx_put(xo);
|
|
||||||
ntfs_inode_close(xoni);
|
|
||||||
}
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* HAVE_SETXATTR */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove an object id index entry if attribute present
|
|
||||||
*
|
|
||||||
* Returns the size of existing object id
|
|
||||||
* (the existing object_d is returned)
|
|
||||||
* -1 if failure, explained by errno
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, OBJECT_ID_ATTR *old_attr)
|
|
||||||
{
|
|
||||||
OBJECT_ID_INDEX_KEY key;
|
|
||||||
struct OBJECT_ID_INDEX *entry;
|
|
||||||
s64 size;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = na->data_size;
|
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
/* read the existing object id attribute */
|
|
||||||
size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr);
|
|
||||||
if (size >= (s64) sizeof(GUID))
|
|
||||||
{
|
|
||||||
memcpy(&key.object_id, &old_attr->object_id, sizeof(GUID));
|
|
||||||
size = sizeof(GUID);
|
|
||||||
if (!ntfs_index_lookup(&key, sizeof(OBJECT_ID_INDEX_KEY), xo))
|
|
||||||
{
|
|
||||||
entry = (struct OBJECT_ID_INDEX*) xo->entry;
|
|
||||||
memcpy(&old_attr->birth_volume_id, &entry->data.birth_volume_id, sizeof(GUID));
|
|
||||||
memcpy(&old_attr->birth_object_id, &entry->data.birth_object_id, sizeof(GUID));
|
|
||||||
memcpy(&old_attr->domain_id, &entry->data.domain_id, sizeof(GUID));
|
|
||||||
size = sizeof(OBJECT_ID_ATTR);
|
|
||||||
if (ntfs_index_rm(xo)) ret = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret = -1;
|
|
||||||
errno = ENODATA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update the object id and index
|
|
||||||
*
|
|
||||||
* The object_id attribute should have been created and the
|
|
||||||
* non-duplication of the GUID should have been checked before.
|
|
||||||
*
|
|
||||||
* Returns 0 if success
|
|
||||||
* -1 if failure, explained by errno
|
|
||||||
* If could not remove the existing index, nothing is done,
|
|
||||||
* If could not write the new data, no index entry is inserted
|
|
||||||
* If failed to insert the index, data is removed
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
|
|
||||||
const OBJECT_ID_ATTR *value, size_t size)
|
|
||||||
{
|
|
||||||
OBJECT_ID_ATTR old_attr;
|
|
||||||
ntfs_attr *na;
|
|
||||||
int oldsize;
|
|
||||||
int written;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
|
|
||||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
|
||||||
if (na)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* remove the existing index entry */
|
|
||||||
oldsize = remove_object_id_index(na,xo,&old_attr);
|
|
||||||
if (oldsize < 0)
|
|
||||||
res = -1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* resize attribute */
|
|
||||||
res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
|
|
||||||
/* write the object_id in attribute */
|
|
||||||
if (!res && value)
|
|
||||||
{
|
|
||||||
written = (int)ntfs_attr_pwrite(na,
|
|
||||||
(s64)0, (s64)sizeof(GUID),
|
|
||||||
&value->object_id);
|
|
||||||
if (written != (s64)sizeof(GUID))
|
|
||||||
{
|
|
||||||
ntfs_log_error("Failed to update "
|
|
||||||
"object id\n");
|
|
||||||
errno = EIO;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* write index part if provided */
|
|
||||||
if (!res
|
|
||||||
&& ((size < sizeof(OBJECT_ID_ATTR))
|
|
||||||
|| set_object_id_index(ni,xo,value)))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If cannot index, try to remove the object
|
|
||||||
* id and log the error. There will be an
|
|
||||||
* inconsistency if removal fails.
|
|
||||||
*/
|
|
||||||
ntfs_attr_rm(na);
|
|
||||||
ntfs_log_error("Failed to index object id."
|
|
||||||
" Possible corruption.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ntfs_attr_close(na);
|
|
||||||
NInoSetDirty(ni);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
res = -1;
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a (dummy) object id to an inode if it does not exist
|
|
||||||
*
|
|
||||||
* returns 0 if attribute was inserted (or already present)
|
|
||||||
* -1 if adding failed (explained by errno)
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int add_object_id(ntfs_inode *ni, int flags)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
u8 dummy;
|
|
||||||
|
|
||||||
res = -1; /* default return */
|
|
||||||
if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0))
|
|
||||||
{
|
|
||||||
if (!(flags & XATTR_REPLACE))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* no object id attribute : add one,
|
|
||||||
* apparently, this does not feed the new value in
|
|
||||||
* Note : NTFS version must be >= 3
|
|
||||||
*/
|
|
||||||
if (ni->vol->major_ver >= 3)
|
|
||||||
{
|
|
||||||
res = ntfs_attr_add(ni, AT_OBJECT_ID,
|
|
||||||
AT_UNNAMED, 0, &dummy, (s64)0);
|
|
||||||
NInoSetDirty(ni);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
errno = EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
errno = ENODATA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (flags & XATTR_CREATE)
|
|
||||||
errno = EEXIST;
|
|
||||||
else
|
|
||||||
res = 0;
|
|
||||||
}
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* HAVE_SETXATTR */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Delete an object_id index entry
|
|
||||||
*
|
|
||||||
* Returns 0 if success
|
|
||||||
* -1 if failure, explained by errno
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_delete_object_id_index(ntfs_inode *ni)
|
|
||||||
{
|
|
||||||
ntfs_index_context *xo;
|
|
||||||
ntfs_inode *xoni;
|
|
||||||
ntfs_attr *na;
|
|
||||||
OBJECT_ID_ATTR old_attr;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
|
||||||
if (na)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* read the existing object id
|
|
||||||
* and un-index it
|
|
||||||
*/
|
|
||||||
xo = open_object_id_index(ni->vol);
|
|
||||||
if (xo)
|
|
||||||
{
|
|
||||||
if (remove_object_id_index(na, xo, &old_attr) < 0) res = -1;
|
|
||||||
xoni = xo->ni;
|
|
||||||
ntfs_index_entry_mark_dirty(xo);
|
|
||||||
NInoSetDirty(xoni);
|
|
||||||
ntfs_index_ctx_put(xo);
|
|
||||||
ntfs_inode_close(xoni);
|
|
||||||
}
|
|
||||||
ntfs_attr_close(na);
|
|
||||||
}
|
|
||||||
return (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the ntfs object id into an extended attribute
|
|
||||||
*
|
|
||||||
* If present, the object_id from the attribute and the GUIDs
|
|
||||||
* from the index are returned (formatted as OBJECT_ID_ATTR)
|
|
||||||
*
|
|
||||||
* Returns the global size (can be 0, 16 or 64)
|
|
||||||
* and the buffer is updated if it is long enough
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
|
|
||||||
{
|
|
||||||
OBJECT_ID_ATTR full_objectid;
|
|
||||||
OBJECT_ID_ATTR *objectid_attr;
|
|
||||||
s64 attr_size;
|
|
||||||
int full_size;
|
|
||||||
|
|
||||||
full_size = 0; /* default to no data and some error to be defined */
|
|
||||||
if (ni)
|
|
||||||
{
|
|
||||||
objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
|
|
||||||
AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
|
|
||||||
if (objectid_attr)
|
|
||||||
{
|
|
||||||
/* restrict to only GUID present in attr */
|
|
||||||
if (attr_size == sizeof(GUID))
|
|
||||||
{
|
|
||||||
memcpy(&full_objectid.object_id,
|
|
||||||
objectid_attr,sizeof(GUID));
|
|
||||||
full_size = sizeof(GUID);
|
|
||||||
/* get data from index, if any */
|
|
||||||
if (!merge_index_data(ni, objectid_attr,
|
|
||||||
&full_objectid))
|
|
||||||
{
|
|
||||||
full_size = sizeof(OBJECT_ID_ATTR);
|
|
||||||
}
|
|
||||||
if (full_size <= (s64)size)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
memcpy(value,&full_objectid,
|
|
||||||
full_size);
|
|
||||||
else
|
|
||||||
errno = EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* unexpected size, better return unsupported */
|
|
||||||
errno = EOPNOTSUPP;
|
|
||||||
full_size = 0;
|
|
||||||
}
|
|
||||||
free(objectid_attr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
errno = ENODATA;
|
|
||||||
}
|
|
||||||
return (full_size ? (int)full_size : -errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the object id from an extended attribute
|
|
||||||
*
|
|
||||||
* If the size is 64, the attribute and index are set.
|
|
||||||
* else if the size is not less than 16 only the attribute is set.
|
|
||||||
* The object id index is set accordingly.
|
|
||||||
*
|
|
||||||
* Returns 0, or -1 if there is a problem
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_set_ntfs_object_id(ntfs_inode *ni,
|
|
||||||
const char *value, size_t size, int flags)
|
|
||||||
{
|
|
||||||
OBJECT_ID_INDEX_KEY key;
|
|
||||||
ntfs_inode *xoni;
|
|
||||||
ntfs_index_context *xo;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
if (ni && value && (size >= sizeof(GUID)))
|
|
||||||
{
|
|
||||||
xo = open_object_id_index(ni->vol);
|
|
||||||
if (xo)
|
|
||||||
{
|
|
||||||
/* make sure the GUID was not used somewhere */
|
|
||||||
memcpy(&key.object_id, value, sizeof(GUID));
|
|
||||||
if (ntfs_index_lookup(&key,
|
|
||||||
sizeof(OBJECT_ID_INDEX_KEY), xo))
|
|
||||||
{
|
|
||||||
ntfs_index_ctx_reinit(xo);
|
|
||||||
res = add_object_id(ni, flags);
|
|
||||||
if (!res)
|
|
||||||
{
|
|
||||||
/* update value and index */
|
|
||||||
res = update_object_id(ni,xo,
|
|
||||||
(const OBJECT_ID_ATTR*)value,
|
|
||||||
size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* GUID is present elsewhere */
|
|
||||||
res = -1;
|
|
||||||
errno = EEXIST;
|
|
||||||
}
|
|
||||||
xoni = xo->ni;
|
|
||||||
ntfs_index_entry_mark_dirty(xo);
|
|
||||||
NInoSetDirty(xoni);
|
|
||||||
ntfs_index_ctx_put(xo);
|
|
||||||
ntfs_inode_close(xoni);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
return (res ? -1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove the object id
|
|
||||||
*
|
|
||||||
* Returns 0, or -1 if there is a problem
|
|
||||||
*/
|
|
||||||
|
|
||||||
int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
int olderrno;
|
|
||||||
ntfs_attr *na;
|
|
||||||
ntfs_inode *xoni;
|
|
||||||
ntfs_index_context *xo;
|
|
||||||
int oldsize;
|
|
||||||
OBJECT_ID_ATTR old_attr;
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
if (ni)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* open and delete the object id
|
|
||||||
*/
|
|
||||||
na = ntfs_attr_open(ni, AT_OBJECT_ID,
|
|
||||||
AT_UNNAMED,0);
|
|
||||||
if (na)
|
|
||||||
{
|
|
||||||
/* first remove index (old object id needed) */
|
|
||||||
xo = open_object_id_index(ni->vol);
|
|
||||||
if (xo)
|
|
||||||
{
|
|
||||||
oldsize = remove_object_id_index(na,xo,
|
|
||||||
&old_attr);
|
|
||||||
if (oldsize < 0)
|
|
||||||
{
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* now remove attribute */
|
|
||||||
res = ntfs_attr_rm(na);
|
|
||||||
if (res
|
|
||||||
&& (oldsize > (int)sizeof(GUID)))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If we could not remove the
|
|
||||||
* attribute, try to restore the
|
|
||||||
* index and log the error. There
|
|
||||||
* will be an inconsistency if
|
|
||||||
* the reindexing fails.
|
|
||||||
*/
|
|
||||||
set_object_id_index(ni, xo,
|
|
||||||
&old_attr);
|
|
||||||
ntfs_log_error(
|
|
||||||
"Failed to remove object id."
|
|
||||||
" Possible corruption.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xoni = xo->ni;
|
|
||||||
ntfs_index_entry_mark_dirty(xo);
|
|
||||||
NInoSetDirty(xoni);
|
|
||||||
ntfs_index_ctx_put(xo);
|
|
||||||
ntfs_inode_close(xoni);
|
|
||||||
}
|
|
||||||
olderrno = errno;
|
|
||||||
ntfs_attr_close(na);
|
|
||||||
/* avoid errno pollution */
|
|
||||||
if (errno == ENOENT)
|
|
||||||
errno = olderrno;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = ENODATA;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
NInoSetDirty(ni);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errno = EINVAL;
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
return (res ? -1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* HAVE_SETXATTR */
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,351 +0,0 @@
|
|||||||
/*
|
|
||||||
* security.h - Exports for handling security/ACLs in NTFS.
|
|
||||||
* Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2005-2006 Szabolcs Szakacsits
|
|
||||||
* Copyright (c) 2007-2008 Jean-Pierre Andre
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NTFS_SECURITY_H
|
|
||||||
#define _NTFS_SECURITY_H
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "layout.h"
|
|
||||||
#include "inode.h"
|
|
||||||
#include "dir.h"
|
|
||||||
|
|
||||||
#ifndef POSIXACLS
|
|
||||||
#define POSIXACLS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef u16 be16;
|
|
||||||
typedef u32 be32;
|
|
||||||
|
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
||||||
#define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L))
|
|
||||||
#define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \
|
|
||||||
+ (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L))
|
|
||||||
#else
|
|
||||||
#define const_cpu_to_be16(x) (x)
|
|
||||||
#define const_cpu_to_be32(x) (x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* item in the mapping list
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct MAPPING
|
|
||||||
{
|
|
||||||
struct MAPPING *next;
|
|
||||||
int xid; /* linux id : uid or gid */
|
|
||||||
SID *sid; /* Windows id : usid or gsid */
|
|
||||||
int grcnt; /* group count (for users only) */
|
|
||||||
gid_t *groups; /* groups which the user is member of */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Entry in the permissions cache
|
|
||||||
* Note : this cache is not organized as a generic cache
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct CACHED_PERMISSIONS
|
|
||||||
{
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
le32 inh_fileid;
|
|
||||||
le32 inh_dirid;
|
|
||||||
#if POSIXACLS
|
|
||||||
struct POSIX_SECURITY *pxdesc;
|
|
||||||
unsigned int pxdescsize:16;
|
|
||||||
#endif
|
|
||||||
unsigned int mode :12;
|
|
||||||
unsigned int valid :1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Entry in the permissions cache for directories with no security_id
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct CACHED_PERMISSIONS_LEGACY
|
|
||||||
{
|
|
||||||
struct CACHED_PERMISSIONS_LEGACY *next;
|
|
||||||
struct CACHED_PERMISSIONS_LEGACY *previous;
|
|
||||||
void *variable;
|
|
||||||
size_t varsize;
|
|
||||||
/* above fields must match "struct CACHED_GENERIC" */
|
|
||||||
u64 mft_no;
|
|
||||||
struct CACHED_PERMISSIONS perm;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Entry in the securid cache
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct CACHED_SECURID
|
|
||||||
{
|
|
||||||
struct CACHED_SECURID *next;
|
|
||||||
struct CACHED_SECURID *previous;
|
|
||||||
void *variable;
|
|
||||||
size_t varsize;
|
|
||||||
/* above fields must match "struct CACHED_GENERIC" */
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
unsigned int dmode;
|
|
||||||
le32 securid;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Header of the security cache
|
|
||||||
* (has no cache structure by itself)
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct CACHED_PERMISSIONS_HEADER
|
|
||||||
{
|
|
||||||
unsigned int last;
|
|
||||||
/* statistics for permissions */
|
|
||||||
unsigned long p_writes;
|
|
||||||
unsigned long p_reads;
|
|
||||||
unsigned long p_hits;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The whole permissions cache
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct PERMISSIONS_CACHE
|
|
||||||
{
|
|
||||||
struct CACHED_PERMISSIONS_HEADER head;
|
|
||||||
struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Security flags values
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
SECURITY_DEFAULT, /* rely on fuse for permissions checking */
|
|
||||||
SECURITY_RAW, /* force same ownership/permissions on files */
|
|
||||||
SECURITY_ADDSECURIDS, /* upgrade old security descriptors */
|
|
||||||
SECURITY_STATICGRPS, /* use static groups for access control */
|
|
||||||
SECURITY_WANTED
|
|
||||||
/* a security related option was present */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Security context, needed by most security functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
MAPUSERS, MAPGROUPS, MAPCOUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SECURITY_CONTEXT
|
|
||||||
{
|
|
||||||
ntfs_volume *vol;
|
|
||||||
struct MAPPING *mapping[MAPCOUNT];
|
|
||||||
struct PERMISSIONS_CACHE **pseccache;
|
|
||||||
uid_t uid; /* uid of user requesting (not the mounter) */
|
|
||||||
gid_t gid; /* gid of user requesting (not the mounter) */
|
|
||||||
pid_t tid; /* thread id of thread requesting */
|
|
||||||
mode_t umask; /* umask of requesting thread */
|
|
||||||
};
|
|
||||||
|
|
||||||
#if POSIXACLS
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Posix ACL structures
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct POSIX_ACE
|
|
||||||
{
|
|
||||||
u16 tag;
|
|
||||||
u16 perms;
|
|
||||||
s32 id;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct POSIX_ACL
|
|
||||||
{
|
|
||||||
u8 version;
|
|
||||||
u8 flags;
|
|
||||||
u16 filler;
|
|
||||||
struct POSIX_ACE ace[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct POSIX_SECURITY
|
|
||||||
{
|
|
||||||
mode_t mode;
|
|
||||||
int acccnt;
|
|
||||||
int defcnt;
|
|
||||||
int firstdef;
|
|
||||||
u16 tagsset;
|
|
||||||
struct POSIX_ACL acl;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Posix tags, cpu-endian 16 bits
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
POSIX_ACL_USER_OBJ = 1,
|
|
||||||
POSIX_ACL_USER = 2,
|
|
||||||
POSIX_ACL_GROUP_OBJ = 4,
|
|
||||||
POSIX_ACL_GROUP = 8,
|
|
||||||
POSIX_ACL_MASK = 16,
|
|
||||||
POSIX_ACL_OTHER = 32,
|
|
||||||
POSIX_ACL_SPECIAL = 64 /* internal use only */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define POSIX_ACL_EXTENSIONS (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Posix permissions, cpu-endian 16 bits
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
POSIX_PERM_X = 1,
|
|
||||||
POSIX_PERM_W = 2,
|
|
||||||
POSIX_PERM_R = 4,
|
|
||||||
POSIX_PERM_DENIAL = 64 /* internal use only */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define POSIX_VERSION 2
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern BOOL ntfs_guid_is_zero(const GUID *guid);
|
|
||||||
extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ntfs_sid_is_valid - determine if a SID is valid
|
|
||||||
* @sid: SID for which to determine if it is valid
|
|
||||||
*
|
|
||||||
* Determine if the SID pointed to by @sid is valid.
|
|
||||||
*
|
|
||||||
* Return TRUE if it is valid and FALSE otherwise.
|
|
||||||
*/
|
|
||||||
static __inline__ BOOL ntfs_sid_is_valid(const SID *sid)
|
|
||||||
{
|
|
||||||
if (!sid || sid->revision != SID_REVISION || sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES) return FALSE;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern int ntfs_sid_to_mbs_size(const SID *sid);
|
|
||||||
extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size);
|
|
||||||
extern void ntfs_generate_guid(GUID *guid);
|
|
||||||
extern int ntfs_sd_add_everyone(ntfs_inode *ni);
|
|
||||||
|
|
||||||
extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len);
|
|
||||||
|
|
||||||
int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, BOOL allowdef);
|
|
||||||
int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, struct stat*);
|
|
||||||
int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode);
|
|
||||||
BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni);
|
|
||||||
int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, int accesstype);
|
|
||||||
BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype);
|
|
||||||
|
|
||||||
#if POSIXACLS
|
|
||||||
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
|
|
||||||
uid_t uid, gid_t gid, ntfs_inode *dir_ni,
|
|
||||||
mode_t mode, BOOL isdir);
|
|
||||||
#else
|
|
||||||
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid, mode_t mode, BOOL isdir);
|
|
||||||
#endif
|
|
||||||
int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid);
|
|
||||||
int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode);
|
|
||||||
#if POSIXACLS
|
|
||||||
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx,
|
|
||||||
ntfs_inode *ni, uid_t uid, gid_t gid,
|
|
||||||
mode_t mode, struct POSIX_SECURITY *pxdesc);
|
|
||||||
#else
|
|
||||||
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode);
|
|
||||||
#endif
|
|
||||||
le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, ntfs_inode *dir_ni, BOOL fordir);
|
|
||||||
int ntfs_open_secure(ntfs_volume *vol);
|
|
||||||
void ntfs_close_secure(struct SECURITY_CONTEXT *scx);
|
|
||||||
|
|
||||||
#if POSIXACLS
|
|
||||||
|
|
||||||
int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
|
|
||||||
ntfs_inode *ni, uid_t uid, gid_t gid,
|
|
||||||
ntfs_inode *dir_ni, mode_t mode);
|
|
||||||
int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
|
||||||
const char *name, char *value, size_t size);
|
|
||||||
int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
|
||||||
const char *name, const char *value, size_t size,
|
|
||||||
int flags);
|
|
||||||
int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
|
||||||
const char *name);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, char *value, size_t size);
|
|
||||||
int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, const char *value, size_t size, int flags);
|
|
||||||
|
|
||||||
int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size);
|
|
||||||
int ntfs_set_ntfs_attrib(ntfs_inode *ni, const char *value, size_t size, int flags);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Security API for direct access to security descriptors
|
|
||||||
* based on Win32 API
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define MAGIC_API 0x09042009
|
|
||||||
|
|
||||||
struct SECURITY_API
|
|
||||||
{
|
|
||||||
u32 magic;
|
|
||||||
struct SECURITY_CONTEXT security;
|
|
||||||
struct PERMISSIONS_CACHE *seccache;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following constants are used in interfacing external programs.
|
|
||||||
* They are not to be stored on disk and must be defined in their
|
|
||||||
* native cpu representation.
|
|
||||||
* When disk representation (le) is needed, use SE_DACL_PRESENT, etc.
|
|
||||||
*/
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
OWNER_SECURITY_INFORMATION = 1,
|
|
||||||
GROUP_SECURITY_INFORMATION = 2,
|
|
||||||
DACL_SECURITY_INFORMATION = 4,
|
|
||||||
SACL_SECURITY_INFORMATION = 8
|
|
||||||
};
|
|
||||||
|
|
||||||
int ntfs_get_file_security(struct SECURITY_API *scapi, const char *path, u32 selection, char *buf, u32 buflen,
|
|
||||||
u32 *psize);
|
|
||||||
int ntfs_set_file_security(struct SECURITY_API *scapi, const char *path, u32 selection, const char *attr);
|
|
||||||
int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path);
|
|
||||||
BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, const char *path, s32 attrib);
|
|
||||||
BOOL ntfs_read_directory(struct SECURITY_API *scapi, const char *path, ntfs_filldir_t callback, void *context);
|
|
||||||
int ntfs_read_sds(struct SECURITY_API *scapi, char *buf, u32 size, u32 offset);
|
|
||||||
INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, INDEX_ENTRY *entry);
|
|
||||||
INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, INDEX_ENTRY *entry);
|
|
||||||
struct SECURITY_API *ntfs_initialize_file_security(const char *device, int flags);
|
|
||||||
BOOL ntfs_leave_file_security(struct SECURITY_API *scx);
|
|
||||||
|
|
||||||
int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf);
|
|
||||||
int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf);
|
|
||||||
int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid);
|
|
||||||
int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid);
|
|
||||||
|
|
||||||
#endif /* defined _NTFS_SECURITY_H */
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,307 +0,0 @@
|
|||||||
/*
|
|
||||||
* volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
|
||||||
* Copyright (c) 2004-2005 Richard Russon
|
|
||||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
|
||||||
* Copyright (c) 2005-2009 Szabolcs Szakacsits
|
|
||||||
*
|
|
||||||
* This program/include file is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program/include file is distributed in the hope that it will be
|
|
||||||
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program (in the main directory of the NTFS-3G
|
|
||||||
* distribution in the file COPYING); if not, write to the Free Software
|
|
||||||
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _NTFS_VOLUME_H
|
|
||||||
#define _NTFS_VOLUME_H
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_STDIO_H
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_PARAM_H
|
|
||||||
#include <sys/param.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_MOUNT_H
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_MNTENT_H
|
|
||||||
#include <mntent.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY,
|
|
||||||
* so we define them ourselves.
|
|
||||||
*/
|
|
||||||
#ifndef MS_RDONLY
|
|
||||||
#define MS_RDONLY 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MS_EXCLUSIVE 0x08000000
|
|
||||||
|
|
||||||
#ifndef MS_RECOVER
|
|
||||||
#define MS_RECOVER 0x10000000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MS_IGNORE_HIBERFILE 0x20000000
|
|
||||||
|
|
||||||
/* Forward declaration */
|
|
||||||
typedef struct _ntfs_volume ntfs_volume;
|
|
||||||
|
|
||||||
#include "param.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "support.h"
|
|
||||||
#include "device.h"
|
|
||||||
#include "inode.h"
|
|
||||||
#include "attrib.h"
|
|
||||||
#include "index.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enum ntfs_mount_flags -
|
|
||||||
*
|
|
||||||
* Flags returned by the ntfs_check_if_mounted() function.
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
NTFS_MF_MOUNTED = 1, /* Device is mounted. */
|
|
||||||
NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */
|
|
||||||
NTFS_MF_READONLY = 4,
|
|
||||||
/* Device is mounted read-only. */
|
|
||||||
} ntfs_mount_flags;
|
|
||||||
|
|
||||||
extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags);
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
NTFS_VOLUME_OK = 0,
|
|
||||||
NTFS_VOLUME_SYNTAX_ERROR = 11,
|
|
||||||
NTFS_VOLUME_NOT_NTFS = 12,
|
|
||||||
NTFS_VOLUME_CORRUPT = 13,
|
|
||||||
NTFS_VOLUME_HIBERNATED = 14,
|
|
||||||
NTFS_VOLUME_UNCLEAN_UNMOUNT = 15,
|
|
||||||
NTFS_VOLUME_LOCKED = 16,
|
|
||||||
NTFS_VOLUME_RAID = 17,
|
|
||||||
NTFS_VOLUME_UNKNOWN_REASON = 18,
|
|
||||||
NTFS_VOLUME_NO_PRIVILEGE = 19,
|
|
||||||
NTFS_VOLUME_OUT_OF_MEMORY = 20,
|
|
||||||
NTFS_VOLUME_FUSE_ERROR = 21,
|
|
||||||
NTFS_VOLUME_INSECURE = 22
|
|
||||||
} ntfs_volume_status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* enum ntfs_volume_state_bits -
|
|
||||||
*
|
|
||||||
* Defined bits for the state field in the ntfs_volume structure.
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
NV_ReadOnly, /* 1: Volume is read-only. */
|
|
||||||
NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */
|
|
||||||
NV_LogFileEmpty, /* 1: $logFile journal is empty. */
|
|
||||||
NV_ShowSysFiles, /* 1: Show NTFS metafiles. */
|
|
||||||
NV_ShowHidFiles, /* 1: Show files marked hidden. */
|
|
||||||
NV_HideDotFiles, /* 1: Set hidden flag on dot files */
|
|
||||||
NV_Compression,
|
|
||||||
/* 1: allow compression */
|
|
||||||
} ntfs_volume_state_bits;
|
|
||||||
|
|
||||||
#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
|
|
||||||
#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state)
|
|
||||||
#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state)
|
|
||||||
|
|
||||||
#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly)
|
|
||||||
#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly)
|
|
||||||
#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly)
|
|
||||||
|
|
||||||
#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive)
|
|
||||||
#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive)
|
|
||||||
#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive)
|
|
||||||
|
|
||||||
#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty)
|
|
||||||
#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty)
|
|
||||||
#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty)
|
|
||||||
|
|
||||||
#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles)
|
|
||||||
#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles)
|
|
||||||
#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles)
|
|
||||||
|
|
||||||
#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles)
|
|
||||||
#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles)
|
|
||||||
#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles)
|
|
||||||
|
|
||||||
#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles)
|
|
||||||
#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles)
|
|
||||||
#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles)
|
|
||||||
|
|
||||||
#define NVolCompression(nv) test_nvol_flag(nv, Compression)
|
|
||||||
#define NVolSetCompression(nv) set_nvol_flag(nv, Compression)
|
|
||||||
#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NTFS version 1.1 and 1.2 are used by Windows NT4.
|
|
||||||
* NTFS version 2.x is used by Windows 2000 Beta
|
|
||||||
* NTFS version 3.0 is used by Windows 2000.
|
|
||||||
* NTFS version 3.1 is used by Windows XP, 2003 and Vista.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1)
|
|
||||||
#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2)
|
|
||||||
#define NTFS_V2_X(major, minor) ((major) == 2)
|
|
||||||
#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0)
|
|
||||||
#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1)
|
|
||||||
|
|
||||||
#define NTFS_BUF_SIZE 8192
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct _ntfs_volume - structure describing an open volume in memory.
|
|
||||||
*/
|
|
||||||
struct _ntfs_volume
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct ntfs_device *dev; /* NTFS device associated with
|
|
||||||
the volume. */
|
|
||||||
void *sb; /* For kernel porting compatibility. */
|
|
||||||
};
|
|
||||||
char *vol_name; /* Name of the volume. */
|
|
||||||
unsigned long state; /* NTFS specific flags describing this volume.
|
|
||||||
See ntfs_volume_state_bits above. */
|
|
||||||
|
|
||||||
ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */
|
|
||||||
u8 major_ver; /* Ntfs major version of volume. */
|
|
||||||
u8 minor_ver; /* Ntfs minor version of volume. */
|
|
||||||
le16 flags; /* Bit array of VOLUME_* flags. */
|
|
||||||
|
|
||||||
u16 sector_size; /* Byte size of a sector. */
|
|
||||||
u8 sector_size_bits; /* Log(2) of the byte size of a sector. */
|
|
||||||
u32 cluster_size; /* Byte size of a cluster. */
|
|
||||||
u32 mft_record_size; /* Byte size of a mft record. */
|
|
||||||
u32 indx_record_size; /* Byte size of a INDX record. */
|
|
||||||
u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */
|
|
||||||
u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */
|
|
||||||
u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */
|
|
||||||
|
|
||||||
/* Variables used by the cluster and mft allocators. */
|
|
||||||
u8 mft_zone_multiplier; /* Initial mft zone multiplier. */
|
|
||||||
u8 full_zones; /* cluster zones which are full */
|
|
||||||
s64 mft_data_pos; /* Mft record number at which to allocate the
|
|
||||||
next mft record. */
|
|
||||||
LCN mft_zone_start; /* First cluster of the mft zone. */
|
|
||||||
LCN mft_zone_end; /* First cluster beyond the mft zone. */
|
|
||||||
LCN mft_zone_pos; /* Current position in the mft zone. */
|
|
||||||
LCN data1_zone_pos; /* Current position in the first data zone. */
|
|
||||||
LCN data2_zone_pos; /* Current position in the second data zone. */
|
|
||||||
|
|
||||||
s64 nr_clusters; /* Volume size in clusters, hence also the
|
|
||||||
number of bits in lcn_bitmap. */
|
|
||||||
ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */
|
|
||||||
ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute
|
|
||||||
of FILE_Bitmap. Each bit represents a
|
|
||||||
cluster on the volume, bit 0 representing
|
|
||||||
lcn 0 and so on. A set bit means that the
|
|
||||||
cluster and vice versa. */
|
|
||||||
|
|
||||||
LCN mft_lcn; /* Logical cluster number of the data attribute
|
|
||||||
for FILE_MFT. */
|
|
||||||
ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */
|
|
||||||
ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute
|
|
||||||
of FILE_MFT. */
|
|
||||||
ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute
|
|
||||||
of FILE_MFT. Each bit represents an mft
|
|
||||||
record in the $DATA attribute, bit 0
|
|
||||||
representing mft record 0 and so on. A set
|
|
||||||
bit means that the mft record is in use and
|
|
||||||
vice versa. */
|
|
||||||
|
|
||||||
ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */
|
|
||||||
ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */
|
|
||||||
ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */
|
|
||||||
int secure_reentry; /* check for non-rentries */
|
|
||||||
unsigned int secure_flags; /* flags, see security.h for values */
|
|
||||||
|
|
||||||
int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */
|
|
||||||
LCN mftmirr_lcn; /* Logical cluster number of the data attribute
|
|
||||||
for FILE_MFTMirr. */
|
|
||||||
ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */
|
|
||||||
ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute
|
|
||||||
of FILE_MFTMirr. */
|
|
||||||
|
|
||||||
ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte
|
|
||||||
Unicode characters. Obtained from
|
|
||||||
FILE_UpCase. */
|
|
||||||
u32 upcase_len; /* Length in Unicode characters of the upcase
|
|
||||||
table. */
|
|
||||||
ntfschar *locase; /* Lower case equivalents of all 65536 2-byte
|
|
||||||
Unicode characters. Only if option
|
|
||||||
case_ignore is set. */
|
|
||||||
|
|
||||||
ATTR_DEF *attrdef; /* Attribute definitions. Obtained from
|
|
||||||
FILE_AttrDef. */
|
|
||||||
s32 attrdef_len; /* Size of the attribute definition table in
|
|
||||||
bytes. */
|
|
||||||
|
|
||||||
s64 free_clusters; /* Track the number of free clusters which
|
|
||||||
greatly improves statfs() performance */
|
|
||||||
s64 free_mft_records; /* Same for free mft records (see above) */
|
|
||||||
BOOL efs_raw; /* volume is mounted for raw access to
|
|
||||||
efs-encrypted files */
|
|
||||||
|
|
||||||
#if CACHE_INODE_SIZE
|
|
||||||
struct CACHE_HEADER *xinode_cache;
|
|
||||||
#endif
|
|
||||||
#if CACHE_NIDATA_SIZE
|
|
||||||
struct CACHE_HEADER *nidata_cache;
|
|
||||||
#endif
|
|
||||||
#if CACHE_LOOKUP_SIZE
|
|
||||||
struct CACHE_HEADER *lookup_cache;
|
|
||||||
#endif
|
|
||||||
#if CACHE_SECURID_SIZE
|
|
||||||
struct CACHE_HEADER *securid_cache;
|
|
||||||
#endif
|
|
||||||
#if CACHE_LEGACY_SIZE
|
|
||||||
struct CACHE_HEADER *legacy_cache;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const char *ntfs_home;
|
|
||||||
|
|
||||||
extern ntfs_volume *ntfs_volume_alloc(void);
|
|
||||||
|
|
||||||
extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags);
|
|
||||||
|
|
||||||
extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags);
|
|
||||||
|
|
||||||
extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags);
|
|
||||||
extern int ntfs_umount(ntfs_volume *vol, const BOOL force);
|
|
||||||
|
|
||||||
extern int ntfs_version_is_supported(ntfs_volume *vol);
|
|
||||||
extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose);
|
|
||||||
extern int ntfs_logfile_reset(ntfs_volume *vol);
|
|
||||||
|
|
||||||
extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags);
|
|
||||||
|
|
||||||
extern int ntfs_volume_error(int err);
|
|
||||||
extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err);
|
|
||||||
|
|
||||||
extern int ntfs_volume_get_free_space(ntfs_volume *vol);
|
|
||||||
|
|
||||||
extern int ntfs_set_shown_files(ntfs_volume *vol, BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files);
|
|
||||||
extern int ntfs_set_locale(void);
|
|
||||||
extern int ntfs_set_ignore_case(ntfs_volume *vol);
|
|
||||||
|
|
||||||
#endif /* defined _NTFS_VOLUME_H */
|
|
||||||
|
|
57
source/libs/libfat/bit_ops.h
Normal file
57
source/libs/libfat/bit_ops.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
bit_ops.h
|
||||||
|
Functions for dealing with conversion of data between types
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIT_OPS_H
|
||||||
|
#define _BIT_OPS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
Functions to deal with little endian values stored in uint8_t arrays
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) {
|
||||||
|
return ( item[offset] | (item[offset + 1] << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) {
|
||||||
|
return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) {
|
||||||
|
item[offset] = (uint8_t) value;
|
||||||
|
item[offset + 1] = (uint8_t)(value >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) {
|
||||||
|
item[offset] = (uint8_t) value;
|
||||||
|
item[offset + 1] = (uint8_t)(value >> 8);
|
||||||
|
item[offset + 2] = (uint8_t)(value >> 16);
|
||||||
|
item[offset + 3] = (uint8_t)(value >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _BIT_OPS_H
|
130
source/libs/libfat/cache.h
Normal file
130
source/libs/libfat/cache.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
cache.h
|
||||||
|
The cache is not visible to the user. It should be flushed
|
||||||
|
when any file is closed or changes are made to the filesystem.
|
||||||
|
|
||||||
|
This cache implements a least-used-page replacement policy. This will
|
||||||
|
distribute sectors evenly over the pages, so if less than the maximum
|
||||||
|
pages are used at once, they should all eventually remain in the cache.
|
||||||
|
This also has the benefit of throwing out old sectors, so as not to keep
|
||||||
|
too many stale pages around.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CACHE_H
|
||||||
|
#define _CACHE_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "disc.h"
|
||||||
|
|
||||||
|
#define PAGE_SECTORS 64
|
||||||
|
#define CACHE_PAGE_SIZE (BYTES_PER_READ * PAGE_SECTORS)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sec_t sector;
|
||||||
|
unsigned int count;
|
||||||
|
unsigned int last_access;
|
||||||
|
bool dirty;
|
||||||
|
uint8_t* cache;
|
||||||
|
} CACHE_ENTRY;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const DISC_INTERFACE* disc;
|
||||||
|
sec_t endOfPartition;
|
||||||
|
unsigned int numberOfPages;
|
||||||
|
unsigned int sectorsPerPage;
|
||||||
|
CACHE_ENTRY* cacheEntries;
|
||||||
|
} CACHE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read data from a sector in the cache
|
||||||
|
If the sector is not in the cache, it will be swapped in
|
||||||
|
offset is the position to start reading from
|
||||||
|
size is the amount of data to read
|
||||||
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
|
bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write data to a sector in the cache
|
||||||
|
If the sector is not in the cache, it will be swapped in.
|
||||||
|
When the sector is swapped out, the data will be written to the disc
|
||||||
|
offset is the position to start writing to
|
||||||
|
size is the amount of data to write
|
||||||
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
|
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write data to a sector in the cache, zeroing the sector first
|
||||||
|
If the sector is not in the cache, it will be swapped in.
|
||||||
|
When the sector is swapped out, the data will be written to the disc
|
||||||
|
offset is the position to start writing to
|
||||||
|
size is the amount of data to write
|
||||||
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read several sectors from the cache
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read a full sector from the cache
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) {
|
||||||
|
return _FAT_cache_readPartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write a full sector to the cache
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) {
|
||||||
|
return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write any dirty sectors back to disc and clear out the contents of the cache
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_flush (CACHE* cache);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Clear out the contents of the cache without writing any dirty sectors first
|
||||||
|
*/
|
||||||
|
void _FAT_cache_invalidate (CACHE* cache);
|
||||||
|
|
||||||
|
CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition);
|
||||||
|
|
||||||
|
void _FAT_cache_destructor (CACHE* cache);
|
||||||
|
|
||||||
|
#endif // _CACHE_H
|
||||||
|
|
79
source/libs/libfat/common.h
Normal file
79
source/libs/libfat/common.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
common.h
|
||||||
|
Common definitions and included files for the FATlib
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _COMMON_H
|
||||||
|
#define _COMMON_H
|
||||||
|
|
||||||
|
#define BYTES_PER_READ 512
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <fat.h>
|
||||||
|
|
||||||
|
// When compiling for NDS, make sure NDS is defined
|
||||||
|
#ifndef NDS
|
||||||
|
#if defined ARM9 || defined ARM7
|
||||||
|
#define NDS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Platform specific includes
|
||||||
|
#if defined(__gamecube__) || defined (__wii__)
|
||||||
|
#include <gctypes.h>
|
||||||
|
#include <ogc/disc_io.h>
|
||||||
|
#include <gccore.h>
|
||||||
|
#elif defined(NDS)
|
||||||
|
#include <nds/ndstypes.h>
|
||||||
|
#include <nds/system.h>
|
||||||
|
#include <nds/disc_io.h>
|
||||||
|
#elif defined(GBA)
|
||||||
|
#include <gba_types.h>
|
||||||
|
#include <disc_io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Platform specific options
|
||||||
|
#if defined (__wii__)
|
||||||
|
#define DEFAULT_CACHE_PAGES 4
|
||||||
|
#define DEFAULT_SECTORS_PAGE 64
|
||||||
|
#define USE_LWP_LOCK
|
||||||
|
#define USE_RTC_TIME
|
||||||
|
#elif defined (__gamecube__)
|
||||||
|
#define DEFAULT_CACHE_PAGES 4
|
||||||
|
#define DEFAULT_SECTORS_PAGE 64
|
||||||
|
#define USE_LWP_LOCK
|
||||||
|
#define USE_RTC_TIME
|
||||||
|
#elif defined (NDS)
|
||||||
|
#define DEFAULT_CACHE_PAGES 4
|
||||||
|
#define DEFAULT_SECTORS_PAGE 8
|
||||||
|
#define USE_RTC_TIME
|
||||||
|
#elif defined (GBA)
|
||||||
|
#define DEFAULT_CACHE_PAGES 2
|
||||||
|
#define DEFAULT_SECTORS_PAGE 8
|
||||||
|
#define LIMIT_SECTORS 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _COMMON_H
|
1119
source/libs/libfat/directory.c
Normal file
1119
source/libs/libfat/directory.c
Normal file
File diff suppressed because it is too large
Load Diff
178
source/libs/libfat/directory.h
Normal file
178
source/libs/libfat/directory.h
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
directory.h
|
||||||
|
Reading, writing and manipulation of the directory structure on
|
||||||
|
a FAT partition
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DIRECTORY_H
|
||||||
|
#define _DIRECTORY_H
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "partition.h"
|
||||||
|
|
||||||
|
#define DIR_ENTRY_DATA_SIZE 0x20
|
||||||
|
#define MAX_LFN_LENGTH 256
|
||||||
|
#define MAX_FILENAME_LENGTH 768 // 256 UCS-2 characters encoded into UTF-8 can use up to 768 UTF-8 chars
|
||||||
|
#define MAX_ALIAS_LENGTH 13
|
||||||
|
#define LFN_ENTRY_LENGTH 13
|
||||||
|
#define ALIAS_ENTRY_LENGTH 11
|
||||||
|
#define MAX_ALIAS_EXT_LENGTH 3
|
||||||
|
#define MAX_ALIAS_PRI_LENGTH 8
|
||||||
|
#define MAX_NUMERIC_TAIL 999999
|
||||||
|
#define FAT16_ROOT_DIR_CLUSTER 0
|
||||||
|
|
||||||
|
#define DIR_SEPARATOR '/'
|
||||||
|
|
||||||
|
// File attributes
|
||||||
|
#define ATTRIB_ARCH 0x20 // Archive
|
||||||
|
#define ATTRIB_DIR 0x10 // Directory
|
||||||
|
#define ATTRIB_LFN 0x0F // Long file name
|
||||||
|
#define ATTRIB_VOL 0x08 // Volume
|
||||||
|
#define ATTRIB_SYS 0x04 // System
|
||||||
|
#define ATTRIB_HID 0x02 // Hidden
|
||||||
|
#define ATTRIB_RO 0x01 // Read only
|
||||||
|
|
||||||
|
typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t cluster;
|
||||||
|
sec_t sector;
|
||||||
|
int32_t offset;
|
||||||
|
} DIR_ENTRY_POSITION;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t entryData[DIR_ENTRY_DATA_SIZE];
|
||||||
|
DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
||||||
|
DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry
|
||||||
|
char filename[MAX_FILENAME_LENGTH];
|
||||||
|
} DIR_ENTRY;
|
||||||
|
|
||||||
|
// Directory entry offsets
|
||||||
|
enum DIR_ENTRY_offset {
|
||||||
|
DIR_ENTRY_name = 0x00,
|
||||||
|
DIR_ENTRY_extension = 0x08,
|
||||||
|
DIR_ENTRY_attributes = 0x0B,
|
||||||
|
DIR_ENTRY_reserved = 0x0C,
|
||||||
|
DIR_ENTRY_cTime_ms = 0x0D,
|
||||||
|
DIR_ENTRY_cTime = 0x0E,
|
||||||
|
DIR_ENTRY_cDate = 0x10,
|
||||||
|
DIR_ENTRY_aDate = 0x12,
|
||||||
|
DIR_ENTRY_clusterHigh = 0x14,
|
||||||
|
DIR_ENTRY_mTime = 0x16,
|
||||||
|
DIR_ENTRY_mDate = 0x18,
|
||||||
|
DIR_ENTRY_cluster = 0x1A,
|
||||||
|
DIR_ENTRY_fileSize = 0x1C
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns true if the file specified by entry is a directory
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) {
|
||||||
|
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) {
|
||||||
|
return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) {
|
||||||
|
return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') ||
|
||||||
|
((entry->filename[1] == '.') && entry->filename[2] == '\0')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads the first directory entry from the directory starting at dirCluster
|
||||||
|
Places result in entry
|
||||||
|
entry will be destroyed even if no directory entry is found
|
||||||
|
Returns true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads the next directory entry after the one already pointed to by entry
|
||||||
|
Places result in entry
|
||||||
|
entry will be destroyed even if no directory entry is found
|
||||||
|
Returns true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets the directory entry corrsponding to the supplied path
|
||||||
|
entry will be destroyed even if no directory entry is found
|
||||||
|
pathEnd specifies the end of the path string, for cutting strings short if needed
|
||||||
|
specify NULL to use the full length of path
|
||||||
|
pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR
|
||||||
|
after pathEND.
|
||||||
|
Returns true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Changes the current directory to the one specified by path
|
||||||
|
Returns true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_chdir (PARTITION* partition, const char* path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes the directory entry specified by entry
|
||||||
|
Assumes that entry is valid
|
||||||
|
Returns true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add a directory entry to the directory specified by dirCluster
|
||||||
|
The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are
|
||||||
|
updated with the new directory entry position and alias.
|
||||||
|
Returns true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the start cluster of a file from it's entry data
|
||||||
|
*/
|
||||||
|
uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fill in the file name and entry data of DIR_ENTRY* entry.
|
||||||
|
Assumes that the entry's dataStart and dataEnd are correct
|
||||||
|
Returns true on success, false on failure
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fill in a stat struct based on a file entry
|
||||||
|
*/
|
||||||
|
void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get volume label
|
||||||
|
*/
|
||||||
|
bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label);
|
||||||
|
|
||||||
|
#endif // _DIRECTORY_H
|
110
source/libs/libfat/disc.h
Normal file
110
source/libs/libfat/disc.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
disc.h
|
||||||
|
Interface to the low level disc functions. Used by the higher level
|
||||||
|
file system code.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef _DISC_H
|
||||||
|
#define _DISC_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
A list of all default devices to try at startup,
|
||||||
|
terminated by a {NULL,NULL} entry.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
const DISC_INTERFACE* (*getInterface)(void);
|
||||||
|
} INTERFACE_ID;
|
||||||
|
extern const INTERFACE_ID _FAT_disc_interfaces[];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check if a disc is inserted
|
||||||
|
Return true if a disc is inserted and ready, false otherwise
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) {
|
||||||
|
return disc->isInserted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read numSectors sectors from a disc, starting at sector.
|
||||||
|
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
||||||
|
else it is at least 1
|
||||||
|
sector is 0 or greater
|
||||||
|
buffer is a pointer to the memory to fill
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) {
|
||||||
|
return disc->readSectors (sector, numSectors, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write numSectors sectors to a disc, starting at sector.
|
||||||
|
numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
|
||||||
|
else it is at least 1
|
||||||
|
sector is 0 or greater
|
||||||
|
buffer is a pointer to the memory to read from
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) {
|
||||||
|
return disc->writeSectors (sector, numSectors, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reset the card back to a ready state
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) {
|
||||||
|
return disc->clearStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialise the disc to a state ready for data reading or writing
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) {
|
||||||
|
return disc->startup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Put the disc in a state ready for power down.
|
||||||
|
Complete any pending writes and disable the disc if necessary
|
||||||
|
*/
|
||||||
|
static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) {
|
||||||
|
return disc->shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return a 32 bit value unique to each type of interface
|
||||||
|
*/
|
||||||
|
static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) {
|
||||||
|
return disc->ioType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return a 32 bit value that specifies the capabilities of the disc
|
||||||
|
*/
|
||||||
|
static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) {
|
||||||
|
return disc->features;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _DISC_H
|
104
source/libs/libfat/fat.h
Normal file
104
source/libs/libfat/fat.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
fat.h
|
||||||
|
Simple functionality for startup, mounting and unmounting of FAT-based devices.
|
||||||
|
|
||||||
|
Copyright (c) 2006 - 2009
|
||||||
|
Michael "Chishm" Chisholm
|
||||||
|
Dave "WinterMute" Murphy
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _LIBFAT_H
|
||||||
|
#define _LIBFAT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// When compiling for NDS, make sure NDS is defined
|
||||||
|
#ifndef NDS
|
||||||
|
#if defined ARM9 || defined ARM7
|
||||||
|
#define NDS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#if defined(__gamecube__) || defined (__wii__)
|
||||||
|
# include <ogc/disc_io.h>
|
||||||
|
#else
|
||||||
|
# ifdef NDS
|
||||||
|
# include "nds/disc_io.h"
|
||||||
|
# else
|
||||||
|
# include "disc_io.h"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialise any inserted block-devices.
|
||||||
|
Add the fat device driver to the devoptab, making it available for standard file functions.
|
||||||
|
cacheSize: The number of pages to allocate for each inserted block-device
|
||||||
|
setAsDefaultDevice: if true, make this the default device driver for file operations
|
||||||
|
*/
|
||||||
|
extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
|
||||||
|
*/
|
||||||
|
extern bool fatInitDefault (void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
|
You can then access the filesystem using "name:/".
|
||||||
|
This will mount the active partition or the first valid partition on the disc,
|
||||||
|
and will use a cache size optimized for the host system.
|
||||||
|
*/
|
||||||
|
extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
|
||||||
|
You can then access the filesystem using "name:/".
|
||||||
|
If startSector = 0, it will mount the active partition of the first valid partition on
|
||||||
|
the disc. Otherwise it will try to mount the partition starting at startSector.
|
||||||
|
cacheSize specifies the number of pages to allocate for the cache.
|
||||||
|
This will not startup the disc, so you need to call interface->startup(); first.
|
||||||
|
*/
|
||||||
|
extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unmount the partition specified by name.
|
||||||
|
If there are open files, it will attempt to synchronise them to disc.
|
||||||
|
*/
|
||||||
|
extern void fatUnmount (const char* name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get Volume Label
|
||||||
|
*/
|
||||||
|
extern void fatGetVolumeLabel (const char* name, char *label);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _LIBFAT_H
|
366
source/libs/libfat/fat_cache.c
Normal file
366
source/libs/libfat/fat_cache.c
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
cache.c
|
||||||
|
The cache is not visible to the user. It should be flushed
|
||||||
|
when any file is closed or changes are made to the filesystem.
|
||||||
|
|
||||||
|
This cache implements a least-used-page replacement policy. This will
|
||||||
|
distribute sectors evenly over the pages, so if less than the maximum
|
||||||
|
pages are used at once, they should all eventually remain in the cache.
|
||||||
|
This also has the benefit of throwing out old sectors, so as not to keep
|
||||||
|
too many stale pages around.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "cache.h"
|
||||||
|
#include "disc.h"
|
||||||
|
|
||||||
|
#include "mem_allocate.h"
|
||||||
|
#include "bit_ops.h"
|
||||||
|
#include "file_allocation_table.h"
|
||||||
|
|
||||||
|
#define CACHE_FREE UINT_MAX
|
||||||
|
|
||||||
|
CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition) {
|
||||||
|
CACHE* cache;
|
||||||
|
unsigned int i;
|
||||||
|
CACHE_ENTRY* cacheEntries;
|
||||||
|
|
||||||
|
if (numberOfPages < 2) {
|
||||||
|
numberOfPages = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectorsPerPage < 8) {
|
||||||
|
sectorsPerPage = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE));
|
||||||
|
if (cache == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache->disc = discInterface;
|
||||||
|
cache->endOfPartition = endOfPartition;
|
||||||
|
cache->numberOfPages = numberOfPages;
|
||||||
|
cache->sectorsPerPage = sectorsPerPage;
|
||||||
|
|
||||||
|
|
||||||
|
cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages);
|
||||||
|
if (cacheEntries == NULL) {
|
||||||
|
_FAT_mem_free (cache);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < numberOfPages; i++) {
|
||||||
|
cacheEntries[i].sector = CACHE_FREE;
|
||||||
|
cacheEntries[i].count = 0;
|
||||||
|
cacheEntries[i].last_access = 0;
|
||||||
|
cacheEntries[i].dirty = false;
|
||||||
|
cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * BYTES_PER_READ );
|
||||||
|
}
|
||||||
|
|
||||||
|
cache->cacheEntries = cacheEntries;
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _FAT_cache_destructor (CACHE* cache) {
|
||||||
|
unsigned int i;
|
||||||
|
// Clear out cache before destroying it
|
||||||
|
_FAT_cache_flush(cache);
|
||||||
|
|
||||||
|
// Free memory in reverse allocation order
|
||||||
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
|
_FAT_mem_free (cache->cacheEntries[i].cache);
|
||||||
|
}
|
||||||
|
_FAT_mem_free (cache->cacheEntries);
|
||||||
|
_FAT_mem_free (cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static u32 accessCounter = 0;
|
||||||
|
|
||||||
|
static u32 accessTime(){
|
||||||
|
accessCounter++;
|
||||||
|
return accessCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
||||||
|
unsigned int numberOfPages = cache->numberOfPages;
|
||||||
|
unsigned int sectorsPerPage = cache->sectorsPerPage;
|
||||||
|
|
||||||
|
bool foundFree = false;
|
||||||
|
unsigned int oldUsed = 0;
|
||||||
|
unsigned int oldAccess = UINT_MAX;
|
||||||
|
|
||||||
|
for(i=0;i<numberOfPages;i++) {
|
||||||
|
if(sector>=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) {
|
||||||
|
cacheEntries[i].last_access = accessTime();
|
||||||
|
return &(cacheEntries[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_access<oldAccess)) {
|
||||||
|
if(cacheEntries[i].sector==CACHE_FREE) foundFree = true;
|
||||||
|
oldUsed = i;
|
||||||
|
oldAccess = cacheEntries[i].last_access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundFree==false && cacheEntries[oldUsed].dirty==true) {
|
||||||
|
if(!_FAT_disc_writeSectors(cache->disc,cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL;
|
||||||
|
cacheEntries[oldUsed].dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size
|
||||||
|
sec_t next_page = sector + sectorsPerPage;
|
||||||
|
if(next_page > cache->endOfPartition) next_page = cache->endOfPartition;
|
||||||
|
|
||||||
|
if(!_FAT_disc_readSectors(cache->disc,sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL;
|
||||||
|
|
||||||
|
cacheEntries[oldUsed].sector = sector;
|
||||||
|
cacheEntries[oldUsed].count = next_page-sector;
|
||||||
|
cacheEntries[oldUsed].last_access = accessTime();
|
||||||
|
|
||||||
|
return &(cacheEntries[oldUsed]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _FAT_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
sec_t secs_to_read;
|
||||||
|
CACHE_ENTRY *entry;
|
||||||
|
uint8_t *dest = buffer;
|
||||||
|
|
||||||
|
while(numSectors>0) {
|
||||||
|
entry = _FAT_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
secs_to_read = entry->count - sec;
|
||||||
|
if(secs_to_read>numSectors) secs_to_read = numSectors;
|
||||||
|
|
||||||
|
memcpy(dest,entry->cache + (sec*BYTES_PER_READ),(secs_to_read*BYTES_PER_READ));
|
||||||
|
|
||||||
|
dest += (secs_to_read*BYTES_PER_READ);
|
||||||
|
sector += secs_to_read;
|
||||||
|
numSectors -= secs_to_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads some data from a cache page, determined by the sector number
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
CACHE_ENTRY *entry;
|
||||||
|
|
||||||
|
if (offset + size > BYTES_PER_READ) return false;
|
||||||
|
|
||||||
|
entry = _FAT_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
memcpy(buffer,entry->cache + ((sec*BYTES_PER_READ) + offset),size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) {
|
||||||
|
uint8_t buf[4];
|
||||||
|
if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
||||||
|
|
||||||
|
switch(num_bytes) {
|
||||||
|
case 1: *value = buf[0]; break;
|
||||||
|
case 2: *value = u8array_to_u16(buf,0); break;
|
||||||
|
case 4: *value = u8array_to_u32(buf,0); break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Writes some data to a cache page, making sure it is loaded into memory first.
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
CACHE_ENTRY *entry;
|
||||||
|
|
||||||
|
if (offset + size > BYTES_PER_READ) return false;
|
||||||
|
|
||||||
|
entry = _FAT_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size);
|
||||||
|
|
||||||
|
entry->dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) {
|
||||||
|
uint8_t buf[4] = {0, 0, 0, 0};
|
||||||
|
|
||||||
|
switch(size) {
|
||||||
|
case 1: buf[0] = value; break;
|
||||||
|
case 2: u16_to_u8array(buf, 0, value); break;
|
||||||
|
case 4: u32_to_u8array(buf, 0, value); break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _FAT_cache_writePartialSector(cache, buf, sector, offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Writes some data to a cache page, zeroing out the page first
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
CACHE_ENTRY *entry;
|
||||||
|
|
||||||
|
if (offset + size > BYTES_PER_READ) return false;
|
||||||
|
|
||||||
|
entry = _FAT_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
memset(entry->cache + (sec*BYTES_PER_READ),0,BYTES_PER_READ);
|
||||||
|
memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size);
|
||||||
|
|
||||||
|
entry->dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static CACHE_ENTRY* _FAT_cache_findPage(CACHE *cache, sec_t sector, sec_t count) {
|
||||||
|
|
||||||
|
unsigned int i;
|
||||||
|
CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
||||||
|
unsigned int numberOfPages = cache->numberOfPages;
|
||||||
|
CACHE_ENTRY *entry = NULL;
|
||||||
|
sec_t lowest = UINT_MAX;
|
||||||
|
|
||||||
|
for(i=0;i<numberOfPages;i++) {
|
||||||
|
if (cacheEntries[i].sector != CACHE_FREE) {
|
||||||
|
bool intersect;
|
||||||
|
if (sector > cacheEntries[i].sector) {
|
||||||
|
intersect = sector - cacheEntries[i].sector < cacheEntries[i].count;
|
||||||
|
} else {
|
||||||
|
intersect = cacheEntries[i].sector - sector < count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( intersect && (cacheEntries[i].sector < lowest)) {
|
||||||
|
lowest = cacheEntries[i].sector;
|
||||||
|
entry = &cacheEntries[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
sec_t secs_to_write;
|
||||||
|
CACHE_ENTRY* entry;
|
||||||
|
const uint8_t *src = buffer;
|
||||||
|
|
||||||
|
while(numSectors>0)
|
||||||
|
{
|
||||||
|
entry = _FAT_cache_findPage(cache,sector,numSectors);
|
||||||
|
|
||||||
|
if(entry!=NULL) {
|
||||||
|
|
||||||
|
if ( entry->sector > sector) {
|
||||||
|
|
||||||
|
secs_to_write = entry->sector - sector;
|
||||||
|
|
||||||
|
_FAT_disc_writeSectors(cache->disc,sector,secs_to_write,src);
|
||||||
|
src += (secs_to_write*BYTES_PER_READ);
|
||||||
|
sector += secs_to_write;
|
||||||
|
numSectors -= secs_to_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
secs_to_write = entry->count - sec;
|
||||||
|
|
||||||
|
if(secs_to_write>numSectors) secs_to_write = numSectors;
|
||||||
|
|
||||||
|
memcpy(entry->cache + (sec*BYTES_PER_READ),src,(secs_to_write*BYTES_PER_READ));
|
||||||
|
|
||||||
|
src += (secs_to_write*BYTES_PER_READ);
|
||||||
|
sector += secs_to_write;
|
||||||
|
numSectors -= secs_to_write;
|
||||||
|
|
||||||
|
entry->dirty = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_FAT_disc_writeSectors(cache->disc,sector,numSectors,src);
|
||||||
|
numSectors=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Flushes all dirty pages to disc, clearing the dirty flag.
|
||||||
|
*/
|
||||||
|
bool _FAT_cache_flush (CACHE* cache) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
|
if (cache->cacheEntries[i].dirty) {
|
||||||
|
if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache->cacheEntries[i].dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _FAT_cache_invalidate (CACHE* cache) {
|
||||||
|
unsigned int i;
|
||||||
|
_FAT_cache_flush(cache);
|
||||||
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
|
cache->cacheEntries[i].sector = CACHE_FREE;
|
||||||
|
cache->cacheEntries[i].last_access = 0;
|
||||||
|
cache->cacheEntries[i].count = 0;
|
||||||
|
cache->cacheEntries[i].dirty = false;
|
||||||
|
}
|
||||||
|
}
|
105
source/libs/libfat/fat_disc.c
Normal file
105
source/libs/libfat/fat_disc.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
disc.c
|
||||||
|
Interface to the low level disc functions. Used by the higher level
|
||||||
|
file system code.
|
||||||
|
|
||||||
|
Copyright (c) 2008 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "disc.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
The list of interfaces consists of a series of name/interface pairs.
|
||||||
|
The interface is returned via a simple function. This allows for
|
||||||
|
platforms where the interface has to be "assembled" before it can
|
||||||
|
be used, like DLDI on the NDS. For cases where a simple struct
|
||||||
|
is available, wrapper functions are used.
|
||||||
|
The list is terminated by a NULL/NULL entry.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ====================== Wii ====================== */
|
||||||
|
#if defined (__wii__)
|
||||||
|
#include <sdcard/wiisd_io.h>
|
||||||
|
#include <ogc/usbstorage.h>
|
||||||
|
#include <sdcard/gcsd.h>
|
||||||
|
|
||||||
|
static const DISC_INTERFACE* get_io_wiisd (void) {
|
||||||
|
return &__io_wiisd;
|
||||||
|
}
|
||||||
|
static const DISC_INTERFACE* get_io_usbstorage (void) {
|
||||||
|
return &__io_usbstorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const DISC_INTERFACE* get_io_gcsda (void) {
|
||||||
|
return &__io_gcsda;
|
||||||
|
}
|
||||||
|
static const DISC_INTERFACE* get_io_gcsdb (void) {
|
||||||
|
return &__io_gcsdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||||
|
{"sd", get_io_wiisd},
|
||||||
|
{"usb", get_io_usbstorage},
|
||||||
|
{"carda", get_io_gcsda},
|
||||||
|
{"cardb", get_io_gcsdb},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ==================== Gamecube ==================== */
|
||||||
|
#elif defined (__gamecube__)
|
||||||
|
#include <sdcard/gcsd.h>
|
||||||
|
|
||||||
|
static const DISC_INTERFACE* get_io_gcsda (void) {
|
||||||
|
return &__io_gcsda;
|
||||||
|
}
|
||||||
|
static const DISC_INTERFACE* get_io_gcsdb (void) {
|
||||||
|
return &__io_gcsdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||||
|
{"carda", get_io_gcsda},
|
||||||
|
{"cardb", get_io_gcsdb},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ====================== NDS ====================== */
|
||||||
|
#elif defined (NDS)
|
||||||
|
#include <nds/arm9/dldi.h>
|
||||||
|
|
||||||
|
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||||
|
{"fat", dldiGetInternal},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ====================== GBA ====================== */
|
||||||
|
#elif defined (GBA)
|
||||||
|
#include <disc.h>
|
||||||
|
|
||||||
|
const INTERFACE_ID _FAT_disc_interfaces[] = {
|
||||||
|
{"fat", discGetInterface},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
610
source/libs/libfat/fatdir.c
Normal file
610
source/libs/libfat/fatdir.c
Normal file
@ -0,0 +1,610 @@
|
|||||||
|
/*
|
||||||
|
fatdir.c
|
||||||
|
|
||||||
|
Functions used by the newlib disc stubs to interface with
|
||||||
|
this library
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/dir.h>
|
||||||
|
|
||||||
|
#include "fatdir.h"
|
||||||
|
|
||||||
|
#include "cache.h"
|
||||||
|
#include "file_allocation_table.h"
|
||||||
|
#include "partition.h"
|
||||||
|
#include "directory.h"
|
||||||
|
#include "bit_ops.h"
|
||||||
|
#include "filetime.h"
|
||||||
|
#include "lock.h"
|
||||||
|
|
||||||
|
|
||||||
|
int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) {
|
||||||
|
PARTITION* partition = NULL;
|
||||||
|
DIR_ENTRY dirEntry;
|
||||||
|
|
||||||
|
// Get the partition this file is on
|
||||||
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
if (partition == NULL) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the path pointer to the start of the actual path
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
path = strchr (path, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Search for the file on the disc
|
||||||
|
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the stat struct
|
||||||
|
_FAT_directory_entryStat (partition, &dirEntry, st);
|
||||||
|
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink) {
|
||||||
|
r->_errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_unlink_r (struct _reent *r, const char *path) {
|
||||||
|
PARTITION* partition = NULL;
|
||||||
|
DIR_ENTRY dirEntry;
|
||||||
|
DIR_ENTRY dirContents;
|
||||||
|
uint32_t cluster;
|
||||||
|
bool nextEntry;
|
||||||
|
bool errorOccured = false;
|
||||||
|
|
||||||
|
// Get the partition this directory is on
|
||||||
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
if (partition == NULL) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we aren't trying to write to a read-only disc
|
||||||
|
if (partition->readOnly) {
|
||||||
|
r->_errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the path pointer to the start of the actual path
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
path = strchr (path, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Search for the file on the disc
|
||||||
|
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
|
||||||
|
|
||||||
|
|
||||||
|
// If this is a directory, make sure it is empty
|
||||||
|
if (_FAT_directory_isDirectory (&dirEntry)) {
|
||||||
|
nextEntry = _FAT_directory_getFirstEntry (partition, &dirContents, cluster);
|
||||||
|
|
||||||
|
while (nextEntry) {
|
||||||
|
if (!_FAT_directory_isDot (&dirContents)) {
|
||||||
|
// The directory had something in it that isn't a reference to itself or it's parent
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nextEntry = _FAT_directory_getNextEntry (partition, &dirContents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_FAT_fat_isValidCluster(partition, cluster)) {
|
||||||
|
// Remove the cluster chain for this file
|
||||||
|
if (!_FAT_fat_clearLinks (partition, cluster)) {
|
||||||
|
r->_errno = EIO;
|
||||||
|
errorOccured = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the directory entry for this file
|
||||||
|
if (!_FAT_directory_removeEntry (partition, &dirEntry)) {
|
||||||
|
r->_errno = EIO;
|
||||||
|
errorOccured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush any sectors in the disc cache
|
||||||
|
if (!_FAT_cache_flush(partition->cache)) {
|
||||||
|
r->_errno = EIO;
|
||||||
|
errorOccured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
if (errorOccured) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_chdir_r (struct _reent *r, const char *path) {
|
||||||
|
PARTITION* partition = NULL;
|
||||||
|
|
||||||
|
// Get the partition this directory is on
|
||||||
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
if (partition == NULL) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the path pointer to the start of the actual path
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
path = strchr (path, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Try changing directory
|
||||||
|
if (_FAT_directory_chdir (partition, path)) {
|
||||||
|
// Successful
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// Failed
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOTDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
|
||||||
|
PARTITION* partition = NULL;
|
||||||
|
DIR_ENTRY oldDirEntry;
|
||||||
|
DIR_ENTRY newDirEntry;
|
||||||
|
const char *pathEnd;
|
||||||
|
uint32_t dirCluster;
|
||||||
|
|
||||||
|
// Get the partition this directory is on
|
||||||
|
partition = _FAT_partition_getPartitionFromPath (oldName);
|
||||||
|
if (partition == NULL) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Make sure the same partition is used for the old and new names
|
||||||
|
if (partition != _FAT_partition_getPartitionFromPath (newName)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EXDEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we aren't trying to write to a read-only disc
|
||||||
|
if (partition->readOnly) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the path pointer to the start of the actual path
|
||||||
|
if (strchr (oldName, ':') != NULL) {
|
||||||
|
oldName = strchr (oldName, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr (oldName, ':') != NULL) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (strchr (newName, ':') != NULL) {
|
||||||
|
newName = strchr (newName, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr (newName, ':') != NULL) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the file on the disc
|
||||||
|
if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there is no existing file / directory with the new name
|
||||||
|
if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EEXIST;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new file entry
|
||||||
|
// Get the directory it has to go in
|
||||||
|
pathEnd = strrchr (newName, DIR_SEPARATOR);
|
||||||
|
if (pathEnd == NULL) {
|
||||||
|
// No path was specified
|
||||||
|
dirCluster = partition->cwdCluster;
|
||||||
|
pathEnd = newName;
|
||||||
|
} else {
|
||||||
|
// Path was specified -- get the right dirCluster
|
||||||
|
// Recycling newDirEntry, since it needs to be recreated anyway
|
||||||
|
if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) ||
|
||||||
|
!_FAT_directory_isDirectory(&newDirEntry)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOTDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
dirCluster = _FAT_directory_entryGetCluster (partition, newDirEntry.entryData);
|
||||||
|
// Move the pathEnd past the last DIR_SEPARATOR
|
||||||
|
pathEnd += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the entry data
|
||||||
|
memcpy (&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY));
|
||||||
|
|
||||||
|
// Set the new name
|
||||||
|
strncpy (newDirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
|
||||||
|
|
||||||
|
// Write the new entry
|
||||||
|
if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the old entry
|
||||||
|
if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush any sectors in the disc cache
|
||||||
|
if (!_FAT_cache_flush (partition->cache)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
|
||||||
|
PARTITION* partition = NULL;
|
||||||
|
bool fileExists;
|
||||||
|
DIR_ENTRY dirEntry;
|
||||||
|
const char* pathEnd;
|
||||||
|
uint32_t parentCluster, dirCluster;
|
||||||
|
uint8_t newEntryData[DIR_ENTRY_DATA_SIZE];
|
||||||
|
|
||||||
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
if (partition == NULL) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the path pointer to the start of the actual path
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
path = strchr (path, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Search for the file/directory on the disc
|
||||||
|
fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
|
||||||
|
|
||||||
|
// Make sure it doesn't exist
|
||||||
|
if (fileExists) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EEXIST;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partition->readOnly) {
|
||||||
|
// We can't write to a read-only partition
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the directory it has to go in
|
||||||
|
pathEnd = strrchr (path, DIR_SEPARATOR);
|
||||||
|
if (pathEnd == NULL) {
|
||||||
|
// No path was specified
|
||||||
|
parentCluster = partition->cwdCluster;
|
||||||
|
pathEnd = path;
|
||||||
|
} else {
|
||||||
|
// Path was specified -- get the right parentCluster
|
||||||
|
// Recycling dirEntry, since it needs to be recreated anyway
|
||||||
|
if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
|
||||||
|
!_FAT_directory_isDirectory(&dirEntry)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOTDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
parentCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
|
||||||
|
// Move the pathEnd past the last DIR_SEPARATOR
|
||||||
|
pathEnd += 1;
|
||||||
|
}
|
||||||
|
// Create the entry data
|
||||||
|
strncpy (dirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
|
||||||
|
memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE);
|
||||||
|
|
||||||
|
// Set the creation time and date
|
||||||
|
dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0;
|
||||||
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC());
|
||||||
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
|
||||||
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC());
|
||||||
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC());
|
||||||
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
|
||||||
|
|
||||||
|
// Set the directory attribute
|
||||||
|
dirEntry.entryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
|
||||||
|
|
||||||
|
// Get a cluster for the new directory
|
||||||
|
dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE);
|
||||||
|
if (!_FAT_fat_isValidCluster(partition, dirCluster)) {
|
||||||
|
// No space left on disc for the cluster
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cluster, dirCluster);
|
||||||
|
u16_to_u8array (dirEntry.entryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
|
||||||
|
|
||||||
|
// Write the new directory's entry to it's parent
|
||||||
|
if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the dot entry within the directory
|
||||||
|
memset (newEntryData, 0, DIR_ENTRY_DATA_SIZE);
|
||||||
|
memset (newEntryData, ' ', 11);
|
||||||
|
newEntryData[DIR_ENTRY_name] = '.';
|
||||||
|
newEntryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
|
||||||
|
u16_to_u8array (newEntryData, DIR_ENTRY_cluster, dirCluster);
|
||||||
|
u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
|
||||||
|
|
||||||
|
// Write it to the directory, erasing that sector in the process
|
||||||
|
_FAT_cache_eraseWritePartialSector ( partition->cache, newEntryData,
|
||||||
|
_FAT_fat_clusterToSector (partition, dirCluster), 0, DIR_ENTRY_DATA_SIZE);
|
||||||
|
|
||||||
|
|
||||||
|
// Create the double dot entry within the directory
|
||||||
|
|
||||||
|
// if ParentDir == Rootdir then ".."" always link to Cluster 0
|
||||||
|
if(parentCluster == partition->rootDirCluster)
|
||||||
|
parentCluster = FAT16_ROOT_DIR_CLUSTER;
|
||||||
|
|
||||||
|
newEntryData[DIR_ENTRY_name + 1] = '.';
|
||||||
|
u16_to_u8array (newEntryData, DIR_ENTRY_cluster, parentCluster);
|
||||||
|
u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, parentCluster >> 16);
|
||||||
|
|
||||||
|
// Write it to the directory
|
||||||
|
_FAT_cache_writePartialSector ( partition->cache, newEntryData,
|
||||||
|
_FAT_fat_clusterToSector (partition, dirCluster), DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
|
||||||
|
|
||||||
|
// Flush any sectors in the disc cache
|
||||||
|
if (!_FAT_cache_flush(partition->cache)) {
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
r->_errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
||||||
|
{
|
||||||
|
PARTITION* partition = NULL;
|
||||||
|
unsigned int freeClusterCount;
|
||||||
|
|
||||||
|
// Get the partition of the requested path
|
||||||
|
partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
if (partition == NULL) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
freeClusterCount = _FAT_fat_freeClusterCount (partition);
|
||||||
|
|
||||||
|
// FAT clusters = POSIX blocks
|
||||||
|
buf->f_bsize = partition->bytesPerCluster; // File system block size.
|
||||||
|
buf->f_frsize = partition->bytesPerCluster; // Fundamental file system block size.
|
||||||
|
|
||||||
|
buf->f_blocks = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of blocks on file system in units of f_frsize.
|
||||||
|
buf->f_bfree = freeClusterCount; // Total number of free blocks.
|
||||||
|
buf->f_bavail = freeClusterCount; // Number of free blocks available to non-privileged process.
|
||||||
|
|
||||||
|
// Treat requests for info on inodes as clusters
|
||||||
|
buf->f_files = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of file serial numbers.
|
||||||
|
buf->f_ffree = freeClusterCount; // Total number of free file serial numbers.
|
||||||
|
buf->f_favail = freeClusterCount; // Number of file serial numbers available to non-privileged process.
|
||||||
|
|
||||||
|
// File system ID. 32bit ioType value
|
||||||
|
buf->f_fsid = _FAT_disc_hostType(partition->disc);
|
||||||
|
|
||||||
|
// Bit mask of f_flag values.
|
||||||
|
buf->f_flag = ST_NOSUID /* No support for ST_ISUID and ST_ISGID file mode bits */
|
||||||
|
| (partition->readOnly ? ST_RDONLY /* Read only file system */ : 0 ) ;
|
||||||
|
// Maximum filename length.
|
||||||
|
buf->f_namemax = MAX_FILENAME_LENGTH;
|
||||||
|
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) {
|
||||||
|
DIR_ENTRY dirEntry;
|
||||||
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
|
bool fileExists;
|
||||||
|
|
||||||
|
state->partition = _FAT_partition_getPartitionFromPath (path);
|
||||||
|
if (state->partition == NULL) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the path pointer to the start of the actual path
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
path = strchr (path, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr (path, ':') != NULL) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_lock(&state->partition->lock);
|
||||||
|
|
||||||
|
// Get the start cluster of the directory
|
||||||
|
fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL);
|
||||||
|
|
||||||
|
if (!fileExists) {
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure it is a directory
|
||||||
|
if (! _FAT_directory_isDirectory (&dirEntry)) {
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
r->_errno = ENOTDIR;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the start cluster for use when resetting the directory data
|
||||||
|
state->startCluster = _FAT_directory_entryGetCluster (state->partition, dirEntry.entryData);
|
||||||
|
|
||||||
|
// Get the first entry for use with a call to dirnext
|
||||||
|
state->validEntry =
|
||||||
|
_FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
|
||||||
|
|
||||||
|
// We are now using this entry
|
||||||
|
state->inUse = true;
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
return (DIR_ITER*) state;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) {
|
||||||
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
|
|
||||||
|
_FAT_lock(&state->partition->lock);
|
||||||
|
|
||||||
|
// Make sure we are still using this entry
|
||||||
|
if (!state->inUse) {
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first entry for use with a call to dirnext
|
||||||
|
state->validEntry =
|
||||||
|
_FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
|
||||||
|
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
||||||
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
|
|
||||||
|
_FAT_lock(&state->partition->lock);
|
||||||
|
|
||||||
|
// Make sure we are still using this entry
|
||||||
|
if (!state->inUse) {
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there is another file to report on
|
||||||
|
if (! state->validEntry) {
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the filename
|
||||||
|
strncpy (filename, state->currentEntry.filename, MAX_FILENAME_LENGTH);
|
||||||
|
// Get the stats, if requested
|
||||||
|
if (filestat != NULL) {
|
||||||
|
_FAT_directory_entryStat (state->partition, &(state->currentEntry), filestat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the next entry for use next time
|
||||||
|
state->validEntry =
|
||||||
|
_FAT_directory_getNextEntry (state->partition, &(state->currentEntry));
|
||||||
|
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState) {
|
||||||
|
DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
|
||||||
|
|
||||||
|
// We are no longer using this entry
|
||||||
|
_FAT_lock(&state->partition->lock);
|
||||||
|
state->inUse = false;
|
||||||
|
_FAT_unlock(&state->partition->lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
73
source/libs/libfat/fatdir.h
Normal file
73
source/libs/libfat/fatdir.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
fatdir.h
|
||||||
|
|
||||||
|
Functions used by the newlib disc stubs to interface with
|
||||||
|
this library
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _FATDIR_H
|
||||||
|
#define _FATDIR_H
|
||||||
|
|
||||||
|
#include <sys/reent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "directory.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PARTITION* partition;
|
||||||
|
DIR_ENTRY currentEntry;
|
||||||
|
uint32_t startCluster;
|
||||||
|
bool inUse;
|
||||||
|
bool validEntry;
|
||||||
|
} DIR_STATE_STRUCT;
|
||||||
|
|
||||||
|
extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st);
|
||||||
|
|
||||||
|
extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink);
|
||||||
|
|
||||||
|
extern int _FAT_unlink_r (struct _reent *r, const char *name);
|
||||||
|
|
||||||
|
extern int _FAT_chdir_r (struct _reent *r, const char *name);
|
||||||
|
|
||||||
|
extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
||||||
|
|
||||||
|
extern int _FAT_mkdir_r (struct _reent *r, const char *path, int mode);
|
||||||
|
|
||||||
|
extern int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Directory iterator functions
|
||||||
|
*/
|
||||||
|
extern DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path);
|
||||||
|
extern int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState);
|
||||||
|
extern int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
||||||
|
extern int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _FATDIR_H
|
1182
source/libs/libfat/fatfile.c
Normal file
1182
source/libs/libfat/fatfile.c
Normal file
File diff suppressed because it is too large
Load Diff
105
source/libs/libfat/fatfile.h
Normal file
105
source/libs/libfat/fatfile.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
fatfile.h
|
||||||
|
|
||||||
|
Functions used by the newlib disc stubs to interface with
|
||||||
|
this library
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _FATFILE_H
|
||||||
|
#define _FATFILE_H
|
||||||
|
|
||||||
|
#include <sys/reent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "partition.h"
|
||||||
|
#include "directory.h"
|
||||||
|
|
||||||
|
#define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 cluster;
|
||||||
|
sec_t sector;
|
||||||
|
s32 byte;
|
||||||
|
} FILE_POSITION;
|
||||||
|
|
||||||
|
struct _FILE_STRUCT;
|
||||||
|
|
||||||
|
struct _FILE_STRUCT {
|
||||||
|
uint32_t filesize;
|
||||||
|
uint32_t startCluster;
|
||||||
|
uint32_t currentPosition;
|
||||||
|
FILE_POSITION rwPosition;
|
||||||
|
FILE_POSITION appendPosition;
|
||||||
|
DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
|
||||||
|
DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry
|
||||||
|
PARTITION* partition;
|
||||||
|
struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files
|
||||||
|
struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files
|
||||||
|
bool read;
|
||||||
|
bool write;
|
||||||
|
bool append;
|
||||||
|
bool inUse;
|
||||||
|
bool modified;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _FILE_STRUCT FILE_STRUCT;
|
||||||
|
|
||||||
|
extern int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||||
|
|
||||||
|
extern int _FAT_close_r (struct _reent *r, int fd);
|
||||||
|
|
||||||
|
extern ssize_t _FAT_write_r (struct _reent *r,int fd, const char *ptr, size_t len);
|
||||||
|
|
||||||
|
extern ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len);
|
||||||
|
|
||||||
|
extern off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir);
|
||||||
|
|
||||||
|
extern int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st);
|
||||||
|
|
||||||
|
extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st);
|
||||||
|
|
||||||
|
extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink);
|
||||||
|
|
||||||
|
extern int _FAT_unlink_r (struct _reent *r, const char *name);
|
||||||
|
|
||||||
|
extern int _FAT_chdir_r (struct _reent *r, const char *name);
|
||||||
|
|
||||||
|
extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
||||||
|
|
||||||
|
extern int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len);
|
||||||
|
|
||||||
|
extern int _FAT_fsync_r (struct _reent *r, int fd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Synchronizes the file data to disc.
|
||||||
|
Does no locking of its own -- lock the partition before calling.
|
||||||
|
Returns 0 on success, an error code on failure.
|
||||||
|
*/
|
||||||
|
extern int _FAT_syncToDisc (FILE_STRUCT* file);
|
||||||
|
|
||||||
|
#endif // _FATFILE_H
|
383
source/libs/libfat/file_allocation_table.c
Normal file
383
source/libs/libfat/file_allocation_table.c
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
file_allocation_table.c
|
||||||
|
Reading, writing and manipulation of the FAT structure on
|
||||||
|
a FAT partition
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "file_allocation_table.h"
|
||||||
|
#include "partition.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets the cluster linked from input cluster
|
||||||
|
*/
|
||||||
|
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster)
|
||||||
|
{
|
||||||
|
uint32_t nextCluster = CLUSTER_FREE;
|
||||||
|
sec_t sector;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
if (cluster == CLUSTER_FREE) {
|
||||||
|
return CLUSTER_FREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (partition->filesysType)
|
||||||
|
{
|
||||||
|
case FS_UNKNOWN:
|
||||||
|
return CLUSTER_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_FAT12:
|
||||||
|
{
|
||||||
|
u32 nextCluster_h;
|
||||||
|
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
|
||||||
|
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
|
||||||
|
|
||||||
|
|
||||||
|
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8));
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
if (offset >= BYTES_PER_READ) {
|
||||||
|
offset = 0;
|
||||||
|
sector++;
|
||||||
|
}
|
||||||
|
nextCluster_h = 0;
|
||||||
|
|
||||||
|
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8));
|
||||||
|
nextCluster |= (nextCluster_h << 8);
|
||||||
|
|
||||||
|
if (cluster & 0x01) {
|
||||||
|
nextCluster = nextCluster >> 4;
|
||||||
|
} else {
|
||||||
|
nextCluster &= 0x0FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextCluster >= 0x0FF7)
|
||||||
|
{
|
||||||
|
nextCluster = CLUSTER_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FS_FAT16:
|
||||||
|
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
|
||||||
|
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
|
||||||
|
|
||||||
|
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16));
|
||||||
|
|
||||||
|
if (nextCluster >= 0xFFF7) {
|
||||||
|
nextCluster = CLUSTER_EOF;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_FAT32:
|
||||||
|
sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
|
||||||
|
offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
|
||||||
|
|
||||||
|
_FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32));
|
||||||
|
|
||||||
|
if (nextCluster >= 0x0FFFFFF7) {
|
||||||
|
nextCluster = CLUSTER_EOF;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return CLUSTER_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
writes value into the correct offset within a partition's FAT, based
|
||||||
|
on the cluster number.
|
||||||
|
*/
|
||||||
|
static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint32_t value) {
|
||||||
|
sec_t sector;
|
||||||
|
int offset;
|
||||||
|
uint32_t oldValue;
|
||||||
|
|
||||||
|
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (partition->filesysType)
|
||||||
|
{
|
||||||
|
case FS_UNKNOWN:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_FAT12:
|
||||||
|
sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
|
||||||
|
offset = ((cluster * 3) / 2) % BYTES_PER_READ;
|
||||||
|
|
||||||
|
if (cluster & 0x01) {
|
||||||
|
|
||||||
|
_FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8));
|
||||||
|
|
||||||
|
value = (value << 4) | (oldValue & 0x0F);
|
||||||
|
|
||||||
|
_FAT_cache_writeLittleEndianValue (partition->cache, value & 0xFF, sector, offset, sizeof(u8));
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
if (offset >= BYTES_PER_READ) {
|
||||||
|
offset = 0;
|
||||||
|
sector++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_cache_writeLittleEndianValue (partition->cache, (value >> 8) & 0xFF, sector, offset, sizeof(u8));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8));
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
if (offset >= BYTES_PER_READ) {
|
||||||
|
offset = 0;
|
||||||
|
sector++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8));
|
||||||
|
|
||||||
|
value = ((value >> 8) & 0x0F) | (oldValue & 0xF0);
|
||||||
|
|
||||||
|
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_FAT16:
|
||||||
|
sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
|
||||||
|
offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
|
||||||
|
|
||||||
|
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u16));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_FAT32:
|
||||||
|
sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
|
||||||
|
offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
|
||||||
|
|
||||||
|
_FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u32));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
gets the first available free cluster, sets it
|
||||||
|
to end of file, links the input cluster to it then returns the
|
||||||
|
cluster number
|
||||||
|
If an error occurs, return CLUSTER_ERROR
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster) {
|
||||||
|
uint32_t firstFree;
|
||||||
|
uint32_t curLink;
|
||||||
|
uint32_t lastCluster;
|
||||||
|
bool loopedAroundFAT = false;
|
||||||
|
|
||||||
|
lastCluster = partition->fat.lastCluster;
|
||||||
|
|
||||||
|
if (cluster > lastCluster) {
|
||||||
|
return CLUSTER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the cluster already has a link, and return it if so
|
||||||
|
curLink = _FAT_fat_nextCluster(partition, cluster);
|
||||||
|
if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster)) {
|
||||||
|
return curLink; // Return the current link - don't allocate a new one
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a free cluster
|
||||||
|
firstFree = partition->fat.firstFree;
|
||||||
|
// Start at first valid cluster
|
||||||
|
if (firstFree < CLUSTER_FIRST) {
|
||||||
|
firstFree = CLUSTER_FIRST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search until a free cluster is found
|
||||||
|
while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) {
|
||||||
|
firstFree++;
|
||||||
|
if (firstFree > lastCluster) {
|
||||||
|
if (loopedAroundFAT) {
|
||||||
|
// If couldn't get a free cluster then return an error
|
||||||
|
partition->fat.firstFree = firstFree;
|
||||||
|
return CLUSTER_ERROR;
|
||||||
|
} else {
|
||||||
|
// Try looping back to the beginning of the FAT
|
||||||
|
// This was suggested by loopy
|
||||||
|
firstFree = CLUSTER_FIRST;
|
||||||
|
loopedAroundFAT = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
partition->fat.firstFree = firstFree;
|
||||||
|
|
||||||
|
if ((cluster >= CLUSTER_FIRST) && (cluster < lastCluster))
|
||||||
|
{
|
||||||
|
// Update the linked from FAT entry
|
||||||
|
_FAT_fat_writeFatEntry (partition, cluster, firstFree);
|
||||||
|
}
|
||||||
|
// Create the linked to FAT entry
|
||||||
|
_FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF);
|
||||||
|
|
||||||
|
return firstFree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
gets the first available free cluster, sets it
|
||||||
|
to end of file, links the input cluster to it, clears the new
|
||||||
|
cluster to 0 valued bytes, then returns the cluster number
|
||||||
|
If an error occurs, return CLUSTER_ERROR
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) {
|
||||||
|
uint32_t newCluster;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t emptySector[BYTES_PER_READ];
|
||||||
|
|
||||||
|
// Link the cluster
|
||||||
|
newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
|
||||||
|
|
||||||
|
if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR) {
|
||||||
|
return CLUSTER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all the sectors within the cluster
|
||||||
|
memset (emptySector, 0, BYTES_PER_READ);
|
||||||
|
for (i = 0; i < partition->sectorsPerCluster; i++) {
|
||||||
|
_FAT_cache_writeSectors (partition->cache,
|
||||||
|
_FAT_fat_clusterToSector (partition, newCluster) + i,
|
||||||
|
1, emptySector);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
_FAT_fat_clearLinks
|
||||||
|
frees any cluster used by a file
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster) {
|
||||||
|
uint32_t nextCluster;
|
||||||
|
|
||||||
|
if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If this clears up more space in the FAT before the current free pointer, move it backwards
|
||||||
|
if (cluster < partition->fat.firstFree) {
|
||||||
|
partition->fat.firstFree = cluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE) && (cluster != CLUSTER_ERROR)) {
|
||||||
|
// Store next cluster before erasing the link
|
||||||
|
nextCluster = _FAT_fat_nextCluster (partition, cluster);
|
||||||
|
|
||||||
|
// Erase the link
|
||||||
|
_FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE);
|
||||||
|
|
||||||
|
// Move onto next cluster
|
||||||
|
cluster = nextCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
_FAT_fat_trimChain
|
||||||
|
Drop all clusters past the chainLength.
|
||||||
|
If chainLength is 0, all clusters are dropped.
|
||||||
|
If chainLength is 1, the first cluster is kept and the rest are
|
||||||
|
dropped, and so on.
|
||||||
|
Return the last cluster left in the chain.
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength) {
|
||||||
|
uint32_t nextCluster;
|
||||||
|
|
||||||
|
if (chainLength == 0) {
|
||||||
|
// Drop the entire chain
|
||||||
|
_FAT_fat_clearLinks (partition, startCluster);
|
||||||
|
return CLUSTER_FREE;
|
||||||
|
} else {
|
||||||
|
// Find the last cluster in the chain, and the one after it
|
||||||
|
chainLength--;
|
||||||
|
nextCluster = _FAT_fat_nextCluster (partition, startCluster);
|
||||||
|
while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) {
|
||||||
|
chainLength--;
|
||||||
|
startCluster = nextCluster;
|
||||||
|
nextCluster = _FAT_fat_nextCluster (partition, startCluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop all clusters after the last in the chain
|
||||||
|
if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF) {
|
||||||
|
_FAT_fat_clearLinks (partition, nextCluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the last cluster in the chain as the end of the file
|
||||||
|
_FAT_fat_writeFatEntry (partition, startCluster, CLUSTER_EOF);
|
||||||
|
|
||||||
|
return startCluster;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
_FAT_fat_lastCluster
|
||||||
|
Trace the cluster links until the last one is found
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster) {
|
||||||
|
while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
|
||||||
|
cluster = _FAT_fat_nextCluster(partition, cluster);
|
||||||
|
}
|
||||||
|
return cluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
_FAT_fat_freeClusterCount
|
||||||
|
Return the number of free clusters available
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
unsigned int _FAT_fat_freeClusterCount (PARTITION* partition) {
|
||||||
|
unsigned int count = 0;
|
||||||
|
uint32_t curCluster;
|
||||||
|
|
||||||
|
for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) {
|
||||||
|
if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
70
source/libs/libfat/file_allocation_table.h
Normal file
70
source/libs/libfat/file_allocation_table.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
file_allocation_table.h
|
||||||
|
Reading, writing and manipulation of the FAT structure on
|
||||||
|
a FAT partition
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FAT_H
|
||||||
|
#define _FAT_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "partition.h"
|
||||||
|
|
||||||
|
#define CLUSTER_EOF_16 0xFFFF
|
||||||
|
#define CLUSTER_EOF 0x0FFFFFFF
|
||||||
|
#define CLUSTER_FREE 0x00000000
|
||||||
|
#define CLUSTER_ROOT 0x00000000
|
||||||
|
#define CLUSTER_FIRST 0x00000002
|
||||||
|
#define CLUSTER_ERROR 0xFFFFFFFF
|
||||||
|
|
||||||
|
#define CLUSTERS_PER_FAT12 4085
|
||||||
|
#define CLUSTERS_PER_FAT16 65525
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
|
uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster);
|
||||||
|
uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
|
bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
|
uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength);
|
||||||
|
|
||||||
|
uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster);
|
||||||
|
|
||||||
|
unsigned int _FAT_fat_freeClusterCount (PARTITION* partition);
|
||||||
|
|
||||||
|
static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) {
|
||||||
|
return (cluster >= CLUSTER_FIRST) ?
|
||||||
|
((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart :
|
||||||
|
partition->rootDirStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _FAT_fat_isValidCluster (PARTITION* partition, uint32_t cluster) {
|
||||||
|
return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _FAT_H
|
107
source/libs/libfat/filetime.c
Normal file
107
source/libs/libfat/filetime.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
filetime.c
|
||||||
|
Conversion of file time and date values to various other types
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include "filetime.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define MAX_HOUR 23
|
||||||
|
#define MAX_MINUTE 59
|
||||||
|
#define MAX_SECOND 59
|
||||||
|
|
||||||
|
#define MAX_MONTH 11
|
||||||
|
#define MIN_MONTH 0
|
||||||
|
#define MAX_DAY 31
|
||||||
|
#define MIN_DAY 1
|
||||||
|
|
||||||
|
uint16_t _FAT_filetime_getTimeFromRTC (void) {
|
||||||
|
#ifdef USE_RTC_TIME
|
||||||
|
struct tm timeParts;
|
||||||
|
time_t epochTime;
|
||||||
|
|
||||||
|
if (time(&epochTime) == (time_t)-1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
localtime_r(&epochTime, &timeParts);
|
||||||
|
|
||||||
|
// Check that the values are all in range.
|
||||||
|
// If they are not, return 0 (no timestamp)
|
||||||
|
if ((timeParts.tm_hour < 0) || (timeParts.tm_hour > MAX_HOUR)) return 0;
|
||||||
|
if ((timeParts.tm_min < 0) || (timeParts.tm_min > MAX_MINUTE)) return 0;
|
||||||
|
if ((timeParts.tm_sec < 0) || (timeParts.tm_sec > MAX_SECOND)) return 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
((timeParts.tm_hour & 0x1F) << 11) |
|
||||||
|
((timeParts.tm_min & 0x3F) << 5) |
|
||||||
|
((timeParts.tm_sec >> 1) & 0x1F)
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t _FAT_filetime_getDateFromRTC (void) {
|
||||||
|
#ifdef USE_RTC_TIME
|
||||||
|
struct tm timeParts;
|
||||||
|
time_t epochTime;
|
||||||
|
|
||||||
|
if (time(&epochTime) == (time_t)-1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
localtime_r(&epochTime, &timeParts);
|
||||||
|
|
||||||
|
if ((timeParts.tm_mon < MIN_MONTH) || (timeParts.tm_mon > MAX_MONTH)) return 0;
|
||||||
|
if ((timeParts.tm_mday < MIN_DAY) || (timeParts.tm_mday > MAX_DAY)) return 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
(((timeParts.tm_year - 80) & 0x7F) <<9) | // Adjust for MS-FAT base year (1980 vs 1900 for tm_year)
|
||||||
|
(((timeParts.tm_mon + 1) & 0xF) << 5) |
|
||||||
|
(timeParts.tm_mday & 0x1F)
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) {
|
||||||
|
struct tm timeParts;
|
||||||
|
|
||||||
|
timeParts.tm_hour = t >> 11;
|
||||||
|
timeParts.tm_min = (t >> 5) & 0x3F;
|
||||||
|
timeParts.tm_sec = (t & 0x1F) << 1;
|
||||||
|
|
||||||
|
timeParts.tm_mday = d & 0x1F;
|
||||||
|
timeParts.tm_mon = ((d >> 5) & 0x0F) - 1;
|
||||||
|
timeParts.tm_year = (d >> 9) + 80;
|
||||||
|
|
||||||
|
timeParts.tm_isdst = 0;
|
||||||
|
|
||||||
|
return mktime(&timeParts);
|
||||||
|
}
|
@ -3,17 +3,17 @@
|
|||||||
Conversion of file time and date values to various other types
|
Conversion of file time and date values to various other types
|
||||||
|
|
||||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
are permitted provided that the following conditions are met:
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
this list of conditions and the following disclaimer.
|
this list of conditions and the following disclaimer.
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
other materials provided with the distribution.
|
other materials provided with the distribution.
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
from this software without specific prior written permission.
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
@ -24,17 +24,18 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __FILETIME_H
|
#ifndef _FILETIME_H
|
||||||
#define __FILETIME_H
|
#define _FILETIME_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
uint16_t _FAT_filetime_getTimeFromRTC(void);
|
uint16_t _FAT_filetime_getTimeFromRTC (void);
|
||||||
uint16_t _FAT_filetime_getDateFromRTC(void);
|
uint16_t _FAT_filetime_getDateFromRTC (void);
|
||||||
|
|
||||||
|
time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d);
|
||||||
|
|
||||||
time_t _FAT_filetime_to_time_t(uint16_t t, uint16_t d);
|
|
||||||
|
|
||||||
#endif // _FILETIME_H
|
#endif // _FILETIME_H
|
241
source/libs/libfat/libfat.c
Normal file
241
source/libs/libfat/libfat.c
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
libfat.c
|
||||||
|
Simple functionality for startup, mounting and unmounting of FAT-based devices.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "partition.h"
|
||||||
|
#include "fatfile.h"
|
||||||
|
#include "fatdir.h"
|
||||||
|
#include "lock.h"
|
||||||
|
#include "mem_allocate.h"
|
||||||
|
#include "disc.h"
|
||||||
|
|
||||||
|
static const devoptab_t dotab_fat = {
|
||||||
|
"fat",
|
||||||
|
sizeof (FILE_STRUCT),
|
||||||
|
_FAT_open_r,
|
||||||
|
_FAT_close_r,
|
||||||
|
_FAT_write_r,
|
||||||
|
_FAT_read_r,
|
||||||
|
_FAT_seek_r,
|
||||||
|
_FAT_fstat_r,
|
||||||
|
_FAT_stat_r,
|
||||||
|
_FAT_link_r,
|
||||||
|
_FAT_unlink_r,
|
||||||
|
_FAT_chdir_r,
|
||||||
|
_FAT_rename_r,
|
||||||
|
_FAT_mkdir_r,
|
||||||
|
sizeof (DIR_STATE_STRUCT),
|
||||||
|
_FAT_diropen_r,
|
||||||
|
_FAT_dirreset_r,
|
||||||
|
_FAT_dirnext_r,
|
||||||
|
_FAT_dirclose_r,
|
||||||
|
_FAT_statvfs_r,
|
||||||
|
_FAT_ftruncate_r,
|
||||||
|
_FAT_fsync_r,
|
||||||
|
NULL /* Device data */
|
||||||
|
};
|
||||||
|
|
||||||
|
bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage) {
|
||||||
|
PARTITION* partition;
|
||||||
|
devoptab_t* devops;
|
||||||
|
char* nameCopy;
|
||||||
|
|
||||||
|
if(!name || !interface)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!interface->startup())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!interface->isInserted())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1);
|
||||||
|
if (!devops) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Use the space allocated at the end of the devoptab struct for storing the name
|
||||||
|
nameCopy = (char*)(devops+1);
|
||||||
|
|
||||||
|
// Initialize the file system
|
||||||
|
partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector);
|
||||||
|
if (!partition) {
|
||||||
|
_FAT_mem_free (devops);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an entry for this device to the devoptab table
|
||||||
|
memcpy (devops, &dotab_fat, sizeof(dotab_fat));
|
||||||
|
strcpy (nameCopy, name);
|
||||||
|
devops->name = nameCopy;
|
||||||
|
devops->deviceData = partition;
|
||||||
|
|
||||||
|
AddDevice (devops);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) {
|
||||||
|
return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fatUnmount (const char* name) {
|
||||||
|
devoptab_t *devops;
|
||||||
|
PARTITION* partition;
|
||||||
|
|
||||||
|
if(!name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
devops = (devoptab_t*)GetDeviceOpTab (name);
|
||||||
|
if (!devops) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a quick check to make sure we're dealing with a libfat controlled device
|
||||||
|
if (devops->open_r != dotab_fat.open_r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemoveDevice (name) == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition = (PARTITION*)devops->deviceData;
|
||||||
|
_FAT_partition_destructor (partition);
|
||||||
|
_FAT_mem_free (devops);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) {
|
||||||
|
int i;
|
||||||
|
int defaultDevice = -1;
|
||||||
|
const DISC_INTERFACE *disc;
|
||||||
|
|
||||||
|
for (i = 0;
|
||||||
|
_FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
disc = _FAT_disc_interfaces[i].getInterface();
|
||||||
|
if (fatMount (_FAT_disc_interfaces[i].name, disc, 0, cacheSize, DEFAULT_SECTORS_PAGE)) {
|
||||||
|
// The first device to successfully mount is set as the default
|
||||||
|
if (defaultDevice < 0) {
|
||||||
|
defaultDevice = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultDevice < 0) {
|
||||||
|
// None of our devices mounted
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setAsDefaultDevice) {
|
||||||
|
char filePath[MAXPATHLEN * 2];
|
||||||
|
strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name);
|
||||||
|
strcat (filePath, ":/");
|
||||||
|
#ifdef ARGV_MAGIC
|
||||||
|
if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr( __system_argv->argv[0], '/' )!=NULL ) {
|
||||||
|
// Check the app's path against each of our mounted devices, to see
|
||||||
|
// if we can support it. If so, change to that path.
|
||||||
|
for (i = 0;
|
||||||
|
_FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name,
|
||||||
|
strlen(_FAT_disc_interfaces[i].name)))
|
||||||
|
{
|
||||||
|
char *lastSlash;
|
||||||
|
strcpy(filePath, __system_argv->argv[0]);
|
||||||
|
lastSlash = strrchr( filePath, '/' );
|
||||||
|
|
||||||
|
if ( NULL != lastSlash) {
|
||||||
|
if ( *(lastSlash - 1) == ':') lastSlash++;
|
||||||
|
*lastSlash = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
chdir (filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fatInitDefault (void) {
|
||||||
|
return fatInit (DEFAULT_CACHE_PAGES, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fatGetVolumeLabel (const char* name, char *label) {
|
||||||
|
devoptab_t *devops;
|
||||||
|
PARTITION* partition;
|
||||||
|
char *buf;
|
||||||
|
int namelen,i;
|
||||||
|
|
||||||
|
if(!name || !label)
|
||||||
|
return;
|
||||||
|
|
||||||
|
namelen = strlen(name);
|
||||||
|
buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2);
|
||||||
|
strcpy(buf,name);
|
||||||
|
|
||||||
|
if (name[namelen-1] == '/') {
|
||||||
|
buf[namelen-1]='\0';
|
||||||
|
namelen--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name[namelen-1] != ':') {
|
||||||
|
buf[namelen]=':';
|
||||||
|
buf[namelen+1]='\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
devops = (devoptab_t*)GetDeviceOpTab(buf);
|
||||||
|
|
||||||
|
for(i=0;buf[i]!='\0' && buf[i]!=':';i++);
|
||||||
|
if (!devops || strncasecmp(buf,devops->name,i)) {
|
||||||
|
free(buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
// Perform a quick check to make sure we're dealing with a libfat controlled device
|
||||||
|
if (devops->open_r != dotab_fat.open_r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition = (PARTITION*)devops->deviceData;
|
||||||
|
|
||||||
|
if(!_FAT_directory_getVolumeLabel(partition, label)) {
|
||||||
|
strncpy(label,partition->label,11);
|
||||||
|
label[11]='\0';
|
||||||
|
}
|
||||||
|
if(!strncmp(label, "NO NAME", 7)) label[0]='\0';
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
#ifndef __LIBFATVERSION_H__
|
#ifndef __LIBFATVERSION_H__
|
||||||
#define __LIBFATVERSION_H__
|
#define __LIBFATVERSION_H__
|
||||||
|
|
||||||
#define _LIBFAT_MAJOR_ 1
|
#define _LIBFAT_MAJOR_ 1
|
||||||
#define _LIBFAT_MINOR_ 0
|
#define _LIBFAT_MINOR_ 0
|
||||||
#define _LIBFAT_PATCH_ 7
|
#define _LIBFAT_PATCH_ 7
|
||||||
|
|
||||||
#define _LIBFAT_STRING "libFAT Release 1.0.7"
|
#define _LIBFAT_STRING "libFAT Release 1.0.7"
|
||||||
|
|
@ -6,13 +6,13 @@
|
|||||||
Redistribution and use in source and binary forms, with or without modification,
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
are permitted provided that the following conditions are met:
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
this list of conditions and the following disclaimer.
|
this list of conditions and the following disclaimer.
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
other materials provided with the distribution.
|
other materials provided with the distribution.
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
from this software without specific prior written permission.
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
@ -24,33 +24,33 @@
|
|||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __LOCK_H
|
#ifndef _LOCK_H
|
||||||
#define __LOCK_H
|
#define _LOCK_H
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#ifdef USE_LWP_LOCK
|
#ifdef USE_LWP_LOCK
|
||||||
|
|
||||||
static inline void _FAT_lock_init( mutex_t *mutex )
|
static inline void _FAT_lock_init(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
LWP_MutexInit( mutex, false );
|
LWP_MutexInit(mutex, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock_deinit( mutex_t *mutex )
|
static inline void _FAT_lock_deinit(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
LWP_MutexDestroy( *mutex );
|
LWP_MutexDestroy(*mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock( mutex_t *mutex )
|
static inline void _FAT_lock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
LWP_MutexLock( *mutex );
|
LWP_MutexLock(*mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_unlock( mutex_t *mutex )
|
static inline void _FAT_unlock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
LWP_MutexUnlock( *mutex );
|
LWP_MutexUnlock(*mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@ -62,24 +62,26 @@ typedef int mutex_t;
|
|||||||
|
|
||||||
static inline void _FAT_lock_init(mutex_t *mutex)
|
static inline void _FAT_lock_init(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock_deinit(mutex_t *mutex)
|
static inline void _FAT_lock_deinit(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_lock(mutex_t *mutex)
|
static inline void _FAT_lock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_unlock(mutex_t *mutex)
|
static inline void _FAT_unlock(mutex_t *mutex)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_LWP_LOCK
|
#endif // USE_LWP_LOCK
|
||||||
|
|
||||||
|
|
||||||
#endif // _LOCK_H
|
#endif // _LOCK_H
|
||||||
|
|
@ -9,13 +9,13 @@
|
|||||||
Redistribution and use in source and binary forms, with or without modification,
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
are permitted provided that the following conditions are met:
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
this list of conditions and the following disclaimer.
|
this list of conditions and the following disclaimer.
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
this list of conditions and the following disclaimer in the documentation and/or
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
other materials provided with the distribution.
|
other materials provided with the distribution.
|
||||||
3. The name of the author may not be used to endorse or promote products derived
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
from this software without specific prior written permission.
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
@ -26,28 +26,24 @@
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __MEM_ALLOCATE_H_
|
#ifndef _MEM_ALLOCATE_H
|
||||||
#define __MEM_ALLOCATE_H_
|
#define _MEM_ALLOCATE_H
|
||||||
|
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include "memory/mem2.h"
|
#include "memory/mem2.h"
|
||||||
|
|
||||||
static inline void* _FAT_mem_allocate(size_t size)
|
static inline void* _FAT_mem_allocate (size_t size) {
|
||||||
{
|
return MEM2_alloc(size);
|
||||||
return MEM2_alloc(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void* _FAT_mem_align(size_t size)
|
static inline void* _FAT_mem_align (size_t size) {
|
||||||
{
|
return MEM2_alloc(size);
|
||||||
|
|
||||||
return MEM2_alloc(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _FAT_mem_free(void* mem)
|
static inline void _FAT_mem_free (void* mem) {
|
||||||
{
|
MEM2_free(mem);
|
||||||
MEM2_free(mem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _MEM_ALLOCATE_H
|
#endif // _MEM_ALLOCATE_H
|
318
source/libs/libfat/partition.c
Normal file
318
source/libs/libfat/partition.c
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
partition.c
|
||||||
|
Functions for mounting and dismounting partitions
|
||||||
|
on various block devices.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "partition.h"
|
||||||
|
#include "bit_ops.h"
|
||||||
|
#include "file_allocation_table.h"
|
||||||
|
#include "directory.h"
|
||||||
|
#include "mem_allocate.h"
|
||||||
|
#include "fatfile.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
|
||||||
|
sec_t _FAT_startSector;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This device name, as known by devkitPro toolchains
|
||||||
|
*/
|
||||||
|
const char* DEVICE_NAME = "fat";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Data offsets
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BIOS Parameter Block offsets
|
||||||
|
enum BPB {
|
||||||
|
BPB_jmpBoot = 0x00,
|
||||||
|
BPB_OEMName = 0x03,
|
||||||
|
// BIOS Parameter Block
|
||||||
|
BPB_bytesPerSector = 0x0B,
|
||||||
|
BPB_sectorsPerCluster = 0x0D,
|
||||||
|
BPB_reservedSectors = 0x0E,
|
||||||
|
BPB_numFATs = 0x10,
|
||||||
|
BPB_rootEntries = 0x11,
|
||||||
|
BPB_numSectorsSmall = 0x13,
|
||||||
|
BPB_mediaDesc = 0x15,
|
||||||
|
BPB_sectorsPerFAT = 0x16,
|
||||||
|
BPB_sectorsPerTrk = 0x18,
|
||||||
|
BPB_numHeads = 0x1A,
|
||||||
|
BPB_numHiddenSectors = 0x1C,
|
||||||
|
BPB_numSectors = 0x20,
|
||||||
|
// Ext BIOS Parameter Block for FAT16
|
||||||
|
BPB_FAT16_driveNumber = 0x24,
|
||||||
|
BPB_FAT16_reserved1 = 0x25,
|
||||||
|
BPB_FAT16_extBootSig = 0x26,
|
||||||
|
BPB_FAT16_volumeID = 0x27,
|
||||||
|
BPB_FAT16_volumeLabel = 0x2B,
|
||||||
|
BPB_FAT16_fileSysType = 0x36,
|
||||||
|
// Bootcode
|
||||||
|
BPB_FAT16_bootCode = 0x3E,
|
||||||
|
// FAT32 extended block
|
||||||
|
BPB_FAT32_sectorsPerFAT32 = 0x24,
|
||||||
|
BPB_FAT32_extFlags = 0x28,
|
||||||
|
BPB_FAT32_fsVer = 0x2A,
|
||||||
|
BPB_FAT32_rootClus = 0x2C,
|
||||||
|
BPB_FAT32_fsInfo = 0x30,
|
||||||
|
BPB_FAT32_bkBootSec = 0x32,
|
||||||
|
// Ext BIOS Parameter Block for FAT32
|
||||||
|
BPB_FAT32_driveNumber = 0x40,
|
||||||
|
BPB_FAT32_reserved1 = 0x41,
|
||||||
|
BPB_FAT32_extBootSig = 0x42,
|
||||||
|
BPB_FAT32_volumeID = 0x43,
|
||||||
|
BPB_FAT32_volumeLabel = 0x47,
|
||||||
|
BPB_FAT32_fileSysType = 0x52,
|
||||||
|
// Bootcode
|
||||||
|
BPB_FAT32_bootCode = 0x5A,
|
||||||
|
BPB_bootSig_55 = 0x1FE,
|
||||||
|
BPB_bootSig_AA = 0x1FF
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char FAT_SIG[3] = {'F', 'A', 'T'};
|
||||||
|
|
||||||
|
|
||||||
|
sec_t FindFirstValidPartition(const DISC_INTERFACE* disc)
|
||||||
|
{
|
||||||
|
uint8_t part_table[16*4];
|
||||||
|
uint8_t *ptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
uint8_t sectorBuffer[BYTES_PER_READ] = {0};
|
||||||
|
|
||||||
|
// Read first sector of disc
|
||||||
|
if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(part_table,sectorBuffer+0x1BE,16*4);
|
||||||
|
ptr = part_table;
|
||||||
|
|
||||||
|
for(i=0;i<4;i++,ptr+=16) {
|
||||||
|
sec_t part_lba = u8array_to_u32(ptr, 0x8);
|
||||||
|
|
||||||
|
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
|
||||||
|
!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||||
|
return part_lba;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ptr[4]==0) continue;
|
||||||
|
|
||||||
|
if(ptr[4]==0x0F) {
|
||||||
|
sec_t part_lba2=part_lba;
|
||||||
|
sec_t next_lba2=0;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for(n=0;n<8;n++) // max 8 logic partitions
|
||||||
|
{
|
||||||
|
if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0;
|
||||||
|
|
||||||
|
part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ;
|
||||||
|
next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6);
|
||||||
|
|
||||||
|
if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0;
|
||||||
|
|
||||||
|
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
|
||||||
|
!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
||||||
|
{
|
||||||
|
return part_lba2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(next_lba2==0) break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0;
|
||||||
|
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
|
||||||
|
!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||||
|
return part_lba;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) {
|
||||||
|
PARTITION* partition;
|
||||||
|
uint8_t sectorBuffer[BYTES_PER_READ] = {0};
|
||||||
|
|
||||||
|
// Read first sector of disc
|
||||||
|
if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure it is a valid MBR or boot sector
|
||||||
|
if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startSector != 0) {
|
||||||
|
// We're told where to start the partition, so just accept it
|
||||||
|
} else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||||
|
// Check if there is a FAT string, which indicates this is a boot sector
|
||||||
|
startSector = 0;
|
||||||
|
} else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
||||||
|
// Check for FAT32
|
||||||
|
startSector = 0;
|
||||||
|
} else {
|
||||||
|
startSector = FindFirstValidPartition(disc);
|
||||||
|
if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now verify that this is indeed a FAT partition
|
||||||
|
if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) &&
|
||||||
|
memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check again for the last two cases to make sure that we really have a FAT filesystem here
|
||||||
|
// and won't corrupt any data
|
||||||
|
if(memcmp(sectorBuffer + BPB_FAT16_fileSysType, "FAT", 3) != 0 && memcmp(sectorBuffer + BPB_FAT32_fileSysType, "FAT32", 5) != 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
|
||||||
|
if (partition == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the partition lock
|
||||||
|
_FAT_lock_init(&partition->lock);
|
||||||
|
|
||||||
|
_FAT_startSector = startSector;
|
||||||
|
|
||||||
|
if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
||||||
|
strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11);
|
||||||
|
else
|
||||||
|
strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11);
|
||||||
|
partition->label[11] = '\0';
|
||||||
|
|
||||||
|
// Set partition's disc interface
|
||||||
|
partition->disc = disc;
|
||||||
|
|
||||||
|
// Store required information about the file system
|
||||||
|
partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
|
||||||
|
if (partition->fat.sectorsPerFat == 0) {
|
||||||
|
partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32);
|
||||||
|
}
|
||||||
|
|
||||||
|
partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall);
|
||||||
|
if (partition->numberOfSectors == 0) {
|
||||||
|
partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
|
||||||
|
partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ;
|
||||||
|
partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
|
||||||
|
partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
|
||||||
|
|
||||||
|
partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
|
||||||
|
partition->dataStart = partition->rootDirStart +
|
||||||
|
(( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
|
||||||
|
|
||||||
|
partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector;
|
||||||
|
|
||||||
|
// Store info about FAT
|
||||||
|
uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster;
|
||||||
|
partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1;
|
||||||
|
partition->fat.firstFree = CLUSTER_FIRST;
|
||||||
|
|
||||||
|
if (clusterCount < CLUSTERS_PER_FAT12) {
|
||||||
|
partition->filesysType = FS_FAT12; // FAT12 volume
|
||||||
|
} else if (clusterCount < CLUSTERS_PER_FAT16) {
|
||||||
|
partition->filesysType = FS_FAT16; // FAT16 volume
|
||||||
|
} else {
|
||||||
|
partition->filesysType = FS_FAT32; // FAT32 volume
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partition->filesysType != FS_FAT32) {
|
||||||
|
partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
|
||||||
|
} else {
|
||||||
|
// Set up for the FAT32 way
|
||||||
|
partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
|
||||||
|
// Check if FAT mirroring is enabled
|
||||||
|
if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) {
|
||||||
|
// Use the active FAT
|
||||||
|
partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a cache to use
|
||||||
|
partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors);
|
||||||
|
|
||||||
|
// Set current directory to the root
|
||||||
|
partition->cwdCluster = partition->rootDirCluster;
|
||||||
|
|
||||||
|
// Check if this disc is writable, and set the readOnly property appropriately
|
||||||
|
partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
|
||||||
|
|
||||||
|
// There are currently no open files on this partition
|
||||||
|
partition->openFileCount = 0;
|
||||||
|
partition->firstOpenFile = NULL;
|
||||||
|
|
||||||
|
return partition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _FAT_partition_destructor (PARTITION* partition) {
|
||||||
|
FILE_STRUCT* nextFile;
|
||||||
|
|
||||||
|
_FAT_lock(&partition->lock);
|
||||||
|
|
||||||
|
// Synchronize open files
|
||||||
|
nextFile = partition->firstOpenFile;
|
||||||
|
while (nextFile) {
|
||||||
|
_FAT_syncToDisc (nextFile);
|
||||||
|
nextFile = nextFile->nextOpenFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory used by the cache, writing it to disc at the same time
|
||||||
|
_FAT_cache_destructor (partition->cache);
|
||||||
|
|
||||||
|
// Unlock the partition and destroy the lock
|
||||||
|
_FAT_unlock(&partition->lock);
|
||||||
|
_FAT_lock_deinit(&partition->lock);
|
||||||
|
|
||||||
|
// Free memory used by the partition
|
||||||
|
_FAT_mem_free (partition);
|
||||||
|
}
|
||||||
|
|
||||||
|
PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
|
||||||
|
const devoptab_t *devops;
|
||||||
|
|
||||||
|
devops = GetDeviceOpTab (path);
|
||||||
|
|
||||||
|
if (!devops) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (PARTITION*)devops->deviceData;
|
||||||
|
}
|
89
source/libs/libfat/partition.h
Normal file
89
source/libs/libfat/partition.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
partition.h
|
||||||
|
Functions for mounting and dismounting partitions
|
||||||
|
on various block devices.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PARTITION_H
|
||||||
|
#define _PARTITION_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "cache.h"
|
||||||
|
#include "lock.h"
|
||||||
|
|
||||||
|
// Device name
|
||||||
|
extern const char* DEVICE_NAME;
|
||||||
|
|
||||||
|
// Filesystem type
|
||||||
|
typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sec_t fatStart;
|
||||||
|
uint32_t sectorsPerFat;
|
||||||
|
uint32_t lastCluster;
|
||||||
|
uint32_t firstFree;
|
||||||
|
} FAT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const DISC_INTERFACE* disc;
|
||||||
|
CACHE* cache;
|
||||||
|
// Info about the partition
|
||||||
|
FS_TYPE filesysType;
|
||||||
|
uint64_t totalSize;
|
||||||
|
sec_t rootDirStart;
|
||||||
|
uint32_t rootDirCluster;
|
||||||
|
uint32_t numberOfSectors;
|
||||||
|
sec_t dataStart;
|
||||||
|
uint32_t bytesPerSector;
|
||||||
|
uint32_t sectorsPerCluster;
|
||||||
|
uint32_t bytesPerCluster;
|
||||||
|
FAT fat;
|
||||||
|
// Values that may change after construction
|
||||||
|
uint32_t cwdCluster; // Current working directory cluster
|
||||||
|
int openFileCount;
|
||||||
|
struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files
|
||||||
|
mutex_t lock; // A lock for partition operations
|
||||||
|
bool readOnly; // If this is set, then do not try writing to the disc
|
||||||
|
char label[12]; // Volume label
|
||||||
|
} PARTITION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mount the supplied device and return a pointer to the struct necessary to use it
|
||||||
|
*/
|
||||||
|
PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dismount the device and free all structures used.
|
||||||
|
Will also attempt to synchronise all open files to disc.
|
||||||
|
*/
|
||||||
|
void _FAT_partition_destructor (PARTITION* partition);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return the partition specified in a path, as taken from the devoptab.
|
||||||
|
*/
|
||||||
|
PARTITION* _FAT_partition_getPartitionFromPath (const char* path);
|
||||||
|
|
||||||
|
#endif // _PARTITION_H
|
4293
source/libs/libntfs/acls.c
Normal file
4293
source/libs/libntfs/acls.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -56,6 +56,7 @@
|
|||||||
#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid)
|
#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid)
|
||||||
#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid)
|
#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Matching of ntfs permissions to Linux permissions
|
* Matching of ntfs permissions to Linux permissions
|
||||||
* these constants are adapted to endianness
|
* these constants are adapted to endianness
|
||||||
@ -63,7 +64,7 @@
|
|||||||
* when checking, check one is present
|
* when checking, check one is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* flags which are set to mean exec, write or read */
|
/* flags which are set to mean exec, write or read */
|
||||||
|
|
||||||
#define FILE_READ (FILE_READ_DATA)
|
#define FILE_READ (FILE_READ_DATA)
|
||||||
#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \
|
#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \
|
||||||
@ -74,8 +75,8 @@
|
|||||||
| READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
|
| READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
|
||||||
#define DIR_EXEC (FILE_TRAVERSE)
|
#define DIR_EXEC (FILE_TRAVERSE)
|
||||||
|
|
||||||
/* flags tested for meaning exec, write or read */
|
/* flags tested for meaning exec, write or read */
|
||||||
/* tests for write allow for interpretation of a sticky bit */
|
/* tests for write allow for interpretation of a sticky bit */
|
||||||
|
|
||||||
#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ)
|
#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ)
|
||||||
#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)
|
#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)
|
||||||
@ -84,19 +85,19 @@
|
|||||||
#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE)
|
#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE)
|
||||||
#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE)
|
#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE)
|
||||||
|
|
||||||
/* standard owner (and administrator) rights */
|
/* standard owner (and administrator) rights */
|
||||||
|
|
||||||
#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \
|
#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \
|
||||||
| SYNCHRONIZE \
|
| SYNCHRONIZE \
|
||||||
| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \
|
| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \
|
||||||
| FILE_READ_EA | FILE_WRITE_EA)
|
| FILE_READ_EA | FILE_WRITE_EA)
|
||||||
|
|
||||||
/* standard world rights */
|
/* standard world rights */
|
||||||
|
|
||||||
#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \
|
#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \
|
||||||
| SYNCHRONIZE)
|
| SYNCHRONIZE)
|
||||||
|
|
||||||
/* inheritance flags for files and directories */
|
/* inheritance flags for files and directories */
|
||||||
|
|
||||||
#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE
|
#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE
|
||||||
#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)
|
#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)
|
||||||
@ -121,13 +122,12 @@ typedef char BIGSID[40];
|
|||||||
* (private to this module)
|
* (private to this module)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct MAPLIST
|
struct MAPLIST {
|
||||||
{
|
struct MAPLIST *next;
|
||||||
struct MAPLIST *next;
|
char *uidstr; /* uid text from the same record */
|
||||||
char *uidstr; /* uid text from the same record */
|
char *gidstr; /* gid text from the same record */
|
||||||
char *gidstr; /* gid text from the same record */
|
char *sidstr; /* sid text from the same record */
|
||||||
char *sidstr; /* sid text from the same record */
|
char maptext[LINESZ + 1];
|
||||||
char maptext[LINESZ + 1];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos);
|
typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos);
|
||||||
@ -150,11 +150,14 @@ BOOL ntfs_same_sid(const SID *first, const SID *second);
|
|||||||
|
|
||||||
BOOL ntfs_is_user_sid(const SID *usid);
|
BOOL ntfs_is_user_sid(const SID *usid);
|
||||||
|
|
||||||
|
|
||||||
int ntfs_sid_size(const SID * sid);
|
int ntfs_sid_size(const SID * sid);
|
||||||
unsigned int ntfs_attr_size(const char *attr);
|
unsigned int ntfs_attr_size(const char *attr);
|
||||||
|
|
||||||
const SID *ntfs_find_usid(const struct MAPPING *usermapping, uid_t uid, SID *pdefsid);
|
const SID *ntfs_find_usid(const struct MAPPING *usermapping,
|
||||||
const SID *ntfs_find_gsid(const struct MAPPING *groupmapping, gid_t gid, SID *pdefsid);
|
uid_t uid, SID *pdefsid);
|
||||||
|
const SID *ntfs_find_gsid(const struct MAPPING *groupmapping,
|
||||||
|
gid_t gid, SID *pdefsid);
|
||||||
uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid);
|
uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid);
|
||||||
gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid);
|
gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid);
|
||||||
const SID *ntfs_acl_owner(const char *secattr);
|
const SID *ntfs_acl_owner(const char *secattr);
|
||||||
@ -165,25 +168,28 @@ BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc);
|
|||||||
void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc);
|
void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc);
|
||||||
int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode);
|
int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode);
|
||||||
struct POSIX_SECURITY *ntfs_build_inherited_posix(
|
struct POSIX_SECURITY *ntfs_build_inherited_posix(
|
||||||
const struct POSIX_SECURITY *pxdesc, mode_t mode,
|
const struct POSIX_SECURITY *pxdesc, mode_t mode,
|
||||||
mode_t umask, BOOL isdir);
|
mode_t umask, BOOL isdir);
|
||||||
struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc,
|
struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc,
|
||||||
const struct POSIX_ACL *newacl, int count, BOOL deflt);
|
const struct POSIX_ACL *newacl, int count, BOOL deflt);
|
||||||
struct POSIX_SECURITY *ntfs_build_permissions_posix(
|
struct POSIX_SECURITY *ntfs_build_permissions_posix(
|
||||||
struct MAPPING* const mapping[],
|
struct MAPPING* const mapping[],
|
||||||
const char *securattr,
|
const char *securattr,
|
||||||
const SID *usid, const SID *gsid, BOOL isdir);
|
const SID *usid, const SID *gsid, BOOL isdir);
|
||||||
struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first,
|
struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first,
|
||||||
const struct POSIX_SECURITY *second);
|
const struct POSIX_SECURITY *second);
|
||||||
char *ntfs_build_descr_posix(struct MAPPING* const mapping[],
|
char *ntfs_build_descr_posix(struct MAPPING* const mapping[],
|
||||||
struct POSIX_SECURITY *pxdesc,
|
struct POSIX_SECURITY *pxdesc,
|
||||||
int isdir, const SID *usid, const SID *gsid);
|
int isdir, const SID *usid, const SID *gsid);
|
||||||
|
|
||||||
#endif /* POSIXACLS */
|
#endif /* POSIXACLS */
|
||||||
|
|
||||||
int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, const SID *usid, const SID *gsid, BOOL fordir);
|
int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
|
||||||
int ntfs_build_permissions(const char *securattr, const SID *usid, const SID *gsid, BOOL isdir);
|
const SID *usid, const SID *gsid, BOOL fordir);
|
||||||
char *ntfs_build_descr(mode_t mode, int isdir, const SID * usid, const SID * gsid);
|
int ntfs_build_permissions(const char *securattr,
|
||||||
|
const SID *usid, const SID *gsid, BOOL isdir);
|
||||||
|
char *ntfs_build_descr(mode_t mode,
|
||||||
|
int isdir, const SID * usid, const SID * gsid);
|
||||||
struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid);
|
struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid);
|
||||||
struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem);
|
struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem);
|
||||||
struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem);
|
struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem);
|
6401
source/libs/libntfs/attrib.c
Normal file
6401
source/libs/libntfs/attrib.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -49,10 +49,12 @@ extern ntfschar TXF_DATA[10];
|
|||||||
*
|
*
|
||||||
* TODO: Describe them.
|
* TODO: Describe them.
|
||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
LCN_HOLE = -1, /* Keep this as highest value or die! */
|
||||||
LCN_HOLE = -1, /* Keep this as highest value or die! */
|
LCN_RL_NOT_MAPPED = -2,
|
||||||
LCN_RL_NOT_MAPPED = -2, LCN_ENOENT = -3, LCN_EINVAL = -4, LCN_EIO = -5,
|
LCN_ENOENT = -3,
|
||||||
|
LCN_EINVAL = -4,
|
||||||
|
LCN_EIO = -5,
|
||||||
} ntfs_lcn_special_values;
|
} ntfs_lcn_special_values;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,28 +75,31 @@ typedef enum
|
|||||||
* any modification of the search context, to automagically get the next
|
* any modification of the search context, to automagically get the next
|
||||||
* matching attribute.
|
* matching attribute.
|
||||||
*/
|
*/
|
||||||
struct _ntfs_attr_search_ctx
|
struct _ntfs_attr_search_ctx {
|
||||||
{
|
MFT_RECORD *mrec;
|
||||||
MFT_RECORD *mrec;
|
ATTR_RECORD *attr;
|
||||||
ATTR_RECORD *attr;
|
BOOL is_first;
|
||||||
BOOL is_first;
|
ntfs_inode *ntfs_ino;
|
||||||
ntfs_inode *ntfs_ino;
|
ATTR_LIST_ENTRY *al_entry;
|
||||||
ATTR_LIST_ENTRY *al_entry;
|
ntfs_inode *base_ntfs_ino;
|
||||||
ntfs_inode *base_ntfs_ino;
|
MFT_RECORD *base_mrec;
|
||||||
MFT_RECORD *base_mrec;
|
ATTR_RECORD *base_attr;
|
||||||
ATTR_RECORD *base_attr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx);
|
extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||||
extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec);
|
extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni,
|
||||||
|
MFT_RECORD *mrec);
|
||||||
extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
|
extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, const u32 name_len, const IGNORE_CASE_BOOL ic,
|
extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
|
||||||
const VCN lowest_vcn, const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx);
|
const u32 name_len, const IGNORE_CASE_BOOL ic,
|
||||||
|
const VCN lowest_vcn, const u8 *val, const u32 val_len,
|
||||||
|
ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx);
|
extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, const ATTR_TYPES type);
|
extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
|
||||||
|
const ATTR_TYPES type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode
|
* ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode
|
||||||
@ -123,7 +128,8 @@ extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, const ATTR_TY
|
|||||||
*/
|
*/
|
||||||
static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
|
static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
|
||||||
{
|
{
|
||||||
return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx);
|
return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0,
|
||||||
|
NULL, 0, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,37 +174,34 @@ static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
|
|||||||
* @state contains NTFS attribute specific flags describing this attribute
|
* @state contains NTFS attribute specific flags describing this attribute
|
||||||
* structure. See ntfs_attr_state_bits above.
|
* structure. See ntfs_attr_state_bits above.
|
||||||
*/
|
*/
|
||||||
struct _ntfs_attr
|
struct _ntfs_attr {
|
||||||
{
|
runlist_element *rl;
|
||||||
runlist_element *rl;
|
ntfs_inode *ni;
|
||||||
ntfs_inode *ni;
|
ATTR_TYPES type;
|
||||||
ATTR_TYPES type;
|
ATTR_FLAGS data_flags;
|
||||||
ATTR_FLAGS data_flags;
|
ntfschar *name;
|
||||||
ntfschar *name;
|
u32 name_len;
|
||||||
u32 name_len;
|
unsigned long state;
|
||||||
unsigned long state;
|
s64 allocated_size;
|
||||||
s64 allocated_size;
|
s64 data_size;
|
||||||
s64 data_size;
|
s64 initialized_size;
|
||||||
s64 initialized_size;
|
s64 compressed_size;
|
||||||
s64 compressed_size;
|
u32 compression_block_size;
|
||||||
u32 compression_block_size;
|
u8 compression_block_size_bits;
|
||||||
u8 compression_block_size_bits;
|
u8 compression_block_clusters;
|
||||||
u8 compression_block_clusters;
|
s8 unused_runs; /* pre-reserved entries available */
|
||||||
s8 unused_runs; /* pre-reserved entries available */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr
|
* enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr
|
||||||
* structure
|
* structure
|
||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
NA_Initialized, /* 1: structure is initialized. */
|
||||||
NA_Initialized, /* 1: structure is initialized. */
|
NA_NonResident, /* 1: Attribute is not resident. */
|
||||||
NA_NonResident, /* 1: Attribute is not resident. */
|
NA_BeingNonResident, /* 1: Attribute is being made not resident. */
|
||||||
NA_BeingNonResident, /* 1: Attribute is being made not resident. */
|
NA_FullyMapped, /* 1: Attribute has been fully mapped */
|
||||||
NA_FullyMapped, /* 1: Attribute has been fully mapped */
|
NA_ComprClosing, /* 1: Compressed attribute is being closed */
|
||||||
NA_ComprClosing,
|
|
||||||
/* 1: Compressed attribute is being closed */
|
|
||||||
} ntfs_attr_state_bits;
|
} ntfs_attr_state_bits;
|
||||||
|
|
||||||
#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state)
|
#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state)
|
||||||
@ -231,8 +234,8 @@ extern void NAttrSet##func_name(ntfs_attr *na); \
|
|||||||
extern void NAttrClear##func_name(ntfs_attr *na);
|
extern void NAttrClear##func_name(ntfs_attr *na);
|
||||||
|
|
||||||
GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED)
|
GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED)
|
||||||
GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED)
|
GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED)
|
||||||
GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE)
|
GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE)
|
||||||
#undef GenNAttrIno
|
#undef GenNAttrIno
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,46 +243,54 @@ GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE)
|
|||||||
*
|
*
|
||||||
* For convenience. Used in the attr structure.
|
* For convenience. Used in the attr structure.
|
||||||
*/
|
*/
|
||||||
typedef union
|
typedef union {
|
||||||
{
|
u8 _default; /* Unnamed u8 to serve as default when just using
|
||||||
u8 _default; /* Unnamed u8 to serve as default when just using
|
a_val without specifying any of the below. */
|
||||||
a_val without specifying any of the below. */
|
STANDARD_INFORMATION std_inf;
|
||||||
STANDARD_INFORMATION std_inf;
|
ATTR_LIST_ENTRY al_entry;
|
||||||
ATTR_LIST_ENTRY al_entry;
|
FILE_NAME_ATTR filename;
|
||||||
FILE_NAME_ATTR filename;
|
OBJECT_ID_ATTR obj_id;
|
||||||
OBJECT_ID_ATTR obj_id;
|
SECURITY_DESCRIPTOR_ATTR sec_desc;
|
||||||
SECURITY_DESCRIPTOR_ATTR sec_desc;
|
VOLUME_NAME vol_name;
|
||||||
VOLUME_NAME vol_name;
|
VOLUME_INFORMATION vol_inf;
|
||||||
VOLUME_INFORMATION vol_inf;
|
DATA_ATTR data;
|
||||||
DATA_ATTR data;
|
INDEX_ROOT index_root;
|
||||||
INDEX_ROOT index_root;
|
INDEX_BLOCK index_blk;
|
||||||
INDEX_BLOCK index_blk;
|
BITMAP_ATTR bmp;
|
||||||
BITMAP_ATTR bmp;
|
REPARSE_POINT reparse;
|
||||||
REPARSE_POINT reparse;
|
EA_INFORMATION ea_inf;
|
||||||
EA_INFORMATION ea_inf;
|
EA_ATTR ea;
|
||||||
EA_ATTR ea;
|
PROPERTY_SET property_set;
|
||||||
PROPERTY_SET property_set;
|
LOGGED_UTILITY_STREAM logged_util_stream;
|
||||||
LOGGED_UTILITY_STREAM logged_util_stream;
|
EFS_ATTR_HEADER efs;
|
||||||
EFS_ATTR_HEADER efs;
|
|
||||||
} attr_val;
|
} attr_val;
|
||||||
|
|
||||||
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, const ATTR_FLAGS data_flags, const BOOL encrypted,
|
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||||
const BOOL sparse, const s64 allocated_size, const s64 data_size, const s64 initialized_size,
|
const ATTR_FLAGS data_flags, const BOOL encrypted,
|
||||||
const s64 compressed_size, const u8 compression_unit);
|
const BOOL sparse,
|
||||||
|
const s64 allocated_size, const s64 data_size,
|
||||||
|
const s64 initialized_size, const s64 compressed_size,
|
||||||
|
const u8 compression_unit);
|
||||||
|
|
||||||
/* warning : in the following "name" has to be freeable */
|
/* warning : in the following "name" has to be freeable */
|
||||||
/* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */
|
/* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */
|
||||||
extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len);
|
extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
|
ntfschar *name, u32 name_len);
|
||||||
extern void ntfs_attr_close(ntfs_attr *na);
|
extern void ntfs_attr_close(ntfs_attr *na);
|
||||||
|
|
||||||
extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b);
|
extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
|
||||||
extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b);
|
void *b);
|
||||||
|
extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count,
|
||||||
|
const void *b);
|
||||||
extern int ntfs_attr_pclose(ntfs_attr *na);
|
extern int ntfs_attr_pclose(ntfs_attr *na);
|
||||||
|
|
||||||
extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len, s64 *data_size);
|
extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
|
ntfschar *name, u32 name_len, s64 *data_size);
|
||||||
|
|
||||||
extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, const u32 bk_size, void *dst);
|
extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos,
|
||||||
extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, const u32 bk_size, void *src);
|
const s64 bk_cnt, const u32 bk_size, void *dst);
|
||||||
|
extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos,
|
||||||
|
s64 bk_cnt, const u32 bk_size, void *src);
|
||||||
|
|
||||||
extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn);
|
extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn);
|
||||||
extern int ntfs_attr_map_whole_runlist(ntfs_attr *na);
|
extern int ntfs_attr_map_whole_runlist(ntfs_attr *na);
|
||||||
@ -287,26 +298,33 @@ extern int ntfs_attr_map_whole_runlist(ntfs_attr *na);
|
|||||||
extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn);
|
extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn);
|
||||||
extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn);
|
extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn);
|
||||||
|
|
||||||
extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, const s64 size);
|
extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol,
|
||||||
extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type);
|
const ATTR_TYPES type, const s64 size);
|
||||||
int ntfs_attr_make_non_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx);
|
extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
|
||||||
|
const ATTR_TYPES type);
|
||||||
|
int ntfs_attr_make_non_resident(ntfs_attr *na,
|
||||||
|
ntfs_attr_search_ctx *ctx);
|
||||||
int ntfs_attr_force_non_resident(ntfs_attr *na);
|
int ntfs_attr_force_non_resident(ntfs_attr *na);
|
||||||
extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
|
extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
|
||||||
|
|
||||||
extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, ntfschar *name, u8 name_len, u8 *val,
|
extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
u32 size, ATTR_FLAGS flags);
|
ntfschar *name, u8 name_len, u8 *val, u32 size,
|
||||||
extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, ntfschar *name, u8 name_len,
|
ATTR_FLAGS flags);
|
||||||
VCN lowest_vcn, int dataruns_size, ATTR_FLAGS flags);
|
extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
|
ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
|
||||||
|
ATTR_FLAGS flags);
|
||||||
extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
|
extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, ntfschar *name, u8 name_len, u8 *val, s64 size);
|
extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, ntfschar *name, u8 name_len, ATTR_FLAGS flags,
|
ntfschar *name, u8 name_len, u8 *val, s64 size);
|
||||||
ATTR_FLAGS mask);
|
extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
|
ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask);
|
||||||
extern int ntfs_attr_rm(ntfs_attr *na);
|
extern int ntfs_attr_rm(ntfs_attr *na);
|
||||||
|
|
||||||
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
|
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
|
||||||
|
|
||||||
extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, const u32 new_size);
|
extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
|
||||||
|
const u32 new_size);
|
||||||
|
|
||||||
extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni);
|
extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni);
|
||||||
extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra);
|
extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra);
|
||||||
@ -342,13 +360,16 @@ extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a);
|
|||||||
* then nothing was read due to a zero-length attribute value, otherwise
|
* then nothing was read due to a zero-length attribute value, otherwise
|
||||||
* errno describes the error.
|
* errno describes the error.
|
||||||
*/
|
*/
|
||||||
extern s64 ntfs_get_attribute_value(const ntfs_volume *vol, const ATTR_RECORD *a, u8 *b);
|
extern s64 ntfs_get_attribute_value(const ntfs_volume *vol,
|
||||||
|
const ATTR_RECORD *a, u8 *b);
|
||||||
|
|
||||||
extern void ntfs_attr_name_free(char **name);
|
extern void ntfs_attr_name_free(char **name);
|
||||||
extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len);
|
extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len);
|
||||||
extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len);
|
extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, u32 name_len);
|
ntfschar *name, u32 name_len);
|
||||||
extern s64 ntfs_attr_get_free_bits(ntfs_attr *na);
|
extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
|
ntfschar *name, u32 name_len);
|
||||||
|
extern s64 ntfs_attr_get_free_bits(ntfs_attr *na);
|
||||||
|
|
||||||
#endif /* defined _NTFS_ATTRIB_H */
|
#endif /* defined _NTFS_ATTRIB_H */
|
||||||
|
|
5914
source/libs/libntfs/attrib_frag.c
Normal file
5914
source/libs/libntfs/attrib_frag.c
Normal file
File diff suppressed because it is too large
Load Diff
314
source/libs/libntfs/attrlist.c
Normal file
314
source/libs/libntfs/attrlist.c
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/**
|
||||||
|
* attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS
|
||||||
|
* project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2005 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2006 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program/include file is distributed in the hope that it will be
|
||||||
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program (in the main directory of the NTFS-3G
|
||||||
|
* distribution in the file COPYING); if not, write to the Free Software
|
||||||
|
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "attrlist.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "unistr.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrlist_need - check whether inode need attribute list
|
||||||
|
* @ni: opened ntfs inode for which perform check
|
||||||
|
*
|
||||||
|
* Check whether all are attributes belong to one MFT record, in that case
|
||||||
|
* attribute list is not needed.
|
||||||
|
*
|
||||||
|
* Return 1 if inode need attribute list, 0 if not, -1 on error with errno set
|
||||||
|
* to the error code. If function succeed errno set to 0. The following error
|
||||||
|
* codes are defined:
|
||||||
|
* EINVAL - Invalid arguments passed to function or attribute haven't got
|
||||||
|
* attribute list.
|
||||||
|
*/
|
||||||
|
int ntfs_attrlist_need(ntfs_inode *ni)
|
||||||
|
{
|
||||||
|
ATTR_LIST_ENTRY *ale;
|
||||||
|
|
||||||
|
if (!ni) {
|
||||||
|
ntfs_log_trace("Invalid arguments.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
|
||||||
|
|
||||||
|
if (!NInoAttrList(ni)) {
|
||||||
|
ntfs_log_trace("Inode haven't got attribute list.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ni->attr_list) {
|
||||||
|
ntfs_log_trace("Corrupt in-memory struct.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
ale = (ATTR_LIST_ENTRY *)ni->attr_list;
|
||||||
|
while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
|
||||||
|
if (MREF_LE(ale->mft_reference) != ni->mft_no)
|
||||||
|
return 1;
|
||||||
|
ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
||||||
|
* @ni: opened ntfs inode, which contains that attribute
|
||||||
|
* @attr: attribute record to add to attribute list
|
||||||
|
*
|
||||||
|
* Return 0 on success and -1 on error with errno set to the error code. The
|
||||||
|
* following error codes are defined:
|
||||||
|
* EINVAL - Invalid arguments passed to function.
|
||||||
|
* ENOMEM - Not enough memory to allocate necessary buffers.
|
||||||
|
* EIO - I/O error occurred or damaged filesystem.
|
||||||
|
* EEXIST - Such attribute already present in attribute list.
|
||||||
|
*/
|
||||||
|
int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
|
||||||
|
{
|
||||||
|
ATTR_LIST_ENTRY *ale;
|
||||||
|
MFT_REF mref;
|
||||||
|
ntfs_attr *na = NULL;
|
||||||
|
ntfs_attr_search_ctx *ctx;
|
||||||
|
u8 *new_al;
|
||||||
|
int entry_len, entry_offset, err;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
|
||||||
|
(long long) ni->mft_no,
|
||||||
|
(unsigned) le32_to_cpu(attr->type));
|
||||||
|
|
||||||
|
if (!ni || !attr) {
|
||||||
|
ntfs_log_trace("Invalid arguments.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
|
||||||
|
|
||||||
|
if (ni->nr_extents == -1)
|
||||||
|
ni = ni->base_ni;
|
||||||
|
|
||||||
|
if (!NInoAttrList(ni)) {
|
||||||
|
ntfs_log_trace("Attribute list isn't present.\n");
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine size and allocate memory for new attribute list. */
|
||||||
|
entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
|
||||||
|
attr->name_length + 7) & ~7;
|
||||||
|
new_al = ntfs_calloc(ni->attr_list_size + entry_len);
|
||||||
|
if (!new_al)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Find place for the new entry. */
|
||||||
|
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||||
|
if (!ctx) {
|
||||||
|
err = errno;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*)
|
||||||
|
((u8*)attr + le16_to_cpu(attr->name_offset)) :
|
||||||
|
AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
|
||||||
|
(attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) :
|
||||||
|
0, (attr->non_resident) ? NULL : ((u8*)attr +
|
||||||
|
le16_to_cpu(attr->value_offset)), (attr->non_resident) ?
|
||||||
|
0 : le32_to_cpu(attr->value_length), ctx)) {
|
||||||
|
/* Found some extent, check it to be before new extent. */
|
||||||
|
if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) {
|
||||||
|
err = EEXIST;
|
||||||
|
ntfs_log_trace("Such attribute already present in the "
|
||||||
|
"attribute list.\n");
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
/* Add new entry after this extent. */
|
||||||
|
ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
|
||||||
|
le16_to_cpu(ctx->al_entry->length));
|
||||||
|
} else {
|
||||||
|
/* Check for real errors. */
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("Attribute lookup failed.\n");
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
/* No previous extents found. */
|
||||||
|
ale = ctx->al_entry;
|
||||||
|
}
|
||||||
|
/* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
|
||||||
|
/* Determine new entry offset. */
|
||||||
|
entry_offset = ((u8 *)ale - ni->attr_list);
|
||||||
|
/* Set pointer to new entry. */
|
||||||
|
ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset);
|
||||||
|
/* Zero it to fix valgrind warning. */
|
||||||
|
memset(ale, 0, entry_len);
|
||||||
|
/* Form new entry. */
|
||||||
|
ale->type = attr->type;
|
||||||
|
ale->length = cpu_to_le16(entry_len);
|
||||||
|
ale->name_length = attr->name_length;
|
||||||
|
ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
|
||||||
|
if (attr->non_resident)
|
||||||
|
ale->lowest_vcn = attr->lowest_vcn;
|
||||||
|
else
|
||||||
|
ale->lowest_vcn = 0;
|
||||||
|
ale->mft_reference = mref;
|
||||||
|
ale->instance = attr->instance;
|
||||||
|
memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
|
||||||
|
attr->name_length * sizeof(ntfschar));
|
||||||
|
|
||||||
|
/* Resize $ATTRIBUTE_LIST to new length. */
|
||||||
|
na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||||
|
if (!na) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy entries from old attribute list to new. */
|
||||||
|
memcpy(new_al, ni->attr_list, entry_offset);
|
||||||
|
memcpy(new_al + entry_offset + entry_len, ni->attr_list +
|
||||||
|
entry_offset, ni->attr_list_size - entry_offset);
|
||||||
|
|
||||||
|
/* Set new runlist. */
|
||||||
|
free(ni->attr_list);
|
||||||
|
ni->attr_list = new_al;
|
||||||
|
ni->attr_list_size = ni->attr_list_size + entry_len;
|
||||||
|
NInoAttrListSetDirty(ni);
|
||||||
|
/* Done! */
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
return 0;
|
||||||
|
err_out:
|
||||||
|
if (na)
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
free(new_al);
|
||||||
|
errno = err;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrlist_entry_rm - remove an attribute list attribute entry
|
||||||
|
* @ctx: attribute search context describing the attribute list entry
|
||||||
|
*
|
||||||
|
* Remove the attribute list entry @ctx->al_entry from the attribute list.
|
||||||
|
*
|
||||||
|
* Return 0 on success and -1 on error with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
|
||||||
|
{
|
||||||
|
u8 *new_al;
|
||||||
|
int new_al_len;
|
||||||
|
ntfs_inode *base_ni;
|
||||||
|
ntfs_attr *na;
|
||||||
|
ATTR_LIST_ENTRY *ale;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
|
||||||
|
ntfs_log_trace("Invalid arguments.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->base_ntfs_ino)
|
||||||
|
base_ni = ctx->base_ntfs_ino;
|
||||||
|
else
|
||||||
|
base_ni = ctx->ntfs_ino;
|
||||||
|
ale = ctx->al_entry;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
|
||||||
|
(long long) ctx->ntfs_ino->mft_no,
|
||||||
|
(unsigned) le32_to_cpu(ctx->al_entry->type),
|
||||||
|
(long long) le64_to_cpu(ctx->al_entry->lowest_vcn));
|
||||||
|
|
||||||
|
if (!NInoAttrList(base_ni)) {
|
||||||
|
ntfs_log_trace("Attribute list isn't present.\n");
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory for new attribute list. */
|
||||||
|
new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
|
||||||
|
new_al = ntfs_calloc(new_al_len);
|
||||||
|
if (!new_al)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Reisze $ATTRIBUTE_LIST to new length. */
|
||||||
|
na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||||
|
if (!na) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (ntfs_attr_truncate(na, new_al_len)) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy entries from old attribute list to new. */
|
||||||
|
memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list);
|
||||||
|
memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu(
|
||||||
|
ale->length), new_al_len - ((u8*)ale - base_ni->attr_list));
|
||||||
|
|
||||||
|
/* Set new runlist. */
|
||||||
|
free(base_ni->attr_list);
|
||||||
|
base_ni->attr_list = new_al;
|
||||||
|
base_ni->attr_list_size = new_al_len;
|
||||||
|
NInoAttrListSetDirty(base_ni);
|
||||||
|
/* Done! */
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
return 0;
|
||||||
|
err_out:
|
||||||
|
if (na)
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
free(new_al);
|
||||||
|
errno = err;
|
||||||
|
return -1;
|
||||||
|
}
|
@ -42,9 +42,10 @@ extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx);
|
|||||||
*/
|
*/
|
||||||
static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni)
|
static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni)
|
||||||
{
|
{
|
||||||
if (ni->nr_extents == -1)
|
if (ni->nr_extents == -1)
|
||||||
NInoAttrListSetDirty(ni->base_ni);
|
NInoAttrListSetDirty(ni->base_ni);
|
||||||
else NInoAttrListSetDirty(ni);
|
else
|
||||||
|
NInoAttrListSetDirty(ni);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* defined _NTFS_ATTRLIST_H */
|
#endif /* defined _NTFS_ATTRLIST_H */
|
57
source/libs/libntfs/bit_ops.h
Normal file
57
source/libs/libntfs/bit_ops.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
bit_ops.h
|
||||||
|
Functions for dealing with conversion of data between types
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIT_OPS_H
|
||||||
|
#define _BIT_OPS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------
|
||||||
|
Functions to deal with little endian values stored in uint8_t arrays
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) {
|
||||||
|
return ( item[offset] | (item[offset + 1] << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) {
|
||||||
|
return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) {
|
||||||
|
item[offset] = (uint8_t) value;
|
||||||
|
item[offset + 1] = (uint8_t)(value >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) {
|
||||||
|
item[offset] = (uint8_t) value;
|
||||||
|
item[offset + 1] = (uint8_t)(value >> 8);
|
||||||
|
item[offset + 2] = (uint8_t)(value >> 16);
|
||||||
|
item[offset + 3] = (uint8_t)(value >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _BIT_OPS_H
|
300
source/libs/libntfs/bitmap.c
Normal file
300
source/libs/libntfs/bitmap.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/**
|
||||||
|
* bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2006 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program/include file is distributed in the hope that it will be
|
||||||
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program (in the main directory of the NTFS-3G
|
||||||
|
* distribution in the file COPYING); if not, write to the Free Software
|
||||||
|
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bit_set - set a bit in a field of bits
|
||||||
|
* @bitmap: field of bits
|
||||||
|
* @bit: bit to set
|
||||||
|
* @new_value: value to set bit to (0 or 1)
|
||||||
|
*
|
||||||
|
* Set the bit @bit in the @bitmap to @new_value. Ignore all errors.
|
||||||
|
*/
|
||||||
|
void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||||
|
{
|
||||||
|
if (!bitmap || new_value > 1)
|
||||||
|
return;
|
||||||
|
if (!new_value)
|
||||||
|
bitmap[bit >> 3] &= ~(1 << (bit & 7));
|
||||||
|
else
|
||||||
|
bitmap[bit >> 3] |= (1 << (bit & 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bit_get - get value of a bit in a field of bits
|
||||||
|
* @bitmap: field of bits
|
||||||
|
* @bit: bit to get
|
||||||
|
*
|
||||||
|
* Get and return the value of the bit @bit in @bitmap (0 or 1).
|
||||||
|
* Return -1 on error.
|
||||||
|
*/
|
||||||
|
char ntfs_bit_get(const u8 *bitmap, const u64 bit)
|
||||||
|
{
|
||||||
|
if (!bitmap)
|
||||||
|
return -1;
|
||||||
|
return (bitmap[bit >> 3] >> (bit & 7)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bit_get_and_set - get value of a bit in a field of bits and set it
|
||||||
|
* @bitmap: field of bits
|
||||||
|
* @bit: bit to get/set
|
||||||
|
* @new_value: value to set bit to (0 or 1)
|
||||||
|
*
|
||||||
|
* Return the value of the bit @bit and set it to @new_value (0 or 1).
|
||||||
|
* Return -1 on error.
|
||||||
|
*/
|
||||||
|
char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||||
|
{
|
||||||
|
register u8 old_bit, shift;
|
||||||
|
|
||||||
|
if (!bitmap || new_value > 1)
|
||||||
|
return -1;
|
||||||
|
shift = bit & 7;
|
||||||
|
old_bit = (bitmap[bit >> 3] >> shift) & 1;
|
||||||
|
if (new_value != old_bit)
|
||||||
|
bitmap[bit >> 3] ^= 1 << shift;
|
||||||
|
return old_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @start_bit: first bit to set
|
||||||
|
* @count: number of bits to set
|
||||||
|
* @value: value to set the bits to (i.e. 0 or 1)
|
||||||
|
*
|
||||||
|
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||||
|
* attribute @na to @value, where @value is either 0 or 1.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
|
||||||
|
s64 count, int value)
|
||||||
|
{
|
||||||
|
s64 bufsize, br;
|
||||||
|
u8 *buf, *lastbyte_buf;
|
||||||
|
int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1;
|
||||||
|
|
||||||
|
if (!na || start_bit < 0 || count < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)",
|
||||||
|
__FUNCTION__, na, (long long)start_bit, (long long)count);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bit = start_bit & 7;
|
||||||
|
if (bit)
|
||||||
|
firstbyte = 1;
|
||||||
|
else
|
||||||
|
firstbyte = 0;
|
||||||
|
|
||||||
|
/* Calculate the required buffer size in bytes, capping it at 8kiB. */
|
||||||
|
bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
|
||||||
|
if (bufsize > 8192)
|
||||||
|
bufsize = 8192;
|
||||||
|
|
||||||
|
buf = ntfs_malloc(bufsize);
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Depending on @value, zero or set all bits in the allocated buffer. */
|
||||||
|
memset(buf, value ? 0xff : 0, bufsize);
|
||||||
|
|
||||||
|
/* If there is a first partial byte... */
|
||||||
|
if (bit) {
|
||||||
|
/* read it in... */
|
||||||
|
br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
|
||||||
|
if (br != 1) {
|
||||||
|
if (br >= 0)
|
||||||
|
errno = EIO;
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
/* and set or clear the appropriate bits in it. */
|
||||||
|
while ((bit & 7) && count--) {
|
||||||
|
if (value)
|
||||||
|
*buf |= 1 << bit++;
|
||||||
|
else
|
||||||
|
*buf &= ~(1 << bit++);
|
||||||
|
}
|
||||||
|
/* Update @start_bit to the new position. */
|
||||||
|
start_bit = (start_bit + 7) & ~7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop until @count reaches zero. */
|
||||||
|
lastbyte = 0;
|
||||||
|
lastbyte_buf = NULL;
|
||||||
|
bit = count & 7;
|
||||||
|
do {
|
||||||
|
/* If there is a last partial byte... */
|
||||||
|
if (count > 0 && bit) {
|
||||||
|
lastbyte_pos = ((count + 7) >> 3) + firstbyte;
|
||||||
|
if (!lastbyte_pos) {
|
||||||
|
// FIXME: Eeek! BUG!
|
||||||
|
ntfs_log_error("Lastbyte is zero. Leaving "
|
||||||
|
"inconsistent metadata.\n");
|
||||||
|
errno = EIO;
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
/* and it is in the currently loaded bitmap window... */
|
||||||
|
if (lastbyte_pos <= bufsize) {
|
||||||
|
lastbyte_buf = buf + lastbyte_pos - 1;
|
||||||
|
|
||||||
|
/* read the byte in... */
|
||||||
|
br = ntfs_attr_pread(na, (start_bit + count) >>
|
||||||
|
3, 1, lastbyte_buf);
|
||||||
|
if (br != 1) {
|
||||||
|
// FIXME: Eeek! We need rollback! (AIA)
|
||||||
|
if (br >= 0)
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("Reading of last byte "
|
||||||
|
"failed (%lld). Leaving inconsistent "
|
||||||
|
"metadata", (long long)br);
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
/* and set/clear the appropriate bits in it. */
|
||||||
|
while (bit && count--) {
|
||||||
|
if (value)
|
||||||
|
*lastbyte_buf |= 1 << --bit;
|
||||||
|
else
|
||||||
|
*lastbyte_buf &= ~(1 << --bit);
|
||||||
|
}
|
||||||
|
/* We don't want to come back here... */
|
||||||
|
bit = 0;
|
||||||
|
/* We have a last byte that we have handled. */
|
||||||
|
lastbyte = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the prepared buffer to disk. */
|
||||||
|
tmp = (start_bit >> 3) - firstbyte;
|
||||||
|
br = ntfs_attr_pwrite(na, tmp, bufsize, buf);
|
||||||
|
if (br != bufsize) {
|
||||||
|
// FIXME: Eeek! We need rollback! (AIA)
|
||||||
|
if (br >= 0)
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("Failed to write buffer to bitmap "
|
||||||
|
"(%lld != %lld). Leaving inconsistent metadata",
|
||||||
|
(long long)br, (long long)bufsize);
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update counters. */
|
||||||
|
tmp = (bufsize - firstbyte - lastbyte) << 3;
|
||||||
|
if (firstbyte) {
|
||||||
|
firstbyte = 0;
|
||||||
|
/*
|
||||||
|
* Re-set the partial first byte so a subsequent write
|
||||||
|
* of the buffer does not have stale, incorrect bits.
|
||||||
|
*/
|
||||||
|
*buf = value ? 0xff : 0;
|
||||||
|
}
|
||||||
|
start_bit += tmp;
|
||||||
|
count -= tmp;
|
||||||
|
if (bufsize > (tmp = (count + 7) >> 3))
|
||||||
|
bufsize = tmp;
|
||||||
|
|
||||||
|
if (lastbyte && count != 0) {
|
||||||
|
// FIXME: Eeek! BUG!
|
||||||
|
ntfs_log_error("Last buffer but count is not zero "
|
||||||
|
"(%lld). Leaving inconsistent metadata.\n",
|
||||||
|
(long long)count);
|
||||||
|
errno = EIO;
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
} while (count > 0);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
free_err_out:
|
||||||
|
free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_set_run - set a run of bits in a bitmap
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @start_bit: first bit to set
|
||||||
|
* @count: number of bits to set
|
||||||
|
*
|
||||||
|
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||||
|
* attribute @na.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ntfs_log_enter("Set from bit %lld, count %lld\n",
|
||||||
|
(long long)start_bit, (long long)count);
|
||||||
|
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1);
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_clear_run - clear a run of bits in a bitmap
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @start_bit: first bit to clear
|
||||||
|
* @count: number of bits to clear
|
||||||
|
*
|
||||||
|
* Clear @count bits starting at bit @start_bit in the bitmap described by the
|
||||||
|
* attribute @na.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ntfs_log_enter("Clear from bit %lld, count %lld\n",
|
||||||
|
(long long)start_bit, (long long)count);
|
||||||
|
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -39,8 +39,8 @@
|
|||||||
extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||||
extern char ntfs_bit_get(const u8 *bitmap, const u64 bit);
|
extern char ntfs_bit_get(const u8 *bitmap, const u64 bit);
|
||||||
extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||||
extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count);
|
extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||||
extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count);
|
extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ntfs_bitmap_set_bit - set a bit in a bitmap
|
* ntfs_bitmap_set_bit - set a bit in a bitmap
|
||||||
@ -53,7 +53,7 @@ extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count);
|
|||||||
*/
|
*/
|
||||||
static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit)
|
static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit)
|
||||||
{
|
{
|
||||||
return ntfs_bitmap_set_run(na, bit, 1);
|
return ntfs_bitmap_set_run(na, bit, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +67,7 @@ static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit)
|
|||||||
*/
|
*/
|
||||||
static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit)
|
static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit)
|
||||||
{
|
{
|
||||||
return ntfs_bitmap_clear_run(na, bit, 1);
|
return ntfs_bitmap_clear_run(na, bit, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -78,7 +78,7 @@ static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit)
|
|||||||
*/
|
*/
|
||||||
static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift)
|
static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift)
|
||||||
{
|
{
|
||||||
return (word << shift) | (word >> (32 - shift));
|
return (word << shift) | (word >> (32 - shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -89,7 +89,7 @@ static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift)
|
|||||||
*/
|
*/
|
||||||
static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift)
|
static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift)
|
||||||
{
|
{
|
||||||
return (word >> shift) | (word << (32 - shift));
|
return (word >> shift) | (word << (32 - shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* defined _NTFS_BITMAP_H */
|
#endif /* defined _NTFS_BITMAP_H */
|
285
source/libs/libntfs/bootsect.c
Normal file
285
source/libs/libntfs/bootsect.c
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/**
|
||||||
|
* bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2003-2008 Szabolcs Szakacsits
|
||||||
|
* Copyright (c) 2005 Yura Pakhuchiy
|
||||||
|
*
|
||||||
|
* This program/include file is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program/include file is distributed in the hope that it will be
|
||||||
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program (in the main directory of the NTFS-3G
|
||||||
|
* distribution in the file COPYING); if not, write to the Free Software
|
||||||
|
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
#include "bootsect.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector
|
||||||
|
* @b: buffer containing putative boot sector to analyze
|
||||||
|
* @silent: if zero, output progress messages to stderr
|
||||||
|
*
|
||||||
|
* Check if the buffer @b contains a valid ntfs boot sector. The buffer @b
|
||||||
|
* must be at least 512 bytes in size.
|
||||||
|
*
|
||||||
|
* If @silent is zero, output progress messages to stderr. Otherwise, do not
|
||||||
|
* output any messages (except when configured with --enable-debug in which
|
||||||
|
* case warning/debug messages may be displayed).
|
||||||
|
*
|
||||||
|
* Return TRUE if @b contains a valid ntfs boot sector and FALSE if not.
|
||||||
|
*/
|
||||||
|
BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
|
||||||
|
{
|
||||||
|
u32 i;
|
||||||
|
BOOL ret = FALSE;
|
||||||
|
|
||||||
|
ntfs_log_debug("Beginning bootsector check.\n");
|
||||||
|
|
||||||
|
ntfs_log_debug("Checking OEMid, NTFS signature.\n");
|
||||||
|
if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */
|
||||||
|
ntfs_log_error("NTFS signature is missing.\n");
|
||||||
|
goto not_ntfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_debug("Checking bytes per sector.\n");
|
||||||
|
if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 ||
|
||||||
|
le16_to_cpu(b->bpb.bytes_per_sector) > 4096) {
|
||||||
|
ntfs_log_error("Unexpected bytes per sector value (%d).\n",
|
||||||
|
le16_to_cpu(b->bpb.bytes_per_sector));
|
||||||
|
goto not_ntfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_debug("Checking sectors per cluster.\n");
|
||||||
|
switch (b->bpb.sectors_per_cluster) {
|
||||||
|
case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ntfs_log_error("Unexpected sectors per cluster value (%d).\n",
|
||||||
|
b->bpb.sectors_per_cluster);
|
||||||
|
goto not_ntfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_debug("Checking cluster size.\n");
|
||||||
|
i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) *
|
||||||
|
b->bpb.sectors_per_cluster;
|
||||||
|
if (i > 65536) {
|
||||||
|
ntfs_log_error("Unexpected cluster size (%d).\n", i);
|
||||||
|
goto not_ntfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_debug("Checking reserved fields are zero.\n");
|
||||||
|
if (le16_to_cpu(b->bpb.reserved_sectors) ||
|
||||||
|
le16_to_cpu(b->bpb.root_entries) ||
|
||||||
|
le16_to_cpu(b->bpb.sectors) ||
|
||||||
|
le16_to_cpu(b->bpb.sectors_per_fat) ||
|
||||||
|
le32_to_cpu(b->bpb.large_sectors) ||
|
||||||
|
b->bpb.fats) {
|
||||||
|
ntfs_log_error("Reserved fields aren't zero "
|
||||||
|
"(%d, %d, %d, %d, %d, %d).\n",
|
||||||
|
le16_to_cpu(b->bpb.reserved_sectors),
|
||||||
|
le16_to_cpu(b->bpb.root_entries),
|
||||||
|
le16_to_cpu(b->bpb.sectors),
|
||||||
|
le16_to_cpu(b->bpb.sectors_per_fat),
|
||||||
|
le32_to_cpu(b->bpb.large_sectors),
|
||||||
|
b->bpb.fats);
|
||||||
|
goto not_ntfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_debug("Checking clusters per mft record.\n");
|
||||||
|
if ((u8)b->clusters_per_mft_record < 0xe1 ||
|
||||||
|
(u8)b->clusters_per_mft_record > 0xf7) {
|
||||||
|
switch (b->clusters_per_mft_record) {
|
||||||
|
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ntfs_log_error("Unexpected clusters per mft record "
|
||||||
|
"(%d).\n", b->clusters_per_mft_record);
|
||||||
|
goto not_ntfs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_debug("Checking clusters per index block.\n");
|
||||||
|
if ((u8)b->clusters_per_index_record < 0xe1 ||
|
||||||
|
(u8)b->clusters_per_index_record > 0xf7) {
|
||||||
|
switch (b->clusters_per_index_record) {
|
||||||
|
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ntfs_log_error("Unexpected clusters per index record "
|
||||||
|
"(%d).\n", b->clusters_per_index_record);
|
||||||
|
goto not_ntfs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b->end_of_sector_marker != cpu_to_le16(0xaa55))
|
||||||
|
ntfs_log_debug("Warning: Bootsector has invalid end of sector "
|
||||||
|
"marker.\n");
|
||||||
|
|
||||||
|
ntfs_log_debug("Bootsector check completed successfully.\n");
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
not_ntfs:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *last_sector_error =
|
||||||
|
"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n"
|
||||||
|
" or it was not setup correctly (e.g. by not using mdadm --build ...),\n"
|
||||||
|
" or a wrong device is tried to be mounted,\n"
|
||||||
|
" or the partition table is corrupt (partition is smaller than NTFS),\n"
|
||||||
|
" or the NTFS boot sector is corrupt (NTFS size is not valid).\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector
|
||||||
|
* @vol: ntfs_volume to setup
|
||||||
|
* @bs: buffer containing ntfs boot sector to parse
|
||||||
|
*
|
||||||
|
* Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the
|
||||||
|
* obtained values.
|
||||||
|
*
|
||||||
|
* Return 0 on success or -1 on error with errno set to the error code EINVAL.
|
||||||
|
*/
|
||||||
|
int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
|
||||||
|
{
|
||||||
|
s64 sectors;
|
||||||
|
u8 sectors_per_cluster;
|
||||||
|
s8 c;
|
||||||
|
|
||||||
|
/* We return -1 with errno = EINVAL on error. */
|
||||||
|
errno = EINVAL;
|
||||||
|
|
||||||
|
vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector);
|
||||||
|
vol->sector_size_bits = ffs(vol->sector_size) - 1;
|
||||||
|
ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size);
|
||||||
|
ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits);
|
||||||
|
/*
|
||||||
|
* The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
|
||||||
|
* below or equal the number_of_clusters) really belong in the
|
||||||
|
* ntfs_boot_sector_is_ntfs but in this way we can just do this once.
|
||||||
|
*/
|
||||||
|
sectors_per_cluster = bs->bpb.sectors_per_cluster;
|
||||||
|
ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
|
||||||
|
if (sectors_per_cluster & (sectors_per_cluster - 1)) {
|
||||||
|
ntfs_log_error("sectors_per_cluster (%d) is not a power of 2."
|
||||||
|
"\n", sectors_per_cluster);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors = sle64_to_cpu(bs->number_of_sectors);
|
||||||
|
ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors);
|
||||||
|
if (!sectors) {
|
||||||
|
ntfs_log_error("Volume size is set to zero.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (vol->dev->d_ops->seek(vol->dev,
|
||||||
|
(sectors - 1) << vol->sector_size_bits,
|
||||||
|
SEEK_SET) == -1) {
|
||||||
|
ntfs_log_perror("Failed to read last sector (%lld)",
|
||||||
|
(long long)sectors);
|
||||||
|
ntfs_log_error("%s", last_sector_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1);
|
||||||
|
|
||||||
|
vol->mft_lcn = sle64_to_cpu(bs->mft_lcn);
|
||||||
|
vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn);
|
||||||
|
ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn);
|
||||||
|
ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn);
|
||||||
|
if (vol->mft_lcn > vol->nr_clusters ||
|
||||||
|
vol->mftmirr_lcn > vol->nr_clusters) {
|
||||||
|
ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is "
|
||||||
|
"greater than the number of clusters (%lld).\n",
|
||||||
|
(long long)vol->mft_lcn, (long long)vol->mftmirr_lcn,
|
||||||
|
(long long)vol->nr_clusters);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vol->cluster_size = sectors_per_cluster * vol->sector_size;
|
||||||
|
if (vol->cluster_size & (vol->cluster_size - 1)) {
|
||||||
|
ntfs_log_error("cluster_size (%d) is not a power of 2.\n",
|
||||||
|
vol->cluster_size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
|
||||||
|
/*
|
||||||
|
* Need to get the clusters per mft record and handle it if it is
|
||||||
|
* negative. Then calculate the mft_record_size. A value of 0x80 is
|
||||||
|
* illegal, thus signed char is actually ok!
|
||||||
|
*/
|
||||||
|
c = bs->clusters_per_mft_record;
|
||||||
|
ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size);
|
||||||
|
ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits);
|
||||||
|
ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c);
|
||||||
|
/*
|
||||||
|
* When clusters_per_mft_record is negative, it means that it is to
|
||||||
|
* be taken to be the negative base 2 logarithm of the mft_record_size
|
||||||
|
* min bytes. Then:
|
||||||
|
* mft_record_size = 2^(-clusters_per_mft_record) bytes.
|
||||||
|
*/
|
||||||
|
if (c < 0)
|
||||||
|
vol->mft_record_size = 1 << -c;
|
||||||
|
else
|
||||||
|
vol->mft_record_size = c << vol->cluster_size_bits;
|
||||||
|
if (vol->mft_record_size & (vol->mft_record_size - 1)) {
|
||||||
|
ntfs_log_error("mft_record_size (%d) is not a power of 2.\n",
|
||||||
|
vol->mft_record_size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
|
||||||
|
ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size);
|
||||||
|
ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
|
||||||
|
/* Same as above for INDX record. */
|
||||||
|
c = bs->clusters_per_index_record;
|
||||||
|
ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c);
|
||||||
|
if (c < 0)
|
||||||
|
vol->indx_record_size = 1 << -c;
|
||||||
|
else
|
||||||
|
vol->indx_record_size = c << vol->cluster_size_bits;
|
||||||
|
vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1;
|
||||||
|
ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size);
|
||||||
|
ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits);
|
||||||
|
/*
|
||||||
|
* Work out the size of the MFT mirror in number of mft records. If the
|
||||||
|
* cluster size is less than or equal to the size taken by four mft
|
||||||
|
* records, the mft mirror stores the first four mft records. If the
|
||||||
|
* cluster size is bigger than the size taken by four mft records, the
|
||||||
|
* mft mirror contains as many mft records as will fit into one
|
||||||
|
* cluster.
|
||||||
|
*/
|
||||||
|
if (vol->cluster_size <= 4 * vol->mft_record_size)
|
||||||
|
vol->mftmirr_size = 4;
|
||||||
|
else
|
||||||
|
vol->mftmirr_size = vol->cluster_size / vol->mft_record_size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user