From 626c79ea2d7aae320722bc5c91389f49c15cbc49 Mon Sep 17 00:00:00 2001 From: dimok321 <15055714+dimok789@users.noreply.github.com> Date: Sun, 15 Nov 2009 21:30:44 +0000 Subject: [PATCH] *Added the custom libfat source to our source so you don't need to replace the original one in libogc *Added NewSuperMarioBrosPatch for NTSC Version --- Makefile | 4 +- gui.pnproj | 2 +- gui.pnps | 2 +- source/banner/banner.c | 252 ++-- source/banner/openingbnr.c | 14 +- source/cheats/cheatmenu.cpp | 6 +- source/fatmounter.c | 8 +- source/libfat/bit_ops.h | 57 + source/libfat/cache.c | 366 ++++++ source/libfat/cache.h | 130 ++ source/libfat/common.h | 48 + source/libfat/directory.c | 1075 ++++++++++++++++ source/libfat/directory.h | 173 +++ source/libfat/disc_fat.c | 67 + source/libfat/disc_fat.h | 110 ++ source/libfat/fat.h | 80 ++ source/libfat/fatdir.c | 610 +++++++++ source/libfat/fatdir.h | 73 ++ source/libfat/fatfile.c | 1131 +++++++++++++++++ source/libfat/fatfile.h | 105 ++ source/libfat/file_allocation_table.c | 383 ++++++ source/libfat/file_allocation_table.h | 70 + source/libfat/filetime.c | 107 ++ source/libfat/filetime.h | 41 + source/libfat/libfat.c | 197 +++ source/libfat/lock.h | 87 ++ source/libfat/mem_allocate.h | 49 + source/libfat/partition.c | 312 +++++ source/libfat/partition.h | 88 ++ source/listfiles.c | 2 +- source/main.cpp | 2 +- source/prompts/PromptWindows.cpp | 2 +- source/prompts/PromptWindows.h | 2 +- source/settings/Settings.cpp | 2 +- source/usbloader/apploader.c | 20 +- .../{partition.c => partition_usbloader.c} | 380 +++--- .../{partition.h => partition_usbloader.h} | 0 source/usbloader/wbfs.c | 2 +- source/usbloader/wbfs_fat.c | 2 +- source/wad/title.h | 4 +- 40 files changed, 5721 insertions(+), 344 deletions(-) create mode 100644 source/libfat/bit_ops.h create mode 100644 source/libfat/cache.c create mode 100644 source/libfat/cache.h create mode 100644 source/libfat/common.h create mode 100644 source/libfat/directory.c create mode 100644 source/libfat/directory.h create mode 100644 source/libfat/disc_fat.c create mode 100644 source/libfat/disc_fat.h create mode 100644 source/libfat/fat.h create mode 100644 source/libfat/fatdir.c create mode 100644 source/libfat/fatdir.h create mode 100644 source/libfat/fatfile.c create mode 100644 source/libfat/fatfile.h create mode 100644 source/libfat/file_allocation_table.c create mode 100644 source/libfat/file_allocation_table.h create mode 100644 source/libfat/filetime.c create mode 100644 source/libfat/filetime.h create mode 100644 source/libfat/libfat.c create mode 100644 source/libfat/lock.h create mode 100644 source/libfat/mem_allocate.h create mode 100644 source/libfat/partition.c create mode 100644 source/libfat/partition.h rename source/usbloader/{partition.c => partition_usbloader.c} (95%) rename source/usbloader/{partition.h => partition_usbloader.h} (100%) diff --git a/Makefile b/Makefile index d676a032..0b623e39 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ SOURCES := source source/libwiigui source/images source/fonts source/sounds \ source/libwbfs source/unzip source/language source/mload source/patches \ source/usbloader source/xml source/network source/settings source/prompts \ source/ramdisk source/wad source/banner source/cheats source/homebrewboot \ - source/themes source/menu + source/themes source/menu source/libfat DATA := data INCLUDES := source @@ -35,7 +35,7 @@ LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map,--section-start,.init=0x80B00 #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lfat -lpngu -lpng -lm -lz -lwiiuse -lbte -lasnd -logc -lfreetype -ltremor -lmad -lmxml -ljpeg +LIBS := -lpngu -lpng -lm -lz -lwiiuse -lbte -lasnd -logc -lfreetype -ltremor -lmad -lmxml -ljpeg #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib diff --git a/gui.pnproj b/gui.pnproj index bf0ccfb3..0b9dce41 100644 --- a/gui.pnproj +++ b/gui.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/gui.pnps b/gui.pnps index 094633ff..faba12af 100644 --- a/gui.pnps +++ b/gui.pnps @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/source/banner/banner.c b/source/banner/banner.c index 5ac1fbcd..62340b83 100644 --- a/source/banner/banner.c +++ b/source/banner/banner.c @@ -1,127 +1,127 @@ -/**************************************************************************** - * USB Loader GX Team - * banner.c - * - * Dump opening.bnr thanks to Wiipower - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fatmounter.h" +/**************************************************************************** + * USB Loader GX Team + * banner.c + * + * Dump opening.bnr thanks to Wiipower + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libfat/fat.h" +#include "fatmounter.h" #include "usbloader/wdvd.h" -#include "usbloader/disc.h" -#include "banner.h" -#include "patches/fst.h" -#include "usbloader/fstfile.h" - -s32 dump_banner(const u8* discid,const char * dest) -{ - // Mount the disc - //Disc_SetWBFS(1, (u8*)discid); - Disc_SetUSB(discid); - - Disc_Open(); - - u64 offset; - s32 ret; - - ret = __Disc_FindPartition(&offset); - if (ret < 0) - return ret; - - ret = WDVD_OpenPartition(offset); - - if (ret < 0) { - //printf("ERROR: OpenPartition(0x%llx) %d\n", offset, ret); - return ret; - } - - // Read where to find the fst.bin - u32 *buffer = memalign(32, 0x20); - - if (buffer == NULL) - { - //Out of memory - return -1; - } - - ret = WDVD_Read(buffer, 0x20, 0x420); - if (ret < 0) - return ret; - - // Read fst.bin - void *fstbuffer = memalign(32, buffer[2]*4); - FST_ENTRY *fst = (FST_ENTRY *)fstbuffer; - - if (fst == NULL) - { - //Out of memory - free(buffer); - return -1; - } - - ret = WDVD_Read(fstbuffer, buffer[2]*4, buffer[1]*4); - if (ret < 0) - return ret; - - free(buffer); - - // Search the fst.bin - u32 count = fst[0].filelen; - int i; - u32 index = 0; - - for (i=1;i #include #include -#include +#include "libfat/fat.h" #include "MD5.h" #include "banner.h" @@ -115,7 +115,7 @@ typedef struct u8 zeroes[16]; } U8_archive_header; -static int write_file(void* data, size_t size, char* name) +static int write_file(void* data, size_t size, char* name) { size_t written=0; FILE *out; @@ -126,7 +126,7 @@ static int write_file(void* data, size_t size, char* name) fclose(out); } return (written == size) ? 1 : -1; -} +} u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size) { @@ -376,7 +376,7 @@ void do_U8_archivebanner(FILE *fp) fread(string_table, 1, rest_size, fp); for (i = 0; i < num_nodes; i++) { - U8_node* node = &nodes[i]; + U8_node* node = &nodes[i]; u16 type = be16((u8*)&node->type); u16 name_offset = be16((u8*)&node->name_offset); u32 my_data_offset = be32((u8*)&node->data_offset); @@ -499,7 +499,7 @@ int unpackBanner(const u8 *gameid, int what, const char *outdir) size_t size; u8 *data; fseek(fp, 0, SEEK_END); - size = ftell(fp); + size = ftell(fp); if(!size) { ret = -1; @@ -512,14 +512,14 @@ int unpackBanner(const u8 *gameid, int what, const char *outdir) ret = -1; goto error; } - if(fread(data, 1, size, fp) != size) + if(fread(data, 1, size, fp) != size) { ret = -1; goto error; } ret = write_file(data, size, path); } -error: fclose(fp); +error: fclose(fp); } ramdiskUnmount("BANNER"); error2: diff --git a/source/cheats/cheatmenu.cpp b/source/cheats/cheatmenu.cpp index 64e40fa8..3e751079 100644 --- a/source/cheats/cheatmenu.cpp +++ b/source/cheats/cheatmenu.cpp @@ -1,12 +1,12 @@ #include #include -#include +#include "libfat/fat.h" #include "libwiigui/gui.h" #include "libwiigui/gui_customoptionbrowser.h" #include "prompts/PromptWindows.h" #include "language/gettext.h" -#include "fatmounter.h" +#include "fatmounter.h" #include "listfiles.h" #include "menu.h" #include "filelist.h" @@ -136,7 +136,7 @@ int CheatMenu(const char * gameID) { } if (x == 0) { WindowPrompt(tr("Error"),tr("No cheats were selected"),tr("OK")); - } else { + } else { subfoldercreate(Settings.Cheatcodespath); string chtpath = Settings.Cheatcodespath; string gctfname = chtpath + c.getGameID() + ".gct"; diff --git a/source/fatmounter.c b/source/fatmounter.c index fc9b4e2f..64cca03b 100644 --- a/source/fatmounter.c +++ b/source/fatmounter.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -8,6 +7,7 @@ #include "usbloader/sdhc.h" #include "usbloader/usbstorage.h" +#include "libfat/fat.h" //these are the only stable and speed is good #define CACHE 32 @@ -41,7 +41,7 @@ int USBDevice_Init() { return -1; } } - + fat_usb_mount = 1; fat_usb_sec = _FAT_startSector; return 0; @@ -67,7 +67,7 @@ int WBFSDevice_Init(u32 sector) { return -1; } } - + fat_wbfs_mount = 1; fat_wbfs_sec = _FAT_startSector; if (sector && fat_wbfs_sec != sector) { @@ -112,7 +112,7 @@ int SDCard_Init() { void SDCard_deInit() { //closing all open Files write back the cache and then shutdown em! fatUnmount("SD:/"); - + fat_sd_mount = MOUNT_NONE; fat_sd_sec = 0; } diff --git a/source/libfat/bit_ops.h b/source/libfat/bit_ops.h new file mode 100644 index 00000000..8a5fb3e1 --- /dev/null +++ b/source/libfat/bit_ops.h @@ -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 + +/*----------------------------------------------------------------- +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 diff --git a/source/libfat/cache.c b/source/libfat/cache.c new file mode 100644 index 00000000..94633f55 --- /dev/null +++ b/source/libfat/cache.c @@ -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 +#include + +#include "common.h" +#include "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=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_accessdisc,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 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; + } +} diff --git a/source/libfat/cache.h b/source/libfat/cache.h new file mode 100644 index 00000000..ce754c6d --- /dev/null +++ b/source/libfat/cache.h @@ -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_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 + diff --git a/source/libfat/common.h b/source/libfat/common.h new file mode 100644 index 00000000..85d15db9 --- /dev/null +++ b/source/libfat/common.h @@ -0,0 +1,48 @@ +/* + 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 +#include + + +// Platform specific includes +#include +#include +#include +// Platform specific options +#define DEFAULT_CACHE_PAGES 4 +#define DEFAULT_SECTORS_PAGE 64 +#define USE_LWP_LOCK +#define USE_RTC_TIME + +#endif // _COMMON_H diff --git a/source/libfat/directory.c b/source/libfat/directory.c new file mode 100644 index 00000000..279a361d --- /dev/null +++ b/source/libfat/directory.c @@ -0,0 +1,1075 @@ +/* + directory.c + 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. +*/ + +#include +#include +#include +#include +#include +#include + +#include "directory.h" +#include "common.h" +#include "partition.h" +#include "file_allocation_table.h" +#include "bit_ops.h" +#include "filetime.h" + +// Directory entry codes +#define DIR_ENTRY_LAST 0x00 +#define DIR_ENTRY_FREE 0xE5 + +#define LAST_LFN_POS (19*13) +#define LAST_LFN_POS_CORRECTION (MAX_LFN_LENGTH-15) + +typedef unsigned short ucs2_t; + +// Long file name directory entry +enum LFN_offset { + LFN_offset_ordinal = 0x00, // Position within LFN + LFN_offset_char0 = 0x01, + LFN_offset_char1 = 0x03, + LFN_offset_char2 = 0x05, + LFN_offset_char3 = 0x07, + LFN_offset_char4 = 0x09, + LFN_offset_flag = 0x0B, // Should be equal to ATTRIB_LFN + LFN_offset_reserved1 = 0x0C, // Always 0x00 + LFN_offset_checkSum = 0x0D, // Checksum of short file name (alias) + LFN_offset_char5 = 0x0E, + LFN_offset_char6 = 0x10, + LFN_offset_char7 = 0x12, + LFN_offset_char8 = 0x14, + LFN_offset_char9 = 0x16, + LFN_offset_char10 = 0x18, + LFN_offset_reserved2 = 0x1A, // Always 0x0000 + LFN_offset_char11 = 0x1C, + LFN_offset_char12 = 0x1E +}; +static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; + +#define LFN_END 0x40 +#define LFN_DEL 0x80 + +static const char ILLEGAL_ALIAS_CHARACTERS[] = "\\/:;*?\"<>|&+,=[] "; +static const char ILLEGAL_LFN_CHARACTERS[] = "\\/:*?\"<>|"; + +/* +Returns number of UCS-2 characters needed to encode an LFN +Returns -1 if it is an invalid LFN +*/ +#define ABOVE_UCS_RANGE 0xF0 +static int _FAT_directory_lfnLength (const char* name) { + unsigned int i; + size_t nameLength; + int ucsLength; + const char* tempName = name; + + nameLength = strnlen(name, MAX_FILENAME_LENGTH); + // Make sure the name is short enough to be valid + if ( nameLength >= MAX_FILENAME_LENGTH) { + return -1; + } + // Make sure it doesn't contain any invalid characters + if (strpbrk (name, ILLEGAL_LFN_CHARACTERS) != NULL) { + return -1; + } + // Make sure the name doesn't contain any control codes or codes not representable in UCS-2 + for (i = 0; i < nameLength; i++) { + if (name[i] < 0x20 || name[i] >= ABOVE_UCS_RANGE) { + return -1; + } + } + // Convert to UCS-2 and get the resulting length + ucsLength = mbsrtowcs(NULL, &tempName, MAX_LFN_LENGTH, NULL); + if (ucsLength < 0 || ucsLength >= MAX_LFN_LENGTH) { + return -1; + } + + // Otherwise it is valid + return ucsLength; +} + +/* +Convert a multibyte encoded string into a NUL-terminated UCS-2 string, storing at most len characters +return number of characters stored +*/ +static size_t _FAT_directory_mbstoucs2 (ucs2_t* dst, const char* src, size_t len) { + mbstate_t ps = {0}; + wchar_t tempChar; + int bytes; + size_t count = 0; + + while (count < len-1 && src != '\0') { + bytes = mbrtowc (&tempChar, src, MB_CUR_MAX, &ps); + if (bytes > 0) { + *dst = (ucs2_t)tempChar; + src += bytes; + dst++; + count++; + } else if (bytes == 0) { + break; + } else { + return -1; + } + } + *dst = '\0'; + + return count; +} + +/* +Convert a UCS-2 string into a NUL-terminated multibyte string, storing at most len chars +return number of chars stored, or (size_t)-1 on error +*/ +static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len) { + mbstate_t ps = {0}; + size_t count = 0; + int bytes; + char buff[MB_CUR_MAX]; + int i; + + while (count < len - 1 && *src != '\0') { + bytes = wcrtomb (buff, *src, &ps); + if (bytes < 0) { + return -1; + } + if (count + bytes < len && bytes > 0) { + for (i = 0; i < bytes; i++) { + *dst++ = buff[i]; + } + src++; + count += bytes; + } else { + break; + } + } + *dst = L'\0'; + + return count; +} + +/* +Case-independent comparison of two multibyte encoded strings +*/ +static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t len1) { + wchar_t wc1, wc2; + mbstate_t ps1 = {0}; + mbstate_t ps2 = {0}; + size_t b1 = 0; + size_t b2 = 0; + + if (len1 == 0) { + return 0; + } + + do { + s1 += b1; + s2 += b2; + b1 = mbrtowc(&wc1, s1, MB_CUR_MAX, &ps1); + b2 = mbrtowc(&wc2, s2, MB_CUR_MAX, &ps2); + if ((int)b1 < 0 || (int)b2 < 0) { + break; + } + len1 -= b1; + } while (len1 > 0 && towlower(wc1) == towlower(wc2) && wc1 != 0); + + return towlower(wc1) - towlower(wc2); +} + + +static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) { + int i=0; + int j=0; + + destName[0] = '\0'; + if (entryData[0] != DIR_ENTRY_FREE) { + if (entryData[0] == '.') { + destName[0] = '.'; + if (entryData[1] == '.') { + destName[1] = '.'; + destName[2] = '\0'; + } else { + destName[1] = '\0'; + } + } else { + // Copy the filename from the dirEntry to the string + for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) { + destName[i] = entryData[DIR_ENTRY_name + i]; + } + // Copy the extension from the dirEntry to the string + if (entryData[DIR_ENTRY_extension] != ' ') { + destName[i++] = '.'; + for ( j = 0; (j < 3) && (entryData[DIR_ENTRY_extension + j] != ' '); j++) { + destName[i++] = entryData[DIR_ENTRY_extension + j]; + } + } + destName[i] = '\0'; + } + } + + return (destName[0] != '\0'); +} + +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData) { + if (partition->filesysType == FS_FAT32) { + // Only use high 16 bits of start cluster when we are certain they are correctly defined + return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16); + } else { + return u8array_to_u16(entryData,DIR_ENTRY_cluster); + } +} + +static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) { + DIR_ENTRY_POSITION position = *entryPosition; + uint32_t tempCluster; + + // Increment offset, wrapping at the end of a sector + ++ position.offset; + if (position.offset == BYTES_PER_READ / DIR_ENTRY_DATA_SIZE) { + position.offset = 0; + // Increment sector when wrapping + ++ position.sector; + // But wrap at the end of a cluster + if ((position.sector == partition->sectorsPerCluster) && (position.cluster != FAT16_ROOT_DIR_CLUSTER)) { + position.sector = 0; + // Move onto the next cluster, making sure there is another cluster to go to + tempCluster = _FAT_fat_nextCluster(partition, position.cluster); + if (tempCluster == CLUSTER_EOF) { + if (extendDirectory) { + tempCluster = _FAT_fat_linkFreeClusterCleared (partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempCluster)) { + return false; // This will only happen if the disc is full + } + } else { + return false; // Got to the end of the directory, not extending it + } + } + position.cluster = tempCluster; + } else if ((position.cluster == FAT16_ROOT_DIR_CLUSTER) && (position.sector == (partition->dataStart - partition->rootDirStart))) { + return false; // Got to end of root directory, can't extend it + } + } + *entryPosition = position; + return true; +} + +bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[0x20]; + ucs2_t lfn[MAX_LFN_LENGTH]; + bool notFound, found; + int lfnPos; + uint8_t lfnChkSum, chkSum; + bool lfnExists; + int i; + + lfnChkSum = 0; + + entryStart = entry->dataEnd; + + // Make sure we are using the correct root directory, in case of FAT32 + if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) { + entryStart.cluster = partition->rootDirCluster; + } + + entryEnd = entryStart; + + lfnExists = false; + + found = false; + notFound = false; + + while (!found && !notFound) { + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { + notFound = true; + } + + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) { + // It's an LFN + if (entryData[LFN_offset_ordinal] & LFN_DEL) { + lfnExists = false; + } else if (entryData[LFN_offset_ordinal] & LFN_END) { + // Last part of LFN, make sure it isn't deleted using previous if(Thanks MoonLight) + entryStart = entryEnd; // This is the start of a directory entry + lfnExists = true; + lfnPos = (entryData[LFN_offset_ordinal] & ~LFN_END) * 13; + if (lfnPos > MAX_LFN_LENGTH - 1) { + lfnPos = MAX_LFN_LENGTH - 1; + } + lfn[lfnPos] = '\0'; // Set end of lfn to null character + lfnChkSum = entryData[LFN_offset_checkSum]; + } if (lfnChkSum != entryData[LFN_offset_checkSum]) { + lfnExists = false; + } + if (lfnExists) { + lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; + if (lfnPos > LAST_LFN_POS) { + // Force it within the buffer. Will corrupt the filename but prevent buffer overflows + lfnPos = LAST_LFN_POS; + } + for (i = 0; i < 13; i++) { + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } else if (entryData[DIR_ENTRY_attributes] & ATTRIB_VOL) { + // This is a volume name, don't bother with it + } else if (entryData[0] == DIR_ENTRY_LAST) { + notFound = true; + } else if ((entryData[0] != DIR_ENTRY_FREE) && (entryData[0] > 0x20) && !(entryData[DIR_ENTRY_attributes] & ATTRIB_VOL)) { + if (lfnExists) { + // Calculate file checksum + chkSum = 0; + for (i=0; i < 11; i++) { + // NOTE: The operation is an unsigned char rotate right + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i]; + } + if (chkSum != lfnChkSum) { + lfnExists = false; + entry->filename[0] = '\0'; + } + } + + if (lfnExists) { + if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) { + // Failed to convert the file name to UTF-8. Maybe the wrong locale is set? + return false; + } + } else { + entryStart = entryEnd; + _FAT_directory_entryGetAlias (entryData, entry->filename); + } + found = true; + } + } + + // If no file is found, return false + if (notFound) { + return false; + } else { + // Fill in the directory entry struct + entry->dataStart = entryStart; + entry->dataEnd = entryEnd; + memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); + return true; + } +} + +bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { + entry->dataStart.cluster = dirCluster; + entry->dataStart.sector = 0; + entry->dataStart.offset = -1; // Start before the beginning of the directory + + entry->dataEnd = entry->dataStart; + + return _FAT_directory_getNextEntry (partition, entry); +} + +bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) { + entry->dataStart.cluster = 0; + entry->dataStart.sector = 0; + entry->dataStart.offset = 0; + + entry->dataEnd = entry->dataStart; + + memset (entry->filename, '\0', MAX_FILENAME_LENGTH); + entry->filename[0] = '.'; + + memset (entry->entryData, 0, DIR_ENTRY_DATA_SIZE); + memset (entry->entryData, ' ', 11); + entry->entryData[0] = '.'; + + entry->entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; + + u16_to_u8array (entry->entryData, DIR_ENTRY_cluster, partition->rootDirCluster); + u16_to_u8array (entry->entryData, DIR_ENTRY_clusterHigh, partition->rootDirCluster >> 16); + + return true; +} + +bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart = entry->dataStart; + DIR_ENTRY_POSITION entryEnd = entry->dataEnd; + bool entryStillValid; + bool finished; + ucs2_t lfn[MAX_LFN_LENGTH]; + int i; + int lfnPos; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + + memset (entry->filename, '\0', MAX_FILENAME_LENGTH); + + // Create an empty directory entry to overwrite the old ones with + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) + { + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, + entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + if ((entryStart.cluster == entryEnd.cluster) + && (entryStart.sector == entryEnd.sector) + && (entryStart.offset == entryEnd.offset)) { + // Copy the entry data and stop, since this is the last section of the directory entry + memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); + finished = true; + } else { + // Copy the long file name data + lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; + if (lfnPos > LAST_LFN_POS) { + lfnPos = LAST_LFN_POS_CORRECTION; + } + for (i = 0; i < 13; i++) { + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } + + if (!entryStillValid) { + return false; + } + + if ((entryStart.cluster == entryEnd.cluster) + && (entryStart.sector == entryEnd.sector) + && (entryStart.offset == entryEnd.offset)) { + // Since the entry doesn't have a long file name, extract the short filename + if (!_FAT_directory_entryGetAlias (entry->entryData, entry->filename)) { + return false; + } + } else { + // Encode the long file name into a multibyte string + if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) { + return false; + } + } + + return true; +} + + + +bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd) { + size_t dirnameLength; + const char* pathPosition; + const char* nextPathPosition; + uint32_t dirCluster; + bool foundFile; + char alias[MAX_ALIAS_LENGTH]; + bool found, notFound; + + pathPosition = path; + + found = false; + notFound = false; + + if (pathEnd == NULL) { + // Set pathEnd to the end of the path string + pathEnd = strchr (path, '\0'); + } + + if (pathPosition[0] == DIR_SEPARATOR) { + // Start at root directory + dirCluster = partition->rootDirCluster; + // Consume separator(s) + while (pathPosition[0] == DIR_SEPARATOR) { + pathPosition++; + } + // If the path is only specifying a directory in the form of "/" return it + if (pathPosition >= pathEnd) { + _FAT_directory_getRootEntry (partition, entry); + found = true; + } + } else { + // Start in current working directory + dirCluster = partition->cwdCluster; + } + + // If the path is only specifying a directory in the form "." + // and this is the root directory, return it + if ((dirCluster == partition->rootDirCluster) && (strcmp(".", pathPosition) == 0)) { + _FAT_directory_getRootEntry (partition, entry); + found = true; + } + + while (!found && !notFound) { + // Get the name of the next required subdirectory within the path + nextPathPosition = strchr (pathPosition, DIR_SEPARATOR); + if (nextPathPosition != NULL) { + dirnameLength = nextPathPosition - pathPosition; + } else { + dirnameLength = strlen(pathPosition); + } + + if (dirnameLength > MAX_FILENAME_LENGTH) { + // The path is too long to bother with + return false; + } + + // Look for the directory within the path + foundFile = _FAT_directory_getFirstEntry (partition, entry, dirCluster); + + while (foundFile && !found && !notFound) { // It hasn't already found the file + // Check if the filename matches + if ((dirnameLength == strnlen(entry->filename, MAX_FILENAME_LENGTH)) + && (_FAT_directory_mbsncasecmp(pathPosition, entry->filename, dirnameLength) == 0)) { + found = true; + } + + // Check if the alias matches + _FAT_directory_entryGetAlias (entry->entryData, alias); + if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH)) + && (strncasecmp(pathPosition, alias, dirnameLength) == 0)) { + found = true; + } + + if (found && !(entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && (nextPathPosition != NULL)) { + // Make sure that we aren't trying to follow a file instead of a directory in the path + found = false; + } + + if (!found) { + foundFile = _FAT_directory_getNextEntry (partition, entry); + } + } + + if (!foundFile) { + // Check that the search didn't get to the end of the directory + notFound = true; + found = false; + } else if ((nextPathPosition == NULL) || (nextPathPosition >= pathEnd)) { + // Check that we reached the end of the path + found = true; + } else if (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) { + dirCluster = _FAT_directory_entryGetCluster (partition, entry->entryData); + pathPosition = nextPathPosition; + // Consume separator(s) + while (pathPosition[0] == DIR_SEPARATOR) { + pathPosition++; + } + // The requested directory was found + if (pathPosition >= pathEnd) { + found = true; + } else { + found = false; + } + } + } + + if (found && !notFound) { + if (partition->filesysType == FS_FAT32 && (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && + _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT) + { + // On FAT32 it should specify an actual cluster for the root entry, + // not cluster 0 as on FAT16 + _FAT_directory_getRootEntry (partition, entry); + } + return true; + } else { + return false; + } +} + +bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart = entry->dataStart; + DIR_ENTRY_POSITION entryEnd = entry->dataEnd; + bool entryStillValid; + bool finished; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + + // Create an empty directory entry to overwrite the old ones with + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) + { + _FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + entryData[0] = DIR_ENTRY_FREE; + _FAT_cache_writePartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) { + finished = true; + } + } + + if (!entryStillValid) { + return false; + } + + return true; +} + +static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster, size_t size) { + DIR_ENTRY_POSITION gapStart; + DIR_ENTRY_POSITION gapEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + size_t dirEntryRemain; + bool endOfDirectory, entryStillValid; + + // Scan Dir for free entry + gapEnd.offset = 0; + gapEnd.sector = 0; + gapEnd.cluster = dirCluster; + + gapStart = gapEnd; + + entryStillValid = true; + dirEntryRemain = size; + endOfDirectory = false; + + while (entryStillValid && !endOfDirectory && (dirEntryRemain > 0)) { + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + if (entryData[0] == DIR_ENTRY_LAST) { + gapStart = gapEnd; + -- dirEntryRemain; + endOfDirectory = true; + } else if (entryData[0] == DIR_ENTRY_FREE) { + if (dirEntryRemain == size) { + gapStart = gapEnd; + } + -- dirEntryRemain; + } else { + dirEntryRemain = size; + } + + if (!endOfDirectory && (dirEntryRemain > 0)) { + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); + } + } + + // Make sure the scanning didn't fail + if (!entryStillValid) { + return false; + } + + // Save the start entry, since we know it is valid + entry->dataStart = gapStart; + + if (endOfDirectory) { + memset (entryData, DIR_ENTRY_LAST, DIR_ENTRY_DATA_SIZE); + dirEntryRemain += 1; // Increase by one to take account of End Of Directory Marker + while ((dirEntryRemain > 0) && entryStillValid) { + // Get the gapEnd before incrementing it, so the second to last one is saved + entry->dataEnd = gapEnd; + // Increment gapEnd, moving onto the next entry + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); + -- dirEntryRemain; + // Fill the entry with blanks + _FAT_cache_writePartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } + if (!entryStillValid) { + return false; + } + } else { + entry->dataEnd = gapEnd; + } + + return true; +} + +static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, uint32_t dirCluster) { + DIR_ENTRY tempEntry; + bool foundFile; + char alias[MAX_ALIAS_LENGTH]; + size_t dirnameLength; + + dirnameLength = strnlen(name, MAX_FILENAME_LENGTH); + + if (dirnameLength >= MAX_FILENAME_LENGTH) { + return false; + } + + // Make sure the entry doesn't already exist + foundFile = _FAT_directory_getFirstEntry (partition, &tempEntry, dirCluster); + + while (foundFile) { // It hasn't already found the file + // Check if the filename matches + if ((dirnameLength == strnlen(tempEntry.filename, MAX_FILENAME_LENGTH)) + && (_FAT_directory_mbsncasecmp(name, tempEntry.filename, dirnameLength) == 0)) { + return true; + } + + // Check if the alias matches + _FAT_directory_entryGetAlias (tempEntry.entryData, alias); + if ((strncasecmp(name, alias, MAX_ALIAS_LENGTH) == 0)) { + return true; + } + foundFile = _FAT_directory_getNextEntry (partition, &tempEntry); + } + return false; +} + +/* +Creates an alias for a long file name. If the alias is not an exact match for the +filename, it returns the number of characters in the alias. If the two names match, +it returns 0. If there was an error, it returns -1. +*/ +static int _FAT_directory_createAlias (char* alias, const char* lfn) { + bool lossyConversion = false; // Set when the alias had to be modified to be valid + int lfnPos = 0; + int aliasPos = 0; + wchar_t lfnChar; + int oemChar; + mbstate_t ps = {0}; + int bytesUsed = 0; + const char* lfnExt; + int aliasExtLen; + + // Strip leading periods + while (lfn[lfnPos] == '.') { + lfnPos ++; + lossyConversion = true; + } + + // Primary portion of alias + while (aliasPos < 8 && lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { + bytesUsed = mbrtowc(&lfnChar, lfn + lfnPos, MAX_FILENAME_LENGTH - lfnPos, &ps); + if (bytesUsed < 0) { + return -1; + } + oemChar = wctob(towupper((wint_t)lfnChar)); + if (wctob((wint_t)lfnChar) != oemChar) { + // Case of letter was changed + lossyConversion = true; + } + if (oemChar == ' ') { + // Skip spaces in filename + lossyConversion = true; + lfnPos += bytesUsed; + continue; + } + if (oemChar == EOF) { + oemChar = '_'; // Replace unconvertable characters with underscores + lossyConversion = true; + } + if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { + // Invalid Alias character + oemChar = '_'; // Replace illegal characters with underscores + lossyConversion = true; + } + + alias[aliasPos] = (char)oemChar; + aliasPos++; + lfnPos += bytesUsed; + } + + if (lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { + // Name was more than 8 characters long + lossyConversion = true; + } + + // Alias extension + lfnExt = strrchr (lfn, '.'); + if (lfnExt != NULL && lfnExt != strchr (lfn, '.')) { + // More than one period in name + lossyConversion = true; + } + if (lfnExt != NULL && lfnExt[1] != '\0') { + lfnExt++; + alias[aliasPos] = '.'; + aliasPos++; + memset (&ps, 0, sizeof(ps)); + for (aliasExtLen = 0; aliasExtLen < MAX_ALIAS_EXT_LENGTH && *lfnExt != '\0'; aliasExtLen++) { + bytesUsed = mbrtowc(&lfnChar, lfnExt, MAX_FILENAME_LENGTH - lfnPos, &ps); + if (bytesUsed < 0) { + return -1; + } + oemChar = wctob(towupper((wint_t)lfnChar)); + if (wctob((wint_t)lfnChar) != oemChar) { + // Case of letter was changed + lossyConversion = true; + } + if (oemChar == ' ') { + // Skip spaces in alias + lossyConversion = true; + lfnExt += bytesUsed; + continue; + } + if (oemChar == EOF) { + oemChar = '_'; // Replace unconvertable characters with underscores + lossyConversion = true; + } + if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { + // Invalid Alias character + oemChar = '_'; // Replace illegal characters with underscores + lossyConversion = true; + } + + alias[aliasPos] = (char)oemChar; + aliasPos++; + lfnExt += bytesUsed; + } + if (*lfnExt != '\0') { + // Extension was more than 3 characters long + lossyConversion = true; + } + } + + alias[aliasPos] = '\0'; + if (lossyConversion) { + return aliasPos; + } else { + return 0; + } +} + +bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { + size_t entrySize; + uint8_t lfnEntry[DIR_ENTRY_DATA_SIZE]; + int i,j; // Must be signed for use when decrementing in for loop + char *tmpCharPtr; + DIR_ENTRY_POSITION curEntryPos; + bool entryStillValid; + uint8_t aliasCheckSum = 0; + char alias [MAX_ALIAS_LENGTH]; + int aliasLen; + int lfnLen; + + // Make sure the filename is not 0 length + if (strnlen (entry->filename, MAX_FILENAME_LENGTH) < 1) { + return false; + } + + // Make sure the filename is at least a valid LFN + lfnLen = _FAT_directory_lfnLength (entry->filename); + if (lfnLen < 0) { + return false; + } + + // Remove trailing spaces + for (i = strlen (entry->filename) - 1; (i > 0) && (entry->filename[i] == ' '); --i) { + entry->filename[i] = '\0'; + } + // Remove leading spaces + for (i = 0; (i < (int)strlen (entry->filename)) && (entry->filename[i] == ' '); ++i) ; + if (i > 0) { + memmove (entry->filename, entry->filename + i, strlen (entry->filename + i)); + } + + // Remove junk in filename + i = strlen (entry->filename); + memset (entry->filename + i, '\0', MAX_FILENAME_LENGTH - i); + + // Make sure the entry doesn't already exist + if (_FAT_directory_entryExists (partition, entry->filename, dirCluster)) { + return false; + } + + // Clear out alias, so we can generate a new one + memset (entry->entryData, ' ', 11); + + if ( strncmp(entry->filename, ".", MAX_FILENAME_LENGTH) == 0) { + // "." entry + entry->entryData[0] = '.'; + entrySize = 1; + } else if ( strncmp(entry->filename, "..", MAX_FILENAME_LENGTH) == 0) { + // ".." entry + entry->entryData[0] = '.'; + entry->entryData[1] = '.'; + entrySize = 1; + } else { + // Normal file name + aliasLen = _FAT_directory_createAlias (alias, entry->filename); + if (aliasLen < 0) { + return false; + } else if (aliasLen == 0) { + // It's a normal short filename + entrySize = 1; + } else { + // It's a long filename with an alias + entrySize = ((lfnLen + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1; + + // Generate full alias for all cases except when the alias is simply an upper case version of the LFN + // and there isn't already a file with that name + if (strncasecmp (alias, entry->filename, MAX_ALIAS_LENGTH) != 0 || + _FAT_directory_entryExists (partition, alias, dirCluster)) + { + // expand primary part to 8 characters long by padding the end with underscores + i = MAX_ALIAS_PRI_LENGTH - 1; + // Move extension to last 3 characters + while (alias[i] != '.' && i > 0) i--; + if (i > 0) { + j = MAX_ALIAS_LENGTH - MAX_ALIAS_EXT_LENGTH - 2; // 1 char for '.', one for NUL, 3 for extension + memmove (alias + j, alias + i, strlen(alias) - i); + // Pad primary component + memset (alias + i, '_', j - i); + alias[MAX_ALIAS_LENGTH-1]=0; + } + + // Generate numeric tail + for (i = 1; i <= MAX_NUMERIC_TAIL; i++) { + j = i; + tmpCharPtr = alias + MAX_ALIAS_PRI_LENGTH - 1; + while (j > 0) { + *tmpCharPtr = '0' + (j % 10); // ASCII numeric value + tmpCharPtr--; + j /= 10; + } + *tmpCharPtr = '~'; + if (!_FAT_directory_entryExists (partition, alias, dirCluster)) { + break; + } + } + if (i > MAX_NUMERIC_TAIL) { + // Couldn't get a valid alias + return false; + } + } + } + + // Copy alias or short file name into directory entry data + for (i = 0, j = 0; (j < 8) && (alias[i] != '.') && (alias[i] != '\0'); i++, j++) { + entry->entryData[j] = alias[i]; + } + while (j < 8) { + entry->entryData[j] = ' '; + ++ j; + } + if (alias[i] == '.') { + // Copy extension + ++ i; + while ((alias[i] != '\0') && (j < 11)) { + entry->entryData[j] = alias[i]; + ++ i; + ++ j; + } + } + while (j < 11) { + entry->entryData[j] = ' '; + ++ j; + } + + // Generate alias checksum + for (i=0; i < ALIAS_ENTRY_LENGTH; i++) { + // NOTE: The operation is an unsigned char rotate right + aliasCheckSum = ((aliasCheckSum & 1) ? 0x80 : 0) + (aliasCheckSum >> 1) + entry->entryData[i]; + } + } + + // Find or create space for the entry + if (_FAT_directory_findEntryGap (partition, entry, dirCluster, entrySize) == false) { + return false; + } + + // Write out directory entry + curEntryPos = entry->dataStart; + + { + // lfn is only pushed onto the stack here, reducing overall stack usage + ucs2_t lfn[MAX_LFN_LENGTH] = {0}; + _FAT_directory_mbstoucs2 (lfn, entry->filename, MAX_LFN_LENGTH); + + for (entryStillValid = true, i = entrySize; entryStillValid && i > 0; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &curEntryPos, false), -- i ) + { + if (i > 1) { + // Long filename entry + lfnEntry[LFN_offset_ordinal] = (i - 1) | ((size_t)i == entrySize ? LFN_END : 0); + for (j = 0; j < 13; j++) { + if (lfn [(i - 2) * 13 + j] == '\0') { + if ((j > 1) && (lfn [(i - 2) * 13 + (j-1)] == '\0')) { + u16_to_u8array (lfnEntry, LFN_offset_table[j], 0xffff); // Padding + } else { + u16_to_u8array (lfnEntry, LFN_offset_table[j], 0x0000); // Terminating null character + } + } else { + u16_to_u8array (lfnEntry, LFN_offset_table[j], lfn [(i - 2) * 13 + j]); + } + } + + lfnEntry[LFN_offset_checkSum] = aliasCheckSum; + lfnEntry[LFN_offset_flag] = ATTRIB_LFN; + lfnEntry[LFN_offset_reserved1] = 0; + u16_to_u8array (lfnEntry, LFN_offset_reserved2, 0); + _FAT_cache_writePartialSector (partition->cache, lfnEntry, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } else { + // Alias & file data + _FAT_cache_writePartialSector (partition->cache, entry->entryData, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } + } + } + + return true; +} + +bool _FAT_directory_chdir (PARTITION* partition, const char* path) { + DIR_ENTRY entry; + + if (!_FAT_directory_entryFromPath (partition, &entry, path, NULL)) { + return false; + } + + if (!(entry.entryData[DIR_ENTRY_attributes] & ATTRIB_DIR)) { + return false; + } + + partition->cwdCluster = _FAT_directory_entryGetCluster (partition, entry.entryData); + + return true; +} + +void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st) { + // Fill in the stat struct + // Some of the values are faked for the sake of compatibility + st->st_dev = _FAT_disc_hostType(partition->disc); // The device is the 32bit ioType value + st->st_ino = (ino_t)(_FAT_directory_entryGetCluster(partition, entry->entryData)); // The file serial number is the start cluster + st->st_mode = (_FAT_directory_isDirectory(entry) ? S_IFDIR : S_IFREG) | + (S_IRUSR | S_IRGRP | S_IROTH) | + (_FAT_directory_isWritable (entry) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Mode bits based on dirEntry ATTRIB byte + st->st_nlink = 1; // Always one hard link on a FAT file + st->st_uid = 1; // Faked for FAT + st->st_gid = 2; // Faked for FAT + st->st_rdev = st->st_dev; + st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size + st->st_atime = _FAT_filetime_to_time_t ( + 0, + u8array_to_u16 (entry->entryData, DIR_ENTRY_aDate) + ); + st->st_spare1 = 0; + st->st_mtime = _FAT_filetime_to_time_t ( + u8array_to_u16 (entry->entryData, DIR_ENTRY_mTime), + u8array_to_u16 (entry->entryData, DIR_ENTRY_mDate) + ); + st->st_spare2 = 0; + st->st_ctime = _FAT_filetime_to_time_t ( + u8array_to_u16 (entry->entryData, DIR_ENTRY_cTime), + u8array_to_u16 (entry->entryData, DIR_ENTRY_cDate) + ); + st->st_spare3 = 0; + st->st_blksize = BYTES_PER_READ; // Prefered file I/O block size + st->st_blocks = (st->st_size + BYTES_PER_READ - 1) / BYTES_PER_READ; // File size in blocks + st->st_spare4[0] = 0; + st->st_spare4[1] = 0; +} diff --git a/source/libfat/directory.h b/source/libfat/directory.h new file mode 100644 index 00000000..02bd82ff --- /dev/null +++ b/source/libfat/directory.h @@ -0,0 +1,173 @@ +/* + 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 + +#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 diff --git a/source/libfat/disc_fat.c b/source/libfat/disc_fat.c new file mode 100644 index 00000000..f6753d7a --- /dev/null +++ b/source/libfat/disc_fat.c @@ -0,0 +1,67 @@ +/* + 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 +#include +#include + +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} +}; + diff --git a/source/libfat/disc_fat.h b/source/libfat/disc_fat.h new file mode 100644 index 00000000..14a323b2 --- /dev/null +++ b/source/libfat/disc_fat.h @@ -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 diff --git a/source/libfat/fat.h b/source/libfat/fat.h new file mode 100644 index 00000000..d7b11176 --- /dev/null +++ b/source/libfat/fat.h @@ -0,0 +1,80 @@ +/* + 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 +#include + +/* +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 diff --git a/source/libfat/fatdir.c b/source/libfat/fatdir.c new file mode 100644 index 00000000..55faa0b9 --- /dev/null +++ b/source/libfat/fatdir.c @@ -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 +#include +#include +#include +#include + +#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; +} diff --git a/source/libfat/fatdir.h b/source/libfat/fatdir.h new file mode 100644 index 00000000..1b47a914 --- /dev/null +++ b/source/libfat/fatdir.h @@ -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 +#include +#include +#include +#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 diff --git a/source/libfat/fatfile.c b/source/libfat/fatfile.c new file mode 100644 index 00000000..23d44ddc --- /dev/null +++ b/source/libfat/fatfile.c @@ -0,0 +1,1131 @@ +/* + fatfile.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. + + 2009-10-23 oggzee: fixes for cluster aligned file size (write, truncate, seek) +*/ + + +#include "fatfile.h" + +#include +#include +#include +#include +#include + +#include "cache.h" +#include "file_allocation_table.h" +#include "bit_ops.h" +#include "filetime.h" +#include "lock.h" + +int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { + PARTITION* partition = NULL; + bool fileExists; + DIR_ENTRY dirEntry; + const char* pathEnd; + uint32_t dirCluster; + FILE_STRUCT* file = (FILE_STRUCT*) fileStruct; + 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; + } + + // Determine which mode the file is openned for + if ((flags & 0x03) == O_RDONLY) { + // Open the file for read-only access + file->read = true; + file->write = false; + file->append = false; + } else if ((flags & 0x03) == O_WRONLY) { + // Open file for write only access + file->read = false; + file->write = true; + file->append = false; + } else if ((flags & 0x03) == O_RDWR) { + // Open file for read/write access + file->read = true; + file->write = true; + file->append = false; + } else { + r->_errno = EACCES; + return -1; + } + + // Make sure we aren't trying to write to a read-only disc + if (file->write && partition->readOnly) { + r->_errno = EROFS; + return -1; + } + + // Search for the file on the disc + _FAT_lock(&partition->lock); + fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL); + + // The file shouldn't exist if we are trying to create it + if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists) { + _FAT_unlock(&partition->lock); + r->_errno = EEXIST; + return -1; + } + + // It should not be a directory if we're openning a file, + if (fileExists && _FAT_directory_isDirectory(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EISDIR; + return -1; + } + + // We haven't modified the file yet + file->modified = false; + + // If the file doesn't exist, create it if we're allowed to + if (!fileExists) { + if (flags & O_CREAT) { + if (partition->readOnly) { + // We can't write to a read-only partition + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + // Create the file + // Get the directory it has to go in + pathEnd = strrchr (path, DIR_SEPARATOR); + if (pathEnd == NULL) { + // No path was specified + dirCluster = partition->cwdCluster; + pathEnd = path; + } else { + // Path was specified -- get the right dirCluster + // 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; + } + dirCluster = _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()); + + if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + + // File entry is modified + file->modified = true; + } else { + // file doesn't exist, and we aren't creating it + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + } + + file->filesize = u8array_to_u32 (dirEntry.entryData, DIR_ENTRY_fileSize); + + /* Allow LARGEFILEs with undefined results + // Make sure that the file size can fit in the available space + if (!(flags & O_LARGEFILE) && (file->filesize >= (1<<31))) { + r->_errno = EFBIG; + return -1; + } + */ + + // Make sure we aren't trying to write to a read-only file + if (file->write && !_FAT_directory_isWritable(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + + // Associate this file with a particular partition + file->partition = partition; + + file->startCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + + // Truncate the file if requested + if ((flags & O_TRUNC) && file->write && (file->startCluster != 0)) { + _FAT_fat_clearLinks (partition, file->startCluster); + file->startCluster = CLUSTER_FREE; + file->filesize = 0; + // File is modified since we just cut it all off + file->modified = true; + } + + // Remember the position of this file's directory entry + file->dirEntryStart = dirEntry.dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN + file->dirEntryEnd = dirEntry.dataEnd; + + // Reset read/write pointer + file->currentPosition = 0; + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + + if (flags & O_APPEND) { + file->append = true; + + // Set append pointer to the end of the file + file->appendPosition.cluster = _FAT_fat_lastCluster (partition, file->startCluster); + file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ; + file->appendPosition.byte = file->filesize % BYTES_PER_READ; + + // Check if the end of the file is on the end of a cluster + if ( (file->filesize > 0) && ((file->filesize % partition->bytesPerCluster)==0) ){ + // Set flag to allocate a new cluster + file->appendPosition.sector = partition->sectorsPerCluster; + file->appendPosition.byte = 0; + } + } else { + file->append = false; + // Use something sane for the append pointer, so the whole file struct contains known values + file->appendPosition = file->rwPosition; + } + + file->inUse = true; + + // Insert this file into the double-linked list of open files + partition->openFileCount += 1; + if (partition->firstOpenFile) { + file->nextOpenFile = partition->firstOpenFile; + partition->firstOpenFile->prevOpenFile = file; + } else { + file->nextOpenFile = NULL; + } + file->prevOpenFile = NULL; + partition->firstOpenFile = file; + + _FAT_unlock(&partition->lock); + + return (int) file; +} + +/* +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. +*/ +int _FAT_syncToDisc (FILE_STRUCT* file) { + uint8_t dirEntryData[DIR_ENTRY_DATA_SIZE]; + + if (!file || !file->inUse) { + return EBADF; + } + + if (file->write && file->modified) { + // Load the old entry + _FAT_cache_readPartialSector (file->partition->cache, dirEntryData, + _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector, + file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Write new data to the directory entry + // File size + u32_to_u8array (dirEntryData, DIR_ENTRY_fileSize, file->filesize); + + // Start cluster + u16_to_u8array (dirEntryData, DIR_ENTRY_cluster, file->startCluster); + u16_to_u8array (dirEntryData, DIR_ENTRY_clusterHigh, file->startCluster >> 16); + + // Modification time and date + u16_to_u8array (dirEntryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC()); + + // Access date + u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC()); + + // Set archive attribute + dirEntryData[DIR_ENTRY_attributes] |= ATTRIB_ARCH; + + // Write the new entry + _FAT_cache_writePartialSector (file->partition->cache, dirEntryData, + _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector, + file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush(file->partition->cache)) { + return EIO; + } + } + + file->modified = false; + + return 0; +} + + +int _FAT_close_r (struct _reent *r, int fd) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + int ret = 0; + + if (!file->inUse) { + r->_errno = EBADF; + return -1; + } + + _FAT_lock(&file->partition->lock); + + if (file->write) { + ret = _FAT_syncToDisc (file); + if (ret != 0) { + r->_errno = ret; + ret = -1; + } + } + + file->inUse = false; + + // Remove this file from the double-linked list of open files + file->partition->openFileCount -= 1; + if (file->nextOpenFile) { + file->nextOpenFile->prevOpenFile = file->prevOpenFile; + } + if (file->prevOpenFile) { + file->prevOpenFile->nextOpenFile = file->nextOpenFile; + } else { + file->partition->firstOpenFile = file->nextOpenFile; + } + + _FAT_unlock(&file->partition->lock); + + return ret; +} + +ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + CACHE* cache; + FILE_POSITION position; + uint32_t tempNextCluster; + unsigned int tempVar; + size_t remain; + bool flagNoError = true; + + // Short circuit cases where len is 0 (or less) + if (len <= 0) { + return 0; + } + + // Make sure we can actually read from the file + if ((file == NULL) || !file->inUse || !file->read) { + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + // Don't try to read if the read pointer is past the end of file + if (file->currentPosition >= file->filesize || file->startCluster == CLUSTER_FREE) { + r->_errno = EOVERFLOW; + _FAT_unlock(&partition->lock); + return 0; + } + + // Don't read past end of file + if (len + file->currentPosition > file->filesize) { + r->_errno = EOVERFLOW; + len = file->filesize - file->currentPosition; + } + + remain = len; + position = file->rwPosition; + cache = file->partition->cache; + + // Align to sector + tempVar = BYTES_PER_READ - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < BYTES_PER_READ) && flagNoError) + { + _FAT_cache_readPartialSector ( cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, + position.byte, tempVar); + + remain -= tempVar; + ptr += tempVar; + + position.byte += tempVar; + if (position.byte >= BYTES_PER_READ) { + position.byte = 0; + position.sector++; + } + } + + // align to cluster + // tempVar is number of sectors to read + if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / BYTES_PER_READ; + } + + if ((tempVar > 0) && flagNoError) { + if (! _FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, + tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * BYTES_PER_READ; + remain -= tempVar * BYTES_PER_READ; + position.sector += tempVar; + } + } + + // Move onto next cluster + // It should get to here without reading anything if a cluster is due to be allocated + if ((position.sector >= partition->sectorsPerCluster) && flagNoError) { + tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster); + if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) { + position.sector = partition->sectorsPerCluster; + } else if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + r->_errno = EIO; + flagNoError = false; + } else { + position.sector = 0; + position.cluster = tempNextCluster; + } + } + + // Read in whole clusters, contiguous blocks at a time + while ((remain >= partition->bytesPerCluster) && flagNoError) { + uint32_t chunkEnd; + uint32_t nextChunkStart = position.cluster; + size_t chunkSize = 0; + + do { + chunkEnd = nextChunkStart; + nextChunkStart = _FAT_fat_nextCluster (partition, chunkEnd); + chunkSize += partition->bytesPerCluster; + } while ((nextChunkStart == chunkEnd + 1) && +#ifdef LIMIT_SECTORS + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) && +#endif + (chunkSize + partition->bytesPerCluster <= remain)); + + if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), + chunkSize / BYTES_PER_READ, ptr)) + { + flagNoError = false; + r->_errno = EIO; + break; + } + ptr += chunkSize; + remain -= chunkSize; + + // Advance to next cluster + if ((remain == 0) && (nextChunkStart == CLUSTER_EOF)) { + position.sector = partition->sectorsPerCluster; + position.cluster = chunkEnd; + } else if (!_FAT_fat_isValidCluster(partition, nextChunkStart)) { + r->_errno = EIO; + flagNoError = false; + } else { + position.sector = 0; + position.cluster = nextChunkStart; + } + } + + // Read remaining sectors + tempVar = remain / BYTES_PER_READ; // Number of sectors left + if ((tempVar > 0) && flagNoError) { + if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), + tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * BYTES_PER_READ; + remain -= tempVar * BYTES_PER_READ; + position.sector += tempVar; + } + } + + // Last remaining sector + // Check if anything is left + if ((remain > 0) && flagNoError) { + _FAT_cache_readPartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + position.byte += remain; + remain = 0; + } + + // Length read is the wanted length minus the stuff not read + len = len - remain; + + // Update file information + file->rwPosition = position; + file->currentPosition += len; + + _FAT_unlock(&partition->lock); + return len; +} + +// if current position is on the cluster border and more data has to be written +// then get next cluster or allocate next cluster +// this solves the over-allocation problems when file size is aligned to cluster size +// return true on succes, false on error +static bool _FAT_check_position_for_next_cluster(struct _reent *r, + FILE_POSITION *position, PARTITION* partition, size_t remain, bool *flagNoError) +{ + uint32_t tempNextCluster; + // do nothing if no more data to write + if (remain == 0) return true; + if (flagNoError && *flagNoError == false) return false; + if ((remain < 0) || (position->sector > partition->sectorsPerCluster)) { + // invalid arguments - internal error + r->_errno = EINVAL; + goto err; + } + if (position->sector == partition->sectorsPerCluster) { + // need to advance to next cluster + tempNextCluster = _FAT_fat_nextCluster(partition, position->cluster); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) { + // Ran out of clusters so get a new one + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position->cluster); + } + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + goto err; + } + position->sector = 0; + position->cluster = tempNextCluster; + } + return true; +err: + if (flagNoError) *flagNoError = false; + return false; +} + +/* +Extend a file so that the size is the same as the rwPosition +*/ +static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { + PARTITION* partition = file->partition; + CACHE* cache = file->partition->cache; + FILE_POSITION position; + uint8_t zeroBuffer [BYTES_PER_READ] = {0}; + uint32_t remain; + uint32_t tempNextCluster; + unsigned int sector; + + position.byte = file->filesize % BYTES_PER_READ; + position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ; + // It is assumed that there is always a startCluster + // This will be true when _FAT_file_extend_r is called from _FAT_write_r + position.cluster = _FAT_fat_lastCluster (partition, file->startCluster); + + remain = file->currentPosition - file->filesize; + + if ((remain > 0) && (file->filesize > 0) && (position.sector == 0) && (position.byte == 0)) { + // Get a new cluster on the edge of a cluster boundary + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + return false; + } + position.cluster = tempNextCluster; + position.sector = 0; + } + + if (remain + position.byte < BYTES_PER_READ) { + // Only need to clear to the end of the sector + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain); + position.byte += remain; + } else { + if (position.byte > 0) { + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, + BYTES_PER_READ - position.byte); + remain -= (BYTES_PER_READ - position.byte); + position.byte = 0; + position.sector ++; + } + + while (remain >= BYTES_PER_READ) { + if (position.sector >= partition->sectorsPerCluster) { + position.sector = 0; + // Ran out of clusters so get a new one + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + return false; + } + position.cluster = tempNextCluster; + } + + sector = _FAT_fat_clusterToSector (partition, position.cluster) + position.sector; + _FAT_cache_writeSectors (cache, sector, 1, zeroBuffer); + + remain -= BYTES_PER_READ; + position.sector ++; + } + + if (!_FAT_check_position_for_next_cluster(r, &position, partition, remain, NULL)) { + // error already marked + return false; + } + + if (remain > 0) { + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + position.byte = remain; + } + } + + file->rwPosition = position; + file->filesize = file->currentPosition; + return true; +} + +ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + CACHE* cache; + FILE_POSITION position; + uint32_t tempNextCluster; + unsigned int tempVar; + size_t remain; + bool flagNoError = true; + bool flagAppending = false; + + // Make sure we can actually write to the file + if ((file == NULL) || !file->inUse || !file->write) { + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + cache = file->partition->cache; + _FAT_lock(&partition->lock); + + // Only write up to the maximum file size, taking into account wrap-around of ints + if (remain + file->filesize > FILE_MAX_SIZE || len + file->filesize < file->filesize) { + len = FILE_MAX_SIZE - file->filesize; + } + remain = len; + + // Short circuit cases where len is 0 (or less) + if (len <= 0) { + _FAT_unlock(&partition->lock); + return 0; + } + + // Get a new cluster for the start of the file if required + if (file->startCluster == CLUSTER_FREE) { + tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort immediately + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + file->startCluster = tempNextCluster; + + // Appending starts at the begining for a 0 byte file + file->appendPosition.cluster = file->startCluster; + file->appendPosition.sector = 0; + file->appendPosition.byte = 0; + + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + } + + if (file->append) { + position = file->appendPosition; + flagAppending = true; + } else { + // If the write pointer is past the end of the file, extend the file to that size + if (file->currentPosition > file->filesize) { + if (!_FAT_file_extend_r (r, file)) { + _FAT_unlock(&partition->lock); + return -1; + } + } + + // Write at current read pointer + position = file->rwPosition; + + // If it is writing past the current end of file, set appending flag + if (len + file->currentPosition > file->filesize) { + flagAppending = true; + } + } + + // Move onto next cluster if needed + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + + // Align to sector + tempVar = BYTES_PER_READ - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < BYTES_PER_READ) && flagNoError) { + // Write partial sector to disk + _FAT_cache_writePartialSector (cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar); + + remain -= tempVar; + ptr += tempVar; + position.byte += tempVar; + + + // Move onto next sector + if (position.byte >= BYTES_PER_READ) { + position.byte = 0; + position.sector ++; + } + } + + // Align to cluster + // tempVar is number of sectors to write + if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / BYTES_PER_READ; + } + + if ((tempVar > 0 && tempVar < partition->sectorsPerCluster) && flagNoError) { + if (!_FAT_cache_writeSectors (cache, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * BYTES_PER_READ; + remain -= tempVar * BYTES_PER_READ; + position.sector += tempVar; + } + } + + // Write whole clusters + while ((remain >= partition->bytesPerCluster) && flagNoError) { + // allocate next cluster + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + if (!flagNoError) break; + // set indexes to the current position + uint32_t chunkEnd = position.cluster; + uint32_t nextChunkStart = position.cluster; + size_t chunkSize = partition->bytesPerCluster; + FILE_POSITION next_position = position; + + // group consecutive clusters + while (flagNoError && +#ifdef LIMIT_SECTORS + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) && +#endif + (chunkSize + partition->bytesPerCluster < remain)) + { + // pretend to use up all sectors in next_position + next_position.sector = partition->sectorsPerCluster; + // get or allocate next cluster + _FAT_check_position_for_next_cluster(r, &next_position, partition, + remain - chunkSize, &flagNoError); + if (!flagNoError) break; // exit loop on error + nextChunkStart = next_position.cluster; + if (nextChunkStart != chunkEnd + 1) break; // exit loop if not consecutive + chunkEnd = nextChunkStart; + chunkSize += partition->bytesPerCluster; + } + + if ( !_FAT_cache_writeSectors (cache, + _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / BYTES_PER_READ, ptr)) + { + flagNoError = false; + r->_errno = EIO; + break; + } + ptr += chunkSize; + remain -= chunkSize; + + if ((chunkEnd != nextChunkStart) && _FAT_fat_isValidCluster(partition, nextChunkStart)) { + // new cluster is already allocated (because it was not consecutive) + position.cluster = nextChunkStart; + position.sector = 0; + } else { + // Allocate a new cluster when next writing the file + position.cluster = chunkEnd; + position.sector = partition->sectorsPerCluster; + } + } + + // allocate next cluster if needed + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + + // Write remaining sectors + tempVar = remain / BYTES_PER_READ; // Number of sectors left + if ((tempVar > 0) && flagNoError) { + if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * BYTES_PER_READ; + remain -= tempVar * BYTES_PER_READ; + position.sector += tempVar; + } + } + + // Last remaining sector + if ((remain > 0) && flagNoError) { + if (flagAppending) { + _FAT_cache_eraseWritePartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + } else { + _FAT_cache_writePartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + } + position.byte += remain; + remain = 0; + } + + + // Amount written is the originally requested amount minus stuff remaining + len = len - remain; + + // Update file information + file->modified = true; + if (file->append) { + // Appending doesn't affect the read pointer + file->appendPosition = position; + file->filesize += len; + } else { + // Writing also shifts the read pointer + file->rwPosition = position; + file->currentPosition += len; + if (file->filesize < file->currentPosition) { + file->filesize = file->currentPosition; + } + } + _FAT_unlock(&partition->lock); + + return len; +} + + +off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + uint32_t cluster, nextCluster; + int clusCount; + off_t newPosition; + uint32_t position; + + if ((file == NULL) || (file->inUse == false)) { + // invalid file + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + switch (dir) { + case SEEK_SET: + newPosition = pos; + break; + case SEEK_CUR: + newPosition = (off_t)file->currentPosition + pos; + break; + case SEEK_END: + newPosition = (off_t)file->filesize + pos; + break; + default: + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + if ((pos > 0) && (newPosition < 0)) { + _FAT_unlock(&partition->lock); + r->_errno = EOVERFLOW; + return -1; + } + + // newPosition can only be larger than the FILE_MAX_SIZE on platforms where + // off_t is larger than 32 bits. + if (newPosition < 0 || ((sizeof(newPosition) > 4) && newPosition > (off_t)FILE_MAX_SIZE)) { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + position = (uint32_t)newPosition; + + // Only change the read/write position if it is within the bounds of the current filesize, + // or at the very edge of the file + if (position <= file->filesize && file->startCluster != CLUSTER_FREE) { + // Calculate where the correct cluster is + // how many clusters from start of file + clusCount = position / partition->bytesPerCluster; + cluster = file->startCluster; + if (position >= file->currentPosition) { + // start from current cluster + int currentCount = file->currentPosition / partition->bytesPerCluster; + if (file->rwPosition.sector == partition->sectorsPerCluster) { + currentCount--; + } + clusCount -= currentCount; + cluster = file->rwPosition.cluster; + } + // Calculate the sector and byte of the current position, + // and store them + file->rwPosition.sector = (position % partition->bytesPerCluster) / BYTES_PER_READ; + file->rwPosition.byte = position % BYTES_PER_READ; + + nextCluster = _FAT_fat_nextCluster (partition, cluster); + while ((clusCount > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) { + clusCount--; + cluster = nextCluster; + nextCluster = _FAT_fat_nextCluster (partition, cluster); + } + + // Check if ran out of clusters and it needs to allocate a new one + if (clusCount > 0) { + if ((clusCount == 1) && (file->filesize == position) && (file->rwPosition.sector == 0)) { + // Set flag to allocate a new cluster + file->rwPosition.sector = partition->sectorsPerCluster; + file->rwPosition.byte = 0; + } else { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + } + + file->rwPosition.cluster = cluster; + } + + // Save position + file->currentPosition = position; + + _FAT_unlock(&partition->lock); + return position; +} + + + +int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + DIR_ENTRY fileEntry; + + if ((file == NULL) || (file->inUse == false)) { + // invalid file + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + // Get the file's entry data + fileEntry.dataStart = file->dirEntryStart; + fileEntry.dataEnd = file->dirEntryEnd; + + if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + // Fill in the stat struct + _FAT_directory_entryStat (partition, &fileEntry, st); + + // Fix stats that have changed since the file was openned + st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster + st->st_size = file->filesize; // File size + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + int ret=0; + uint32_t newSize = (uint32_t)len; + + if (len < 0) { + // Trying to truncate to a negative size + r->_errno = EINVAL; + return -1; + } + + if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE) { + // Trying to extend the file beyond what FAT supports + r->_errno = EFBIG; + return -1; + } + + if (!file || !file->inUse) { + // invalid file + r->_errno = EBADF; + return -1; + } + + if (!file->write) { + // Read-only file + r->_errno = EINVAL; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + if (newSize > file->filesize) { + // Expanding the file + FILE_POSITION savedPosition; + uint32_t savedOffset; + // Get a new cluster for the start of the file if required + if (file->startCluster == CLUSTER_FREE) { + uint32_t tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort immediately + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + file->startCluster = tempNextCluster; + + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + } + // Save the read/write pointer + savedPosition = file->rwPosition; + savedOffset = file->currentPosition; + // Set the position to the new size + file->currentPosition = newSize; + // Extend the file to the new position + if (!_FAT_file_extend_r (r, file)) { + ret = -1; + } + // Set the append position to the new rwPointer + if (file->append) { + file->appendPosition = file->rwPosition; + } + // Restore the old rwPointer; + file->rwPosition = savedPosition; + file->currentPosition = savedOffset; + } else if (newSize < file->filesize){ + // Shrinking the file + if (len == 0) { + // Cutting the file down to nothing, clear all clusters used + _FAT_fat_clearLinks (partition, file->startCluster); + file->startCluster = CLUSTER_FREE; + + file->appendPosition.cluster = CLUSTER_FREE; + file->appendPosition.sector = 0; + file->appendPosition.byte = 0; + } else { + // Trimming the file down to the required size + unsigned int chainLength; + uint32_t lastCluster; + + // Drop the unneeded end of the cluster chain. + // If the end falls on a cluster boundary, drop that cluster too, + // then set a flag to allocate a cluster as needed + chainLength = ((newSize-1) / partition->bytesPerCluster) + 1; + lastCluster = _FAT_fat_trimChain (partition, file->startCluster, chainLength); + + if (file->append) { + file->appendPosition.byte = newSize % BYTES_PER_READ; + // Does the end of the file fall on the edge of a cluster? + if (newSize % partition->bytesPerCluster == 0) { + // Set a flag to allocate a new cluster + file->appendPosition.sector = partition->sectorsPerCluster; + } else { + file->appendPosition.sector = (newSize % partition->bytesPerCluster) / BYTES_PER_READ; + } + file->appendPosition.cluster = lastCluster; + } + } + } else { + // Truncating to same length, so don't do anything + } + + file->filesize = newSize; + file->modified = true; + + _FAT_unlock(&partition->lock); + return ret; +} + +int _FAT_fsync_r (struct _reent *r, int fd) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + int ret = 0; + + if (!file->inUse) { + r->_errno = EBADF; + return -1; + } + + _FAT_lock(&file->partition->lock); + + ret = _FAT_syncToDisc (file); + if (ret != 0) { + r->_errno = ret; + ret = -1; + } + + _FAT_unlock(&file->partition->lock); + + return ret; +} diff --git a/source/libfat/fatfile.h b/source/libfat/fatfile.h new file mode 100644 index 00000000..f53600af --- /dev/null +++ b/source/libfat/fatfile.h @@ -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 +#include + +#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 diff --git a/source/libfat/file_allocation_table.c b/source/libfat/file_allocation_table.c new file mode 100644 index 00000000..639a6374 --- /dev/null +++ b/source/libfat/file_allocation_table.c @@ -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 + +/* +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; +} + diff --git a/source/libfat/file_allocation_table.h b/source/libfat/file_allocation_table.h new file mode 100644 index 00000000..de500496 --- /dev/null +++ b/source/libfat/file_allocation_table.h @@ -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 diff --git a/source/libfat/filetime.c b/source/libfat/filetime.c new file mode 100644 index 00000000..d297bf64 --- /dev/null +++ b/source/libfat/filetime.c @@ -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 +#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); +} diff --git a/source/libfat/filetime.h b/source/libfat/filetime.h new file mode 100644 index 00000000..8ffd539a --- /dev/null +++ b/source/libfat/filetime.h @@ -0,0 +1,41 @@ +/* + filetime.h + 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. +*/ + +#ifndef __FILETIME_H +#define __FILETIME_H + +#include "common.h" +#include + +uint16_t _FAT_filetime_getTimeFromRTC (void); +uint16_t _FAT_filetime_getDateFromRTC (void); + +time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d); + + +#endif // _FILETIME_H diff --git a/source/libfat/libfat.c b/source/libfat/libfat.c new file mode 100644 index 00000000..0b3cbd06 --- /dev/null +++ b/source/libfat/libfat.c @@ -0,0 +1,197 @@ +/* + 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 +#include +#include + +#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); +} + + diff --git a/source/libfat/lock.h b/source/libfat/lock.h new file mode 100644 index 00000000..73b8902b --- /dev/null +++ b/source/libfat/lock.h @@ -0,0 +1,87 @@ +/* + lock.h + + Copyright (c) 2008 Sven Peter + + 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 __LOCK_H +#define __LOCK_H + +#include "common.h" + +#ifdef USE_LWP_LOCK + +static inline void _FAT_lock_init(mutex_t *mutex) +{ + LWP_MutexInit(mutex, false); +} + +static inline void _FAT_lock_deinit(mutex_t *mutex) +{ + LWP_MutexDestroy(*mutex); +} + +static inline void _FAT_lock(mutex_t *mutex) +{ + LWP_MutexLock(*mutex); +} + +static inline void _FAT_unlock(mutex_t *mutex) +{ + LWP_MutexUnlock(*mutex); +} + +#else + +// We still need a blank lock type +#ifndef mutex_t +typedef int mutex_t; +#endif + +static inline void _FAT_lock_init(mutex_t *mutex) +{ + return; +} + +static inline void _FAT_lock_deinit(mutex_t *mutex) +{ + return; +} + +static inline void _FAT_lock(mutex_t *mutex) +{ + return; +} + +static inline void _FAT_unlock(mutex_t *mutex) +{ + return; +} + +#endif // USE_LWP_LOCK + + +#endif // _LOCK_H + diff --git a/source/libfat/mem_allocate.h b/source/libfat/mem_allocate.h new file mode 100644 index 00000000..2d38f9ce --- /dev/null +++ b/source/libfat/mem_allocate.h @@ -0,0 +1,49 @@ +/* + mem_allocate.h + Memory allocation and destruction calls + Replace these calls with custom allocators if + malloc is unavailable + + 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 __MEM_ALLOCATE_H_ +#define __MEM_ALLOCATE_H_ + +#include + +static inline void* _FAT_mem_allocate (size_t size) { + return malloc (size); +} + +static inline void* _FAT_mem_align (size_t size) { + + return memalign (32, size); +} + +static inline void _FAT_mem_free (void* mem) { + free (mem); +} + +#endif // _MEM_ALLOCATE_H diff --git a/source/libfat/partition.c b/source/libfat/partition.c new file mode 100644 index 00000000..955468de --- /dev/null +++ b/source/libfat/partition.c @@ -0,0 +1,312 @@ +/* + 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 +#include +#include + +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; +} diff --git a/source/libfat/partition.h b/source/libfat/partition.h new file mode 100644 index 00000000..7cfb5f91 --- /dev/null +++ b/source/libfat/partition.h @@ -0,0 +1,88 @@ +/* + 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 +} 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 diff --git a/source/listfiles.c b/source/listfiles.c index 5f831d67..8537a789 100644 --- a/source/listfiles.c +++ b/source/listfiles.c @@ -2,12 +2,12 @@ #include #include #include -#include #include #include #include #include "listfiles.h" +#include "libfat/fat.h" static char alldirfiles[300][70]; diff --git a/source/main.cpp b/source/main.cpp index 6d51c940..3ec2450d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -36,7 +36,7 @@ #include "fat.h" #include "gecko.h" #include "svnrev.h" -#include "usbloader/partition.h" +#include "usbloader/partition_usbloader.h" #include "usbloader/usbstorage.h" extern bool geckoinit; diff --git a/source/prompts/PromptWindows.cpp b/source/prompts/PromptWindows.cpp index afbe4cfa..b78c636c 100644 --- a/source/prompts/PromptWindows.cpp +++ b/source/prompts/PromptWindows.cpp @@ -9,7 +9,7 @@ #include "usbloader/wbfs.h" #include "usbloader/wdvd.h" -#include "usbloader/partition.h" +#include "usbloader/partition_usbloader.h" #include "usbloader/usbstorage.h" #include "usbloader/getentries.h" #include "language/gettext.h" diff --git a/source/prompts/PromptWindows.h b/source/prompts/PromptWindows.h index ce22deae..587196d3 100644 --- a/source/prompts/PromptWindows.h +++ b/source/prompts/PromptWindows.h @@ -8,7 +8,7 @@ #ifndef _PROMPTWINDOWS_H_ #define _PROMPTWINDOWS_H_ -#include "usbloader/partition.h" +#include "usbloader/partition_usbloader.h" #define NOTFULLCHANNEL int WindowPrompt(const char *title, const char *msg = NULL, const char *btn1Label = NULL, diff --git a/source/settings/Settings.cpp b/source/settings/Settings.cpp index b9d0ee89..523cb5a8 100644 --- a/source/settings/Settings.cpp +++ b/source/settings/Settings.cpp @@ -17,7 +17,7 @@ #include "listfiles.h" #include "sys.h" #include "cfg.h" -#include "usbloader/partition.h" +#include "usbloader/partition_usbloader.h" #include "usbloader/utils.h" #define MAXOPTIONS 13 diff --git a/source/usbloader/apploader.c b/source/usbloader/apploader.c index 84c63abc..174f1b5d 100644 --- a/source/usbloader/apploader.c +++ b/source/usbloader/apploader.c @@ -256,7 +256,25 @@ void PretendThereIsADiscInTheDrive(void *buffer, u32 len) /** Thanks to WiiPower **/ bool NewSuperMarioBrosPatch(void *Address, int Size) { - if (memcmp("SMN", (char *)0x80000000, 3) == 0) + if (memcmp("SMNE", (char *)0x80000000, 4) == 0) + { + u8 SearchPattern[32] = { 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD7, 0x89, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 }; + u8 PatchData[32] = { 0x4E, 0x80, 0x00, 0x20, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD7, 0x89, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 }; + + void *Addr = Address; + void *Addr_end = Address+Size; + + while(Addr <= Addr_end-sizeof(SearchPattern)) + { + if(memcmp(Addr, SearchPattern, sizeof(SearchPattern))==0) + { + memcpy(Addr,PatchData,sizeof(PatchData)); + return true; + } + Addr += 4; + } + } + else if (memcmp("SMN", (char *)0x80000000, 3) == 0) { u8 SearchPattern[32] = { 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD9, 0x39, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 }; u8 PatchData[32] = { 0x4E, 0x80, 0x00, 0x20, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD9, 0x39, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 }; diff --git a/source/usbloader/partition.c b/source/usbloader/partition_usbloader.c similarity index 95% rename from source/usbloader/partition.c rename to source/usbloader/partition_usbloader.c index 58233185..8ccccd2b 100644 --- a/source/usbloader/partition.c +++ b/source/usbloader/partition_usbloader.c @@ -1,190 +1,190 @@ -#include -#include -#include - -#include "partition.h" -#include "usbstorage.h" -#include "sdhc.h" -#include "utils.h" -#include "libwbfs/libwbfs.h" -#include "wbfs.h" - -/* 'partition table' structure */ -typedef struct { - /* Zero bytes */ - u8 padding[446]; - - /* Partition table entries */ - partitionEntry entries[MAX_PARTITIONS]; -} ATTRIBUTE_PACKED partitionTable; - -s32 Partition_GetEntries(partitionEntry *outbuf, u32 *outval) { - static partitionTable table ATTRIBUTE_ALIGN(32); - - u32 cnt, sector_size; - s32 ret; - - /* Get sector size */ - ret = USBStorage_GetCapacity(§or_size); - if (ret < 0) - return ret; - - /* Read partition table */ - ret = USBStorage_ReadSectors(0, 1, &table); - if (ret < 0) - return ret; - - /* Swap endianess */ - for (cnt = 0; cnt < 4; cnt++) { - partitionEntry *entry = &table.entries[cnt]; - - entry->sector = swap32(entry->sector); - entry->size = swap32(entry->size); - } - - /* Set partition entries */ - memcpy(outbuf, table.entries, sizeof(table.entries)); - - /* Set sector size */ - *outval = sector_size; - - return 0; -} - -s32 Partition_GetEntriesEx(partitionEntry *outbuf, u32 *outval, int *num) -{ - static partitionTable table ATTRIBUTE_ALIGN(32); - partitionEntry *entry; - - u32 i, sector_size; - s32 ret; - int maxpart = *num; - - // Get sector size - ret = USBStorage_GetCapacity(§or_size); - if (ret == 0) return -1; - - u32 ext = 0; - u32 next = 0; - - // Read partition table - ret = USBStorage_ReadSectors(0, 1, &table); - if (!ret) return -1; - /* Swap endianess */ - for (i = 0; i < 4; i++) { - entry = &table.entries[i]; - entry->sector = swap32(entry->sector); - entry->size = swap32(entry->size); - if (!ext && entry->type == 0x0f) ext = entry->sector; - } - /* Set partition entries */ - memcpy(outbuf, table.entries, sizeof(table.entries)); - /* Set sector size */ - *outval = sector_size; - // num primary - *num = 4; - - next = ext; - // scan extended partition for logical - if (ext) for(i=0; isector = swap32(entry->sector); - entry->size = swap32(entry->size); - if (entry->type && entry->size && entry->sector) { - // rebase to abolute address - entry->sector += next; - // add logical - memcpy(&outbuf[*num], entry, sizeof(*entry)); - (*num)++; - // get next - entry++; - if (entry->type && entry->size && entry->sector) { - next = ext + swap32(entry->sector); - } else { - break; - } - } - - } - - return 0; -} - -char* part_type_data(int type) -{ - switch (type) { - case 0x01: return "FAT12"; - case 0x04: return "FAT16"; - case 0x06: return "FAT16"; //+ - case 0x07: return "NTFS"; - case 0x0b: return "FAT32"; - case 0x0c: return "FAT32"; - case 0x0e: return "FAT16"; - case 0x82: return "LxSWP"; - case 0x83: return "LINUX"; - case 0x8e: return "LxLVM"; - case 0xa8: return "OSX"; - case 0xab: return "OSXBT"; - case 0xaf: return "OSXHF"; - case 0xe8: return "LUKS"; - } - return NULL; -} - -int get_fs_type(char *buf) -{ - // WBFS - wbfs_head_t *head = (wbfs_head_t *)buf; - if (head->magic == wbfs_htonl(WBFS_MAGIC)) return FS_TYPE_WBFS; - // 55AA - if (buf[0x1FE] == 0x55 && buf[0x1FF] == 0xAA) { - // FAT - if (memcmp(buf+0x36,"FAT",3) == 0) return FS_TYPE_FAT16; - if (memcmp(buf+0x52,"FAT",3) == 0) return FS_TYPE_FAT32; - // NTFS - if (memcmp(buf+0x03,"NTFS",4) == 0) return FS_TYPE_NTFS; - } - return FS_TYPE_UNK; -} - -bool is_type_fat(int type) -{ - return (type == FS_TYPE_FAT16 || type == FS_TYPE_FAT32); -} - -s32 Partition_GetList(PartList *plist) -{ - partitionEntry *entry = NULL; - PartInfo *pinfo = NULL; - int i, ret; - - memset(plist, 0, sizeof(PartList)); - - // Get partition entries - plist->num = MAX_PARTITIONS_EX; - ret = Partition_GetEntriesEx(plist->pentry, &plist->sector_size, &plist->num); - if (ret < 0) { - return -1; - } - char buf[plist->sector_size]; - - // scan partitions for filesystem type - for (i = 0; i < plist->num; i++) { - pinfo = &plist->pinfo[i]; - entry = &plist->pentry[i]; - if (!entry->size) continue; - if (!part_type_data(entry->type)) continue; - if (!USBStorage_ReadSectors(entry->sector, 1, buf)) continue; - pinfo->fs_type = get_fs_type(buf); - if (pinfo->fs_type == FS_TYPE_WBFS) { - plist->wbfs_n++; - pinfo->wbfs_i = plist->wbfs_n; - } else if (is_type_fat(pinfo->fs_type)) { - plist->fat_n++; - pinfo->fat_i = plist->fat_n; - } - } - return 0; -} +#include +#include +#include + +#include "partition_usbloader.h" +#include "usbstorage.h" +#include "sdhc.h" +#include "utils.h" +#include "libwbfs/libwbfs.h" +#include "wbfs.h" + +/* 'partition table' structure */ +typedef struct { + /* Zero bytes */ + u8 padding[446]; + + /* Partition table entries */ + partitionEntry entries[MAX_PARTITIONS]; +} ATTRIBUTE_PACKED partitionTable; + +s32 Partition_GetEntries(partitionEntry *outbuf, u32 *outval) { + static partitionTable table ATTRIBUTE_ALIGN(32); + + u32 cnt, sector_size; + s32 ret; + + /* Get sector size */ + ret = USBStorage_GetCapacity(§or_size); + if (ret < 0) + return ret; + + /* Read partition table */ + ret = USBStorage_ReadSectors(0, 1, &table); + if (ret < 0) + return ret; + + /* Swap endianess */ + for (cnt = 0; cnt < 4; cnt++) { + partitionEntry *entry = &table.entries[cnt]; + + entry->sector = swap32(entry->sector); + entry->size = swap32(entry->size); + } + + /* Set partition entries */ + memcpy(outbuf, table.entries, sizeof(table.entries)); + + /* Set sector size */ + *outval = sector_size; + + return 0; +} + +s32 Partition_GetEntriesEx(partitionEntry *outbuf, u32 *outval, int *num) +{ + static partitionTable table ATTRIBUTE_ALIGN(32); + partitionEntry *entry; + + u32 i, sector_size; + s32 ret; + int maxpart = *num; + + // Get sector size + ret = USBStorage_GetCapacity(§or_size); + if (ret == 0) return -1; + + u32 ext = 0; + u32 next = 0; + + // Read partition table + ret = USBStorage_ReadSectors(0, 1, &table); + if (!ret) return -1; + /* Swap endianess */ + for (i = 0; i < 4; i++) { + entry = &table.entries[i]; + entry->sector = swap32(entry->sector); + entry->size = swap32(entry->size); + if (!ext && entry->type == 0x0f) ext = entry->sector; + } + /* Set partition entries */ + memcpy(outbuf, table.entries, sizeof(table.entries)); + /* Set sector size */ + *outval = sector_size; + // num primary + *num = 4; + + next = ext; + // scan extended partition for logical + if (ext) for(i=0; isector = swap32(entry->sector); + entry->size = swap32(entry->size); + if (entry->type && entry->size && entry->sector) { + // rebase to abolute address + entry->sector += next; + // add logical + memcpy(&outbuf[*num], entry, sizeof(*entry)); + (*num)++; + // get next + entry++; + if (entry->type && entry->size && entry->sector) { + next = ext + swap32(entry->sector); + } else { + break; + } + } + + } + + return 0; +} + +char* part_type_data(int type) +{ + switch (type) { + case 0x01: return "FAT12"; + case 0x04: return "FAT16"; + case 0x06: return "FAT16"; //+ + case 0x07: return "NTFS"; + case 0x0b: return "FAT32"; + case 0x0c: return "FAT32"; + case 0x0e: return "FAT16"; + case 0x82: return "LxSWP"; + case 0x83: return "LINUX"; + case 0x8e: return "LxLVM"; + case 0xa8: return "OSX"; + case 0xab: return "OSXBT"; + case 0xaf: return "OSXHF"; + case 0xe8: return "LUKS"; + } + return NULL; +} + +int get_fs_type(char *buf) +{ + // WBFS + wbfs_head_t *head = (wbfs_head_t *)buf; + if (head->magic == wbfs_htonl(WBFS_MAGIC)) return FS_TYPE_WBFS; + // 55AA + if (buf[0x1FE] == 0x55 && buf[0x1FF] == 0xAA) { + // FAT + if (memcmp(buf+0x36,"FAT",3) == 0) return FS_TYPE_FAT16; + if (memcmp(buf+0x52,"FAT",3) == 0) return FS_TYPE_FAT32; + // NTFS + if (memcmp(buf+0x03,"NTFS",4) == 0) return FS_TYPE_NTFS; + } + return FS_TYPE_UNK; +} + +bool is_type_fat(int type) +{ + return (type == FS_TYPE_FAT16 || type == FS_TYPE_FAT32); +} + +s32 Partition_GetList(PartList *plist) +{ + partitionEntry *entry = NULL; + PartInfo *pinfo = NULL; + int i, ret; + + memset(plist, 0, sizeof(PartList)); + + // Get partition entries + plist->num = MAX_PARTITIONS_EX; + ret = Partition_GetEntriesEx(plist->pentry, &plist->sector_size, &plist->num); + if (ret < 0) { + return -1; + } + char buf[plist->sector_size]; + + // scan partitions for filesystem type + for (i = 0; i < plist->num; i++) { + pinfo = &plist->pinfo[i]; + entry = &plist->pentry[i]; + if (!entry->size) continue; + if (!part_type_data(entry->type)) continue; + if (!USBStorage_ReadSectors(entry->sector, 1, buf)) continue; + pinfo->fs_type = get_fs_type(buf); + if (pinfo->fs_type == FS_TYPE_WBFS) { + plist->wbfs_n++; + pinfo->wbfs_i = plist->wbfs_n; + } else if (is_type_fat(pinfo->fs_type)) { + plist->fat_n++; + pinfo->fat_i = plist->fat_n; + } + } + return 0; +} diff --git a/source/usbloader/partition.h b/source/usbloader/partition_usbloader.h similarity index 100% rename from source/usbloader/partition.h rename to source/usbloader/partition_usbloader.h diff --git a/source/usbloader/wbfs.c b/source/usbloader/wbfs.c index f1938ac5..c6ff999f 100644 --- a/source/usbloader/wbfs.c +++ b/source/usbloader/wbfs.c @@ -12,7 +12,7 @@ #include "wbfs.h" #include "wbfs_fat.h" #include "fatmounter.h" -#include "partition.h" +#include "partition_usbloader.h" #include "libwbfs/libwbfs.h" diff --git a/source/usbloader/wbfs_fat.c b/source/usbloader/wbfs_fat.c index c1475d3a..62ea2756 100644 --- a/source/usbloader/wbfs_fat.c +++ b/source/usbloader/wbfs_fat.c @@ -18,7 +18,7 @@ #include "wdvd.h" #include "splits.h" #include "fat.h" -#include "partition.h" +#include "partition_usbloader.h" #include "wpad.h" #include "wbfs_fat.h" #include "disc.h" diff --git a/source/wad/title.h b/source/wad/title.h index acde9a2c..7667256f 100644 --- a/source/wad/title.h +++ b/source/wad/title.h @@ -9,7 +9,7 @@ Copyright (C) 2008 tona and/or waninkoko #include #include #include -#include +#include "libfat/fat.h" // Turn upper and lower into a full title ID #define TITLE_ID(x,y) (((u64)(x) << 32) | (y)) @@ -60,7 +60,7 @@ extern "C" { char *__getTitleName(u64 titleid, int language); s32 Uninstall_FromTitle(const u64 tid); - + //check for a game save present on nand based on game ID int CheckForSave(const char *gameID);