*Update to new libfat

*Rearranged the libs location a bit
This commit is contained in:
dimok321 2010-10-01 18:53:38 +00:00
parent 297eb8cd55
commit dbe694cedf
183 changed files with 57045 additions and 58968 deletions

View File

@ -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.

View File

@ -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

View File

@ -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>

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 } };

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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_ */

View File

@ -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;
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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 */

View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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

View 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;
}
}

View 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
View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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

View 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);
}

View File

@ -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
View 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';
}

View File

@ -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"

View File

@ -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

View File

@ -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

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View 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;
}

View File

@ -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 */

View 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

View 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;
}

View File

@ -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 */

View 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