diff --git a/Makefile.common b/Makefile.common index 1ba0da7f23..727d1ea7ad 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1624,6 +1624,29 @@ OBJ += gfx/video_filters/2xsai.o \ gfx/video_filters/phosphor2x.o endif +ifeq ($(WANT_IOSUHAX), 1) +DEFINES += -I$(DEPS_DIR)/libiosuhax +CFLAGS += -I$(DEPS_DIR)/libiosuhax +OBJ += $(DEPS_DIR)/libiosuhax/iosuhax.o \ + $(DEPS_DIR)/libiosuhax/iosuhax_devoptab.o \ + $(DEPS_DIR)/libiosuhax/iosuhax_disc_interface.o +endif + +ifeq ($(WANT_LIBFAT), 1) +DEFINES += -I$(DEPS_DIR)/libfat/include +CFLAGS += -I$(DEPS_DIR)/libfat/include +OBJ += $(DEPS_DIR)/libfat/cache.o \ + $(DEPS_DIR)/libfat/directory.o \ + $(DEPS_DIR)/libfat/disc.o \ + $(DEPS_DIR)/libfat/fatdir.o \ + $(DEPS_DIR)/libfat/fatfile.o \ + $(DEPS_DIR)/libfat/file_allocation_table.o \ + $(DEPS_DIR)/libfat/filetime.o \ + $(DEPS_DIR)/libfat/libfat.o \ + $(DEPS_DIR)/libfat/lock.o \ + $(DEPS_DIR)/libfat/partition.o +endif + ifeq ($(HAVE_STATIC_AUDIO_FILTERS), 1) OBJ += libretro-common/audio/dsp_filters/echo.o \ libretro-common/audio/dsp_filters/eq.o \ diff --git a/Makefile.wiiu b/Makefile.wiiu index b3389af1af..99fa1a9c7e 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -73,6 +73,8 @@ else # WANT_IFADDRS = 1 HAVE_STATIC_VIDEO_FILTERS = 1 HAVE_STATIC_AUDIO_FILTERS = 1 + WANT_LIBFAT = 1 + WANT_IOSUHAX = 1 include Makefile.common BLACKLIST := @@ -166,7 +168,15 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -D_GNU_SOURCE LDFLAGS += -Wl,--gc-sections -LIBS := $(WHOLE_START) -lretro_wiiu $(WHOLE_END) -lm -lfat -liosuhax +LIBS := $(WHOLE_START) -lretro_wiiu $(WHOLE_END) -lm + +ifneq ($(WANT_LIBFAT), 1) +LIBS += -lfat +endif + +ifneq ($(WANT_IOSUHAX), 1) +LIBS += -liosuhax +endif RPX_OBJ = wiiu/system/stubs_rpl.o diff --git a/deps/libfat/bit_ops.h b/deps/libfat/bit_ops.h new file mode 100644 index 0000000000..b3d894ec1a --- /dev/null +++ b/deps/libfat/bit_ops.h @@ -0,0 +1,61 @@ +/* + 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/deps/libfat/cache.c b/deps/libfat/cache.c new file mode 100644 index 0000000000..be3ba2e599 --- /dev/null +++ b/deps/libfat/cache.c @@ -0,0 +1,336 @@ +/* + 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.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, unsigned int bytesPerSector) +{ + 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; + cache->bytesPerSector = bytesPerSector; + + + 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 * bytesPerSector ); + } + + 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 = (uint8_t *)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*cache->bytesPerSector),(secs_to_read*cache->bytesPerSector)); + + dest += (secs_to_read*cache->bytesPerSector); + 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 > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->bytesPerSector) + 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 > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->bytesPerSector) + 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 > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->bytesPerSector),0,cache->bytesPerSector); + memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); + + entry->dirty = true; + return true; +} + + +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 = (const uint8_t *)buffer; + + while(numSectors>0) + { + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->bytesPerSector),src,(secs_to_write*cache->bytesPerSector)); + + src += (secs_to_write*cache->bytesPerSector); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + } + 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/deps/libfat/cache.h b/deps/libfat/cache.h new file mode 100644 index 0000000000..07bb14c23e --- /dev/null +++ b/deps/libfat/cache.h @@ -0,0 +1,128 @@ +/* + cache.h + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include "common.h" +#include "disc.h" + +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; + unsigned int bytesPerSector; + 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, cache->bytesPerSector); +} + +/* +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, cache->bytesPerSector); +} + +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, unsigned int bytesPerSector); + +void _FAT_cache_destructor (CACHE* cache); + +#endif // _CACHE_H + diff --git a/deps/libfat/common.h b/deps/libfat/common.h new file mode 100644 index 0000000000..b38889ee5f --- /dev/null +++ b/deps/libfat/common.h @@ -0,0 +1,92 @@ +/* + 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 + +#include +#include +#include + +/* When compiling for NDS, make sure NDS is defined */ +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +/* Platform specific includes */ +#if defined (__wiiu__) + #include + typedef uint8_t u8; + typedef uint16_t u16; + typedef int32_t s32; + typedef uint32_t u32; + typedef int mutex_t; +#elif defined(__gamecube__) || defined (__wii__) + #include + #include + #include +#elif defined(NDS) + #include + #include + #include +#elif defined(GBA) + #include + #include +#endif + +/* Platform specific options */ +#if defined (__wiiu__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 +#if 0 +#define USE_LWP_LOCK +#define USE_RTC_TIME +#endif +#elif defined (__wii__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (__gamecube__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (NDS) + #define DEFAULT_CACHE_PAGES 16 + #define DEFAULT_SECTORS_PAGE 8 + #define USE_RTC_TIME +#elif defined (GBA) + #define DEFAULT_CACHE_PAGES 2 + #define DEFAULT_SECTORS_PAGE 8 + #define LIMIT_SECTORS 128 +#endif + +#endif /* _COMMON_H */ diff --git a/deps/libfat/directory.c b/deps/libfat/directory.c new file mode 100644 index 0000000000..40f896aab4 --- /dev/null +++ b/deps/libfat/directory.c @@ -0,0 +1,1180 @@ +/* + 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 + +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; + int ucsLength; + const char* tempName = name; + size_t nameLength = strnlen(name, NAME_MAX); + + /* Make sure the name is short enough to be valid */ + if ( nameLength >= NAME_MAX) + 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++) + { + unsigned char ch = (unsigned char) name[i]; + if (ch < 0x20 || ch >= 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) +{ + char c; + bool caseInfo; + 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 */ + caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_BASE; + for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) + { + c = entryData[DIR_ENTRY_name + i]; + destName[i] = (caseInfo ? tolower((unsigned char)c) : c); + } + /* Copy the extension from the dirEntry to the string */ + if (entryData[DIR_ENTRY_extension] != ' ') + { + destName[i++] = '.'; + caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_EXT; + for ( j = 0; (j < 3) && (entryData[DIR_ENTRY_extension + j] != ' '); j++) + { + c = entryData[DIR_ENTRY_extension + j]; + destName[i++] = (caseInfo ? tolower((unsigned char)c) : c); + } + } + destName[i] = '\0'; + } + } + + return (destName[0] != '\0'); +} + +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData) +{ + /* Only use high 16 bits of start cluster when we are certain they are correctly defined */ + if (partition->filesysType == FS_FAT32) + return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16); + 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 == partition->bytesPerSector / 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 entryEnd; + uint8_t entryData[0x20]; + ucs2_t lfn[MAX_LFN_LENGTH]; + bool notFound, found; + int lfnPos; + uint8_t chkSum; + bool lfnExists; + int i; + uint8_t lfnChkSum = 0; + DIR_ENTRY_POSITION 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; + break; + } + + _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; + for (i = 0; i < 13; i++) + { + if (lfnPos + i < MAX_LFN_LENGTH - 1) + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } + /* This is a volume name, don't bother with it */ + else if (entryData[DIR_ENTRY_attributes] & ATTRIB_VOL) { } + 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; + /* NOTE: The operation is an unsigned char rotate right */ + for (i=0; i < 11; i++) + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i]; + + if (chkSum != lfnChkSum) + { + lfnExists = false; + entry->filename[0] = '\0'; + } + } + + if (lfnExists) + { + /* Failed to convert the file name to UTF-8. Maybe the wrong locale is set? */ + if (_FAT_directory_ucs2tombs (entry->filename, lfn, NAME_MAX) == (size_t)-1) + return false; + } + else + { + entryStart = entryEnd; + _FAT_directory_entryGetAlias (entryData, entry->filename); + } + found = true; + } + } + + /* If no file is found, return false */ + if (notFound) + return false; + + /* 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', NAME_MAX); + 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_getVolumeLabel (PARTITION* partition, char *label) +{ + DIR_ENTRY entry; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + int i; + bool end; + + _FAT_directory_getRootEntry(partition, &entry); + + entryEnd = entry.dataEnd; + + /* Make sure we are using the correct root directory, in case of FAT32 */ + if (entryEnd.cluster == FAT16_ROOT_DIR_CLUSTER) + entryEnd.cluster = partition->rootDirCluster; + + label[0]='\0'; + label[11]='\0'; + end = false; + + /* this entry should be among the first 3 entries in the root directory table, + * if not, then system can have trouble displaying the right volume label */ + + while(!end) + { + /* error reading */ + if(!_FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE)) + return false; + + if (entryData[DIR_ENTRY_attributes] == ATTRIB_VOL && entryData[0] != DIR_ENTRY_FREE) + { + for (i = 0; i < 11; i++) + label[i] = entryData[DIR_ENTRY_name + i]; + return true; + } + else if (entryData[0] == DIR_ENTRY_LAST) + end = true; + + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) + end = true; + } + return false; +} + +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', NAME_MAX); + + /* 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; + for (i = 0; i < 13; i++) + { + if (lfnPos + i < MAX_LFN_LENGTH - 1) + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } + + if (!entryStillValid) + return false; + + entryStart = entry->dataStart; + 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, NAME_MAX) == (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* nextPathPosition; + uint32_t dirCluster; + bool foundFile; + char alias[MAX_ALIAS_LENGTH]; + const char *pathPosition = path; + bool found = false; + bool notFound = false; + + /* Set pathEnd to the end of the path string */ + if (pathEnd == NULL) + 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; + } + } + /* Start in current working directory */ + else + dirCluster = partition->cwdCluster; + + 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); + + /* The path is too long to bother with */ + if (dirnameLength > NAME_MAX) + return false; + + /* Check for "." or ".." when the dirCluster is root cluster + * These entries do not exist, so we must fake it */ + if ((dirCluster == partition->rootDirCluster) + && ((strncmp(".", pathPosition, dirnameLength) == 0) + || (strncmp("..", pathPosition, dirnameLength) == 0))) + { + foundFile = true; + _FAT_directory_getRootEntry(partition, entry); + } + else + { + /* 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, NAME_MAX)) + && (_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; + + /* Make sure that we aren't trying to follow a file instead of a directory in the path */ + if (found && !(entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && (nextPathPosition != NULL)) + 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; + } + /* Check that we reached the end of the path */ + else if ((nextPathPosition == NULL) || (nextPathPosition >= pathEnd)) + found = true; + else if (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) + { + dirCluster = _FAT_directory_entryGetCluster (partition, entry->entryData); + if (dirCluster == CLUSTER_ROOT) + dirCluster = partition->rootDirCluster; + 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) + { + /* On FAT32 it should specify an actual cluster for the root entry, + * not cluster 0 as on FAT16 */ + if (partition->filesysType == FS_FAT32 && (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && + _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT) + _FAT_directory_getRootEntry (partition, entry); + return true; + } + + 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) + { + if (dirEntryRemain == size) + 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 = strnlen(name, NAME_MAX); + + if (dirnameLength >= NAME_MAX) + 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, NAME_MAX)) + && (_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, NAME_MAX - lfnPos, &ps); + if (bytesUsed < 0) + return -1; + + oemChar = wctob(towupper((wint_t)lfnChar)); + + /* Case of letter was changed */ + if (wctob((wint_t)lfnChar) != oemChar) + 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; + } + + /* Name was more than 8 characters long */ + if (lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') + lossyConversion = true; + + /* Alias extension */ + lfnExt = strrchr (lfn, '.'); + /* More than one period in name */ + if (lfnExt != NULL && lfnExt != strchr (lfn, '.')) + 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, NAME_MAX - lfnPos, &ps); + if (bytesUsed < 0) + return -1; + oemChar = wctob(towupper((wint_t)lfnChar)); + + /* Case of letter was changed */ + if (wctob((wint_t)lfnChar) != oemChar) + 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; + } + /* Extension was more than 3 characters long */ + if (*lfnExt != '\0') + lossyConversion = true; + } + + alias[aliasPos] = '\0'; + if (lossyConversion) + return aliasPos; + 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; + + /* Remove trailing spaces */ + for (i = strlen (entry->filename) - 1; (i >= 0) && (entry->filename[i] == ' '); --i) + entry->filename[i] = '\0'; + +#if 0 + /* Remove leading spaces */ + for (i = 0; entry->filename[i] == ' '; ++i) ; + if (i > 0) + memmove (entry->filename, entry->filename + i, strlen (entry->filename + i)); +#endif + + /* Make sure the filename is not 0 length */ + if (strnlen (entry->filename, NAME_MAX) < 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 junk in filename */ + i = strlen (entry->filename); + memset (entry->filename + i, '\0', NAME_MAX - 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, ".", NAME_MAX) == 0) + { + /* "." entry */ + entry->entryData[0] = '.'; + entrySize = 1; + } + else if ( strncmp(entry->filename, "..", NAME_MAX) == 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; + /* It's a normal short filename */ + else if (aliasLen == 0) + 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 = 0; + j = MAX_ALIAS_PRI_LENGTH; + /* Move extension to last 3 characters */ + while (alias[i] != '.' && alias[i] != '\0') i++; + + if (i < j) + { + memmove (alias + j, alias + i, aliasLen - i + 1); + /* Pad primary component */ + memset (alias + i, '_', j - i); + } + + /* 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; + } + + /* Couldn't get a valid alias */ + if (i > MAX_NUMERIC_TAIL) + 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 + * NOTE: The operation is an unsigned char rotate right */ + for (i=0; i < ALIAS_ENTRY_LENGTH; i++) + 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 = partition->bytesPerSector; /* Prefered file I/O block size */ + st->st_blocks = (st->st_size + partition->bytesPerSector - 1) / partition->bytesPerSector; /* File size in blocks */ + st->st_spare4[0] = 0; + st->st_spare4[1] = 0; +} diff --git a/deps/libfat/directory.h b/deps/libfat/directory.h new file mode 100644 index 0000000000..826bf6d13b --- /dev/null +++ b/deps/libfat/directory.h @@ -0,0 +1,181 @@ +/* + 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 + +#include "common.h" +#include "partition.h" + +#define DIR_ENTRY_DATA_SIZE 0x20 +#define MAX_LFN_LENGTH 256 +#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 */ + +#define CASE_LOWER_EXT 0x10 /* WinNT lowercase extension */ +#define CASE_LOWER_BASE 0x08 /* WinNT lowercase basename */ + +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[NAME_MAX]; +} DIR_ENTRY; + +/* Directory entry offsets */ +enum DIR_ENTRY_offset { + DIR_ENTRY_name = 0x00, + DIR_ENTRY_extension = 0x08, + DIR_ENTRY_attributes = 0x0B, + DIR_ENTRY_caseInfo = 0x0C, + DIR_ENTRY_cTime_ms = 0x0D, + DIR_ENTRY_cTime = 0x0E, + DIR_ENTRY_cDate = 0x10, + DIR_ENTRY_aDate = 0x12, + DIR_ENTRY_clusterHigh = 0x14, + DIR_ENTRY_mTime = 0x16, + DIR_ENTRY_mDate = 0x18, + DIR_ENTRY_cluster = 0x1A, + DIR_ENTRY_fileSize = 0x1C +}; + +/* +Returns true if the file specified by entry is a directory +*/ +static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) { + return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0); +} + +static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) { + return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0); +} + +static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) { + return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') || + ((entry->filename[1] == '.') && entry->filename[2] == '\0'))); +} + +/* +Reads the first directory entry from the directory starting at dirCluster +Places result in entry +entry will be destroyed even if no directory entry is found +Returns true on success, false on failure +*/ +bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); + +/* +Reads the next directory entry after the one already pointed to by entry +Places result in entry +entry will be destroyed even if no directory entry is found +Returns true on success, false on failure +*/ +bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry); + +/* +Gets the directory entry corrsponding to the supplied path +entry will be destroyed even if no directory entry is found +pathEnd specifies the end of the path string, for cutting strings short if needed + specify NULL to use the full length of path + pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR + after pathEND. +Returns true on success, false on failure +*/ +bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd); + +/* +Changes the current directory to the one specified by path +Returns true on success, false on failure +*/ +bool _FAT_directory_chdir (PARTITION* partition, const char* path); + +/* +Removes the directory entry specified by entry +Assumes that entry is valid +Returns true on success, false on failure +*/ +bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry); + +/* +Add a directory entry to the directory specified by dirCluster +The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are +updated with the new directory entry position and alias. +Returns true on success, false on failure +*/ +bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); + +/* +Get the start cluster of a file from it's entry data +*/ +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); + +/* +Fill in the file name and entry data of DIR_ENTRY* entry. +Assumes that the entry's dataStart and dataEnd are correct +Returns true on success, false on failure +*/ +bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry); + +/* +Fill in a stat struct based on a file entry +*/ +void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st); + +/* +Get volume label +*/ +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label); + +#endif /* _DIRECTORY_H */ diff --git a/deps/libfat/disc.c b/deps/libfat/disc.c new file mode 100644 index 0000000000..15397e9953 --- /dev/null +++ b/deps/libfat/disc.c @@ -0,0 +1,139 @@ +/* + disc.c + Interface to the low level disc functions. Used by the higher level + file system code. + + Copyright (c) 2008 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "disc.h" + +/* +The list of interfaces consists of a series of name/interface pairs. +The interface is returned via a simple function. This allows for +platforms where the interface has to be "assembled" before it can +be used, like DLDI on the NDS. For cases where a simple struct +is available, wrapper functions are used. +The list is terminated by a NULL/NULL entry. +*/ + +/* ====================== Wii U ====================== */ +#if defined (__wiiu__) +#include + +static const DISC_INTERFACE* get_io_wiiu_sd (void) +{ + return &IOSUHAX_sdio_disc_interface; +} + +static const DISC_INTERFACE* get_io_wiiu_usb (void) +{ + return &IOSUHAX_usb_disc_interface; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"sd", get_io_wiiu_sd}, + {"usb", get_io_wiiu_usb}, + {NULL, NULL} +}; + +/* ====================== Wii ====================== */ +#elif defined (__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} +}; + +/* ==================== Gamecube ==================== */ +#elif defined (__gamecube__) +#include + +static const DISC_INTERFACE* get_io_gcsda (void) +{ + return &__io_gcsda; +} +static const DISC_INTERFACE* get_io_gcsdb (void) +{ + return &__io_gcsdb; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"carda", get_io_gcsda}, + {"cardb", get_io_gcsdb}, + {NULL, NULL} +}; + +/* ====================== NDS ====================== */ +#elif defined (NDS) +#include +#include + +static const DISC_INTERFACE* get_io_dsisd (void) +{ + return isDSiMode() ? &__io_dsisd : NULL; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"sd", get_io_dsisd}, + {"fat", dldiGetInternal}, + {NULL, NULL} +}; + +/* ====================== GBA ====================== */ +#elif defined (GBA) +#include + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"fat", discGetInterface}, + {NULL, NULL} +}; + +#endif + diff --git a/deps/libfat/disc.h b/deps/libfat/disc.h new file mode 100644 index 0000000000..ef9e2057ba --- /dev/null +++ b/deps/libfat/disc.h @@ -0,0 +1,119 @@ +/* + 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/deps/libfat/fatdir.c b/deps/libfat/fatdir.c new file mode 100644 index 0000000000..682a9c6635 --- /dev/null +++ b/deps/libfat/fatdir.c @@ -0,0 +1,666 @@ +/* + 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 = ENOTEMPTY; + 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; + 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; + } + + /* Failed */ + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; +} + +int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) +{ + DIR_ENTRY oldDirEntry; + DIR_ENTRY newDirEntry; + const char *pathEnd; + uint32_t dirCluster; + + /* Get the partition this directory is on */ + PARTITION *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, NAME_MAX - 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, NAME_MAX - 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); + + if(memcmp(&buf->f_flag, "SCAN", 4) == 0) + { + /* Special command was given to sync the numberFreeCluster */ + _FAT_partition_createFSinfo(partition); + } + + if(partition->filesysType == FS_FAT32) + freeClusterCount = partition->fat.numberFreeCluster; + else + 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 = NAME_MAX; + + _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); + return -1; + } + + /* Get the filename */ + strncpy (filename, state->currentEntry.filename, NAME_MAX); + /* 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/deps/libfat/fatdir.h b/deps/libfat/fatdir.h new file mode 100644 index 0000000000..426dd30bcd --- /dev/null +++ b/deps/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/deps/libfat/fatfile.c b/deps/libfat/fatfile.c new file mode 100644 index 0000000000..ce176dc3a5 --- /dev/null +++ b/deps/libfat/fatfile.c @@ -0,0 +1,1235 @@ +/* + 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" + +bool _FAT_findEntry(const char *path, DIR_ENTRY *dirEntry) { + PARTITION *partition = _FAT_partition_getPartitionFromPath(path); + + /* Check Partition */ + if( !partition ) + return false; + + /* Move the path pointer to the start of the actual path */ + if (strchr (path, ':') != NULL) + path = strchr (path, ':') + 1; + if (strchr (path, ':') != NULL) + return false; + + /* Search for the file on the disc */ + return _FAT_directory_entryFromPath (partition, dirEntry, path, NULL); + +} + +int FAT_getAttr(const char *file) { + DIR_ENTRY dirEntry; + if (!_FAT_findEntry(file,&dirEntry)) return -1; + + return dirEntry.entryData[DIR_ENTRY_attributes]; +} + +int FAT_setAttr(const char *file, uint8_t attr) { + + /* Defines... */ + DIR_ENTRY_POSITION entryEnd; + PARTITION *partition = NULL; + DIR_ENTRY dirEntry; + + /* Get Partition */ + partition = _FAT_partition_getPartitionFromPath( file ); + + /* Check Partition */ + if( !partition ) + return -1; + + /* Move the path pointer to the start of the actual path */ + if (strchr (file, ':') != NULL) + file = strchr (file, ':') + 1; + if (strchr (file, ':') != NULL) + return -1; + + /* Get DIR_ENTRY */ + if( !_FAT_directory_entryFromPath (partition, &dirEntry, file, NULL) ) + return -1; + + /* Get Entry-End */ + entryEnd = dirEntry.dataEnd; + + /* Lock Partition */ + _FAT_lock(&partition->lock); + + + /* Write Data */ + _FAT_cache_writePartialSector ( + partition->cache /* Cache to write */ + , &attr /* Value to be written */ + , _FAT_fat_clusterToSector( partition , entryEnd.cluster ) + entryEnd.sector /* cluster */ + , entryEnd.offset * DIR_ENTRY_DATA_SIZE + DIR_ENTRY_attributes /* offset */ + , 1 /* Size in bytes */ + ); + + /* Flush any sectors in the disc cache */ + if ( !_FAT_cache_flush( partition->cache ) ) + { + _FAT_unlock(&partition->lock); /* Unlock Partition */ + return -1; + } + + /* Unlock Partition */ + _FAT_unlock(&partition->lock); + + return 0; +} + + +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 opening 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, NAME_MAX - 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); + +#if 0 + /* 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; + } +#endif + + /* 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) / partition->bytesPerSector; + file->appendPosition.byte = file->filesize % partition->bytesPerSector; + + /* 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, void *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, void *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 = partition->bytesPerSector - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < partition->bytesPerSector) && 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 >= partition->bytesPerSector) { + position.byte = 0; + position.sector++; + } + } + + /* align to cluster + * tempVar is number of sectors to read */ + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / partition->bytesPerSector; + } + + 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 * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + 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 * partition->bytesPerSector) && +#endif + (chunkSize + partition->bytesPerCluster <= remain)); + + if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), + chunkSize / partition->bytesPerSector, 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 / partition->bytesPerSector; /* 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 * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + 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 (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 [partition->bytesPerSector]; + memset(zeroBuffer, 0, partition->bytesPerSector); + uint32_t remain; + uint32_t tempNextCluster; + unsigned int sector; + + position.byte = file->filesize % partition->bytesPerSector; + position.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector; + // 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 < partition->bytesPerSector) { + // 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, + partition->bytesPerSector - position.byte); + remain -= (partition->bytesPerSector - position.byte); + position.byte = 0; + position.sector ++; + } + + while (remain >= partition->bytesPerSector) { + 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 -= partition->bytesPerSector; + position.sector ++; + } + + // error already marked + if (!_FAT_check_position_for_next_cluster(r, &position, partition, remain, NULL)) + 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, void *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 (len + file->filesize > FILE_MAX_SIZE || len + file->filesize < file->filesize) { + len = FILE_MAX_SIZE - file->filesize; + } + + /* Short circuit cases where len is 0 (or less) */ + if (len <= 0) { + _FAT_unlock(&partition->lock); + return 0; + } + + remain = len; + + /* 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 = partition->bytesPerSector - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < partition->bytesPerSector) && 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 >= partition->bytesPerSector) { + position.byte = 0; + position.sector ++; + } + } + + // Align to cluster + // tempVar is number of sectors to write + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / partition->bytesPerSector; + } + + 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 * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + 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 * partition->bytesPerSector) && +#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 / partition->bytesPerSector, 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 / partition->bytesPerSector; // 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 * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + 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, void *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) / partition->bytesPerSector; + file->rwPosition.byte = position % partition->bytesPerSector; + + 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, void *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, void *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 % partition->bytesPerSector; + // 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) / partition->bytesPerSector; + } + 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, void *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/deps/libfat/fatfile.h b/deps/libfat/fatfile.h new file mode 100644 index 0000000000..984c3d01c5 --- /dev/null +++ b/deps/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; + +int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); + +int _FAT_close_r (struct _reent *r, void *fd); + +ssize_t _FAT_write_r (struct _reent *r,void *fd, const char *ptr, size_t len); + +ssize_t _FAT_read_r (struct _reent *r, void *fd, char *ptr, size_t len); + +off_t _FAT_seek_r (struct _reent *r, void *fd, off_t pos, int dir); + +int _FAT_fstat_r (struct _reent *r, void *fd, struct stat *st); + +int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st); + +int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink); + +int _FAT_unlink_r (struct _reent *r, const char *name); + +int _FAT_chdir_r (struct _reent *r, const char *name); + +int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName); + +int _FAT_ftruncate_r (struct _reent *r, void *fd, off_t len); + +int _FAT_fsync_r (struct _reent *r, void *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/deps/libfat/file_allocation_table.c b/deps/libfat/file_allocation_table.c new file mode 100644 index 0000000000..959cbdf481 --- /dev/null +++ b/deps/libfat/file_allocation_table.c @@ -0,0 +1,395 @@ +/* + 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 "mem_allocate.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) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; + + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8)); + + offset++; + + if (offset >= partition->bytesPerSector) + { + 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) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 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) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 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) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; + + 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 >= partition->bytesPerSector) + { + 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 >= partition->bytesPerSector) + { + 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) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 1)) << 1; + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u16)); + + break; + + case FS_FAT32: + sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 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; + } + + /* Try looping back to the beginning of the FAT + * This was suggested by loopy */ + firstFree = CLUSTER_FIRST; + loopedAroundFAT = true; + } + } + partition->fat.firstFree = firstFree; + if(partition->fat.numberFreeCluster) + partition->fat.numberFreeCluster--; + partition->fat.numberLastAllocCluster = firstFree; + + /* Update the linked from FAT entry */ + if ((cluster >= CLUSTER_FIRST) && (cluster <= lastCluster)) + _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 i; + uint8_t *emptySector; + + /* Link the cluster */ + uint32_t newCluster = _FAT_fat_linkFreeCluster(partition, cluster); + + if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR) + return CLUSTER_ERROR; + + emptySector = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); + + /* Clear all the sectors within the cluster */ + memset (emptySector, 0, partition->bytesPerSector); + for (i = 0; i < partition->sectorsPerCluster; i++) + { + _FAT_cache_writeSectors (partition->cache, + _FAT_fat_clusterToSector (partition, newCluster) + i, + 1, emptySector); + } + + _FAT_mem_free(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); + + if(partition->fat.numberFreeCluster < (partition->numberOfSectors/partition->sectorsPerCluster)) + partition->fat.numberFreeCluster++; + /* 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; + } + + /* 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/deps/libfat/file_allocation_table.h b/deps/libfat/file_allocation_table.h new file mode 100644 index 0000000000..e420dfeadf --- /dev/null +++ b/deps/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/deps/libfat/filetime.c b/deps/libfat/filetime.c new file mode 100644 index 0000000000..72c70d1e16 --- /dev/null +++ b/deps/libfat/filetime.c @@ -0,0 +1,111 @@ +/* + 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/deps/libfat/filetime.h b/deps/libfat/filetime.h new file mode 100644 index 0000000000..7088c1ae6d --- /dev/null +++ b/deps/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/deps/libfat/include/fat.h b/deps/libfat/include/fat.h new file mode 100644 index 0000000000..d466ffee89 --- /dev/null +++ b/deps/libfat/include/fat.h @@ -0,0 +1,124 @@ +/* + fat.h + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 - 2012 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _LIBFAT_H +#define _LIBFAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* When compiling for NDS, make sure NDS is defined */ +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#include + +#if defined (__wiiu__) +# include +#elif defined(__gamecube__) || defined (__wii__) +# include +#else +# ifdef NDS +# include +# else +# include +# endif +#endif + +/* +Initialise any inserted block-devices. +Add the fat device driver to the devoptab, making it available for standard file functions. +cacheSize: The number of pages to allocate for each inserted block-device +setAsDefaultDevice: if true, make this the default device driver for file operations +*/ +extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); + +/* +Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. +*/ +extern bool fatInitDefault (void); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +This will mount the active partition or the first valid partition on the disc, +and will use a cache size optimized for the host system. +*/ +extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +If startSector = 0, it will mount the active partition of the first valid partition on +the disc. Otherwise it will try to mount the partition starting at startSector. +cacheSize specifies the number of pages to allocate for the cache. +This will not startup the disc, so you need to call interface->startup(); first. +*/ +extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + +/* +Unmount the partition specified by name. +If there are open files, it will attempt to synchronise them to disc. +*/ +extern void fatUnmount (const char* name); + +/* +Get Volume Label +*/ +extern void fatGetVolumeLabel (const char* name, char *label); + +/* File attributes */ +#define ATTR_ARCHIVE 0x20 /* Archive */ +#define ATTR_DIRECTORY 0x10 /* Directory */ +#define ATTR_VOLUME 0x08 /* Volume */ +#define ATTR_SYSTEM 0x04 /* System */ +#define ATTR_HIDDEN 0x02 /* Hidden */ +#define ATTR_READONLY 0x01 /* Read only */ + +/* +Methods to modify DOS File Attributes +*/ +int FAT_getAttr(const char *file); +int FAT_setAttr(const char *file, uint8_t attr ); + +#define LIBFAT_FEOS_MULTICWD + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBFAT_H */ diff --git a/deps/libfat/include/libfatversion.h b/deps/libfat/include/libfatversion.h new file mode 100644 index 0000000000..a306565704 --- /dev/null +++ b/deps/libfat/include/libfatversion.h @@ -0,0 +1,10 @@ +#ifndef __LIBFATVERSION_H__ +#define __LIBFATVERSION_H__ + +#define _LIBFAT_MAJOR_ 1 +#define _LIBFAT_MINOR_ 1 +#define _LIBFAT_PATCH_ 0 + +#define _LIBFAT_STRING "libFAT Release 1.1.0" + +#endif /* __LIBFATVERSION_H__ */ diff --git a/deps/libfat/libfat.c b/deps/libfat/libfat.c new file mode 100644 index 0000000000..34e58c4186 --- /dev/null +++ b/deps/libfat/libfat.c @@ -0,0 +1,264 @@ +/* + 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 +#include + +#include "common.h" +#include "partition.h" +#include "fatfile.h" +#include "fatdir.h" +#include "lock.h" +#include "mem_allocate.h" +#include "disc.h" + +static const devoptab_t dotab_fat = { + "fat", + sizeof (FILE_STRUCT), + _FAT_open_r, + _FAT_close_r, + _FAT_write_r, + _FAT_read_r, + _FAT_seek_r, + _FAT_fstat_r, + _FAT_stat_r, + _FAT_link_r, + _FAT_unlink_r, + _FAT_chdir_r, + _FAT_rename_r, + _FAT_mkdir_r, + sizeof (DIR_STATE_STRUCT), + _FAT_diropen_r, + _FAT_dirreset_r, + _FAT_dirnext_r, + _FAT_dirclose_r, + _FAT_statvfs_r, + _FAT_ftruncate_r, + _FAT_fsync_r, + NULL, /* Device data */ + NULL, /* chmod_r */ + NULL /* fchmod_r */ +}; + +bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage) +{ + char devname[10]; + PARTITION* partition; + devoptab_t* devops; + char* nameCopy; + + if(!name || strlen(name) > 8 || !interface) + return false; + + if(!interface->startup()) + return false; + + if(!interface->isInserted()) + return false; + + strcpy(devname, name); + strcat(devname, ":"); + if(FindDevice(devname) >= 0) + return true; + + devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1); + if (!devops) + return false; + /* Use the space allocated at the end of the devoptab struct for storing the name */ + nameCopy = (char*)(devops+1); + + /* Initialize the file system */ + partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector); + if (!partition) + { + _FAT_mem_free (devops); + return false; + } + + /* Add an entry for this device to the devoptab table */ + memcpy (devops, &dotab_fat, sizeof(dotab_fat)); + strcpy (nameCopy, name); + devops->name = nameCopy; + devops->deviceData = partition; + + AddDevice (devops); + + return true; +} + +bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) +{ + return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE); +} + +void fatUnmount (const char* name) +{ + devoptab_t *devops; + PARTITION* partition; + + if(!name) + return; + + devops = (devoptab_t*)GetDeviceOpTab (name); + if (!devops) + return; + + /* Perform a quick check to make sure we're dealing with a libfat controlled device */ + if (devops->open_r != dotab_fat.open_r) + return; + + if (RemoveDevice (name) == -1) + return; + + partition = (PARTITION*)devops->deviceData; + _FAT_partition_destructor (partition); + _FAT_mem_free (devops); +} + +bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) +{ + int i; + int defaultDevice = -1; + const DISC_INTERFACE *disc; + + for (i = 0; + _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; + i++) + { + disc = _FAT_disc_interfaces[i].getInterface(); + if (!disc) + continue; + + 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; + } + } + + /* None of our devices mounted */ + if (defaultDevice < 0) + return false; + + if (setAsDefaultDevice) + { + char filePath[PATH_MAX]; + strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name); + strcat (filePath, ":/"); +#ifdef ARGV_MAGIC + if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr( __system_argv->argv[0], '/' )!=NULL ) + { + /* Check the app's path against each of our mounted devices, to see + * if we can support it. If so, change to that path. */ + for (i = 0; + _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; + i++) + { + if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name, + strlen(_FAT_disc_interfaces[i].name))) + { + char *lastSlash; + strcpy(filePath, __system_argv->argv[0]); + lastSlash = strrchr( filePath, '/' ); + + if ( NULL != lastSlash) + { + if ( *(lastSlash - 1) == ':') + lastSlash++; + *lastSlash = 0; + } + } + } + } +#endif + chdir (filePath); + } + + return true; +} + +bool fatInitDefault (void) +{ + return fatInit (DEFAULT_CACHE_PAGES, true); +} + +void fatGetVolumeLabel (const char* name, char *label) +{ + devoptab_t *devops; + PARTITION* partition; + char *buf; + int namelen,i; + + if(!name || !label) + return; + + namelen = strlen(name); + buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2); + strcpy(buf,name); + + if (name[namelen-1] == '/') + { + buf[namelen-1]='\0'; + namelen--; + } + + if (name[namelen-1] != ':') + { + buf[namelen]=':'; + buf[namelen+1]='\0'; + } + + devops = (devoptab_t*)GetDeviceOpTab(buf); + + for(i=0;buf[i]!='\0' && buf[i]!=':';i++); + if (!devops || strncasecmp(buf,devops->name,i)) + { + _FAT_mem_free(buf); + return; + } + + _FAT_mem_free(buf); + + /* Perform a quick check to make sure we're dealing with a libfat controlled device */ + if (devops->open_r != dotab_fat.open_r) + return; + + partition = (PARTITION*)devops->deviceData; + + if(!_FAT_directory_getVolumeLabel(partition, label)) + { + strncpy(label,partition->label,11); + label[11]='\0'; + } + if(!strncmp(label, "NO NAME", 7)) + label[0]='\0'; +} diff --git a/deps/libfat/lock.c b/deps/libfat/lock.c new file mode 100644 index 0000000000..8c55d77f3e --- /dev/null +++ b/deps/libfat/lock.c @@ -0,0 +1,29 @@ +#include "common.h" + +#ifndef USE_LWP_LOCK + +#ifndef mutex_t +typedef int mutex_t; +#endif + +void __attribute__ ((weak)) _FAT_lock_init(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_lock_deinit(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_lock(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_unlock(mutex_t *mutex) +{ + return; +} + +#endif /* USE_LWP_LOCK */ diff --git a/deps/libfat/lock.h b/deps/libfat/lock.h new file mode 100644 index 0000000000..8a44ec9181 --- /dev/null +++ b/deps/libfat/lock.h @@ -0,0 +1,72 @@ +/* + 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 + +void _FAT_lock_init(mutex_t *mutex); +void _FAT_lock_deinit(mutex_t *mutex); +void _FAT_lock(mutex_t *mutex); +void _FAT_unlock(mutex_t *mutex); + +#endif /* USE_LWP_LOCK */ + + +#endif /* _LOCK_H */ + diff --git a/deps/libfat/mem_allocate.h b/deps/libfat/mem_allocate.h new file mode 100644 index 0000000000..35ad3966b6 --- /dev/null +++ b/deps/libfat/mem_allocate.h @@ -0,0 +1,55 @@ +/* + 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) +{ +#ifdef __wii__ + return memalign (32, size); +#else + return malloc (size); +#endif +} + +static inline void _FAT_mem_free (void* mem) +{ + free (mem); +} + +#endif /* _MEM_ALLOCATE_H */ diff --git a/deps/libfat/partition.c b/deps/libfat/partition.c new file mode 100644 index 0000000000..0080b6e93c --- /dev/null +++ b/deps/libfat/partition.c @@ -0,0 +1,464 @@ +/* + 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 + +/* +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 +}; + +/* File system information block offsets */ +enum FSIB +{ + FSIB_SIG1 = 0x00, + FSIB_SIG2 = 0x1e4, + FSIB_numberOfFreeCluster = 0x1e8, + FSIB_numberLastAllocCluster = 0x1ec, + FSIB_bootSig_55 = 0x1FE, + FSIB_bootSig_AA = 0x1FF +}; + +static const char FAT_SIG[3] = {'F', 'A', 'T'}; +static const char FS_INFO_SIG1[4] = {'R', 'R', 'a', 'A'}; +static const char FS_INFO_SIG2[4] = {'r', 'r', 'A', 'a'}; + +sec_t FindFirstValidPartition_buf(const DISC_INTERFACE* disc, uint8_t *sectorBuffer) +{ + uint8_t part_table[16*4]; + uint8_t *ptr; + int i; + + /* 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; +} + +sec_t FindFirstValidPartition(const DISC_INTERFACE* disc) +{ + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); + if (!sectorBuffer) + return 0; + sec_t ret = FindFirstValidPartition_buf(disc, sectorBuffer); + _FAT_mem_free(sectorBuffer); + return ret; +} + + +PARTITION* _FAT_partition_constructor_buf (const DISC_INTERFACE* disc, + uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector, + uint8_t *sectorBuffer) +{ + PARTITION* partition; + uint32_t clusterCount; + + /* 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) +#if defined(__gamecube__) || defined (__wii__) || defined (__wiiu__) + && (sectorBuffer[BPB_bootSig_AA] != 0xAB) +#endif + )) + return NULL; + + /* We're told where to start the partition, so just accept it */ + if (startSector != 0) { } + /* Check if there is a FAT string, which indicates this is a boot sector */ + else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + startSector = 0; + /* Check for FAT32 */ + else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + startSector = 0; + else + { + startSector = FindFirstValidPartition_buf(disc, sectorBuffer); + 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; + + partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION)); + if (partition == NULL) + return NULL; + + /* Init the partition lock */ + _FAT_lock_init(&partition->lock); + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11); + else + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11); + partition->label[11] = '\0'; + + /* Set partition's disc interface */ + partition->disc = disc; + + /* Store required information about the file system */ + partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT); + if (partition->fat.sectorsPerFat == 0) + partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32); + + partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall); + if (partition->numberOfSectors == 0) + partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors); + + partition->bytesPerSector = u8array_to_u16(sectorBuffer, BPB_bytesPerSector); + if(partition->bytesPerSector < MIN_SECTOR_SIZE || partition->bytesPerSector > MAX_SECTOR_SIZE) + { + /* Unsupported sector size */ + _FAT_mem_free(partition); + return NULL; + } + + partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster]; + 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; + + /* FS info sector */ + partition->fsInfoSector = startSector + (u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) ? u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) : 1); + + /* Store info about FAT */ + clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster; + + partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1; + partition->fat.firstFree = CLUSTER_FIRST; + partition->fat.numberFreeCluster = 0; + partition->fat.numberLastAllocCluster = 0; + + 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, partition->bytesPerSector); + + /* 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; + + _FAT_partition_readFSinfo(partition); + + return partition; +} + +PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) +{ + PARTITION *ret = NULL; + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); + if (!sectorBuffer) + return NULL; + + ret = _FAT_partition_constructor_buf(disc, cacheSize, + sectorsPerPage, startSector, sectorBuffer); + _FAT_mem_free(sectorBuffer); + return ret; +} + + +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; + } + + /* Write out the fs info sector */ + _FAT_partition_writeFSinfo(partition); + + /* 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 = GetDeviceOpTab (path); + + if (!devops) + return NULL; + + return (PARTITION*)devops->deviceData; +} + +void _FAT_partition_createFSinfo(PARTITION * partition) +{ + int i; + uint8_t *sectorBuffer; + + if(partition->readOnly || partition->filesysType != FS_FAT32) + return; + + sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) + return; + memset(sectorBuffer, 0, partition->bytesPerSector); + + for(i = 0; i < 4; ++i) + { + sectorBuffer[FSIB_SIG1+i] = FS_INFO_SIG1[i]; + sectorBuffer[FSIB_SIG2+i] = FS_INFO_SIG2[i]; + } + + partition->fat.numberFreeCluster = _FAT_fat_freeClusterCount(partition); + u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); + u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); + + sectorBuffer[FSIB_bootSig_55] = 0x55; + sectorBuffer[FSIB_bootSig_AA] = 0xAA; + + _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); + + _FAT_mem_free(sectorBuffer); +} + +void _FAT_partition_readFSinfo(PARTITION * partition) +{ + uint8_t *sectorBuffer = NULL; + if(partition->filesysType != FS_FAT32) + return; + + sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) + return; + memset(sectorBuffer, 0, partition->bytesPerSector); + + /* Read first sector of disc */ + if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) + { + _FAT_mem_free(sectorBuffer); + return; + } + + /* sector does not yet exist, create one! */ + if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) != 0 || + memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4) != 0 || + u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0) + _FAT_partition_createFSinfo(partition); + else + { + partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster); + partition->fat.numberLastAllocCluster = u8array_to_u32(sectorBuffer, FSIB_numberLastAllocCluster); + } + _FAT_mem_free(sectorBuffer); +} + +void _FAT_partition_writeFSinfo(PARTITION * partition) +{ + uint8_t *sectorBuffer = NULL; + if(partition->filesysType != FS_FAT32) + return; + + sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) + return; + memset(sectorBuffer, 0, partition->bytesPerSector); + /* Read first sector of disc */ + if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) + { + _FAT_mem_free(sectorBuffer); + return; + } + + if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4)) + { + _FAT_mem_free(sectorBuffer); + return; + } + + u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); + u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); + + /* Write first sector of disc */ + _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); + _FAT_mem_free(sectorBuffer); +} + +uint32_t* _FAT_getCwdClusterPtr(const char* name) +{ + PARTITION *partition = _FAT_partition_getPartitionFromPath(name); + + if (!partition) + return NULL; + + return &partition->cwdCluster; +} diff --git a/deps/libfat/partition.h b/deps/libfat/partition.h new file mode 100644 index 0000000000..e4aecccd27 --- /dev/null +++ b/deps/libfat/partition.h @@ -0,0 +1,107 @@ +/* + 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" + +#define MIN_SECTOR_SIZE 512 +#define MAX_SECTOR_SIZE 4096 + +/* 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; + uint32_t numberFreeCluster; + uint32_t numberLastAllocCluster; +} 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; + uint32_t fsInfoSector; + FAT fat; + /* Values that may change after construction */ + uint32_t cwdCluster; /* Current working directory cluster */ + int openFileCount; + struct _FILE_STRUCT* firstOpenFile; /* The start of a linked list of files */ + mutex_t lock; /* A lock for partition operations */ + bool readOnly; /* If this is set, then do not try writing to the disc */ + char label[12]; /* Volume label */ +} PARTITION; + +/* +Mount the supplied device and return a pointer to the struct necessary to use it +*/ +PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector); + +/* +Dismount the device and free all structures used. +Will also attempt to synchronise all open files to disc. +*/ +void _FAT_partition_destructor (PARTITION* partition); + +/* +Return the partition specified in a path, as taken from the devoptab. +*/ +PARTITION* _FAT_partition_getPartitionFromPath (const char* path); + +/* +Create the fs info sector. +*/ +void _FAT_partition_createFSinfo(PARTITION * partition); + +/* +Read the fs info sector data. +*/ +void _FAT_partition_readFSinfo(PARTITION * partition); + +/* +Write the fs info sector data. +*/ +void _FAT_partition_writeFSinfo(PARTITION * partition); + +#endif /* _PARTITION_H */ diff --git a/deps/libiosuhax/iosuhax.c b/deps/libiosuhax/iosuhax.c new file mode 100644 index 0000000000..0a2f8221b8 --- /dev/null +++ b/deps/libiosuhax/iosuhax.c @@ -0,0 +1,966 @@ +/*************************************************************************** + * Copyright (C) 2016 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#include +#include +#include "os_functions.h" +#include "iosuhax.h" + +#define IOSUHAX_MAGIC_WORD 0x4E696365 + +#define IOCTL_MEM_WRITE 0x00 +#define IOCTL_MEM_READ 0x01 +#define IOCTL_SVC 0x02 +#define IOCTL_MEMCPY 0x04 +#define IOCTL_REPEATED_WRITE 0x05 +#define IOCTL_KERN_READ32 0x06 +#define IOCTL_KERN_WRITE32 0x07 + +#define IOCTL_FSA_OPEN 0x40 +#define IOCTL_FSA_CLOSE 0x41 +#define IOCTL_FSA_MOUNT 0x42 +#define IOCTL_FSA_UNMOUNT 0x43 +#define IOCTL_FSA_GETDEVICEINFO 0x44 +#define IOCTL_FSA_OPENDIR 0x45 +#define IOCTL_FSA_READDIR 0x46 +#define IOCTL_FSA_CLOSEDIR 0x47 +#define IOCTL_FSA_MAKEDIR 0x48 +#define IOCTL_FSA_OPENFILE 0x49 +#define IOCTL_FSA_READFILE 0x4A +#define IOCTL_FSA_WRITEFILE 0x4B +#define IOCTL_FSA_STATFILE 0x4C +#define IOCTL_FSA_CLOSEFILE 0x4D +#define IOCTL_FSA_SETFILEPOS 0x4E +#define IOCTL_FSA_GETSTAT 0x4F +#define IOCTL_FSA_REMOVE 0x50 +#define IOCTL_FSA_REWINDDIR 0x51 +#define IOCTL_FSA_CHDIR 0x52 +#define IOCTL_FSA_RENAME 0x53 +#define IOCTL_FSA_RAW_OPEN 0x54 +#define IOCTL_FSA_RAW_READ 0x55 +#define IOCTL_FSA_RAW_WRITE 0x56 +#define IOCTL_FSA_RAW_CLOSE 0x57 +#define IOCTL_FSA_CHANGEMODE 0x58 +#define IOCTL_FSA_FLUSHVOLUME 0x59 +#define IOCTL_CHECK_IF_IOSUHAX 0x5B + +static int iosuhaxHandle = -1; + +int IOSUHAX_Open(const char *dev) +{ + if(iosuhaxHandle >= 0) + return iosuhaxHandle; + + iosuhaxHandle = IOS_Open((char*)(dev ? dev : "/dev/iosuhax"), 0); + if(iosuhaxHandle >= 0 && dev) /* make sure device is actually iosuhax */ + { + unsigned int res = 0; + IOS_Ioctl(iosuhaxHandle, IOCTL_CHECK_IF_IOSUHAX, (void*)0, 0, &res, 4); + if(res != IOSUHAX_MAGIC_WORD) + { + IOS_Close(iosuhaxHandle); + iosuhaxHandle = -1; + } + } + + return iosuhaxHandle; +} + +int IOSUHAX_Close(void) +{ + if(iosuhaxHandle < 0) + return 0; + + int res = IOS_Close(iosuhaxHandle); + iosuhaxHandle = -1; + return res; +} + +int IOSUHAX_memwrite(uint32_t address, const uint8_t * buffer, uint32_t size) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, size + 4); + if(!io_buf) + return -2; + + io_buf[0] = address; + memcpy(io_buf + 1, buffer, size); + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_MEM_WRITE, io_buf, size + 4, 0, 0); + + free(io_buf); + return res; +} + +int IOSUHAX_memread(uint32_t address, uint8_t * out_buffer, uint32_t size) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + return IOS_Ioctl(iosuhaxHandle, IOCTL_MEM_READ, &address, sizeof(address), out_buffer, size); +} + +int IOSUHAX_memcpy(uint32_t dst, uint32_t src, uint32_t size) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + uint32_t io_buf[3]; + io_buf[0] = dst; + io_buf[1] = src; + io_buf[2] = size; + + return IOS_Ioctl(iosuhaxHandle, IOCTL_MEMCPY, io_buf, sizeof(io_buf), 0, 0); +} + +int IOSUHAX_SVC(uint32_t svc_id, uint32_t * args, uint32_t arg_cnt) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + uint32_t arguments[9]; + arguments[0] = svc_id; + + if(args && arg_cnt) + { + if(arg_cnt > 8) + arg_cnt = 8; + + memcpy(arguments + 1, args, arg_cnt * 4); + } + + int result; + int ret = IOS_Ioctl(iosuhaxHandle, IOCTL_SVC, arguments, (1 + arg_cnt) * 4, &result, sizeof(result)); + if(ret < 0) + return ret; + + return result; +} + +int IOSUHAX_FSA_Open(void) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + int fsaFd; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_OPEN, 0, 0, &fsaFd, sizeof(fsaFd)); + if(res < 0) + return res; + + return fsaFd; +} + +int IOSUHAX_FSA_Close(int fsaFd) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_CLOSE, &fsaFd, sizeof(fsaFd), &fsaFd, sizeof(fsaFd)); + if(res < 0) + return res; + + return fsaFd; +} + +int IOSUHAX_FSA_Mount(int fsaFd, const char* device_path, const char* volume_path, uint32_t flags, const char* arg_string, int arg_string_len) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 6; + + int io_buf_size = (sizeof(uint32_t) * input_cnt) + strlen(device_path) + strlen(volume_path) + arg_string_len + 3; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + memset(io_buf, 0, io_buf_size); + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + io_buf[2] = io_buf[1] + strlen(device_path) + 1; + io_buf[3] = flags; + io_buf[4] = arg_string_len ? ( io_buf[2] + strlen(volume_path) + 1) : 0; + io_buf[5] = arg_string_len; + + strcpy(((char*)io_buf) + io_buf[1], device_path); + strcpy(((char*)io_buf) + io_buf[2], volume_path); + + if(arg_string_len) + memcpy(((char*)io_buf) + io_buf[4], arg_string, arg_string_len); + + int mountRes; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_MOUNT, io_buf, io_buf_size, &mountRes, sizeof(mountRes)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return mountRes; +} + +int IOSUHAX_FSA_Unmount(int fsaFd, const char* path, uint32_t flags) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 3; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + io_buf[2] = flags; + strcpy(((char*)io_buf) + io_buf[1], path); + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_UNMOUNT, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_FlushVolume(int fsaFd, const char *volume_path) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(volume_path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + strcpy(((char*)io_buf) + io_buf[1], volume_path); + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_FLUSHVOLUME, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_GetDeviceInfo(int fsaFd, const char* device_path, int type, uint32_t* out_data) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 3; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(device_path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + io_buf[2] = type; + strcpy(((char*)io_buf) + io_buf[1], device_path); + + uint32_t out_buf[1 + 0x64 / 4]; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_GETDEVICEINFO, io_buf, io_buf_size, out_buf, sizeof(out_buf)); + if(res < 0) + { + free(io_buf); + return res; + } + + memcpy(out_data, out_buf + 1, 0x64); + free(io_buf); + return out_buf[0]; +} + +int IOSUHAX_FSA_MakeDir(int fsaFd, const char* path, uint32_t flags) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 3; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + io_buf[2] = flags; + strcpy(((char*)io_buf) + io_buf[1], path); + + int result; + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_MAKEDIR, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_OpenDir(int fsaFd, const char* path, int* outHandle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + strcpy(((char*)io_buf) + io_buf[1], path); + + int result_vec[2]; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_OPENDIR, io_buf, io_buf_size, result_vec, sizeof(result_vec)); + if(res < 0) + { + free(io_buf); + return res; + } + + *outHandle = result_vec[1]; + free(io_buf); + return result_vec[0]; +} + +int IOSUHAX_FSA_ReadDir(int fsaFd, int handle, directoryEntry_s* out_data) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = handle; + + int result_vec_size = 4 + sizeof(directoryEntry_s); + uint8_t *result_vec = (uint8_t*) memalign(0x20, result_vec_size); + if(!result_vec) + { + free(io_buf); + return -2; + } + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_READDIR, io_buf, io_buf_size, result_vec, result_vec_size); + if(res < 0) + { + free(result_vec); + free(io_buf); + return res; + } + + int result = *(int*)result_vec; + memcpy(out_data, result_vec + 4, sizeof(directoryEntry_s)); + free(io_buf); + free(result_vec); + return result; +} + +int IOSUHAX_FSA_RewindDir(int fsaFd, int dirHandle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = dirHandle; + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_REWINDDIR, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_CloseDir(int fsaFd, int handle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = handle; + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_CLOSEDIR, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_ChangeDir(int fsaFd, const char *path) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + strcpy(((char*)io_buf) + io_buf[1], path); + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_CHDIR, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_OpenFile(int fsaFd, const char* path, const char* mode, int* outHandle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 3; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + strlen(mode) + 2; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + io_buf[2] = io_buf[1] + strlen(path) + 1; + strcpy(((char*)io_buf) + io_buf[1], path); + strcpy(((char*)io_buf) + io_buf[2], mode); + + int result_vec[2]; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_OPENFILE, io_buf, io_buf_size, result_vec, sizeof(result_vec)); + if(res < 0) + { + free(io_buf); + return res; + } + + *outHandle = result_vec[1]; + free(io_buf); + return result_vec[0]; +} + +int IOSUHAX_FSA_ReadFile(int fsaFd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 5; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = size; + io_buf[2] = cnt; + io_buf[3] = fileHandle; + io_buf[4] = flags; + + int out_buf_size = ((size * cnt + 0x40) + 0x3F) & ~0x3F; + + uint32_t *out_buffer = (uint32_t*)memalign(0x40, out_buf_size); + if(!out_buffer) + { + free(io_buf); + return -2; + } + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_READFILE, io_buf, io_buf_size, out_buffer, out_buf_size); + if(res < 0) + { + free(out_buffer); + free(io_buf); + return res; + } + + /* ! data is put to offset 0x40 to align the buffer output */ + memcpy(data, ((uint8_t*)out_buffer) + 0x40, size * cnt); + + int result = out_buffer[0]; + + free(out_buffer); + free(io_buf); + return result; +} + +int IOSUHAX_FSA_WriteFile(int fsaFd, const void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 5; + + int io_buf_size = ((sizeof(uint32_t) * input_cnt + size * cnt + 0x40) + 0x3F) & ~0x3F; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = size; + io_buf[2] = cnt; + io_buf[3] = fileHandle; + io_buf[4] = flags; + + /* ! data is put to offset 0x40 to align the buffer input */ + memcpy(((uint8_t*)io_buf) + 0x40, data, size * cnt); + + int result; + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_WRITEFILE, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + free(io_buf); + return result; +} + +int IOSUHAX_FSA_StatFile(int fsaFd, int fileHandle, fileStat_s* out_data) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = fileHandle; + + int out_buf_size = 4 + sizeof(fileStat_s); + uint32_t *out_buffer = (uint32_t*)memalign(0x20, out_buf_size); + if(!out_buffer) + { + free(io_buf); + return -2; + } + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_STATFILE, io_buf, io_buf_size, out_buffer, out_buf_size); + if(res < 0) + { + free(io_buf); + free(out_buffer); + return res; + } + + int result = out_buffer[0]; + memcpy(out_data, out_buffer + 1, sizeof(fileStat_s)); + + free(io_buf); + free(out_buffer); + return result; +} + +int IOSUHAX_FSA_CloseFile(int fsaFd, int fileHandle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = fileHandle; + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_CLOSEFILE, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_SetFilePos(int fsaFd, int fileHandle, uint32_t position) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 3; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = fileHandle; + io_buf[2] = position; + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_SETFILEPOS, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_GetStat(int fsaFd, const char *path, fileStat_s* out_data) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + strcpy(((char*)io_buf) + io_buf[1], path); + + int out_buf_size = 4 + sizeof(fileStat_s); + uint32_t *out_buffer = (uint32_t*)memalign(0x20, out_buf_size); + if(!out_buffer) + { + free(io_buf); + return -2; + } + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_GETSTAT, io_buf, io_buf_size, out_buffer, out_buf_size); + if(res < 0) + { + free(io_buf); + free(out_buffer); + return res; + } + + int result = out_buffer[0]; + memcpy(out_data, out_buffer + 1, sizeof(fileStat_s)); + + free(io_buf); + free(out_buffer); + return result; +} + +int IOSUHAX_FSA_Remove(int fsaFd, const char *path) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + strcpy(((char*)io_buf) + io_buf[1], path); + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_REMOVE, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_ChangeMode(int fsaFd, const char* path, int mode) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 3; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + io_buf[2] = mode; + strcpy(((char*)io_buf) + io_buf[1], path); + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_CHANGEMODE, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + +int IOSUHAX_FSA_RawOpen(int fsaFd, const char* device_path, int* outHandle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt + strlen(device_path) + 1; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = sizeof(uint32_t) * input_cnt; + strcpy(((char*)io_buf) + io_buf[1], device_path); + + int result_vec[2]; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_RAW_OPEN, io_buf, io_buf_size, result_vec, sizeof(result_vec)); + if(res < 0) + { + free(io_buf); + return res; + } + + if(outHandle) + *outHandle = result_vec[1]; + + free(io_buf); + return result_vec[0]; +} + +int IOSUHAX_FSA_RawRead(int fsaFd, void* data, uint32_t block_size, uint32_t block_cnt, uint64_t sector_offset, int device_handle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 6; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = block_size; + io_buf[2] = block_cnt; + io_buf[3] = (sector_offset >> 32) & 0xFFFFFFFF; + io_buf[4] = sector_offset & 0xFFFFFFFF; + io_buf[5] = device_handle; + + int out_buf_size = ((block_size * block_cnt + 0x40) + 0x3F) & ~0x3F; + + uint32_t *out_buffer = (uint32_t*)memalign(0x40, out_buf_size); + if(!out_buffer) + { + free(io_buf); + return -2; + } + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_RAW_READ, io_buf, io_buf_size, out_buffer, out_buf_size); + if(res < 0) + { + free(out_buffer); + free(io_buf); + return res; + } + + /* ! data is put to offset 0x40 to align the buffer output */ + memcpy(data, ((uint8_t*)out_buffer) + 0x40, block_size * block_cnt); + + int result = out_buffer[0]; + + free(out_buffer); + free(io_buf); + return result; +} + +int IOSUHAX_FSA_RawWrite(int fsaFd, const void* data, uint32_t block_size, uint32_t block_cnt, uint64_t sector_offset, int device_handle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 6; + + int io_buf_size = ((sizeof(uint32_t) * input_cnt + block_size * block_cnt + 0x40) + 0x3F) & ~0x3F; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = block_size; + io_buf[2] = block_cnt; + io_buf[3] = (sector_offset >> 32) & 0xFFFFFFFF; + io_buf[4] = sector_offset & 0xFFFFFFFF; + io_buf[5] = device_handle; + + /* ! data is put to offset 0x40 to align the buffer input */ + memcpy(((uint8_t*)io_buf) + 0x40, data, block_size * block_cnt); + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_RAW_WRITE, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} + + +int IOSUHAX_FSA_RawClose(int fsaFd, int device_handle) +{ + if(iosuhaxHandle < 0) + return iosuhaxHandle; + + const int input_cnt = 2; + + int io_buf_size = sizeof(uint32_t) * input_cnt; + + uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buf_size); + if(!io_buf) + return -2; + + io_buf[0] = fsaFd; + io_buf[1] = device_handle; + + int result; + + int res = IOS_Ioctl(iosuhaxHandle, IOCTL_FSA_RAW_CLOSE, io_buf, io_buf_size, &result, sizeof(result)); + if(res < 0) + { + free(io_buf); + return res; + } + + free(io_buf); + return result; +} diff --git a/deps/libiosuhax/iosuhax.h b/deps/libiosuhax/iosuhax.h new file mode 100644 index 0000000000..25b7367cad --- /dev/null +++ b/deps/libiosuhax/iosuhax.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2016 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#ifndef _LIB_IOSUHAX_H_ +#define _LIB_IOSUHAX_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define IOS_ERROR_UNKNOWN_VALUE 0xFFFFFFD6 +#define IOS_ERROR_INVALID_ARG 0xFFFFFFE3 +#define IOS_ERROR_INVALID_SIZE 0xFFFFFFE9 +#define IOS_ERROR_UNKNOWN 0xFFFFFFF7 +#define IOS_ERROR_NOEXISTS 0xFFFFFFFA + +typedef struct +{ + uint32_t flag; + uint32_t permission; + uint32_t owner_id; + uint32_t group_id; + uint32_t size; // size in bytes + uint32_t physsize; // physical size on disk in bytes + uint32_t unk[3]; + uint32_t id; + uint32_t ctime; + uint32_t mtime; + uint32_t unk2[0x0D]; +}fileStat_s; + +typedef struct +{ + fileStat_s stat; + char name[0x100]; +}directoryEntry_s; + +#define DIR_ENTRY_IS_DIRECTORY 0x80000000 + +#define FSA_MOUNTFLAGS_BINDMOUNT (1 << 0) +#define FSA_MOUNTFLAGS_GLOBAL (1 << 1) + +int IOSUHAX_Open(const char *dev); // if dev == NULL the default path /dev/iosuhax will be used +int IOSUHAX_Close(void); + +int IOSUHAX_memwrite(uint32_t address, const uint8_t * buffer, uint32_t size); // IOSU external input +int IOSUHAX_memread(uint32_t address, uint8_t * out_buffer, uint32_t size); // IOSU external output +int IOSUHAX_memcpy(uint32_t dst, uint32_t src, uint32_t size); // IOSU internal memcpy only + +int IOSUHAX_SVC(uint32_t svc_id, uint32_t * args, uint32_t arg_cnt); + +int IOSUHAX_FSA_Open(); +int IOSUHAX_FSA_Close(int fsaFd); + +int IOSUHAX_FSA_Mount(int fsaFd, const char* device_path, const char* volume_path, uint32_t flags, const char* arg_string, int arg_string_len); +int IOSUHAX_FSA_Unmount(int fsaFd, const char* path, uint32_t flags); +int IOSUHAX_FSA_FlushVolume(int fsaFd, const char* volume_path); + +int IOSUHAX_FSA_GetDeviceInfo(int fsaFd, const char* device_path, int type, uint32_t* out_data); + +int IOSUHAX_FSA_MakeDir(int fsaFd, const char* path, uint32_t flags); +int IOSUHAX_FSA_OpenDir(int fsaFd, const char* path, int* outHandle); +int IOSUHAX_FSA_ReadDir(int fsaFd, int handle, directoryEntry_s* out_data); +int IOSUHAX_FSA_RewindDir(int fsaFd, int dirHandle); +int IOSUHAX_FSA_CloseDir(int fsaFd, int handle); +int IOSUHAX_FSA_ChangeDir(int fsaFd, const char *path); + +int IOSUHAX_FSA_OpenFile(int fsaFd, const char* path, const char* mode, int* outHandle); +int IOSUHAX_FSA_ReadFile(int fsaFd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags); +int IOSUHAX_FSA_WriteFile(int fsaFd, const void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags); +int IOSUHAX_FSA_StatFile(int fsaFd, int fileHandle, fileStat_s* out_data); +int IOSUHAX_FSA_CloseFile(int fsaFd, int fileHandle); +int IOSUHAX_FSA_SetFilePos(int fsaFd, int fileHandle, uint32_t position); +int IOSUHAX_FSA_GetStat(int fsaFd, const char *path, fileStat_s* out_data); +int IOSUHAX_FSA_Remove(int fsaFd, const char *path); +int IOSUHAX_FSA_ChangeMode(int fsaFd, const char* path, int mode); + +int IOSUHAX_FSA_RawOpen(int fsaFd, const char* device_path, int* outHandle); +int IOSUHAX_FSA_RawRead(int fsaFd, void* data, uint32_t block_size, uint32_t block_cnt, uint64_t sector_offset, int device_handle); +int IOSUHAX_FSA_RawWrite(int fsaFd, const void* data, uint32_t block_size, uint32_t block_cnt, uint64_t sector_offset, int device_handle); +int IOSUHAX_FSA_RawClose(int fsaFd, int device_handle); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/libiosuhax/iosuhax_devoptab.c b/deps/libiosuhax/iosuhax_devoptab.c new file mode 100644 index 0000000000..75875bb3ca --- /dev/null +++ b/deps/libiosuhax/iosuhax_devoptab.c @@ -0,0 +1,974 @@ +/*************************************************************************** + * Copyright (C) 2015 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "os_functions.h" +#include "iosuhax.h" + +typedef struct _fs_dev_private_t { + char *mount_path; + int fsaFd; + int mounted; + void *pMutex; +} fs_dev_private_t; + +typedef struct _fs_dev_file_state_t { + fs_dev_private_t *dev; + int fd; /* File descriptor */ + int flags; /* Opening flags */ + int read; /* True if allowed to read from file */ + int write; /* True if allowed to write to file */ + int append; /* True if allowed to append to file */ + uint32_t pos; /* Current position within the file (in bytes) */ + uint32_t len; /* Total length of the file (in bytes) */ + struct _fs_dev_file_state_t *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ + struct _fs_dev_file_state_t *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ +} fs_dev_file_state_t; + +typedef struct _fs_dev_dir_entry_t { + fs_dev_private_t *dev; + int dirHandle; +} fs_dev_dir_entry_t; + +static fs_dev_private_t *fs_dev_get_device_data(const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + /* Get the device name from the path */ + strncpy(name, path, 127); + strtok(name, ":/"); + + // Search the devoptab table for the specified device name + // NOTE: We do this manually due to a 'bug' in GetDeviceOpTab + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 3; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + return (fs_dev_private_t *)devoptab->deviceData; + } + } + } + + return NULL; +} + +static char *fs_dev_real_path (const char *path, fs_dev_private_t *dev) +{ + /* Sanity check */ + if (!path) + return NULL; + + /* Move the path pointer to the start of the actual path */ + if (strchr(path, ':') != NULL) { + path = strchr(path, ':') + 1; + } + + int mount_len = strlen(dev->mount_path); + + char *new_name = (char*)malloc(mount_len + strlen(path) + 1); + if(new_name) { + strcpy(new_name, dev->mount_path); + strcpy(new_name + mount_len, path); + return new_name; + } + return new_name; +} + +static int fs_dev_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fileStruct; + + file->dev = dev; + /* Determine which mode the file is opened for */ + file->flags = flags; + + const char *mode_str; + + if ((flags & 0x03) == O_RDONLY) { + file->read = 1; + file->write = 0; + file->append = 0; + mode_str = "r"; + } else if ((flags & 0x03) == O_WRONLY) { + file->read = 0; + file->write = 1; + file->append = (flags & O_APPEND); + mode_str = file->append ? "a" : "w"; + } else if ((flags & 0x03) == O_RDWR) { + file->read = 1; + file->write = 1; + file->append = (flags & O_APPEND); + mode_str = file->append ? "a+" : "r+"; + } else { + r->_errno = EACCES; + return -1; + } + + int fd = -1; + + OSLockMutex(dev->pMutex); + + char *real_path = fs_dev_real_path(path, dev); + if(!path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = IOSUHAX_FSA_OpenFile(dev->fsaFd, real_path, mode_str, &fd); + + free(real_path); + + if(result == 0) + { + fileStat_s stats; + result = IOSUHAX_FSA_StatFile(dev->fsaFd, fd, &stats); + if(result != 0) { + IOSUHAX_FSA_CloseFile(dev->fsaFd, fd); + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } + file->fd = fd; + file->pos = 0; + file->len = stats.size; + OSUnlockMutex(dev->pMutex); + return (int)file; + } + + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; +} + + +static int fs_dev_close_r (struct _reent *r, int fd) +{ + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(file->dev->pMutex); + + int result = IOSUHAX_FSA_CloseFile(file->dev->fsaFd, file->fd); + + OSUnlockMutex(file->dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return -1; + } + return 0; +} + +static off_t fs_dev_seek_r (struct _reent *r, int fd, off_t pos, int dir) +{ + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return 0; + } + + OSLockMutex(file->dev->pMutex); + + switch(dir) + { + case SEEK_SET: + file->pos = pos; + break; + case SEEK_CUR: + file->pos += pos; + break; + case SEEK_END: + file->pos = file->len + pos; + break; + default: + r->_errno = EINVAL; + return -1; + } + + int result = IOSUHAX_FSA_SetFilePos(file->dev->fsaFd, file->fd, file->pos); + + OSUnlockMutex(file->dev->pMutex); + + if(result == 0) + { + return file->pos; + } + + return result; +} + +static ssize_t fs_dev_write_r (struct _reent *r, int fd, const char *ptr, size_t len) +{ + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return 0; + } + + if(!file->write) + { + r->_errno = EACCES; + return 0; + } + + OSLockMutex(file->dev->pMutex); + + size_t done = 0; + + while(done < len) + { + size_t write_size = len - done; + + int result = IOSUHAX_FSA_WriteFile(file->dev->fsaFd, ptr + done, 0x01, write_size, file->fd, 0); + if(result < 0) + { + r->_errno = result; + break; + } + else if(result == 0) + { + if(write_size > 0) + done = 0; + break; + } + else + { + done += result; + file->pos += result; + } + } + + OSUnlockMutex(file->dev->pMutex); + return done; +} + +static ssize_t fs_dev_read_r (struct _reent *r, int fd, char *ptr, size_t len) +{ + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return 0; + } + + if(!file->read) + { + r->_errno = EACCES; + return 0; + } + + OSLockMutex(file->dev->pMutex); + + size_t done = 0; + + while(done < len) + { + size_t read_size = len - done; + + int result = IOSUHAX_FSA_ReadFile(file->dev->fsaFd, ptr + done, 0x01, read_size, file->fd, 0); + if(result < 0) + { + r->_errno = result; + done = 0; + break; + } + else if(result == 0) + { + /*! TODO: error on read_size > 0 */ + break; + } + else + { + done += result; + file->pos += result; + } + } + + OSUnlockMutex(file->dev->pMutex); + return done; +} + + +static int fs_dev_fstat_r (struct _reent *r, int fd, struct stat *st) +{ + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(file->dev->pMutex); + + /* Zero out the stat buffer */ + memset(st, 0, sizeof(struct stat)); + + fileStat_s stats; + int result = IOSUHAX_FSA_StatFile(file->dev->fsaFd, fd, &stats); + if(result != 0) { + r->_errno = result; + OSUnlockMutex(file->dev->pMutex); + return -1; + } + + st->st_mode = S_IFREG; + st->st_size = stats.size; + st->st_blocks = (stats.size + 511) >> 9; + st->st_nlink = 1; + + /* Fill in the generic entry stats */ + st->st_dev = stats.id; + st->st_uid = stats.owner_id; + st->st_gid = stats.group_id; + st->st_ino = stats.id; + st->st_atime = stats.mtime; + st->st_ctime = stats.ctime; + st->st_mtime = stats.mtime; + OSUnlockMutex(file->dev->pMutex); + return 0; +} + +static int fs_dev_ftruncate_r (struct _reent *r, int fd, off_t len) +{ + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + r->_errno = ENOTSUP; + /* TODO */ + return -1; +} + +static int fs_dev_fsync_r (struct _reent *r, int fd) +{ + fs_dev_file_state_t *file = (fs_dev_file_state_t *)fd; + if(!file->dev) { + r->_errno = ENODEV; + return -1; + } + + r->_errno = ENOTSUP; + /* TODO */ + return -1; +} + +static int fs_dev_stat_r (struct _reent *r, const char *path, struct stat *st) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + // Zero out the stat buffer + memset(st, 0, sizeof(struct stat)); + + char *real_path = fs_dev_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + fileStat_s stats; + + int result = IOSUHAX_FSA_GetStat(dev->fsaFd, real_path, &stats); + + free(real_path); + + if(result < 0) { + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } + + // mark root also as directory + st->st_mode = ((stats.flag & 0x80000000) || (strlen(dev->mount_path) + 1 == strlen(real_path)))? S_IFDIR : S_IFREG; + st->st_nlink = 1; + st->st_size = stats.size; + st->st_blocks = (stats.size + 511) >> 9; + // Fill in the generic entry stats + st->st_dev = stats.id; + st->st_uid = stats.owner_id; + st->st_gid = stats.group_id; + st->st_ino = stats.id; + st->st_atime = stats.mtime; + st->st_ctime = stats.ctime; + st->st_mtime = stats.mtime; + + OSUnlockMutex(dev->pMutex); + + return 0; +} + +static int fs_dev_link_r (struct _reent *r, const char *existing, const char *newLink) +{ + r->_errno = ENOTSUP; + return -1; +} + +static int fs_dev_unlink_r (struct _reent *r, const char *name) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(name); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_path = fs_dev_real_path(name, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = IOSUHAX_FSA_Remove(dev->fsaFd, real_path); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return result; +} + +static int fs_dev_chdir_r (struct _reent *r, const char *name) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(name); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_path = fs_dev_real_path(name, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = IOSUHAX_FSA_ChangeDir(dev->fsaFd, real_path); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int fs_dev_rename_r (struct _reent *r, const char *oldName, const char *newName) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(oldName); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_oldpath = fs_dev_real_path(oldName, dev); + if(!real_oldpath) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + char *real_newpath = fs_dev_real_path(newName, dev); + if(!real_newpath) { + r->_errno = ENOMEM; + free(real_oldpath); + OSUnlockMutex(dev->pMutex); + return -1; + } + + //! TODO + int result = -ENOTSUP; + + free(real_oldpath); + free(real_newpath); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; + +} + +static int fs_dev_mkdir_r (struct _reent *r, const char *path, int mode) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_path = fs_dev_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = IOSUHAX_FSA_MakeDir(dev->fsaFd, real_path, mode); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int fs_dev_chmod_r (struct _reent *r, const char *path, int mode) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + char *real_path = fs_dev_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + int result = IOSUHAX_FSA_ChangeMode(dev->fsaFd, real_path, mode); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) { + r->_errno = result; + return -1; + } + + return 0; +} + +static int fs_dev_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dev->pMutex); + + // Zero out the stat buffer + memset(buf, 0, sizeof(struct statvfs)); + + char *real_path = fs_dev_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return -1; + } + + uint64_t size; + + int result = IOSUHAX_FSA_GetDeviceInfo(dev->fsaFd, real_path, 0x00, (uint32_t*)&size); + + free(real_path); + + if(result < 0) { + r->_errno = result; + OSUnlockMutex(dev->pMutex); + return -1; + } + + // File system block size + buf->f_bsize = 512; + + // Fundamental file system block size + buf->f_frsize = 512; + + // Total number of blocks on file system in units of f_frsize + buf->f_blocks = size >> 9; // this is unknown + + // Free blocks available for all and for non-privileged processes + buf->f_bfree = buf->f_bavail = size >> 9; + + // Number of inodes at this point in time + buf->f_files = 0xffffffff; + + // Free inodes available for all and for non-privileged processes + buf->f_ffree = 0xffffffff; + + // File system id + buf->f_fsid = (int)dev; + + // Bit mask of f_flag values. + buf->f_flag = 0; + + // Maximum length of filenames + buf->f_namemax = 255; + + OSUnlockMutex(dev->pMutex); + + return 0; +} + +static DIR_ITER *fs_dev_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) +{ + fs_dev_private_t *dev = fs_dev_get_device_data(path); + if(!dev) { + r->_errno = ENODEV; + return NULL; + } + + fs_dev_dir_entry_t *dirIter = (fs_dev_dir_entry_t *)dirState->dirStruct; + + OSLockMutex(dev->pMutex); + + char *real_path = fs_dev_real_path(path, dev); + if(!real_path) { + r->_errno = ENOMEM; + OSUnlockMutex(dev->pMutex); + return NULL; + } + + int dirHandle; + + int result = IOSUHAX_FSA_OpenDir(dev->fsaFd, real_path, &dirHandle); + + free(real_path); + + OSUnlockMutex(dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return NULL; + } + + dirIter->dev = dev; + dirIter->dirHandle = dirHandle; + + return dirState; +} + +static int fs_dev_dirclose_r (struct _reent *r, DIR_ITER *dirState) +{ + fs_dev_dir_entry_t *dirIter = (fs_dev_dir_entry_t *)dirState->dirStruct; + if(!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dirIter->dev->pMutex); + + int result = IOSUHAX_FSA_CloseDir(dirIter->dev->fsaFd, dirIter->dirHandle); + + OSUnlockMutex(dirIter->dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return -1; + } + return 0; +} + +static int fs_dev_dirreset_r (struct _reent *r, DIR_ITER *dirState) +{ + fs_dev_dir_entry_t *dirIter = (fs_dev_dir_entry_t *)dirState->dirStruct; + if(!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dirIter->dev->pMutex); + + int result = IOSUHAX_FSA_RewindDir(dirIter->dev->fsaFd, dirIter->dirHandle); + + OSUnlockMutex(dirIter->dev->pMutex); + + if(result < 0) + { + r->_errno = result; + return -1; + } + return 0; +} + +static int fs_dev_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) +{ + fs_dev_dir_entry_t *dirIter = (fs_dev_dir_entry_t *)dirState->dirStruct; + if(!dirIter->dev) { + r->_errno = ENODEV; + return -1; + } + + OSLockMutex(dirIter->dev->pMutex); + + directoryEntry_s * dir_entry = malloc(sizeof(directoryEntry_s)); + + int result = IOSUHAX_FSA_ReadDir(dirIter->dev->fsaFd, dirIter->dirHandle, dir_entry); + if(result < 0) + { + free(dir_entry); + r->_errno = result; + OSUnlockMutex(dirIter->dev->pMutex); + return -1; + } + + // Fetch the current entry + strcpy(filename, dir_entry->name); + + if(st) + { + memset(st, 0, sizeof(struct stat)); + st->st_mode = (dir_entry->stat.flag & 0x80000000) ? S_IFDIR : S_IFREG; + st->st_nlink = 1; + st->st_size = dir_entry->stat.size; + st->st_blocks = (dir_entry->stat.size + 511) >> 9; + st->st_dev = dir_entry->stat.id; + st->st_uid = dir_entry->stat.owner_id; + st->st_gid = dir_entry->stat.group_id; + st->st_ino = dir_entry->stat.id; + st->st_atime = dir_entry->stat.mtime; + st->st_ctime = dir_entry->stat.ctime; + st->st_mtime = dir_entry->stat.mtime; + } + + free(dir_entry); + OSUnlockMutex(dirIter->dev->pMutex); + return 0; +} + +// NTFS device driver devoptab +static const devoptab_t devops_fs = { + NULL, /* Device name */ + sizeof (fs_dev_file_state_t), + fs_dev_open_r, + fs_dev_close_r, + fs_dev_write_r, + fs_dev_read_r, + fs_dev_seek_r, + fs_dev_fstat_r, + fs_dev_stat_r, + fs_dev_link_r, + fs_dev_unlink_r, + fs_dev_chdir_r, + fs_dev_rename_r, + fs_dev_mkdir_r, + sizeof (fs_dev_dir_entry_t), + fs_dev_diropen_r, + fs_dev_dirreset_r, + fs_dev_dirnext_r, + fs_dev_dirclose_r, + fs_dev_statvfs_r, + fs_dev_ftruncate_r, + fs_dev_fsync_r, + fs_dev_chmod_r, + NULL, /* fs_dev_fchmod_r */ + NULL /* Device data */ +}; + +static int fs_dev_add_device (const char *name, const char *mount_path, int fsaFd, int isMounted) +{ + devoptab_t *dev = NULL; + char *devname = NULL; + char *devpath = NULL; + int i; + + // Sanity check + if (!name) { + errno = EINVAL; + return -1; + } + + // Allocate a devoptab for this device + dev = (devoptab_t *) malloc(sizeof(devoptab_t) + strlen(name) + 1); + if (!dev) { + errno = ENOMEM; + return -1; + } + + // Use the space allocated at the end of the devoptab for storing the device name + devname = (char*)(dev + 1); + strcpy(devname, name); + + // create private data + fs_dev_private_t *priv = (fs_dev_private_t *)malloc(sizeof(fs_dev_private_t) + strlen(mount_path) + 1); + if(!priv) { + free(dev); + errno = ENOMEM; + return -1; + } + + devpath = (char*)(priv+1); + strcpy(devpath, mount_path); + + // setup private data + priv->mount_path = devpath; + priv->fsaFd = fsaFd; + priv->mounted = isMounted; + priv->pMutex = malloc(OS_MUTEX_SIZE); + + if(!priv->pMutex) { + free(dev); + free(priv); + errno = ENOMEM; + return -1; + } + + OSInitMutex(priv->pMutex); + + // Setup the devoptab + memcpy(dev, &devops_fs, sizeof(devoptab_t)); + dev->name = devname; + dev->deviceData = priv; + + // Add the device to the devoptab table (if there is a free slot) + for (i = 3; i < STD_MAX; i++) { + if (devoptab_list[i] == devoptab_list[0]) { + devoptab_list[i] = dev; + return 0; + } + } + + // failure, free all memory + free(priv); + free(dev); + + // If we reach here then there are no free slots in the devoptab table for this device + errno = EADDRNOTAVAIL; + return -1; +} + +static int fs_dev_remove_device (const char *path) +{ + const devoptab_t *devoptab = NULL; + char name[128] = {0}; + int i; + + // Get the device name from the path + strncpy(name, path, 127); + strtok(name, ":/"); + + // Find and remove the specified device from the devoptab table + // NOTE: We do this manually due to a 'bug' in RemoveDevice + // which ignores names with suffixes and causes names + // like "ntfs" and "ntfs1" to be seen as equals + for (i = 3; i < STD_MAX; i++) { + devoptab = devoptab_list[i]; + if (devoptab && devoptab->name) { + if (strcmp(name, devoptab->name) == 0) { + devoptab_list[i] = devoptab_list[0]; + + if(devoptab->deviceData) + { + fs_dev_private_t *priv = (fs_dev_private_t *)devoptab->deviceData; + + if(priv->mounted) + IOSUHAX_FSA_Unmount(priv->fsaFd, priv->mount_path, 2); + + if(priv->pMutex) + free(priv->pMutex); + free(devoptab->deviceData); + } + + free((devoptab_t*)devoptab); + return 0; + } + } + } + + return -1; +} + +int mount_fs(const char *virt_name, int fsaFd, const char *dev_path, const char *mount_path) +{ + int isMounted = 0; + + if(dev_path) + { + isMounted = 1; + + int res = IOSUHAX_FSA_Mount(fsaFd, dev_path, mount_path, 2, 0, 0); + if(res != 0) + { + return res; + } + } + + return fs_dev_add_device(virt_name, mount_path, fsaFd, isMounted); +} + +int unmount_fs(const char *virt_name) +{ + return fs_dev_remove_device(virt_name); +} diff --git a/deps/libiosuhax/iosuhax_devoptab.h b/deps/libiosuhax/iosuhax_devoptab.h new file mode 100644 index 0000000000..2c7d5d227c --- /dev/null +++ b/deps/libiosuhax/iosuhax_devoptab.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2015 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#ifndef __IOSUHAX_DEVOPTAB_H_ +#define __IOSUHAX_DEVOPTAB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//! virtual name example: sd or odd (for sd:/ or odd:/ access) +//! fsaFd: fd received by IOSUHAX_FSA_Open(); +//! dev_path: (optional) if a device should be mounted to the mount_path. If NULL no IOSUHAX_FSA_Mount is not executed. +//! mount_path: path to map to virtual device name +int mount_fs(const char *virt_name, int fsaFd, const char *dev_path, const char *mount_path); +int unmount_fs(const char *virt_name); + +#ifdef __cplusplus +} +#endif + +#endif // __IOSUHAX_DEVOPTAB_H_ diff --git a/deps/libiosuhax/iosuhax_disc_interface.c b/deps/libiosuhax/iosuhax_disc_interface.c new file mode 100644 index 0000000000..90c206df87 --- /dev/null +++ b/deps/libiosuhax/iosuhax_disc_interface.c @@ -0,0 +1,262 @@ +/*************************************************************************** + * Copyright (C) 2016 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#include +#include +#include "iosuhax.h" +#include "iosuhax_disc_interface.h" + +#define FSA_REF_SD 0x01 +#define FSA_REF_USB 0x02 + +static int initialized = 0; + +static int fsaFdSd = 0; +static int fsaFdUsb = 0; +static int sdioFd = 0; +static int usbFd = 0; + +static void IOSUHAX_disc_io_initialize(void) +{ + if(initialized == 0) + { + initialized = 1; + fsaFdSd = -1; + fsaFdUsb = -1; + sdioFd = -1; + usbFd = -1; + } +} + +static bool IOSUHAX_disc_io_fsa_open(int fsaFd) +{ + IOSUHAX_disc_io_initialize(); + + if(IOSUHAX_Open(NULL) < 0) + return false; + + if(fsaFd == FSA_REF_SD) + { + if(fsaFdSd < 0) + { + fsaFdSd = IOSUHAX_FSA_Open(); + } + + if(fsaFdSd >= 0) + return true; + } + else if(fsaFd == FSA_REF_USB) + { + if(fsaFdUsb < 0) + { + fsaFdUsb = IOSUHAX_FSA_Open(); + } + + if(fsaFdUsb >= 0) + return true; + } + + return false; +} + +static void IOSUHAX_disc_io_fsa_close(int fsaFd) +{ + if(fsaFd == FSA_REF_SD) + { + if(fsaFdSd >= 0) + { + IOSUHAX_FSA_Close(fsaFdSd); + fsaFdSd = -1; + } + } + else if(fsaFd == FSA_REF_USB) + { + if(fsaFdUsb >= 0) + { + IOSUHAX_FSA_Close(fsaFdUsb); + fsaFdUsb = -1; + } + } +} + +static bool IOSUHAX_sdio_startup(void) +{ + if(!IOSUHAX_disc_io_fsa_open(FSA_REF_SD)) + return false; + + if(sdioFd < 0) + { + int res = IOSUHAX_FSA_RawOpen(fsaFdSd, "/dev/sdcard01", &sdioFd); + if(res < 0) + { + IOSUHAX_disc_io_fsa_close(FSA_REF_SD); + sdioFd = -1; + } + } + + return (sdioFd >= 0); +} + +static bool IOSUHAX_sdio_isInserted(void) +{ + //! TODO: check for SD card inserted with IOSUHAX_FSA_GetDeviceInfo() + return initialized && (fsaFdSd >= 0) && (sdioFd >= 0); +} + +static bool IOSUHAX_sdio_clearStatus(void) +{ + return true; +} + +static bool IOSUHAX_sdio_shutdown(void) +{ + if(!IOSUHAX_sdio_isInserted()) + return false; + + IOSUHAX_FSA_RawClose(fsaFdSd, sdioFd); + IOSUHAX_disc_io_fsa_close(FSA_REF_SD); + sdioFd = -1; + return true; +} + +static bool IOSUHAX_sdio_readSectors(uint32_t sector, uint32_t numSectors, void* buffer) +{ + if(!IOSUHAX_sdio_isInserted()) + return false; + + int res = IOSUHAX_FSA_RawRead(fsaFdSd, buffer, 512, numSectors, sector, sdioFd); + if(res < 0) + { + return false; + } + + return true; +} + +static bool IOSUHAX_sdio_writeSectors(uint32_t sector, uint32_t numSectors, const void* buffer) +{ + if(!IOSUHAX_sdio_isInserted()) + return false; + + int res = IOSUHAX_FSA_RawWrite(fsaFdSd, buffer, 512, numSectors, sector, sdioFd); + if(res < 0) + { + return false; + } + + return true; +} + +const DISC_INTERFACE IOSUHAX_sdio_disc_interface = +{ + DEVICE_TYPE_WII_U_SD, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_U_SD, + IOSUHAX_sdio_startup, + IOSUHAX_sdio_isInserted, + IOSUHAX_sdio_readSectors, + IOSUHAX_sdio_writeSectors, + IOSUHAX_sdio_clearStatus, + IOSUHAX_sdio_shutdown +}; + +static bool IOSUHAX_usb_startup(void) +{ + if(!IOSUHAX_disc_io_fsa_open(FSA_REF_USB)) + return false; + + if(usbFd < 0) + { + int res = IOSUHAX_FSA_RawOpen(fsaFdUsb, "/dev/usb01", &usbFd); + if(res < 0) + { + res = IOSUHAX_FSA_RawOpen(fsaFdUsb, "/dev/usb02", &usbFd); + if(res < 0) + { + IOSUHAX_disc_io_fsa_close(FSA_REF_USB); + usbFd = -1; + } + } + } + return (usbFd >= 0); +} + +static bool IOSUHAX_usb_isInserted(void) +{ + return initialized && (fsaFdUsb >= 0) && (usbFd >= 0); +} + +static bool IOSUHAX_usb_clearStatus(void) +{ + return true; +} + +static bool IOSUHAX_usb_shutdown(void) +{ + if(!IOSUHAX_usb_isInserted()) + return false; + + IOSUHAX_FSA_RawClose(fsaFdUsb, usbFd); + IOSUHAX_disc_io_fsa_close(FSA_REF_USB); + usbFd = -1; + return true; +} + +static bool IOSUHAX_usb_readSectors(uint32_t sector, uint32_t numSectors, void* buffer) +{ + if(!IOSUHAX_usb_isInserted()) + return false; + + int res = IOSUHAX_FSA_RawRead(fsaFdUsb, buffer, 512, numSectors, sector, usbFd); + if(res < 0) + { + return false; + } + + return true; +} + +static bool IOSUHAX_usb_writeSectors(uint32_t sector, uint32_t numSectors, const void* buffer) +{ + if(!IOSUHAX_usb_isInserted()) + return false; + + int res = IOSUHAX_FSA_RawWrite(fsaFdUsb, buffer, 512, numSectors, sector, usbFd); + if(res < 0) + { + return false; + } + + return true; +} + +const DISC_INTERFACE IOSUHAX_usb_disc_interface = +{ + DEVICE_TYPE_WII_U_USB, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_U_USB, + IOSUHAX_usb_startup, + IOSUHAX_usb_isInserted, + IOSUHAX_usb_readSectors, + IOSUHAX_usb_writeSectors, + IOSUHAX_usb_clearStatus, + IOSUHAX_usb_shutdown +}; diff --git a/deps/libiosuhax/iosuhax_disc_interface.h b/deps/libiosuhax/iosuhax_disc_interface.h new file mode 100644 index 0000000000..0ce479547b --- /dev/null +++ b/deps/libiosuhax/iosuhax_disc_interface.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2016 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#ifndef _IOSUHAX_DISC_INTERFACE_H_ +#define _IOSUHAX_DISC_INTERFACE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEVICE_TYPE_WII_U_SD (('W'<<24)|('U'<<16)|('S'<<8)|'D') +#define DEVICE_TYPE_WII_U_USB (('W'<<24)|('U'<<16)|('S'<<8)|'B') +#define FEATURE_WII_U_SD 0x00001000 +#define FEATURE_WII_U_USB 0x00002000 + +#ifndef OGC_DISC_IO_INCLUDE +typedef uint32_t sec_t; + +#define FEATURE_MEDIUM_CANREAD 0x00000001 +#define FEATURE_MEDIUM_CANWRITE 0x00000002 + +typedef bool (* FN_MEDIUM_STARTUP)(void) ; +typedef bool (* FN_MEDIUM_ISINSERTED)(void) ; +typedef bool (* FN_MEDIUM_READSECTORS)(uint32_t sector, uint32_t numSectors, void* buffer) ; +typedef bool (* FN_MEDIUM_WRITESECTORS)(uint32_t sector, uint32_t numSectors, const void* buffer) ; +typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ; +typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ; + +struct DISC_INTERFACE_STRUCT { + unsigned long ioType ; + unsigned long features ; + FN_MEDIUM_STARTUP startup ; + FN_MEDIUM_ISINSERTED isInserted ; + FN_MEDIUM_READSECTORS readSectors ; + FN_MEDIUM_WRITESECTORS writeSectors ; + FN_MEDIUM_CLEARSTATUS clearStatus ; + FN_MEDIUM_SHUTDOWN shutdown ; +} ; + +typedef struct DISC_INTERFACE_STRUCT DISC_INTERFACE ; +#endif + +extern const DISC_INTERFACE IOSUHAX_sdio_disc_interface; +extern const DISC_INTERFACE IOSUHAX_usb_disc_interface; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/libiosuhax/os_functions.h b/deps/libiosuhax/os_functions.h new file mode 100644 index 0000000000..5f5d3a1865 --- /dev/null +++ b/deps/libiosuhax/os_functions.h @@ -0,0 +1,44 @@ +#ifndef __OS_FUNCTIONS_H_ +#define __OS_FUNCTIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define OS_MUTEX_SIZE 44 + +#ifndef __WUT__ +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +//! Mutex functions +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +extern void (* OSInitMutex)(void* mutex); +extern void (* OSLockMutex)(void* mutex); +extern void (* OSUnlockMutex)(void* mutex); + +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +//! IOS function +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +extern int (*IOS_Ioctl)(int fd, unsigned int request, void *input_buffer,unsigned int input_buffer_len, void *output_buffer, unsigned int output_buffer_len); +extern int (*IOS_Open)(char *path, unsigned int mode); +extern int (*IOS_Close)(int fd); +#else +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +//! Mutex functions +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +extern void OSInitMutex(void* mutex); +extern void OSLockMutex(void* mutex); +extern void OSUnlockMutex(void* mutex); + +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +//! IOS function +//!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +extern int IOS_Ioctl(int fd, unsigned int request, void *input_buffer,unsigned int input_buffer_len, void *output_buffer, unsigned int output_buffer_len); +extern int IOS_Open(char *path, unsigned int mode); +extern int IOS_Close(int fd); +#endif // __WUT__ + +#ifdef __cplusplus +} +#endif + +#endif // __OS_FUNCTIONS_H_