├── .gitignore ├── Makefile ├── gba └── Makefile ├── gp2x └── Makefile ├── include └── fat.h ├── libfat.pnproj ├── libogc └── Makefile ├── nds └── Makefile └── source ├── bit_ops.h ├── cache.c ├── cache.h ├── common.h ├── directory.c ├── directory.h ├── disc.c ├── disc.h ├── fatdir.c ├── fatdir.h ├── fatfile.c ├── fatfile.h ├── file_allocation_table.c ├── file_allocation_table.h ├── filetime.c ├── filetime.h ├── libfat.c ├── lock.c ├── lock.h ├── mem_allocate.h ├── partition.c └── partition.h /.gitignore: -------------------------------------------------------------------------------- 1 | gba/lib 2 | gba/release 3 | include/libfatversion.h 4 | libogc/cube_release 5 | libogc/lib 6 | libogc/wii_release 7 | nds/lib 8 | nds/release 9 | gp2x/lib 10 | gp2x/gp2x_release 11 | distribute 12 | gba/include 13 | libogc/include 14 | nds/include 15 | gp2x/include -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITPRO)),) 2 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPro) 3 | endif 4 | 5 | export TOPDIR := $(CURDIR) 6 | 7 | export LIBFAT_MAJOR := 1 8 | export LIBFAT_MINOR := 1 9 | export LIBFAT_PATCH := 5 10 | 11 | export VERSTRING := $(LIBFAT_MAJOR).$(LIBFAT_MINOR).$(LIBFAT_PATCH) 12 | 13 | export DESTDIR := $(DESTDIR) 14 | 15 | default: release 16 | 17 | all: release dist 18 | 19 | release: nds-release gba-release cube-release wii-release gp2x-release 20 | 21 | ogc-release: include/libfatversion.h cube-release wii-release 22 | 23 | nds-release: include/libfatversion.h 24 | $(MAKE) -C nds BUILD=release 25 | 26 | gba-release: include/libfatversion.h 27 | $(MAKE) -C gba BUILD=release 28 | 29 | cube-release: include/libfatversion.h 30 | $(MAKE) -C libogc PLATFORM=cube BUILD=cube_release 31 | 32 | wii-release: include/libfatversion.h 33 | $(MAKE) -C libogc PLATFORM=wii BUILD=wii_release 34 | 35 | gp2x-release: include/libfatversion.h 36 | $(MAKE) -C gp2x PLATFORM=gp2x BUILD=gp2x_release 37 | 38 | debug: nds-debug gba-debug cube-debug wii-debug gp2x-debug 39 | 40 | ogc-debug: cube-debug wii-debug 41 | 42 | nds-debug: include/libfatversion.h 43 | $(MAKE) -C nds BUILD=debug 44 | 45 | gba-debug: include/libfatversion.h 46 | $(MAKE) -C gba BUILD=debug 47 | 48 | cube-debug: include/libfatversion.h 49 | $(MAKE) -C libogc PLATFORM=cube BUILD=wii_debug 50 | 51 | wii-debug: include/libfatversion.h 52 | $(MAKE) -C libogc PLATFORM=wii BUILD=cube_debug 53 | 54 | gp2x-debug: include/libfatversion.h 55 | $(MAKE) -C gp2x BUILD=debug 56 | 57 | clean: nds-clean gba-clean ogc-clean gp2x-clean 58 | 59 | nds-clean: 60 | $(MAKE) -C nds clean 61 | 62 | gba-clean: 63 | $(MAKE) -C gba clean 64 | 65 | ogc-clean: 66 | $(MAKE) -C libogc clean 67 | 68 | gp2x-clean: 69 | $(MAKE) -C gp2x clean 70 | 71 | dist-bin: nds-dist-bin gba-dist-bin ogc-dist-bin gp2x-dist-bin 72 | 73 | nds-dist-bin: include/libfatversion.h nds-release distribute/$(VERSTRING) 74 | $(MAKE) -C nds dist-bin 75 | 76 | gba-dist-bin: include/libfatversion.h gba-release distribute/$(VERSTRING) 77 | $(MAKE) -C gba dist-bin 78 | 79 | ogc-dist-bin: include/libfatversion.h ogc-release distribute/$(VERSTRING) 80 | $(MAKE) -C libogc dist-bin 81 | 82 | gp2x-dist-bin: include/libfatversion.h gp2x-release distribute/$(VERSTRING) 83 | $(MAKE) -C gp2x dist-bin 84 | 85 | dist-src: distribute/$(VERSTRING) 86 | @tar --exclude=.svn --exclude=*CVS* -cvjf distribute/$(VERSTRING)/libfat-src-$(VERSTRING).tar.bz2 \ 87 | source include Makefile \ 88 | nds/Makefile \ 89 | gba/Makefile \ 90 | libogc/Makefile \ 91 | gp2x/Makefile 92 | 93 | dist: dist-bin dist-src 94 | 95 | distribute/$(VERSTRING): 96 | @[ -d $@ ] || mkdir -p $@ 97 | 98 | include/libfatversion.h : Makefile 99 | @echo "#ifndef __LIBFATVERSION_H__" > $@ 100 | @echo "#define __LIBFATVERSION_H__" >> $@ 101 | @echo >> $@ 102 | @echo "#define _LIBFAT_MAJOR_ $(LIBFAT_MAJOR)" >> $@ 103 | @echo "#define _LIBFAT_MINOR_ $(LIBFAT_MINOR)" >> $@ 104 | @echo "#define _LIBFAT_PATCH_ $(LIBFAT_PATCH)" >> $@ 105 | @echo >> $@ 106 | @echo '#define _LIBFAT_STRING "libFAT Release '$(LIBFAT_MAJOR).$(LIBFAT_MINOR).$(LIBFAT_PATCH)'"' >> $@ 107 | @echo >> $@ 108 | @echo "#endif // __LIBFATVERSION_H__" >> $@ 109 | 110 | install: nds-install gba-install ogc-install gp2x-install 111 | 112 | nds-install: nds-release 113 | $(MAKE) -C nds install 114 | 115 | gba-install: gba-release 116 | $(MAKE) -C gba install 117 | 118 | ogc-install: cube-release wii-release 119 | $(MAKE) -C libogc install 120 | 121 | gp2x-install: gp2x-release 122 | $(MAKE) -C gp2x install 123 | -------------------------------------------------------------------------------- /gba/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) 6 | endif 7 | 8 | include $(DEVKITARM)/gba_rules 9 | 10 | 11 | #--------------------------------------------------------------------------------- 12 | # BUILD is the directory where object files & intermediate files will be placed 13 | # SOURCES is a list of directories containing source code 14 | # INCLUDES is a list of directories containing extra header files 15 | # DATA is a list of directories containing binary files 16 | # LIB is where the built library will be placed 17 | # all directories are relative to this makefile 18 | #--------------------------------------------------------------------------------- 19 | BUILD ?= release 20 | SOURCES := ../source 21 | INCLUDES := ../include 22 | DATA := 23 | LIB := $(TOPDIR)/gba/lib 24 | 25 | #--------------------------------------------------------------------------------- 26 | # options for code generation 27 | #--------------------------------------------------------------------------------- 28 | ARCH := -mthumb -mthumb-interwork 29 | 30 | CFLAGS := -g -Wall -O2\ 31 | -mcpu=arm7tdmi -mtune=arm7tdmi\ 32 | -fomit-frame-pointer\ 33 | -ffast-math \ 34 | $(ARCH) 35 | 36 | CFLAGS += $(INCLUDE) -DGBA 37 | CXXFLAGS := $(CFLAGS) 38 | 39 | ASFLAGS := -g $(ARCH) 40 | 41 | ifneq ($(BUILD),debug) 42 | export GBABIN := $(LIB)/libfat.a 43 | else 44 | export GBABIN := $(LIB)/libfatd.a 45 | CFLAGS += -DFAT_DEBUG 46 | endif 47 | 48 | 49 | #--------------------------------------------------------------------------------- 50 | # any extra libraries we wish to link with the project 51 | #--------------------------------------------------------------------------------- 52 | LIBS := 53 | #-lnds9 54 | 55 | #--------------------------------------------------------------------------------- 56 | # list of directories containing libraries, this must be the top level containing 57 | # include and lib 58 | #--------------------------------------------------------------------------------- 59 | LIBDIRS := $(LIBGBA) 60 | 61 | #--------------------------------------------------------------------------------- 62 | # no real need to edit anything past this point unless you need to add additional 63 | # rules for different file extensions 64 | #--------------------------------------------------------------------------------- 65 | ifneq ($(BUILD),$(notdir $(CURDIR))) 66 | #--------------------------------------------------------------------------------- 67 | 68 | export TOPDIR ?= $(CURDIR)/.. 69 | 70 | export DEPSDIR := $(CURDIR)/$(BUILD) 71 | 72 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 73 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 74 | 75 | export CC := $(PREFIX)gcc 76 | export CXX := $(PREFIX)g++ 77 | export AR := $(PREFIX)ar 78 | export OBJCOPY := $(PREFIX)objcopy 79 | 80 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 81 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 82 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 83 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 84 | 85 | #--------------------------------------------------------------------------------- 86 | # use CXX for linking C++ projects, CC for standard C 87 | #--------------------------------------------------------------------------------- 88 | ifeq ($(strip $(CPPFILES)),) 89 | #--------------------------------------------------------------------------------- 90 | export LD := $(CC) 91 | #--------------------------------------------------------------------------------- 92 | else 93 | #--------------------------------------------------------------------------------- 94 | export LD := $(CXX) 95 | #--------------------------------------------------------------------------------- 96 | endif 97 | #--------------------------------------------------------------------------------- 98 | 99 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 100 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 101 | 102 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 103 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 104 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 105 | -I$(CURDIR)/$(BUILD) 106 | 107 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 108 | 109 | .PHONY: $(BUILD) clean 110 | 111 | #--------------------------------------------------------------------------------- 112 | $(BUILD): 113 | @[ -d $@ ] || mkdir -p $@ 114 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 115 | 116 | #--------------------------------------------------------------------------------- 117 | clean: 118 | @echo clean ... 119 | @rm -fr debug release $(LIB) include 120 | 121 | all: $(GBABIN) 122 | 123 | dist-bin: 124 | @mkdir -p include 125 | @cp $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h include 126 | @tar --exclude=.svn --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(VERSTRING)/libfat-gba-$(VERSTRING).tar.bz2 include lib 127 | 128 | install: 129 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/libgba/lib 130 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/libgba/include 131 | @cp lib/libfat.a $(DESTDIR)$(DEVKITPRO)/libgba/lib 132 | @cp $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h $(DESTDIR)$(DEVKITPRO)/libgba/include 133 | 134 | #--------------------------------------------------------------------------------- 135 | else 136 | 137 | DEPENDS := $(OFILES:.o=.d) 138 | 139 | #--------------------------------------------------------------------------------- 140 | # main targets 141 | #--------------------------------------------------------------------------------- 142 | $(GBABIN) : $(OFILES) $(LIB) 143 | @rm -f "$(GBABIN)" 144 | @$(AR) rcs "$(GBABIN)" $(OFILES) 145 | @echo built ... $(notdir $@) 146 | 147 | $(LIB): 148 | mkdir $(LIB) 149 | 150 | #--------------------------------------------------------------------------------- 151 | # you need a rule like this for each extension you use as binary data 152 | #--------------------------------------------------------------------------------- 153 | %.bin.o : %.bin 154 | #--------------------------------------------------------------------------------- 155 | @echo $(notdir $<) 156 | @$(bin2o) 157 | 158 | 159 | -include $(DEPENDS) 160 | 161 | #--------------------------------------------------------------------------------------- 162 | endif 163 | #--------------------------------------------------------------------------------------- 164 | 165 | -------------------------------------------------------------------------------- /gp2x/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) 6 | endif 7 | 8 | include $(DEVKITARM)/gp2x_rules 9 | 10 | 11 | #--------------------------------------------------------------------------------- 12 | # BUILD is the directory where object files & intermediate files will be placed 13 | # SOURCES is a list of directories containing source code 14 | # INCLUDES is a list of directories containing extra header files 15 | # DATA is a list of directories containing binary files 16 | # LIB is where the built library will be placed 17 | # all directories are relative to this makefile 18 | #--------------------------------------------------------------------------------- 19 | BUILD ?= release 20 | SOURCES := ../source 21 | INCLUDES := ../include 22 | DATA := 23 | LIB := $(TOPDIR)/gp2x/lib 24 | 25 | #--------------------------------------------------------------------------------- 26 | # options for code generation 27 | #--------------------------------------------------------------------------------- 28 | ARCH := -mthumb -mthumb-interwork 29 | 30 | CFLAGS := -g -Wall -O2\ 31 | -mcpu=arm9tdmi -mtune=arm9tdmi\ 32 | -fomit-frame-pointer\ 33 | -ffast-math \ 34 | $(ARCH) 35 | 36 | CFLAGS += $(INCLUDE) -DGP2X 37 | CXXFLAGS := $(CFLAGS) 38 | 39 | ASFLAGS := -g $(ARCH) 40 | 41 | ifneq ($(BUILD),debug) 42 | export GP2XBIN := $(LIB)/libfat.a 43 | else 44 | export GP2XBIN := $(LIB)/libfatd.a 45 | CFLAGS += -DFAT_DEBUG 46 | endif 47 | 48 | 49 | #--------------------------------------------------------------------------------- 50 | # any extra libraries we wish to link with the project 51 | #--------------------------------------------------------------------------------- 52 | LIBS := 53 | #-lnds9 54 | 55 | #--------------------------------------------------------------------------------- 56 | # list of directories containing libraries, this must be the top level containing 57 | # include and lib 58 | #--------------------------------------------------------------------------------- 59 | LIBDIRS := $(LIBORCUS) 60 | 61 | #--------------------------------------------------------------------------------- 62 | # no real need to edit anything past this point unless you need to add additional 63 | # rules for different file extensions 64 | #--------------------------------------------------------------------------------- 65 | ifneq ($(BUILD),$(notdir $(CURDIR))) 66 | #--------------------------------------------------------------------------------- 67 | 68 | export TOPDIR ?= $(CURDIR)/.. 69 | 70 | export DEPSDIR := $(CURDIR)/$(BUILD) 71 | 72 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 73 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 74 | 75 | export CC := $(PREFIX)gcc 76 | export CXX := $(PREFIX)g++ 77 | export AR := $(PREFIX)ar 78 | export OBJCOPY := $(PREFIX)objcopy 79 | 80 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 81 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 82 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 83 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 84 | 85 | #--------------------------------------------------------------------------------- 86 | # use CXX for linking C++ projects, CC for standard C 87 | #--------------------------------------------------------------------------------- 88 | ifeq ($(strip $(CPPFILES)),) 89 | #--------------------------------------------------------------------------------- 90 | export LD := $(CC) 91 | #--------------------------------------------------------------------------------- 92 | else 93 | #--------------------------------------------------------------------------------- 94 | export LD := $(CXX) 95 | #--------------------------------------------------------------------------------- 96 | endif 97 | #--------------------------------------------------------------------------------- 98 | 99 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 100 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 101 | 102 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 103 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 104 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 105 | -I$(CURDIR)/$(BUILD) 106 | 107 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 108 | 109 | .PHONY: $(BUILD) clean 110 | 111 | #--------------------------------------------------------------------------------- 112 | $(BUILD): 113 | @[ -d $@ ] || mkdir -p $@ 114 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 115 | 116 | #--------------------------------------------------------------------------------- 117 | clean: 118 | @echo clean ... 119 | @rm -fr debug gp2x_release $(LIB) include 120 | 121 | all: $(GP2XBIN) 122 | 123 | dist-bin: 124 | @mkdir -p include 125 | @cp $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h include 126 | @tar --exclude=.svn --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(VERSTRING)/libfat-gp2x-$(VERSTRING).tar.bz2 include lib 127 | 128 | install: 129 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/liborcus/lib 130 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/liborcus/include 131 | @cp lib/libfat.a $(DESTDIR)$(DEVKITPRO)/liborcus/lib 132 | @cp $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h $(DESTDIR)$(DEVKITPRO)/liborcus/include 133 | 134 | #--------------------------------------------------------------------------------- 135 | else 136 | 137 | DEPENDS := $(OFILES:.o=.d) 138 | 139 | #--------------------------------------------------------------------------------- 140 | # main targets 141 | #--------------------------------------------------------------------------------- 142 | $(GP2XBIN) : $(OFILES) $(LIB) 143 | @rm -f "$(GP2XBIN)" 144 | @$(AR) rcs "$(GP2XBIN)" $(OFILES) 145 | @echo built ... $(notdir $@) 146 | 147 | $(LIB): 148 | mkdir $(LIB) 149 | 150 | #--------------------------------------------------------------------------------- 151 | # you need a rule like this for each extension you use as binary data 152 | #--------------------------------------------------------------------------------- 153 | %.bin.o : %.bin 154 | #--------------------------------------------------------------------------------- 155 | @echo $(notdir $<) 156 | @$(bin2o) 157 | 158 | 159 | -include $(DEPENDS) 160 | 161 | #--------------------------------------------------------------------------------------- 162 | endif 163 | #--------------------------------------------------------------------------------------- 164 | 165 | -------------------------------------------------------------------------------- /include/fat.h: -------------------------------------------------------------------------------- 1 | /* 2 | fat.h 3 | Simple functionality for startup, mounting and unmounting of FAT-based devices. 4 | 5 | Copyright (c) 2006 - 2012 6 | Michael "Chishm" Chisholm 7 | Dave "WinterMute" Murphy 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation and/or 16 | other materials provided with the distribution. 17 | 3. The name of the author may not be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 22 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #ifndef _LIBFAT_H 33 | #define _LIBFAT_H 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #include "libfatversion.h" 40 | 41 | // When compiling for NDS, make sure NDS is defined 42 | #ifndef NDS 43 | #if defined ARM9 || defined ARM7 44 | #define NDS 45 | #endif 46 | #endif 47 | 48 | #include 49 | 50 | #if defined(__gamecube__) || defined (__wii__) 51 | # include 52 | #else 53 | # ifdef NDS 54 | # include 55 | # else 56 | # include 57 | # endif 58 | #endif 59 | 60 | /* 61 | Initialise any inserted block-devices. 62 | Add the fat device driver to the devoptab, making it available for standard file functions. 63 | cacheSize: The number of pages to allocate for each inserted block-device 64 | setAsDefaultDevice: if true, make this the default device driver for file operations 65 | */ 66 | extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); 67 | 68 | /* 69 | Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. 70 | */ 71 | extern bool fatInitDefault (void); 72 | 73 | /* 74 | Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". 75 | You can then access the filesystem using "name:/". 76 | This will mount the active partition or the first valid partition on the disc, 77 | and will use a cache size optimized for the host system. 78 | */ 79 | extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); 80 | 81 | /* 82 | Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". 83 | You can then access the filesystem using "name:/". 84 | If startSector = 0, it will mount the active partition of the first valid partition on 85 | the disc. Otherwise it will try to mount the partition starting at startSector. 86 | cacheSize specifies the number of pages to allocate for the cache. 87 | This will not startup the disc, so you need to call interface->startup(); first. 88 | */ 89 | extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); 90 | 91 | /* 92 | Unmount the partition specified by name. 93 | If there are open files, it will attempt to synchronise them to disc. 94 | */ 95 | extern void fatUnmount (const char* name); 96 | 97 | /* 98 | Get Volume Label 99 | */ 100 | extern void fatGetVolumeLabel (const char* name, char *label); 101 | 102 | // File attributes 103 | #define ATTR_ARCHIVE 0x20 // Archive 104 | #define ATTR_DIRECTORY 0x10 // Directory 105 | #define ATTR_VOLUME 0x08 // Volume 106 | #define ATTR_SYSTEM 0x04 // System 107 | #define ATTR_HIDDEN 0x02 // Hidden 108 | #define ATTR_READONLY 0x01 // Read only 109 | 110 | /* 111 | Methods to modify DOS File Attributes 112 | */ 113 | int FAT_getAttr(const char *file); 114 | int FAT_setAttr(const char *file, uint8_t attr ); 115 | 116 | #define LIBFAT_FEOS_MULTICWD 117 | 118 | #ifdef __cplusplus 119 | } 120 | #endif 121 | 122 | #endif // _LIBFAT_H 123 | -------------------------------------------------------------------------------- /libfat.pnproj: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libogc/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITPPC)),) 5 | $(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC) 6 | endif 7 | 8 | ifeq ($(PLATFORM),wii) 9 | include $(DEVKITPPC)/wii_rules 10 | endif 11 | 12 | ifeq ($(PLATFORM),cube) 13 | include $(DEVKITPPC)/gamecube_rules 14 | endif 15 | 16 | #--------------------------------------------------------------------------------- 17 | # BUILD is the directory where object files & intermediate files will be placed 18 | # SOURCES is a list of directories containing source code 19 | # INCLUDES is a list of directories containing extra header files 20 | # DATA is a list of directories containing binary files 21 | # LIBDIR is where the built library will be placed 22 | # all directories are relative to this makefile 23 | #--------------------------------------------------------------------------------- 24 | BUILD ?= wii_release 25 | SOURCES := ../source 26 | INCLUDES := ../include 27 | DATA := 28 | LIBDIR := $(TOPDIR)/libogc/lib 29 | 30 | #--------------------------------------------------------------------------------- 31 | # options for code generation 32 | #--------------------------------------------------------------------------------- 33 | CFLAGS = -g -Os -Wall $(MACHDEP) $(INCLUDE) 34 | CXXFLAGS = $(CFLAGS) 35 | 36 | ASFLAGS := -g 37 | 38 | ifneq ($(BUILD),debug) 39 | export CUBEBIN := $(LIBDIR)/$(PLATFORM)/libfat.a 40 | else 41 | export CUBEBIN := $(LIBDIR)/$(PLATFORM)/libfatd.a 42 | CFLAGS += -DFAT_DEBUG 43 | endif 44 | 45 | #--------------------------------------------------------------------------------- 46 | # any extra libraries we wish to link with the project 47 | #--------------------------------------------------------------------------------- 48 | LIBS := 49 | 50 | #--------------------------------------------------------------------------------- 51 | # list of directories containing libraries, this must be the top level containing 52 | # include and lib 53 | #--------------------------------------------------------------------------------- 54 | LIBDIRS := 55 | 56 | #--------------------------------------------------------------------------------- 57 | # no real need to edit anything past this point unless you need to add additional 58 | # rules for different file extensions 59 | #--------------------------------------------------------------------------------- 60 | ifneq ($(BUILD),$(notdir $(CURDIR))) 61 | #--------------------------------------------------------------------------------- 62 | 63 | export TOPDIR ?= $(CURDIR)/.. 64 | 65 | 66 | export DEPSDIR := $(CURDIR)/$(BUILD) 67 | 68 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 69 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 70 | 71 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 72 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 73 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 74 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 75 | 76 | 77 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 78 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 79 | 80 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 81 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 82 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 83 | -I$(CURDIR)/$(BUILD) \ 84 | -I$(LIBOGC_INC) 85 | 86 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ 87 | -L$(LIBOGC_LIB) 88 | 89 | .PHONY: $(BUILD) clean 90 | 91 | #--------------------------------------------------------------------------------- 92 | $(BUILD): 93 | @[ -d $@ ] || mkdir -p $@ 94 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 95 | 96 | #--------------------------------------------------------------------------------- 97 | clean: 98 | @echo clean ... 99 | @rm -fr wii_debug wii_release cube_debug cube_release $(LIBDIR) include 100 | 101 | all: $(CUBEBIN) 102 | 103 | dist-bin: 104 | @mkdir -p include 105 | @cp $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h include 106 | @tar --exclude=.svn --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(VERSTRING)/libfat-ogc-$(VERSTRING).tar.bz2 include lib 107 | 108 | install: 109 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/libogc/lib/cube 110 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/libogc/lib/wii 111 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/libogc/include 112 | @cp lib/wii/libfat.a $(DESTDIR)$(DEVKITPRO)/libogc/lib/wii 113 | @cp lib/cube/libfat.a $(DESTDIR)$(DEVKITPRO)/libogc/lib/cube 114 | @cp $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h $(DESTDIR)$(DEVKITPRO)/libogc/include 115 | 116 | #--------------------------------------------------------------------------------- 117 | else 118 | 119 | DEPENDS := $(OFILES:.o=.d) 120 | 121 | #--------------------------------------------------------------------------------- 122 | # main targets 123 | #--------------------------------------------------------------------------------- 124 | $(CUBEBIN) : $(OFILES) $(LIBDIR)/$(PLATFORM) 125 | @rm -f "$(CUBEBIN)" 126 | @$(AR) rcs "$(CUBEBIN)" $(OFILES) 127 | @echo built ... $(notdir $@) 128 | 129 | $(LIBDIR)/$(PLATFORM): 130 | mkdir -p $(LIBDIR)/$(PLATFORM) 131 | 132 | -include $(DEPENDS) 133 | 134 | #--------------------------------------------------------------------------------------- 135 | endif 136 | #--------------------------------------------------------------------------------------- 137 | 138 | -------------------------------------------------------------------------------- /nds/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) 6 | endif 7 | 8 | include $(DEVKITARM)/ds_rules 9 | 10 | #--------------------------------------------------------------------------------- 11 | # BUILD is the directory where object files & intermediate files will be placed 12 | # SOURCES is a list of directories containing source code 13 | # INCLUDES is a list of directories containing extra header files 14 | # DATA is a list of directories containing binary files 15 | # LIB is where the built library will be placed 16 | # all directories are relative to this makefile 17 | #--------------------------------------------------------------------------------- 18 | BUILD ?= release 19 | SOURCES := ../source source 20 | INCLUDES := ../include 21 | DATA := 22 | LIB := $(TOPDIR)/nds/lib 23 | 24 | #--------------------------------------------------------------------------------- 25 | # options for code generation 26 | #--------------------------------------------------------------------------------- 27 | ARCH := -mthumb -mthumb-interwork 28 | 29 | CFLAGS := -g -Wall -O2 \ 30 | -ffunction-sections -fdata-sections \ 31 | -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ 32 | -ffast-math \ 33 | $(ARCH) 34 | 35 | CFLAGS += $(INCLUDE) -DARM9 -DNDS 36 | CXXFLAGS := $(CFLAGS) 37 | 38 | ASFLAGS := -g $(ARCH) 39 | 40 | ifneq ($(BUILD),debug) 41 | export ARM9BIN := $(LIB)/libfat.a 42 | else 43 | export ARM9BIN := $(LIB)/libfatd.a 44 | CFLAGS += -DFAT_DEBUG 45 | endif 46 | 47 | #--------------------------------------------------------------------------------- 48 | # any extra libraries we wish to link with the project 49 | #--------------------------------------------------------------------------------- 50 | LIBS := 51 | 52 | #--------------------------------------------------------------------------------- 53 | # list of directories containing libraries, this must be the top level containing 54 | # include and lib 55 | #--------------------------------------------------------------------------------- 56 | LIBDIRS := $(LIBNDS) 57 | 58 | #--------------------------------------------------------------------------------- 59 | # no real need to edit anything past this point unless you need to add additional 60 | # rules for different file extensions 61 | #--------------------------------------------------------------------------------- 62 | ifneq ($(BUILD),$(notdir $(CURDIR))) 63 | #--------------------------------------------------------------------------------- 64 | 65 | export TOPDIR ?= $(CURDIR)/.. 66 | 67 | 68 | export DEPSDIR := $(CURDIR)/$(BUILD) 69 | 70 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 71 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 72 | 73 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 74 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 75 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 76 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 77 | 78 | 79 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 80 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 81 | 82 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 83 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 84 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 85 | -I$(CURDIR)/$(BUILD) 86 | 87 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 88 | 89 | .PHONY: $(BUILD) clean 90 | 91 | #--------------------------------------------------------------------------------- 92 | $(BUILD): 93 | @[ -d $@ ] || mkdir -p $@ 94 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 95 | 96 | #--------------------------------------------------------------------------------- 97 | clean: 98 | @echo clean ... 99 | @rm -fr debug release $(LIB) include 100 | 101 | all: $(ARM9BIN) 102 | 103 | dist-bin: 104 | @mkdir -p include 105 | @cp $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h include 106 | @tar --exclude=.svn --exclude=*CVS* -cvjf $(TOPDIR)/distribute/$(VERSTRING)/libfat-nds-$(VERSTRING).tar.bz2 include lib 107 | 108 | install: 109 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/libnds/lib 110 | @mkdir -p $(DESTDIR)$(DEVKITPRO)/libnds/include 111 | @cp -v lib/libfat.a $(DESTDIR)$(DEVKITPRO)/libnds/lib 112 | @cp -v $(TOPDIR)/include/fat.h $(TOPDIR)/include/libfatversion.h $(DESTDIR)$(DEVKITPRO)/libnds/include 113 | 114 | #--------------------------------------------------------------------------------- 115 | else 116 | 117 | DEPENDS := $(OFILES:.o=.d) 118 | 119 | #--------------------------------------------------------------------------------- 120 | # main targets 121 | #--------------------------------------------------------------------------------- 122 | $(ARM9BIN) : $(OFILES) $(LIB) 123 | @rm -f "$(ARM9BIN)" 124 | @$(AR) rcs "$(ARM9BIN)" $(OFILES) 125 | @echo built ... $(notdir $@) 126 | 127 | $(LIB): 128 | mkdir $(LIB) 129 | 130 | 131 | -include $(DEPENDS) 132 | 133 | #--------------------------------------------------------------------------------------- 134 | endif 135 | #--------------------------------------------------------------------------------------- 136 | 137 | -------------------------------------------------------------------------------- /source/bit_ops.h: -------------------------------------------------------------------------------- 1 | /* 2 | bit_ops.h 3 | Functions for dealing with conversion of data between types 4 | 5 | Copyright (c) 2006 Michael "Chishm" Chisholm 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 19 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 20 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef _BIT_OPS_H 30 | #define _BIT_OPS_H 31 | 32 | #include 33 | 34 | /*----------------------------------------------------------------- 35 | Functions to deal with little endian values stored in uint8_t arrays 36 | -----------------------------------------------------------------*/ 37 | static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { 38 | return ( item[offset] | (item[offset + 1] << 8)); 39 | } 40 | 41 | static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { 42 | return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); 43 | } 44 | 45 | static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { 46 | item[offset] = (uint8_t) value; 47 | item[offset + 1] = (uint8_t)(value >> 8); 48 | } 49 | 50 | static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { 51 | item[offset] = (uint8_t) value; 52 | item[offset + 1] = (uint8_t)(value >> 8); 53 | item[offset + 2] = (uint8_t)(value >> 16); 54 | item[offset + 3] = (uint8_t)(value >> 24); 55 | } 56 | 57 | #endif // _BIT_OPS_H 58 | -------------------------------------------------------------------------------- /source/cache.c: -------------------------------------------------------------------------------- 1 | /* 2 | cache.c 3 | The cache is not visible to the user. It should be flushed 4 | when any file is closed or changes are made to the filesystem. 5 | 6 | This cache implements a least-used-page replacement policy. This will 7 | distribute sectors evenly over the pages, so if less than the maximum 8 | pages are used at once, they should all eventually remain in the cache. 9 | This also has the benefit of throwing out old sectors, so as not to keep 10 | too many stale pages around. 11 | 12 | Copyright (c) 2006 Michael "Chishm" Chisholm 13 | 14 | Redistribution and use in source and binary forms, with or without modification, 15 | are permitted provided that the following conditions are met: 16 | 17 | 1. Redistributions of source code must retain the above copyright notice, 18 | this list of conditions and the following disclaimer. 19 | 2. Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation and/or 21 | other materials provided with the distribution. 22 | 3. The name of the author may not be used to endorse or promote products derived 23 | from this software without specific prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 26 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 27 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 28 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 33 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #include 37 | #include 38 | 39 | #include "common.h" 40 | #include "cache.h" 41 | #include "disc.h" 42 | 43 | #include "mem_allocate.h" 44 | #include "bit_ops.h" 45 | #include "file_allocation_table.h" 46 | 47 | #define CACHE_FREE UINT_MAX 48 | 49 | CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector) { 50 | CACHE* cache; 51 | unsigned int i; 52 | CACHE_ENTRY* cacheEntries; 53 | 54 | if (numberOfPages < 2) { 55 | numberOfPages = 2; 56 | } 57 | 58 | if (sectorsPerPage < 8) { 59 | sectorsPerPage = 8; 60 | } 61 | 62 | cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE)); 63 | if (cache == NULL) { 64 | return NULL; 65 | } 66 | 67 | cache->disc = discInterface; 68 | cache->endOfPartition = endOfPartition; 69 | cache->numberOfPages = numberOfPages; 70 | cache->sectorsPerPage = sectorsPerPage; 71 | cache->bytesPerSector = bytesPerSector; 72 | 73 | 74 | cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages); 75 | if (cacheEntries == NULL) { 76 | _FAT_mem_free (cache); 77 | return NULL; 78 | } 79 | 80 | for (i = 0; i < numberOfPages; i++) { 81 | cacheEntries[i].sector = CACHE_FREE; 82 | cacheEntries[i].count = 0; 83 | cacheEntries[i].last_access = 0; 84 | cacheEntries[i].dirty = false; 85 | cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * bytesPerSector ); 86 | } 87 | 88 | cache->cacheEntries = cacheEntries; 89 | 90 | return cache; 91 | } 92 | 93 | void _FAT_cache_destructor (CACHE* cache) { 94 | unsigned int i; 95 | // Clear out cache before destroying it 96 | _FAT_cache_flush(cache); 97 | 98 | // Free memory in reverse allocation order 99 | for (i = 0; i < cache->numberOfPages; i++) { 100 | _FAT_mem_free (cache->cacheEntries[i].cache); 101 | } 102 | _FAT_mem_free (cache->cacheEntries); 103 | _FAT_mem_free (cache); 104 | } 105 | 106 | 107 | static u32 accessCounter = 0; 108 | 109 | static u32 accessTime(){ 110 | accessCounter++; 111 | return accessCounter; 112 | } 113 | 114 | 115 | static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector) 116 | { 117 | unsigned int i; 118 | CACHE_ENTRY* cacheEntries = cache->cacheEntries; 119 | unsigned int numberOfPages = cache->numberOfPages; 120 | unsigned int sectorsPerPage = cache->sectorsPerPage; 121 | 122 | bool foundFree = false; 123 | unsigned int oldUsed = 0; 124 | unsigned int oldAccess = UINT_MAX; 125 | 126 | for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { 128 | cacheEntries[i].last_access = accessTime(); 129 | return &(cacheEntries[i]); 130 | } 131 | 132 | if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc,cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; 141 | cacheEntries[oldUsed].dirty = false; 142 | } 143 | 144 | sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size 145 | sec_t next_page = sector + sectorsPerPage; 146 | if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; 147 | 148 | if(!_FAT_disc_readSectors(cache->disc,sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; 149 | 150 | cacheEntries[oldUsed].sector = sector; 151 | cacheEntries[oldUsed].count = next_page-sector; 152 | cacheEntries[oldUsed].last_access = accessTime(); 153 | 154 | return &(cacheEntries[oldUsed]); 155 | } 156 | 157 | bool _FAT_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) 158 | { 159 | sec_t sec; 160 | sec_t secs_to_read; 161 | CACHE_ENTRY *entry; 162 | uint8_t *dest = (uint8_t *)buffer; 163 | 164 | while(numSectors>0) { 165 | entry = _FAT_cache_getPage(cache,sector); 166 | if(entry==NULL) return false; 167 | 168 | sec = sector - entry->sector; 169 | secs_to_read = entry->count - sec; 170 | if(secs_to_read>numSectors) secs_to_read = numSectors; 171 | 172 | memcpy(dest,entry->cache + (sec*cache->bytesPerSector),(secs_to_read*cache->bytesPerSector)); 173 | 174 | dest += (secs_to_read*cache->bytesPerSector); 175 | sector += secs_to_read; 176 | numSectors -= secs_to_read; 177 | } 178 | 179 | return true; 180 | } 181 | 182 | /* 183 | Reads some data from a cache page, determined by the sector number 184 | */ 185 | bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) 186 | { 187 | sec_t sec; 188 | CACHE_ENTRY *entry; 189 | 190 | if (offset + size > cache->bytesPerSector) return false; 191 | 192 | entry = _FAT_cache_getPage(cache,sector); 193 | if(entry==NULL) return false; 194 | 195 | sec = sector - entry->sector; 196 | memcpy(buffer,entry->cache + ((sec*cache->bytesPerSector) + offset),size); 197 | 198 | return true; 199 | } 200 | 201 | bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { 202 | uint8_t buf[4]; 203 | if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; 204 | 205 | switch(num_bytes) { 206 | case 1: *value = buf[0]; break; 207 | case 2: *value = u8array_to_u16(buf,0); break; 208 | case 4: *value = u8array_to_u32(buf,0); break; 209 | default: return false; 210 | } 211 | return true; 212 | } 213 | 214 | /* 215 | Writes some data to a cache page, making sure it is loaded into memory first. 216 | */ 217 | bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) 218 | { 219 | sec_t sec; 220 | CACHE_ENTRY *entry; 221 | 222 | if (offset + size > cache->bytesPerSector) return false; 223 | 224 | entry = _FAT_cache_getPage(cache,sector); 225 | if(entry==NULL) return false; 226 | 227 | sec = sector - entry->sector; 228 | memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); 229 | 230 | entry->dirty = true; 231 | return true; 232 | } 233 | 234 | bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { 235 | uint8_t buf[4] = {0, 0, 0, 0}; 236 | 237 | switch(size) { 238 | case 1: buf[0] = value; break; 239 | case 2: u16_to_u8array(buf, 0, value); break; 240 | case 4: u32_to_u8array(buf, 0, value); break; 241 | default: return false; 242 | } 243 | 244 | return _FAT_cache_writePartialSector(cache, buf, sector, offset, size); 245 | } 246 | 247 | /* 248 | Writes some data to a cache page, zeroing out the page first 249 | */ 250 | bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) 251 | { 252 | sec_t sec; 253 | CACHE_ENTRY *entry; 254 | 255 | if (offset + size > cache->bytesPerSector) return false; 256 | 257 | entry = _FAT_cache_getPage(cache,sector); 258 | if(entry==NULL) return false; 259 | 260 | sec = sector - entry->sector; 261 | memset(entry->cache + (sec*cache->bytesPerSector),0,cache->bytesPerSector); 262 | memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); 263 | 264 | entry->dirty = true; 265 | return true; 266 | } 267 | 268 | 269 | bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) 270 | { 271 | sec_t sec; 272 | sec_t secs_to_write; 273 | CACHE_ENTRY* entry; 274 | const uint8_t *src = (const uint8_t *)buffer; 275 | 276 | while(numSectors>0) 277 | { 278 | entry = _FAT_cache_getPage(cache,sector); 279 | if(entry==NULL) return false; 280 | 281 | sec = sector - entry->sector; 282 | secs_to_write = entry->count - sec; 283 | if(secs_to_write>numSectors) secs_to_write = numSectors; 284 | 285 | memcpy(entry->cache + (sec*cache->bytesPerSector),src,(secs_to_write*cache->bytesPerSector)); 286 | 287 | src += (secs_to_write*cache->bytesPerSector); 288 | sector += secs_to_write; 289 | numSectors -= secs_to_write; 290 | 291 | entry->dirty = true; 292 | } 293 | return true; 294 | } 295 | 296 | /* 297 | Flushes all dirty pages to disc, clearing the dirty flag. 298 | */ 299 | bool _FAT_cache_flush (CACHE* cache) { 300 | unsigned int i; 301 | 302 | for (i = 0; i < cache->numberOfPages; i++) { 303 | if (cache->cacheEntries[i].dirty) { 304 | if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { 305 | return false; 306 | } 307 | } 308 | cache->cacheEntries[i].dirty = false; 309 | } 310 | 311 | return true; 312 | } 313 | 314 | void _FAT_cache_invalidate (CACHE* cache) { 315 | unsigned int i; 316 | _FAT_cache_flush(cache); 317 | for (i = 0; i < cache->numberOfPages; i++) { 318 | cache->cacheEntries[i].sector = CACHE_FREE; 319 | cache->cacheEntries[i].last_access = 0; 320 | cache->cacheEntries[i].count = 0; 321 | cache->cacheEntries[i].dirty = false; 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /source/cache.h: -------------------------------------------------------------------------------- 1 | /* 2 | cache.h 3 | The cache is not visible to the user. It should be flushed 4 | when any file is closed or changes are made to the filesystem. 5 | 6 | This cache implements a least-used-page replacement policy. This will 7 | distribute sectors evenly over the pages, so if less than the maximum 8 | pages are used at once, they should all eventually remain in the cache. 9 | This also has the benefit of throwing out old sectors, so as not to keep 10 | too many stale pages around. 11 | 12 | Copyright (c) 2006 Michael "Chishm" Chisholm 13 | 14 | Redistribution and use in source and binary forms, with or without modification, 15 | are permitted provided that the following conditions are met: 16 | 17 | 1. Redistributions of source code must retain the above copyright notice, 18 | this list of conditions and the following disclaimer. 19 | 2. Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation and/or 21 | other materials provided with the distribution. 22 | 3. The name of the author may not be used to endorse or promote products derived 23 | from this software without specific prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 26 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 27 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 28 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 33 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | #ifndef _CACHE_H 37 | #define _CACHE_H 38 | 39 | #include "common.h" 40 | #include "disc.h" 41 | 42 | typedef struct { 43 | sec_t sector; 44 | unsigned int count; 45 | unsigned int last_access; 46 | bool dirty; 47 | uint8_t* cache; 48 | } CACHE_ENTRY; 49 | 50 | typedef struct { 51 | const DISC_INTERFACE* disc; 52 | sec_t endOfPartition; 53 | unsigned int numberOfPages; 54 | unsigned int sectorsPerPage; 55 | unsigned int bytesPerSector; 56 | CACHE_ENTRY* cacheEntries; 57 | } CACHE; 58 | 59 | /* 60 | Read data from a sector in the cache 61 | If the sector is not in the cache, it will be swapped in 62 | offset is the position to start reading from 63 | size is the amount of data to read 64 | Precondition: offset + size <= BYTES_PER_READ 65 | */ 66 | bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size); 67 | 68 | bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); 69 | 70 | /* 71 | Write data to a sector in the cache 72 | If the sector is not in the cache, it will be swapped in. 73 | When the sector is swapped out, the data will be written to the disc 74 | offset is the position to start writing to 75 | size is the amount of data to write 76 | Precondition: offset + size <= BYTES_PER_READ 77 | */ 78 | bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); 79 | 80 | bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); 81 | 82 | /* 83 | Write data to a sector in the cache, zeroing the sector first 84 | If the sector is not in the cache, it will be swapped in. 85 | When the sector is swapped out, the data will be written to the disc 86 | offset is the position to start writing to 87 | size is the amount of data to write 88 | Precondition: offset + size <= BYTES_PER_READ 89 | */ 90 | bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); 91 | 92 | /* 93 | Read several sectors from the cache 94 | */ 95 | bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer); 96 | 97 | /* 98 | Read a full sector from the cache 99 | */ 100 | static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) { 101 | return _FAT_cache_readPartialSector (cache, buffer, sector, 0, cache->bytesPerSector); 102 | } 103 | 104 | /* 105 | Write a full sector to the cache 106 | */ 107 | static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) { 108 | return _FAT_cache_writePartialSector (cache, buffer, sector, 0, cache->bytesPerSector); 109 | } 110 | 111 | bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer); 112 | 113 | /* 114 | Write any dirty sectors back to disc and clear out the contents of the cache 115 | */ 116 | bool _FAT_cache_flush (CACHE* cache); 117 | 118 | /* 119 | Clear out the contents of the cache without writing any dirty sectors first 120 | */ 121 | void _FAT_cache_invalidate (CACHE* cache); 122 | 123 | CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector); 124 | 125 | void _FAT_cache_destructor (CACHE* cache); 126 | 127 | #endif // _CACHE_H 128 | 129 | -------------------------------------------------------------------------------- /source/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | common.h 3 | Common definitions and included files for the FATlib 4 | 5 | Copyright (c) 2006 Michael "Chishm" Chisholm 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 19 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 20 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef _COMMON_H 30 | #define _COMMON_H 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | // When compiling for NDS, make sure NDS is defined 37 | #ifndef NDS 38 | #if defined ARM9 || defined ARM7 39 | #define NDS 40 | #endif 41 | #endif 42 | 43 | // Platform specific includes 44 | #if defined(__gamecube__) || defined (__wii__) 45 | #include 46 | #include 47 | #include 48 | #elif defined(NDS) 49 | #include 50 | #include 51 | #include 52 | #elif defined(GBA) 53 | #include 54 | #include 55 | #elif defined(GP2X) 56 | #include 57 | #include 58 | #endif 59 | 60 | // Platform specific options 61 | #if defined (__wii__) 62 | #define DEFAULT_CACHE_PAGES 4 63 | #define DEFAULT_SECTORS_PAGE 64 64 | #define USE_LWP_LOCK 65 | #define USE_RTC_TIME 66 | #elif defined (__gamecube__) 67 | #define DEFAULT_CACHE_PAGES 4 68 | #define DEFAULT_SECTORS_PAGE 64 69 | #define USE_LWP_LOCK 70 | #define USE_RTC_TIME 71 | #elif defined (NDS) 72 | #define DEFAULT_CACHE_PAGES 16 73 | #define DEFAULT_SECTORS_PAGE 8 74 | #define USE_RTC_TIME 75 | #elif defined (GBA) 76 | #define DEFAULT_CACHE_PAGES 2 77 | #define DEFAULT_SECTORS_PAGE 8 78 | #define LIMIT_SECTORS 128 79 | #elif defined (GP2X) 80 | #define DEFAULT_CACHE_PAGES 16 81 | #define DEFAULT_SECTORS_PAGE 8 82 | #endif 83 | 84 | #endif // _COMMON_H 85 | -------------------------------------------------------------------------------- /source/directory.c: -------------------------------------------------------------------------------- 1 | /* 2 | directory.c 3 | Reading, writing and manipulation of the directory structure on 4 | a FAT partition 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "directory.h" 38 | #include "common.h" 39 | #include "partition.h" 40 | #include "file_allocation_table.h" 41 | #include "bit_ops.h" 42 | #include "filetime.h" 43 | 44 | // Directory entry codes 45 | #define DIR_ENTRY_LAST 0x00 46 | #define DIR_ENTRY_FREE 0xE5 47 | 48 | typedef unsigned short ucs2_t; 49 | 50 | // Long file name directory entry 51 | enum LFN_offset { 52 | LFN_offset_ordinal = 0x00, // Position within LFN 53 | LFN_offset_char0 = 0x01, 54 | LFN_offset_char1 = 0x03, 55 | LFN_offset_char2 = 0x05, 56 | LFN_offset_char3 = 0x07, 57 | LFN_offset_char4 = 0x09, 58 | LFN_offset_flag = 0x0B, // Should be equal to ATTRIB_LFN 59 | LFN_offset_reserved1 = 0x0C, // Always 0x00 60 | LFN_offset_checkSum = 0x0D, // Checksum of short file name (alias) 61 | LFN_offset_char5 = 0x0E, 62 | LFN_offset_char6 = 0x10, 63 | LFN_offset_char7 = 0x12, 64 | LFN_offset_char8 = 0x14, 65 | LFN_offset_char9 = 0x16, 66 | LFN_offset_char10 = 0x18, 67 | LFN_offset_reserved2 = 0x1A, // Always 0x0000 68 | LFN_offset_char11 = 0x1C, 69 | LFN_offset_char12 = 0x1E 70 | }; 71 | static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; 72 | 73 | #define LFN_END 0x40 74 | #define LFN_DEL 0x80 75 | 76 | static const char ILLEGAL_ALIAS_CHARACTERS[] = "\\/:;*?\"<>|&+,=[] "; 77 | static const char ILLEGAL_LFN_CHARACTERS[] = "\\/:*?\"<>|"; 78 | 79 | /* 80 | Returns number of UCS-2 characters needed to encode an LFN 81 | Returns -1 if it is an invalid LFN 82 | */ 83 | #define ABOVE_UCS_RANGE 0xF0 84 | static int _FAT_directory_lfnLength (const char* name) { 85 | unsigned int i; 86 | size_t nameLength; 87 | int ucsLength; 88 | const char* tempName = name; 89 | 90 | nameLength = strnlen(name, NAME_MAX); 91 | // Make sure the name is short enough to be valid 92 | if ( nameLength >= NAME_MAX) { 93 | return -1; 94 | } 95 | // Make sure it doesn't contain any invalid characters 96 | if (strpbrk (name, ILLEGAL_LFN_CHARACTERS) != NULL) { 97 | return -1; 98 | } 99 | // Make sure the name doesn't contain any control codes or codes not representable in UCS-2 100 | for (i = 0; i < nameLength; i++) { 101 | unsigned char ch = (unsigned char) name[i]; 102 | if (ch < 0x20 || ch >= ABOVE_UCS_RANGE) { 103 | return -1; 104 | } 105 | } 106 | // Convert to UCS-2 and get the resulting length 107 | ucsLength = mbsrtowcs(NULL, &tempName, MAX_LFN_LENGTH, NULL); 108 | if (ucsLength < 0 || ucsLength >= MAX_LFN_LENGTH) { 109 | return -1; 110 | } 111 | 112 | // Otherwise it is valid 113 | return ucsLength; 114 | } 115 | 116 | /* 117 | Convert a multibyte encoded string into a NUL-terminated UCS-2 string, storing at most len characters 118 | return number of characters stored 119 | */ 120 | static size_t _FAT_directory_mbstoucs2 (ucs2_t* dst, const char* src, size_t len) { 121 | mbstate_t ps = {0}; 122 | wchar_t tempChar; 123 | int bytes; 124 | size_t count = 0; 125 | 126 | while (count < len-1 && *src != '\0') { 127 | bytes = mbrtowc (&tempChar, src, MB_CUR_MAX, &ps); 128 | if (bytes > 0) { 129 | *dst = (ucs2_t)tempChar; 130 | src += bytes; 131 | dst++; 132 | count++; 133 | } else if (bytes == 0) { 134 | break; 135 | } else { 136 | return -1; 137 | } 138 | } 139 | *dst = '\0'; 140 | 141 | return count; 142 | } 143 | 144 | /* 145 | Convert a UCS-2 string into a NUL-terminated multibyte string, storing at most len chars 146 | return number of chars stored, or (size_t)-1 on error 147 | */ 148 | static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len) { 149 | mbstate_t ps = {0}; 150 | size_t count = 0; 151 | int bytes; 152 | char buff[MB_CUR_MAX]; 153 | int i; 154 | 155 | while (count < len - 1 && *src != '\0') { 156 | bytes = wcrtomb (buff, *src, &ps); 157 | if (bytes < 0) { 158 | return -1; 159 | } 160 | if (count + bytes < len && bytes > 0) { 161 | for (i = 0; i < bytes; i++) { 162 | *dst++ = buff[i]; 163 | } 164 | src++; 165 | count += bytes; 166 | } else { 167 | break; 168 | } 169 | } 170 | *dst = L'\0'; 171 | 172 | return count; 173 | } 174 | 175 | /* 176 | Case-independent comparison of two multibyte encoded strings 177 | */ 178 | static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t len1) { 179 | wchar_t wc1, wc2; 180 | mbstate_t ps1 = {0}; 181 | mbstate_t ps2 = {0}; 182 | size_t b1 = 0; 183 | size_t b2 = 0; 184 | 185 | if (len1 == 0) { 186 | return 0; 187 | } 188 | 189 | do { 190 | s1 += b1; 191 | s2 += b2; 192 | b1 = mbrtowc(&wc1, s1, MB_CUR_MAX, &ps1); 193 | b2 = mbrtowc(&wc2, s2, MB_CUR_MAX, &ps2); 194 | if ((int)b1 < 0 || (int)b2 < 0) { 195 | break; 196 | } 197 | len1 -= b1; 198 | } while (len1 > 0 && towlower(wc1) == towlower(wc2) && wc1 != 0); 199 | 200 | return towlower(wc1) - towlower(wc2); 201 | } 202 | 203 | 204 | static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) { 205 | char c; 206 | bool caseInfo; 207 | int i = 0; 208 | int j = 0; 209 | 210 | destName[0] = '\0'; 211 | if (entryData[0] != DIR_ENTRY_FREE) { 212 | if (entryData[0] == '.') { 213 | destName[0] = '.'; 214 | if (entryData[1] == '.') { 215 | destName[1] = '.'; 216 | destName[2] = '\0'; 217 | } else { 218 | destName[1] = '\0'; 219 | } 220 | } else { 221 | // Copy the filename from the dirEntry to the string 222 | caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_BASE; 223 | for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) { 224 | c = entryData[DIR_ENTRY_name + i]; 225 | destName[i] = (caseInfo ? tolower((unsigned char)c) : c); 226 | } 227 | // Copy the extension from the dirEntry to the string 228 | if (entryData[DIR_ENTRY_extension] != ' ') { 229 | destName[i++] = '.'; 230 | caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_EXT; 231 | for ( j = 0; (j < 3) && (entryData[DIR_ENTRY_extension + j] != ' '); j++) { 232 | c = entryData[DIR_ENTRY_extension + j]; 233 | destName[i++] = (caseInfo ? tolower((unsigned char)c) : c); 234 | } 235 | } 236 | destName[i] = '\0'; 237 | } 238 | } 239 | 240 | return (destName[0] != '\0'); 241 | } 242 | 243 | uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData) { 244 | if (partition->filesysType == FS_FAT32) { 245 | // Only use high 16 bits of start cluster when we are certain they are correctly defined 246 | return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16); 247 | } else { 248 | return u8array_to_u16(entryData,DIR_ENTRY_cluster); 249 | } 250 | } 251 | 252 | static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) { 253 | DIR_ENTRY_POSITION position = *entryPosition; 254 | uint32_t tempCluster; 255 | 256 | // Increment offset, wrapping at the end of a sector 257 | ++ position.offset; 258 | if (position.offset == partition->bytesPerSector / DIR_ENTRY_DATA_SIZE) { 259 | position.offset = 0; 260 | // Increment sector when wrapping 261 | ++ position.sector; 262 | // But wrap at the end of a cluster 263 | if ((position.sector == partition->sectorsPerCluster) && (position.cluster != FAT16_ROOT_DIR_CLUSTER)) { 264 | position.sector = 0; 265 | // Move onto the next cluster, making sure there is another cluster to go to 266 | tempCluster = _FAT_fat_nextCluster(partition, position.cluster); 267 | if (tempCluster == CLUSTER_EOF) { 268 | if (extendDirectory) { 269 | tempCluster = _FAT_fat_linkFreeClusterCleared (partition, position.cluster); 270 | if (!_FAT_fat_isValidCluster(partition, tempCluster)) { 271 | return false; // This will only happen if the disc is full 272 | } 273 | } else { 274 | return false; // Got to the end of the directory, not extending it 275 | } 276 | } 277 | position.cluster = tempCluster; 278 | } else if ((position.cluster == FAT16_ROOT_DIR_CLUSTER) && (position.sector == (partition->dataStart - partition->rootDirStart))) { 279 | return false; // Got to end of root directory, can't extend it 280 | } 281 | } 282 | *entryPosition = position; 283 | return true; 284 | } 285 | 286 | bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) { 287 | DIR_ENTRY_POSITION entryStart; 288 | DIR_ENTRY_POSITION entryEnd; 289 | uint8_t entryData[0x20]; 290 | ucs2_t lfn[MAX_LFN_LENGTH]; 291 | bool notFound, found; 292 | int lfnPos; 293 | uint8_t lfnChkSum, chkSum; 294 | bool lfnExists; 295 | int i; 296 | 297 | lfnChkSum = 0; 298 | 299 | entryStart = entry->dataEnd; 300 | 301 | // Make sure we are using the correct root directory, in case of FAT32 302 | if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) { 303 | entryStart.cluster = partition->rootDirCluster; 304 | } 305 | 306 | entryEnd = entryStart; 307 | 308 | lfnExists = false; 309 | 310 | found = false; 311 | notFound = false; 312 | 313 | while (!found && !notFound) { 314 | if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { 315 | notFound = true; 316 | break; 317 | } 318 | 319 | _FAT_cache_readPartialSector (partition->cache, entryData, 320 | _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, 321 | entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 322 | 323 | if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) { 324 | // It's an LFN 325 | if (entryData[LFN_offset_ordinal] & LFN_DEL) { 326 | lfnExists = false; 327 | } else if (entryData[LFN_offset_ordinal] & LFN_END) { 328 | // Last part of LFN, make sure it isn't deleted using previous if(Thanks MoonLight) 329 | entryStart = entryEnd; // This is the start of a directory entry 330 | lfnExists = true; 331 | lfnPos = (entryData[LFN_offset_ordinal] & ~LFN_END) * 13; 332 | if (lfnPos > MAX_LFN_LENGTH - 1) { 333 | lfnPos = MAX_LFN_LENGTH - 1; 334 | } 335 | lfn[lfnPos] = '\0'; // Set end of lfn to null character 336 | lfnChkSum = entryData[LFN_offset_checkSum]; 337 | } 338 | if (lfnChkSum != entryData[LFN_offset_checkSum]) { 339 | lfnExists = false; 340 | } 341 | if (lfnExists) { 342 | lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; 343 | for (i = 0; i < 13; i++) { 344 | if (lfnPos + i < MAX_LFN_LENGTH - 1) { 345 | lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); 346 | } 347 | } 348 | } 349 | } else if (entryData[DIR_ENTRY_attributes] & ATTRIB_VOL) { 350 | // This is a volume name, don't bother with it 351 | } else if (entryData[0] == DIR_ENTRY_LAST) { 352 | notFound = true; 353 | } else if ((entryData[0] != DIR_ENTRY_FREE) && (entryData[0] > 0x20) && !(entryData[DIR_ENTRY_attributes] & ATTRIB_VOL)) { 354 | if (lfnExists) { 355 | // Calculate file checksum 356 | chkSum = 0; 357 | for (i=0; i < 11; i++) { 358 | // NOTE: The operation is an unsigned char rotate right 359 | chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i]; 360 | } 361 | if (chkSum != lfnChkSum) { 362 | lfnExists = false; 363 | entry->filename[0] = '\0'; 364 | } 365 | } 366 | 367 | if (lfnExists) { 368 | if (_FAT_directory_ucs2tombs (entry->filename, lfn, NAME_MAX) == (size_t)-1) { 369 | // Failed to convert the file name to UTF-8. Maybe the wrong locale is set? 370 | return false; 371 | } 372 | } else { 373 | entryStart = entryEnd; 374 | _FAT_directory_entryGetAlias (entryData, entry->filename); 375 | } 376 | found = true; 377 | } 378 | } 379 | 380 | // If no file is found, return false 381 | if (notFound) { 382 | return false; 383 | } else { 384 | // Fill in the directory entry struct 385 | entry->dataStart = entryStart; 386 | entry->dataEnd = entryEnd; 387 | memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); 388 | return true; 389 | } 390 | } 391 | 392 | bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { 393 | entry->dataStart.cluster = dirCluster; 394 | entry->dataStart.sector = 0; 395 | entry->dataStart.offset = -1; // Start before the beginning of the directory 396 | 397 | entry->dataEnd = entry->dataStart; 398 | 399 | return _FAT_directory_getNextEntry (partition, entry); 400 | } 401 | 402 | bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) { 403 | entry->dataStart.cluster = 0; 404 | entry->dataStart.sector = 0; 405 | entry->dataStart.offset = 0; 406 | 407 | entry->dataEnd = entry->dataStart; 408 | 409 | memset (entry->filename, '\0', NAME_MAX); 410 | entry->filename[0] = '.'; 411 | 412 | memset (entry->entryData, 0, DIR_ENTRY_DATA_SIZE); 413 | memset (entry->entryData, ' ', 11); 414 | entry->entryData[0] = '.'; 415 | 416 | entry->entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; 417 | 418 | u16_to_u8array (entry->entryData, DIR_ENTRY_cluster, partition->rootDirCluster); 419 | u16_to_u8array (entry->entryData, DIR_ENTRY_clusterHigh, partition->rootDirCluster >> 16); 420 | 421 | return true; 422 | } 423 | 424 | bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label) { 425 | DIR_ENTRY entry; 426 | DIR_ENTRY_POSITION entryEnd; 427 | uint8_t entryData[DIR_ENTRY_DATA_SIZE]; 428 | int i; 429 | bool end; 430 | 431 | _FAT_directory_getRootEntry(partition, &entry); 432 | 433 | entryEnd = entry.dataEnd; 434 | 435 | // Make sure we are using the correct root directory, in case of FAT32 436 | if (entryEnd.cluster == FAT16_ROOT_DIR_CLUSTER) { 437 | entryEnd.cluster = partition->rootDirCluster; 438 | } 439 | 440 | label[0]='\0'; 441 | label[11]='\0'; 442 | end = false; 443 | //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 444 | while(!end) { 445 | if(!_FAT_cache_readPartialSector (partition->cache, entryData, 446 | _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, 447 | entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE)) 448 | { //error reading 449 | return false; 450 | } 451 | 452 | if (entryData[DIR_ENTRY_attributes] == ATTRIB_VOL && entryData[0] != DIR_ENTRY_FREE) { 453 | for (i = 0; i < 11; i++) { 454 | label[i] = entryData[DIR_ENTRY_name + i]; 455 | } 456 | return true; 457 | } else if (entryData[0] == DIR_ENTRY_LAST) { 458 | end = true; 459 | } 460 | 461 | if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { 462 | end = true; 463 | } 464 | } 465 | return false; 466 | } 467 | 468 | bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { 469 | DIR_ENTRY_POSITION entryStart = entry->dataStart; 470 | DIR_ENTRY_POSITION entryEnd = entry->dataEnd; 471 | bool entryStillValid; 472 | bool finished; 473 | ucs2_t lfn[MAX_LFN_LENGTH]; 474 | int i; 475 | int lfnPos; 476 | uint8_t entryData[DIR_ENTRY_DATA_SIZE]; 477 | 478 | memset (entry->filename, '\0', NAME_MAX); 479 | 480 | // Create an empty directory entry to overwrite the old ones with 481 | for ( entryStillValid = true, finished = false; 482 | entryStillValid && !finished; 483 | entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) 484 | { 485 | _FAT_cache_readPartialSector (partition->cache, entryData, 486 | _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, 487 | entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 488 | 489 | if ((entryStart.cluster == entryEnd.cluster) 490 | && (entryStart.sector == entryEnd.sector) 491 | && (entryStart.offset == entryEnd.offset)) { 492 | // Copy the entry data and stop, since this is the last section of the directory entry 493 | memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); 494 | finished = true; 495 | } else { 496 | // Copy the long file name data 497 | lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; 498 | for (i = 0; i < 13; i++) { 499 | if (lfnPos + i < MAX_LFN_LENGTH - 1) { 500 | lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); 501 | } 502 | } 503 | } 504 | } 505 | 506 | if (!entryStillValid) { 507 | return false; 508 | } 509 | 510 | entryStart = entry->dataStart; 511 | if ((entryStart.cluster == entryEnd.cluster) 512 | && (entryStart.sector == entryEnd.sector) 513 | && (entryStart.offset == entryEnd.offset)) { 514 | // Since the entry doesn't have a long file name, extract the short filename 515 | if (!_FAT_directory_entryGetAlias (entry->entryData, entry->filename)) { 516 | return false; 517 | } 518 | } else { 519 | // Encode the long file name into a multibyte string 520 | if (_FAT_directory_ucs2tombs (entry->filename, lfn, NAME_MAX) == (size_t)-1) { 521 | return false; 522 | } 523 | } 524 | 525 | return true; 526 | } 527 | 528 | 529 | 530 | bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd) { 531 | size_t dirnameLength; 532 | const char* pathPosition; 533 | const char* nextPathPosition; 534 | uint32_t dirCluster; 535 | bool foundFile; 536 | char alias[MAX_ALIAS_LENGTH]; 537 | bool found, notFound; 538 | 539 | pathPosition = path; 540 | 541 | found = false; 542 | notFound = false; 543 | 544 | if (pathEnd == NULL) { 545 | // Set pathEnd to the end of the path string 546 | pathEnd = strchr (path, '\0'); 547 | } 548 | 549 | if (pathPosition[0] == DIR_SEPARATOR) { 550 | // Start at root directory 551 | dirCluster = partition->rootDirCluster; 552 | // Consume separator(s) 553 | while (pathPosition[0] == DIR_SEPARATOR) { 554 | pathPosition++; 555 | } 556 | // If the path is only specifying a directory in the form of "/" return it 557 | if (pathPosition >= pathEnd) { 558 | _FAT_directory_getRootEntry (partition, entry); 559 | found = true; 560 | } 561 | } else { 562 | // Start in current working directory 563 | dirCluster = partition->cwdCluster; 564 | } 565 | 566 | while (!found && !notFound) { 567 | // Get the name of the next required subdirectory within the path 568 | nextPathPosition = strchr (pathPosition, DIR_SEPARATOR); 569 | if (nextPathPosition != NULL) { 570 | dirnameLength = nextPathPosition - pathPosition; 571 | } else { 572 | dirnameLength = strlen(pathPosition); 573 | } 574 | 575 | if (dirnameLength > NAME_MAX) { 576 | // The path is too long to bother with 577 | return false; 578 | } 579 | 580 | // Check for "." or ".." when the dirCluster is root cluster 581 | // These entries do not exist, so we must fake it 582 | if ((dirCluster == partition->rootDirCluster) 583 | && ((strncmp(".", pathPosition, dirnameLength) == 0) 584 | || (strncmp("..", pathPosition, dirnameLength) == 0))) { 585 | foundFile = true; 586 | _FAT_directory_getRootEntry(partition, entry); 587 | } else { 588 | // Look for the directory within the path 589 | foundFile = _FAT_directory_getFirstEntry (partition, entry, dirCluster); 590 | 591 | while (foundFile && !found && !notFound) { // It hasn't already found the file 592 | // Check if the filename matches 593 | if ((dirnameLength == strnlen(entry->filename, NAME_MAX)) 594 | && (_FAT_directory_mbsncasecmp(pathPosition, entry->filename, dirnameLength) == 0)) { 595 | found = true; 596 | } 597 | 598 | // Check if the alias matches 599 | _FAT_directory_entryGetAlias (entry->entryData, alias); 600 | if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH)) 601 | && (strncasecmp(pathPosition, alias, dirnameLength) == 0)) { 602 | found = true; 603 | } 604 | 605 | if (found && !(entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && (nextPathPosition != NULL)) { 606 | // Make sure that we aren't trying to follow a file instead of a directory in the path 607 | found = false; 608 | } 609 | 610 | if (!found) { 611 | foundFile = _FAT_directory_getNextEntry (partition, entry); 612 | } 613 | } 614 | } 615 | 616 | if (!foundFile) { 617 | // Check that the search didn't get to the end of the directory 618 | notFound = true; 619 | found = false; 620 | } else if ((nextPathPosition == NULL) || (nextPathPosition >= pathEnd)) { 621 | // Check that we reached the end of the path 622 | found = true; 623 | } else if (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) { 624 | dirCluster = _FAT_directory_entryGetCluster (partition, entry->entryData); 625 | if (dirCluster == CLUSTER_ROOT) 626 | dirCluster = partition->rootDirCluster; 627 | pathPosition = nextPathPosition; 628 | // Consume separator(s) 629 | while (pathPosition[0] == DIR_SEPARATOR) { 630 | pathPosition++; 631 | } 632 | // The requested directory was found 633 | if (pathPosition >= pathEnd) { 634 | found = true; 635 | } else { 636 | found = false; 637 | } 638 | } 639 | } 640 | 641 | if (found && !notFound) { 642 | if (partition->filesysType == FS_FAT32 && (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && 643 | _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT) 644 | { 645 | // On FAT32 it should specify an actual cluster for the root entry, 646 | // not cluster 0 as on FAT16 647 | _FAT_directory_getRootEntry (partition, entry); 648 | } 649 | return true; 650 | } else { 651 | return false; 652 | } 653 | } 654 | 655 | bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) { 656 | DIR_ENTRY_POSITION entryStart = entry->dataStart; 657 | DIR_ENTRY_POSITION entryEnd = entry->dataEnd; 658 | bool entryStillValid; 659 | bool finished; 660 | uint8_t entryData[DIR_ENTRY_DATA_SIZE]; 661 | 662 | // Create an empty directory entry to overwrite the old ones with 663 | for ( entryStillValid = true, finished = false; 664 | entryStillValid && !finished; 665 | entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) 666 | { 667 | _FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 668 | entryData[0] = DIR_ENTRY_FREE; 669 | _FAT_cache_writePartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 670 | if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) { 671 | finished = true; 672 | } 673 | } 674 | 675 | if (!entryStillValid) { 676 | return false; 677 | } 678 | 679 | return true; 680 | } 681 | 682 | static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster, size_t size) { 683 | DIR_ENTRY_POSITION gapStart; 684 | DIR_ENTRY_POSITION gapEnd; 685 | uint8_t entryData[DIR_ENTRY_DATA_SIZE]; 686 | size_t dirEntryRemain; 687 | bool endOfDirectory, entryStillValid; 688 | 689 | // Scan Dir for free entry 690 | gapEnd.offset = 0; 691 | gapEnd.sector = 0; 692 | gapEnd.cluster = dirCluster; 693 | 694 | gapStart = gapEnd; 695 | 696 | entryStillValid = true; 697 | dirEntryRemain = size; 698 | endOfDirectory = false; 699 | 700 | while (entryStillValid && !endOfDirectory && (dirEntryRemain > 0)) { 701 | _FAT_cache_readPartialSector (partition->cache, entryData, 702 | _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, 703 | gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 704 | if (entryData[0] == DIR_ENTRY_LAST) { 705 | if (dirEntryRemain == size) { 706 | gapStart = gapEnd; 707 | } 708 | -- dirEntryRemain; 709 | endOfDirectory = true; 710 | } else if (entryData[0] == DIR_ENTRY_FREE) { 711 | if (dirEntryRemain == size) { 712 | gapStart = gapEnd; 713 | } 714 | -- dirEntryRemain; 715 | } else { 716 | dirEntryRemain = size; 717 | } 718 | 719 | if (!endOfDirectory && (dirEntryRemain > 0)) { 720 | entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); 721 | } 722 | } 723 | 724 | // Make sure the scanning didn't fail 725 | if (!entryStillValid) { 726 | return false; 727 | } 728 | 729 | // Save the start entry, since we know it is valid 730 | entry->dataStart = gapStart; 731 | 732 | if (endOfDirectory) { 733 | memset (entryData, DIR_ENTRY_LAST, DIR_ENTRY_DATA_SIZE); 734 | dirEntryRemain += 1; // Increase by one to take account of End Of Directory Marker 735 | while ((dirEntryRemain > 0) && entryStillValid) { 736 | // Get the gapEnd before incrementing it, so the second to last one is saved 737 | entry->dataEnd = gapEnd; 738 | // Increment gapEnd, moving onto the next entry 739 | entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); 740 | -- dirEntryRemain; 741 | // Fill the entry with blanks 742 | _FAT_cache_writePartialSector (partition->cache, entryData, 743 | _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, 744 | gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 745 | } 746 | if (!entryStillValid) { 747 | return false; 748 | } 749 | } else { 750 | entry->dataEnd = gapEnd; 751 | } 752 | 753 | return true; 754 | } 755 | 756 | static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, uint32_t dirCluster) { 757 | DIR_ENTRY tempEntry; 758 | bool foundFile; 759 | char alias[MAX_ALIAS_LENGTH]; 760 | size_t dirnameLength; 761 | 762 | dirnameLength = strnlen(name, NAME_MAX); 763 | 764 | if (dirnameLength >= NAME_MAX) { 765 | return false; 766 | } 767 | 768 | // Make sure the entry doesn't already exist 769 | foundFile = _FAT_directory_getFirstEntry (partition, &tempEntry, dirCluster); 770 | 771 | while (foundFile) { // It hasn't already found the file 772 | // Check if the filename matches 773 | if ((dirnameLength == strnlen(tempEntry.filename, NAME_MAX)) 774 | && (_FAT_directory_mbsncasecmp(name, tempEntry.filename, dirnameLength) == 0)) { 775 | return true; 776 | } 777 | 778 | // Check if the alias matches 779 | _FAT_directory_entryGetAlias (tempEntry.entryData, alias); 780 | if ((strncasecmp(name, alias, MAX_ALIAS_LENGTH) == 0)) { 781 | return true; 782 | } 783 | foundFile = _FAT_directory_getNextEntry (partition, &tempEntry); 784 | } 785 | return false; 786 | } 787 | 788 | /* 789 | Creates an alias for a long file name. If the alias is not an exact match for the 790 | filename, it returns the number of characters in the alias. If the two names match, 791 | it returns 0. If there was an error, it returns -1. 792 | */ 793 | static int _FAT_directory_createAlias (char* alias, const char* lfn) { 794 | bool lossyConversion = false; // Set when the alias had to be modified to be valid 795 | int lfnPos = 0; 796 | int aliasPos = 0; 797 | wchar_t lfnChar; 798 | int oemChar; 799 | mbstate_t ps = {0}; 800 | int bytesUsed = 0; 801 | const char* lfnExt; 802 | int aliasExtLen; 803 | 804 | // Strip leading periods 805 | while (lfn[lfnPos] == '.') { 806 | lfnPos ++; 807 | lossyConversion = true; 808 | } 809 | 810 | // Primary portion of alias 811 | while (aliasPos < 8 && lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { 812 | bytesUsed = mbrtowc(&lfnChar, lfn + lfnPos, NAME_MAX - lfnPos, &ps); 813 | if (bytesUsed < 0) { 814 | return -1; 815 | } 816 | oemChar = wctob(towupper((wint_t)lfnChar)); 817 | if (wctob((wint_t)lfnChar) != oemChar) { 818 | // Case of letter was changed 819 | lossyConversion = true; 820 | } 821 | if (oemChar == ' ') { 822 | // Skip spaces in filename 823 | lossyConversion = true; 824 | lfnPos += bytesUsed; 825 | continue; 826 | } 827 | if (oemChar == EOF) { 828 | oemChar = '_'; // Replace unconvertable characters with underscores 829 | lossyConversion = true; 830 | } 831 | if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { 832 | // Invalid Alias character 833 | oemChar = '_'; // Replace illegal characters with underscores 834 | lossyConversion = true; 835 | } 836 | 837 | alias[aliasPos] = (char)oemChar; 838 | aliasPos++; 839 | lfnPos += bytesUsed; 840 | } 841 | 842 | if (lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { 843 | // Name was more than 8 characters long 844 | lossyConversion = true; 845 | } 846 | 847 | // Alias extension 848 | lfnExt = strrchr (lfn, '.'); 849 | if (lfnExt != NULL && lfnExt != strchr (lfn, '.')) { 850 | // More than one period in name 851 | lossyConversion = true; 852 | } 853 | if (lfnExt != NULL && lfnExt[1] != '\0') { 854 | lfnExt++; 855 | alias[aliasPos] = '.'; 856 | aliasPos++; 857 | memset (&ps, 0, sizeof(ps)); 858 | for (aliasExtLen = 0; aliasExtLen < MAX_ALIAS_EXT_LENGTH && *lfnExt != '\0'; aliasExtLen++) { 859 | bytesUsed = mbrtowc(&lfnChar, lfnExt, NAME_MAX - lfnPos, &ps); 860 | if (bytesUsed < 0) { 861 | return -1; 862 | } 863 | oemChar = wctob(towupper((wint_t)lfnChar)); 864 | if (wctob((wint_t)lfnChar) != oemChar) { 865 | // Case of letter was changed 866 | lossyConversion = true; 867 | } 868 | if (oemChar == ' ') { 869 | // Skip spaces in alias 870 | lossyConversion = true; 871 | lfnExt += bytesUsed; 872 | continue; 873 | } 874 | if (oemChar == EOF) { 875 | oemChar = '_'; // Replace unconvertable characters with underscores 876 | lossyConversion = true; 877 | } 878 | if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { 879 | // Invalid Alias character 880 | oemChar = '_'; // Replace illegal characters with underscores 881 | lossyConversion = true; 882 | } 883 | 884 | alias[aliasPos] = (char)oemChar; 885 | aliasPos++; 886 | lfnExt += bytesUsed; 887 | } 888 | if (*lfnExt != '\0') { 889 | // Extension was more than 3 characters long 890 | lossyConversion = true; 891 | } 892 | } 893 | 894 | alias[aliasPos] = '\0'; 895 | if (lossyConversion) { 896 | return aliasPos; 897 | } else { 898 | return 0; 899 | } 900 | } 901 | 902 | bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { 903 | size_t entrySize; 904 | uint8_t lfnEntry[DIR_ENTRY_DATA_SIZE]; 905 | int i,j; // Must be signed for use when decrementing in for loop 906 | char *tmpCharPtr; 907 | DIR_ENTRY_POSITION curEntryPos; 908 | bool entryStillValid; 909 | uint8_t aliasCheckSum = 0; 910 | char alias [MAX_ALIAS_LENGTH]; 911 | int aliasLen; 912 | int lfnLen; 913 | 914 | // Remove trailing spaces 915 | for (i = strlen (entry->filename) - 1; (i >= 0) && (entry->filename[i] == ' '); --i) { 916 | entry->filename[i] = '\0'; 917 | } 918 | #if 0 919 | // Remove leading spaces 920 | for (i = 0; entry->filename[i] == ' '; ++i) ; 921 | if (i > 0) { 922 | memmove (entry->filename, entry->filename + i, strlen (entry->filename + i)); 923 | } 924 | #endif 925 | 926 | // Make sure the filename is not 0 length 927 | if (strnlen (entry->filename, NAME_MAX) < 1) { 928 | return false; 929 | } 930 | 931 | // Make sure the filename is at least a valid LFN 932 | lfnLen = _FAT_directory_lfnLength (entry->filename); 933 | if (lfnLen < 0) { 934 | return false; 935 | } 936 | 937 | // Remove junk in filename 938 | i = strlen (entry->filename); 939 | memset (entry->filename + i, '\0', NAME_MAX - i); 940 | 941 | // Make sure the entry doesn't already exist 942 | if (_FAT_directory_entryExists (partition, entry->filename, dirCluster)) { 943 | return false; 944 | } 945 | 946 | // Clear out alias, so we can generate a new one 947 | memset (entry->entryData, ' ', 11); 948 | 949 | if ( strncmp(entry->filename, ".", NAME_MAX) == 0) { 950 | // "." entry 951 | entry->entryData[0] = '.'; 952 | entrySize = 1; 953 | } else if ( strncmp(entry->filename, "..", NAME_MAX) == 0) { 954 | // ".." entry 955 | entry->entryData[0] = '.'; 956 | entry->entryData[1] = '.'; 957 | entrySize = 1; 958 | } else { 959 | // Normal file name 960 | aliasLen = _FAT_directory_createAlias (alias, entry->filename); 961 | if (aliasLen < 0) { 962 | return false; 963 | } else if (aliasLen == 0) { 964 | // It's a normal short filename 965 | entrySize = 1; 966 | } else { 967 | // It's a long filename with an alias 968 | entrySize = ((lfnLen + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1; 969 | 970 | // Generate full alias for all cases except when the alias is simply an upper case version of the LFN 971 | // and there isn't already a file with that name 972 | if (strncasecmp (alias, entry->filename, MAX_ALIAS_LENGTH) != 0 || 973 | _FAT_directory_entryExists (partition, alias, dirCluster)) 974 | { 975 | // expand primary part to 8 characters long by padding the end with underscores 976 | i = 0; 977 | j = MAX_ALIAS_PRI_LENGTH; 978 | // Move extension to last 3 characters 979 | while (alias[i] != '.' && alias[i] != '\0') i++; 980 | if (i < j) { 981 | memmove (alias + j, alias + i, aliasLen - i + 1); 982 | // Pad primary component 983 | memset (alias + i, '_', j - i); 984 | } 985 | // Generate numeric tail 986 | for (i = 1; i <= MAX_NUMERIC_TAIL; i++) { 987 | j = i; 988 | tmpCharPtr = alias + MAX_ALIAS_PRI_LENGTH - 1; 989 | while (j > 0) { 990 | *tmpCharPtr = '0' + (j % 10); // ASCII numeric value 991 | tmpCharPtr--; 992 | j /= 10; 993 | } 994 | *tmpCharPtr = '~'; 995 | if (!_FAT_directory_entryExists (partition, alias, dirCluster)) { 996 | break; 997 | } 998 | } 999 | if (i > MAX_NUMERIC_TAIL) { 1000 | // Couldn't get a valid alias 1001 | return false; 1002 | } 1003 | } 1004 | } 1005 | 1006 | // Copy alias or short file name into directory entry data 1007 | for (i = 0, j = 0; (j < 8) && (alias[i] != '.') && (alias[i] != '\0'); i++, j++) { 1008 | entry->entryData[j] = alias[i]; 1009 | } 1010 | while (j < 8) { 1011 | entry->entryData[j] = ' '; 1012 | ++ j; 1013 | } 1014 | if (alias[i] == '.') { 1015 | // Copy extension 1016 | ++ i; 1017 | while ((alias[i] != '\0') && (j < 11)) { 1018 | entry->entryData[j] = alias[i]; 1019 | ++ i; 1020 | ++ j; 1021 | } 1022 | } 1023 | while (j < 11) { 1024 | entry->entryData[j] = ' '; 1025 | ++ j; 1026 | } 1027 | 1028 | // Generate alias checksum 1029 | for (i=0; i < ALIAS_ENTRY_LENGTH; i++) { 1030 | // NOTE: The operation is an unsigned char rotate right 1031 | aliasCheckSum = ((aliasCheckSum & 1) ? 0x80 : 0) + (aliasCheckSum >> 1) + entry->entryData[i]; 1032 | } 1033 | } 1034 | 1035 | // Find or create space for the entry 1036 | if (_FAT_directory_findEntryGap (partition, entry, dirCluster, entrySize) == false) { 1037 | return false; 1038 | } 1039 | 1040 | // Write out directory entry 1041 | curEntryPos = entry->dataStart; 1042 | 1043 | { 1044 | // lfn is only pushed onto the stack here, reducing overall stack usage 1045 | ucs2_t lfn[MAX_LFN_LENGTH] = {0}; 1046 | _FAT_directory_mbstoucs2 (lfn, entry->filename, MAX_LFN_LENGTH); 1047 | 1048 | for (entryStillValid = true, i = entrySize; entryStillValid && i > 0; 1049 | entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &curEntryPos, false), -- i ) 1050 | { 1051 | if (i > 1) { 1052 | // Long filename entry 1053 | lfnEntry[LFN_offset_ordinal] = (i - 1) | ((size_t)i == entrySize ? LFN_END : 0); 1054 | for (j = 0; j < 13; j++) { 1055 | if (lfn [(i - 2) * 13 + j] == '\0') { 1056 | if ((j > 1) && (lfn [(i - 2) * 13 + (j-1)] == '\0')) { 1057 | u16_to_u8array (lfnEntry, LFN_offset_table[j], 0xffff); // Padding 1058 | } else { 1059 | u16_to_u8array (lfnEntry, LFN_offset_table[j], 0x0000); // Terminating null character 1060 | } 1061 | } else { 1062 | u16_to_u8array (lfnEntry, LFN_offset_table[j], lfn [(i - 2) * 13 + j]); 1063 | } 1064 | } 1065 | 1066 | lfnEntry[LFN_offset_checkSum] = aliasCheckSum; 1067 | lfnEntry[LFN_offset_flag] = ATTRIB_LFN; 1068 | lfnEntry[LFN_offset_reserved1] = 0; 1069 | u16_to_u8array (lfnEntry, LFN_offset_reserved2, 0); 1070 | _FAT_cache_writePartialSector (partition->cache, lfnEntry, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 1071 | } else { 1072 | // Alias & file data 1073 | _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); 1074 | } 1075 | } 1076 | } 1077 | 1078 | return true; 1079 | } 1080 | 1081 | bool _FAT_directory_chdir (PARTITION* partition, const char* path) { 1082 | DIR_ENTRY entry; 1083 | 1084 | if (!_FAT_directory_entryFromPath (partition, &entry, path, NULL)) { 1085 | return false; 1086 | } 1087 | 1088 | if (!(entry.entryData[DIR_ENTRY_attributes] & ATTRIB_DIR)) { 1089 | return false; 1090 | } 1091 | 1092 | partition->cwdCluster = _FAT_directory_entryGetCluster (partition, entry.entryData); 1093 | 1094 | return true; 1095 | } 1096 | 1097 | void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st) { 1098 | // Fill in the stat struct 1099 | // Some of the values are faked for the sake of compatibility 1100 | st->st_dev = _FAT_disc_hostType(partition->disc); // The device is the 32bit ioType value 1101 | st->st_ino = (ino_t)(_FAT_directory_entryGetCluster(partition, entry->entryData)); // The file serial number is the start cluster 1102 | st->st_mode = (_FAT_directory_isDirectory(entry) ? S_IFDIR : S_IFREG) | 1103 | (S_IRUSR | S_IRGRP | S_IROTH) | 1104 | (_FAT_directory_isWritable (entry) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Mode bits based on dirEntry ATTRIB byte 1105 | st->st_nlink = 1; // Always one hard link on a FAT file 1106 | st->st_uid = 1; // Faked for FAT 1107 | st->st_gid = 2; // Faked for FAT 1108 | st->st_rdev = st->st_dev; 1109 | st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size 1110 | st->st_atime = _FAT_filetime_to_time_t ( 1111 | 0, 1112 | u8array_to_u16 (entry->entryData, DIR_ENTRY_aDate) 1113 | ); 1114 | st->st_mtime = _FAT_filetime_to_time_t ( 1115 | u8array_to_u16 (entry->entryData, DIR_ENTRY_mTime), 1116 | u8array_to_u16 (entry->entryData, DIR_ENTRY_mDate) 1117 | ); 1118 | st->st_ctime = _FAT_filetime_to_time_t ( 1119 | u8array_to_u16 (entry->entryData, DIR_ENTRY_cTime), 1120 | u8array_to_u16 (entry->entryData, DIR_ENTRY_cDate) 1121 | ); 1122 | st->st_blksize = partition->bytesPerSector; // Prefered file I/O block size 1123 | st->st_blocks = (st->st_size + partition->bytesPerSector - 1) / partition->bytesPerSector; // File size in blocks 1124 | } 1125 | -------------------------------------------------------------------------------- /source/directory.h: -------------------------------------------------------------------------------- 1 | /* 2 | directory.h 3 | Reading, writing and manipulation of the directory structure on 4 | a FAT partition 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _DIRECTORY_H 31 | #define _DIRECTORY_H 32 | 33 | #include 34 | #include 35 | 36 | #include "common.h" 37 | #include "partition.h" 38 | 39 | #define DIR_ENTRY_DATA_SIZE 0x20 40 | #define MAX_LFN_LENGTH 256 41 | #define MAX_ALIAS_LENGTH 13 42 | #define LFN_ENTRY_LENGTH 13 43 | #define ALIAS_ENTRY_LENGTH 11 44 | #define MAX_ALIAS_EXT_LENGTH 3 45 | #define MAX_ALIAS_PRI_LENGTH 8 46 | #define MAX_NUMERIC_TAIL 999999 47 | #define FAT16_ROOT_DIR_CLUSTER 0 48 | 49 | #define DIR_SEPARATOR '/' 50 | 51 | // File attributes 52 | #define ATTRIB_ARCH 0x20 // Archive 53 | #define ATTRIB_DIR 0x10 // Directory 54 | #define ATTRIB_LFN 0x0F // Long file name 55 | #define ATTRIB_VOL 0x08 // Volume 56 | #define ATTRIB_SYS 0x04 // System 57 | #define ATTRIB_HID 0x02 // Hidden 58 | #define ATTRIB_RO 0x01 // Read only 59 | 60 | #define CASE_LOWER_EXT 0x10 // WinNT lowercase extension 61 | #define CASE_LOWER_BASE 0x08 // WinNT lowercase basename 62 | 63 | typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE; 64 | 65 | typedef struct { 66 | uint32_t cluster; 67 | sec_t sector; 68 | int32_t offset; 69 | } DIR_ENTRY_POSITION; 70 | 71 | typedef struct { 72 | uint8_t entryData[DIR_ENTRY_DATA_SIZE]; 73 | DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN 74 | DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry 75 | char filename[NAME_MAX]; 76 | } DIR_ENTRY; 77 | 78 | // Directory entry offsets 79 | enum DIR_ENTRY_offset { 80 | DIR_ENTRY_name = 0x00, 81 | DIR_ENTRY_extension = 0x08, 82 | DIR_ENTRY_attributes = 0x0B, 83 | DIR_ENTRY_caseInfo = 0x0C, 84 | DIR_ENTRY_cTime_ms = 0x0D, 85 | DIR_ENTRY_cTime = 0x0E, 86 | DIR_ENTRY_cDate = 0x10, 87 | DIR_ENTRY_aDate = 0x12, 88 | DIR_ENTRY_clusterHigh = 0x14, 89 | DIR_ENTRY_mTime = 0x16, 90 | DIR_ENTRY_mDate = 0x18, 91 | DIR_ENTRY_cluster = 0x1A, 92 | DIR_ENTRY_fileSize = 0x1C 93 | }; 94 | 95 | /* 96 | Returns true if the file specified by entry is a directory 97 | */ 98 | static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) { 99 | return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0); 100 | } 101 | 102 | static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) { 103 | return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0); 104 | } 105 | 106 | static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) { 107 | return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') || 108 | ((entry->filename[1] == '.') && entry->filename[2] == '\0'))); 109 | } 110 | 111 | /* 112 | Reads the first directory entry from the directory starting at dirCluster 113 | Places result in entry 114 | entry will be destroyed even if no directory entry is found 115 | Returns true on success, false on failure 116 | */ 117 | bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); 118 | 119 | /* 120 | Reads the next directory entry after the one already pointed to by entry 121 | Places result in entry 122 | entry will be destroyed even if no directory entry is found 123 | Returns true on success, false on failure 124 | */ 125 | bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry); 126 | 127 | /* 128 | Gets the directory entry corrsponding to the supplied path 129 | entry will be destroyed even if no directory entry is found 130 | pathEnd specifies the end of the path string, for cutting strings short if needed 131 | specify NULL to use the full length of path 132 | pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR 133 | after pathEND. 134 | Returns true on success, false on failure 135 | */ 136 | bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd); 137 | 138 | /* 139 | Changes the current directory to the one specified by path 140 | Returns true on success, false on failure 141 | */ 142 | bool _FAT_directory_chdir (PARTITION* partition, const char* path); 143 | 144 | /* 145 | Removes the directory entry specified by entry 146 | Assumes that entry is valid 147 | Returns true on success, false on failure 148 | */ 149 | bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry); 150 | 151 | /* 152 | Add a directory entry to the directory specified by dirCluster 153 | The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are 154 | updated with the new directory entry position and alias. 155 | Returns true on success, false on failure 156 | */ 157 | bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); 158 | 159 | /* 160 | Get the start cluster of a file from it's entry data 161 | */ 162 | uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); 163 | 164 | /* 165 | Fill in the file name and entry data of DIR_ENTRY* entry. 166 | Assumes that the entry's dataStart and dataEnd are correct 167 | Returns true on success, false on failure 168 | */ 169 | bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry); 170 | 171 | /* 172 | Fill in a stat struct based on a file entry 173 | */ 174 | void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st); 175 | 176 | /* 177 | Get volume label 178 | */ 179 | bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label); 180 | 181 | #endif // _DIRECTORY_H 182 | -------------------------------------------------------------------------------- /source/disc.c: -------------------------------------------------------------------------------- 1 | /* 2 | disc.c 3 | Interface to the low level disc functions. Used by the higher level 4 | file system code. 5 | 6 | Copyright (c) 2008 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "disc.h" 31 | 32 | /* 33 | The list of interfaces consists of a series of name/interface pairs. 34 | The interface is returned via a simple function. This allows for 35 | platforms where the interface has to be "assembled" before it can 36 | be used, like DLDI on the NDS. For cases where a simple struct 37 | is available, wrapper functions are used. 38 | The list is terminated by a NULL/NULL entry. 39 | */ 40 | 41 | /* ====================== Wii ====================== */ 42 | #if defined (__wii__) 43 | #include 44 | #include 45 | #include 46 | 47 | static const DISC_INTERFACE* get_io_wiisd (void) { 48 | return &__io_wiisd; 49 | } 50 | static const DISC_INTERFACE* get_io_usbstorage (void) { 51 | return &__io_usbstorage; 52 | } 53 | 54 | static const DISC_INTERFACE* get_io_gcsda (void) { 55 | return &__io_gcsda; 56 | } 57 | 58 | static const DISC_INTERFACE* get_io_gcsdb (void) { 59 | return &__io_gcsdb; 60 | } 61 | 62 | const INTERFACE_ID _FAT_disc_interfaces[] = { 63 | {"sd", get_io_wiisd}, 64 | {"usb", get_io_usbstorage}, 65 | {"carda", get_io_gcsda}, 66 | {"cardb", get_io_gcsdb}, 67 | {NULL, NULL} 68 | }; 69 | 70 | /* ==================== Gamecube ==================== */ 71 | #elif defined (__gamecube__) 72 | #include 73 | 74 | static const DISC_INTERFACE* get_io_gcsd2 (void) { 75 | return &__io_gcsd2; 76 | } 77 | 78 | static const DISC_INTERFACE* get_io_gcsdb (void) { 79 | return &__io_gcsdb; 80 | } 81 | 82 | static const DISC_INTERFACE* get_io_gcsda (void) { 83 | return &__io_gcsda; 84 | } 85 | 86 | const INTERFACE_ID _FAT_disc_interfaces[] = { 87 | {"sd", get_io_gcsd2}, 88 | {"carda", get_io_gcsda}, 89 | {"cardb", get_io_gcsdb}, 90 | {NULL, NULL} 91 | }; 92 | 93 | /* ====================== NDS ====================== */ 94 | #elif defined (NDS) 95 | #include 96 | #include 97 | #include 98 | 99 | const INTERFACE_ID _FAT_disc_interfaces[] = { 100 | {"sd", get_io_dsisd}, 101 | {"fat", dldiGetInternal}, 102 | {NULL, NULL} 103 | }; 104 | 105 | /* ====================== GBA ====================== */ 106 | #elif defined (GBA) 107 | #include 108 | 109 | const INTERFACE_ID _FAT_disc_interfaces[] = { 110 | {"fat", discGetInterface}, 111 | {NULL, NULL} 112 | }; 113 | 114 | /* ====================== GP2X ====================== */ 115 | #elif defined (GP2X) 116 | #include 117 | 118 | const INTERFACE_ID _FAT_disc_interfaces[] = { 119 | {"sd", get_io_gp2xsd}, 120 | {NULL, NULL} 121 | 122 | }; 123 | 124 | #endif 125 | 126 | -------------------------------------------------------------------------------- /source/disc.h: -------------------------------------------------------------------------------- 1 | /* 2 | disc.h 3 | Interface to the low level disc functions. Used by the higher level 4 | file system code. 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifndef _DISC_H 30 | #define _DISC_H 31 | 32 | #include "common.h" 33 | 34 | /* 35 | A list of all default devices to try at startup, 36 | terminated by a {NULL,NULL} entry. 37 | */ 38 | typedef struct { 39 | const char* name; 40 | const DISC_INTERFACE* (*getInterface)(void); 41 | } INTERFACE_ID; 42 | extern const INTERFACE_ID _FAT_disc_interfaces[]; 43 | 44 | /* 45 | Check if a disc is inserted 46 | Return true if a disc is inserted and ready, false otherwise 47 | */ 48 | static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) { 49 | return disc->isInserted(); 50 | } 51 | 52 | /* 53 | Read numSectors sectors from a disc, starting at sector. 54 | numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, 55 | else it is at least 1 56 | sector is 0 or greater 57 | buffer is a pointer to the memory to fill 58 | */ 59 | static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) { 60 | return disc->readSectors (sector, numSectors, buffer); 61 | } 62 | 63 | /* 64 | Write numSectors sectors to a disc, starting at sector. 65 | numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, 66 | else it is at least 1 67 | sector is 0 or greater 68 | buffer is a pointer to the memory to read from 69 | */ 70 | static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) { 71 | return disc->writeSectors (sector, numSectors, buffer); 72 | } 73 | 74 | /* 75 | Reset the card back to a ready state 76 | */ 77 | static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) { 78 | return disc->clearStatus(); 79 | } 80 | 81 | /* 82 | Initialise the disc to a state ready for data reading or writing 83 | */ 84 | static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) { 85 | return disc->startup(); 86 | } 87 | 88 | /* 89 | Put the disc in a state ready for power down. 90 | Complete any pending writes and disable the disc if necessary 91 | */ 92 | static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) { 93 | return disc->shutdown(); 94 | } 95 | 96 | /* 97 | Return a 32 bit value unique to each type of interface 98 | */ 99 | static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) { 100 | return disc->ioType; 101 | } 102 | 103 | /* 104 | Return a 32 bit value that specifies the capabilities of the disc 105 | */ 106 | static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) { 107 | return disc->features; 108 | } 109 | 110 | #endif // _DISC_H 111 | -------------------------------------------------------------------------------- /source/fatdir.c: -------------------------------------------------------------------------------- 1 | /* 2 | fatdir.c 3 | 4 | Functions used by the newlib disc stubs to interface with 5 | this library 6 | 7 | Copyright (c) 2006 Michael "Chishm" Chisholm 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation and/or 16 | other materials provided with the distribution. 17 | 3. The name of the author may not be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 22 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "fatdir.h" 38 | 39 | #include "cache.h" 40 | #include "file_allocation_table.h" 41 | #include "partition.h" 42 | #include "directory.h" 43 | #include "bit_ops.h" 44 | #include "filetime.h" 45 | #include "lock.h" 46 | 47 | 48 | int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) { 49 | PARTITION* partition = NULL; 50 | DIR_ENTRY dirEntry; 51 | 52 | // Get the partition this file is on 53 | partition = _FAT_partition_getPartitionFromPath (path); 54 | if (partition == NULL) { 55 | r->_errno = ENODEV; 56 | return -1; 57 | } 58 | 59 | // Move the path pointer to the start of the actual path 60 | if (strchr (path, ':') != NULL) { 61 | path = strchr (path, ':') + 1; 62 | } 63 | if (strchr (path, ':') != NULL) { 64 | r->_errno = EINVAL; 65 | return -1; 66 | } 67 | 68 | _FAT_lock(&partition->lock); 69 | 70 | // Search for the file on the disc 71 | if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) { 72 | _FAT_unlock(&partition->lock); 73 | r->_errno = ENOENT; 74 | return -1; 75 | } 76 | 77 | // Fill in the stat struct 78 | _FAT_directory_entryStat (partition, &dirEntry, st); 79 | 80 | _FAT_unlock(&partition->lock); 81 | return 0; 82 | } 83 | 84 | int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink) { 85 | r->_errno = ENOTSUP; 86 | return -1; 87 | } 88 | 89 | static int _FAT_unlinkCommon (struct _reent *r, const char *path, bool isRmDir) { 90 | PARTITION* partition = NULL; 91 | DIR_ENTRY dirEntry; 92 | DIR_ENTRY dirContents; 93 | uint32_t cluster; 94 | bool nextEntry; 95 | bool errorOccured = false; 96 | 97 | // Get the partition this directory is on 98 | partition = _FAT_partition_getPartitionFromPath (path); 99 | if (partition == NULL) { 100 | r->_errno = ENODEV; 101 | return -1; 102 | } 103 | 104 | // Make sure we aren't trying to write to a read-only disc 105 | if (partition->readOnly) { 106 | r->_errno = EROFS; 107 | return -1; 108 | } 109 | 110 | // Move the path pointer to the start of the actual path 111 | if (strchr (path, ':') != NULL) { 112 | path = strchr (path, ':') + 1; 113 | } 114 | if (strchr (path, ':') != NULL) { 115 | r->_errno = EINVAL; 116 | return -1; 117 | } 118 | 119 | _FAT_lock(&partition->lock); 120 | 121 | // Search for the file on the disc 122 | if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) { 123 | _FAT_unlock(&partition->lock); 124 | r->_errno = ENOENT; 125 | return -1; 126 | } 127 | 128 | cluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); 129 | 130 | 131 | // If this is a directory, make sure it is empty 132 | if (_FAT_directory_isDirectory (&dirEntry)) { 133 | if (!isRmDir) { 134 | _FAT_unlock(&partition->lock); 135 | r->_errno = EISDIR; 136 | return -1; 137 | } 138 | 139 | nextEntry = _FAT_directory_getFirstEntry (partition, &dirContents, cluster); 140 | 141 | while (nextEntry) { 142 | if (!_FAT_directory_isDot (&dirContents)) { 143 | // The directory had something in it that isn't a reference to itself or it's parent 144 | _FAT_unlock(&partition->lock); 145 | r->_errno = ENOTEMPTY; 146 | return -1; 147 | } 148 | nextEntry = _FAT_directory_getNextEntry (partition, &dirContents); 149 | } 150 | } else if (isRmDir) { 151 | _FAT_unlock(&partition->lock); 152 | r->_errno = ENOTDIR; 153 | return -1; 154 | } 155 | 156 | if (_FAT_fat_isValidCluster(partition, cluster)) { 157 | // Remove the cluster chain for this file 158 | if (!_FAT_fat_clearLinks (partition, cluster)) { 159 | r->_errno = EIO; 160 | errorOccured = true; 161 | } 162 | } 163 | 164 | // Remove the directory entry for this file 165 | if (!_FAT_directory_removeEntry (partition, &dirEntry)) { 166 | r->_errno = EIO; 167 | errorOccured = true; 168 | } 169 | 170 | // Flush any sectors in the disc cache 171 | if (!_FAT_cache_flush(partition->cache)) { 172 | r->_errno = EIO; 173 | errorOccured = true; 174 | } 175 | 176 | _FAT_unlock(&partition->lock); 177 | if (errorOccured) { 178 | return -1; 179 | } else { 180 | return 0; 181 | } 182 | } 183 | 184 | int _FAT_unlink_r (struct _reent *r, const char *path) { 185 | return _FAT_unlinkCommon (r, path, false); 186 | } 187 | 188 | int _FAT_chdir_r (struct _reent *r, const char *path) { 189 | PARTITION* partition = NULL; 190 | 191 | // Get the partition this directory is on 192 | partition = _FAT_partition_getPartitionFromPath (path); 193 | if (partition == NULL) { 194 | r->_errno = ENODEV; 195 | return -1; 196 | } 197 | 198 | // Move the path pointer to the start of the actual path 199 | if (strchr (path, ':') != NULL) { 200 | path = strchr (path, ':') + 1; 201 | } 202 | if (strchr (path, ':') != NULL) { 203 | r->_errno = EINVAL; 204 | return -1; 205 | } 206 | 207 | _FAT_lock(&partition->lock); 208 | 209 | // Try changing directory 210 | if (_FAT_directory_chdir (partition, path)) { 211 | // Successful 212 | _FAT_unlock(&partition->lock); 213 | return 0; 214 | } else { 215 | // Failed 216 | _FAT_unlock(&partition->lock); 217 | r->_errno = ENOTDIR; 218 | return -1; 219 | } 220 | } 221 | 222 | int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) { 223 | PARTITION* partition = NULL; 224 | DIR_ENTRY oldDirEntry; 225 | DIR_ENTRY newDirEntry; 226 | const char *pathEnd; 227 | uint32_t dirCluster; 228 | 229 | // Get the partition this directory is on 230 | partition = _FAT_partition_getPartitionFromPath (oldName); 231 | if (partition == NULL) { 232 | r->_errno = ENODEV; 233 | return -1; 234 | } 235 | 236 | _FAT_lock(&partition->lock); 237 | 238 | // Make sure the same partition is used for the old and new names 239 | if (partition != _FAT_partition_getPartitionFromPath (newName)) { 240 | _FAT_unlock(&partition->lock); 241 | r->_errno = EXDEV; 242 | return -1; 243 | } 244 | 245 | // Make sure we aren't trying to write to a read-only disc 246 | if (partition->readOnly) { 247 | _FAT_unlock(&partition->lock); 248 | r->_errno = EROFS; 249 | return -1; 250 | } 251 | 252 | // Move the path pointer to the start of the actual path 253 | if (strchr (oldName, ':') != NULL) { 254 | oldName = strchr (oldName, ':') + 1; 255 | } 256 | if (strchr (oldName, ':') != NULL) { 257 | _FAT_unlock(&partition->lock); 258 | r->_errno = EINVAL; 259 | return -1; 260 | } 261 | if (strchr (newName, ':') != NULL) { 262 | newName = strchr (newName, ':') + 1; 263 | } 264 | if (strchr (newName, ':') != NULL) { 265 | _FAT_unlock(&partition->lock); 266 | r->_errno = EINVAL; 267 | return -1; 268 | } 269 | 270 | // Search for the file on the disc 271 | if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) { 272 | _FAT_unlock(&partition->lock); 273 | r->_errno = ENOENT; 274 | return -1; 275 | } 276 | 277 | // Make sure there is no existing file / directory with the new name 278 | if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) { 279 | _FAT_unlock(&partition->lock); 280 | r->_errno = EEXIST; 281 | return -1; 282 | } 283 | 284 | // Create the new file entry 285 | // Get the directory it has to go in 286 | pathEnd = strrchr (newName, DIR_SEPARATOR); 287 | if (pathEnd == NULL) { 288 | // No path was specified 289 | dirCluster = partition->cwdCluster; 290 | pathEnd = newName; 291 | } else { 292 | // Path was specified -- get the right dirCluster 293 | // Recycling newDirEntry, since it needs to be recreated anyway 294 | if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) || 295 | !_FAT_directory_isDirectory(&newDirEntry)) { 296 | _FAT_unlock(&partition->lock); 297 | r->_errno = ENOTDIR; 298 | return -1; 299 | } 300 | dirCluster = _FAT_directory_entryGetCluster (partition, newDirEntry.entryData); 301 | // Move the pathEnd past the last DIR_SEPARATOR 302 | pathEnd += 1; 303 | } 304 | 305 | // Copy the entry data 306 | memcpy (&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY)); 307 | 308 | // Set the new name 309 | strncpy (newDirEntry.filename, pathEnd, NAME_MAX - 1); 310 | 311 | // Write the new entry 312 | if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) { 313 | _FAT_unlock(&partition->lock); 314 | r->_errno = ENOSPC; 315 | return -1; 316 | } 317 | 318 | // Remove the old entry 319 | if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) { 320 | _FAT_unlock(&partition->lock); 321 | r->_errno = EIO; 322 | return -1; 323 | } 324 | 325 | // Flush any sectors in the disc cache 326 | if (!_FAT_cache_flush (partition->cache)) { 327 | _FAT_unlock(&partition->lock); 328 | r->_errno = EIO; 329 | return -1; 330 | } 331 | 332 | _FAT_unlock(&partition->lock); 333 | return 0; 334 | } 335 | 336 | int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) { 337 | PARTITION* partition = NULL; 338 | bool fileExists; 339 | DIR_ENTRY dirEntry; 340 | const char* pathEnd; 341 | uint32_t parentCluster, dirCluster; 342 | uint8_t newEntryData[DIR_ENTRY_DATA_SIZE]; 343 | 344 | partition = _FAT_partition_getPartitionFromPath (path); 345 | if (partition == NULL) { 346 | r->_errno = ENODEV; 347 | return -1; 348 | } 349 | 350 | // Move the path pointer to the start of the actual path 351 | if (strchr (path, ':') != NULL) { 352 | path = strchr (path, ':') + 1; 353 | } 354 | if (strchr (path, ':') != NULL) { 355 | r->_errno = EINVAL; 356 | return -1; 357 | } 358 | 359 | _FAT_lock(&partition->lock); 360 | 361 | // Search for the file/directory on the disc 362 | fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL); 363 | 364 | // Make sure it doesn't exist 365 | if (fileExists) { 366 | _FAT_unlock(&partition->lock); 367 | r->_errno = EEXIST; 368 | return -1; 369 | } 370 | 371 | if (partition->readOnly) { 372 | // We can't write to a read-only partition 373 | _FAT_unlock(&partition->lock); 374 | r->_errno = EROFS; 375 | return -1; 376 | } 377 | 378 | // Get the directory it has to go in 379 | pathEnd = strrchr (path, DIR_SEPARATOR); 380 | if (pathEnd == NULL) { 381 | // No path was specified 382 | parentCluster = partition->cwdCluster; 383 | pathEnd = path; 384 | } else { 385 | // Path was specified -- get the right parentCluster 386 | // Recycling dirEntry, since it needs to be recreated anyway 387 | if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) || 388 | !_FAT_directory_isDirectory(&dirEntry)) { 389 | _FAT_unlock(&partition->lock); 390 | r->_errno = ENOTDIR; 391 | return -1; 392 | } 393 | parentCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); 394 | // Move the pathEnd past the last DIR_SEPARATOR 395 | pathEnd += 1; 396 | } 397 | // Create the entry data 398 | strncpy (dirEntry.filename, pathEnd, NAME_MAX - 1); 399 | memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE); 400 | 401 | // Set the creation time and date 402 | dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0; 403 | u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC()); 404 | u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC()); 405 | u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC()); 406 | u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC()); 407 | u16_to_u8array (dirEntry.entryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC()); 408 | 409 | // Set the directory attribute 410 | dirEntry.entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; 411 | 412 | // Get a cluster for the new directory 413 | dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE); 414 | if (!_FAT_fat_isValidCluster(partition, dirCluster)) { 415 | // No space left on disc for the cluster 416 | _FAT_unlock(&partition->lock); 417 | r->_errno = ENOSPC; 418 | return -1; 419 | } 420 | u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cluster, dirCluster); 421 | u16_to_u8array (dirEntry.entryData, DIR_ENTRY_clusterHigh, dirCluster >> 16); 422 | 423 | // Write the new directory's entry to it's parent 424 | if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) { 425 | _FAT_unlock(&partition->lock); 426 | r->_errno = ENOSPC; 427 | return -1; 428 | } 429 | 430 | // Create the dot entry within the directory 431 | memset (newEntryData, 0, DIR_ENTRY_DATA_SIZE); 432 | memset (newEntryData, ' ', 11); 433 | newEntryData[DIR_ENTRY_name] = '.'; 434 | newEntryData[DIR_ENTRY_attributes] = ATTRIB_DIR; 435 | u16_to_u8array (newEntryData, DIR_ENTRY_cluster, dirCluster); 436 | u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, dirCluster >> 16); 437 | 438 | // Write it to the directory, erasing that sector in the process 439 | _FAT_cache_eraseWritePartialSector ( partition->cache, newEntryData, 440 | _FAT_fat_clusterToSector (partition, dirCluster), 0, DIR_ENTRY_DATA_SIZE); 441 | 442 | 443 | // Create the double dot entry within the directory 444 | 445 | // if ParentDir == Rootdir then ".."" always link to Cluster 0 446 | if(parentCluster == partition->rootDirCluster) 447 | parentCluster = FAT16_ROOT_DIR_CLUSTER; 448 | 449 | newEntryData[DIR_ENTRY_name + 1] = '.'; 450 | u16_to_u8array (newEntryData, DIR_ENTRY_cluster, parentCluster); 451 | u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, parentCluster >> 16); 452 | 453 | // Write it to the directory 454 | _FAT_cache_writePartialSector ( partition->cache, newEntryData, 455 | _FAT_fat_clusterToSector (partition, dirCluster), DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); 456 | 457 | // Flush any sectors in the disc cache 458 | if (!_FAT_cache_flush(partition->cache)) { 459 | _FAT_unlock(&partition->lock); 460 | r->_errno = EIO; 461 | return -1; 462 | } 463 | 464 | _FAT_unlock(&partition->lock); 465 | return 0; 466 | } 467 | 468 | int _FAT_rmdir_r (struct _reent *r, const char *path) { 469 | return _FAT_unlinkCommon (r, path, true); 470 | } 471 | 472 | int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) 473 | { 474 | PARTITION* partition = NULL; 475 | unsigned int freeClusterCount; 476 | 477 | // Get the partition of the requested path 478 | partition = _FAT_partition_getPartitionFromPath (path); 479 | if (partition == NULL) { 480 | r->_errno = ENODEV; 481 | return -1; 482 | } 483 | 484 | _FAT_lock(&partition->lock); 485 | 486 | if(partition->filesysType == FS_FAT32) { 487 | // Sync FSinfo block 488 | _FAT_partition_readFSinfo(partition); 489 | freeClusterCount = partition->fat.numberFreeCluster; 490 | } else { 491 | freeClusterCount = _FAT_fat_freeClusterCount (partition); 492 | } 493 | 494 | // FAT clusters = POSIX blocks 495 | buf->f_bsize = partition->bytesPerCluster; // File system block size. 496 | buf->f_frsize = partition->bytesPerCluster; // Fundamental file system block size. 497 | 498 | buf->f_blocks = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of blocks on file system in units of f_frsize. 499 | buf->f_bfree = freeClusterCount; // Total number of free blocks. 500 | buf->f_bavail = freeClusterCount; // Number of free blocks available to non-privileged process. 501 | 502 | // Treat requests for info on inodes as clusters 503 | buf->f_files = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of file serial numbers. 504 | buf->f_ffree = freeClusterCount; // Total number of free file serial numbers. 505 | buf->f_favail = freeClusterCount; // Number of file serial numbers available to non-privileged process. 506 | 507 | // File system ID. 32bit ioType value 508 | buf->f_fsid = _FAT_disc_hostType(partition->disc); 509 | 510 | // Bit mask of f_flag values. 511 | buf->f_flag = ST_NOSUID /* No support for ST_ISUID and ST_ISGID file mode bits */ 512 | | (partition->readOnly ? ST_RDONLY /* Read only file system */ : 0 ) ; 513 | // Maximum filename length. 514 | buf->f_namemax = NAME_MAX; 515 | 516 | _FAT_unlock(&partition->lock); 517 | return 0; 518 | } 519 | 520 | DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) { 521 | DIR_ENTRY dirEntry; 522 | DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); 523 | bool fileExists; 524 | 525 | state->partition = _FAT_partition_getPartitionFromPath (path); 526 | if (state->partition == NULL) { 527 | r->_errno = ENODEV; 528 | return NULL; 529 | } 530 | 531 | // Move the path pointer to the start of the actual path 532 | if (strchr (path, ':') != NULL) { 533 | path = strchr (path, ':') + 1; 534 | } 535 | if (strchr (path, ':') != NULL) { 536 | r->_errno = EINVAL; 537 | return NULL; 538 | } 539 | 540 | _FAT_lock(&state->partition->lock); 541 | 542 | // Get the start cluster of the directory 543 | fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL); 544 | 545 | if (!fileExists) { 546 | _FAT_unlock(&state->partition->lock); 547 | r->_errno = ENOENT; 548 | return NULL; 549 | } 550 | 551 | // Make sure it is a directory 552 | if (! _FAT_directory_isDirectory (&dirEntry)) { 553 | _FAT_unlock(&state->partition->lock); 554 | r->_errno = ENOTDIR; 555 | return NULL; 556 | } 557 | 558 | // Save the start cluster for use when resetting the directory data 559 | state->startCluster = _FAT_directory_entryGetCluster (state->partition, dirEntry.entryData); 560 | 561 | // Get the first entry for use with a call to dirnext 562 | state->validEntry = 563 | _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster); 564 | 565 | // We are now using this entry 566 | state->inUse = true; 567 | _FAT_unlock(&state->partition->lock); 568 | return (DIR_ITER*) state; 569 | } 570 | 571 | int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) { 572 | DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); 573 | 574 | _FAT_lock(&state->partition->lock); 575 | 576 | // Make sure we are still using this entry 577 | if (!state->inUse) { 578 | _FAT_unlock(&state->partition->lock); 579 | r->_errno = EBADF; 580 | return -1; 581 | } 582 | 583 | // Get the first entry for use with a call to dirnext 584 | state->validEntry = 585 | _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster); 586 | 587 | _FAT_unlock(&state->partition->lock); 588 | return 0; 589 | } 590 | 591 | int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { 592 | DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); 593 | 594 | _FAT_lock(&state->partition->lock); 595 | 596 | // Make sure we are still using this entry 597 | if (!state->inUse) { 598 | _FAT_unlock(&state->partition->lock); 599 | r->_errno = EBADF; 600 | return -1; 601 | } 602 | 603 | // Make sure there is another file to report on 604 | if (! state->validEntry) { 605 | _FAT_unlock(&state->partition->lock); 606 | return -1; 607 | } 608 | 609 | // Get the filename 610 | strncpy (filename, state->currentEntry.filename, NAME_MAX); 611 | // Get the stats, if requested 612 | if (filestat != NULL) { 613 | _FAT_directory_entryStat (state->partition, &(state->currentEntry), filestat); 614 | } 615 | 616 | // Look for the next entry for use next time 617 | state->validEntry = 618 | _FAT_directory_getNextEntry (state->partition, &(state->currentEntry)); 619 | 620 | _FAT_unlock(&state->partition->lock); 621 | return 0; 622 | } 623 | 624 | int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState) { 625 | DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); 626 | 627 | // We are no longer using this entry 628 | _FAT_lock(&state->partition->lock); 629 | state->inUse = false; 630 | _FAT_unlock(&state->partition->lock); 631 | 632 | return 0; 633 | } 634 | -------------------------------------------------------------------------------- /source/fatdir.h: -------------------------------------------------------------------------------- 1 | /* 2 | fatdir.h 3 | 4 | Functions used by the newlib disc stubs to interface with 5 | this library 6 | 7 | Copyright (c) 2006 Michael "Chishm" Chisholm 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation and/or 16 | other materials provided with the distribution. 17 | 3. The name of the author may not be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 22 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #ifndef _FATDIR_H 33 | #define _FATDIR_H 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "common.h" 40 | #include "directory.h" 41 | 42 | typedef struct { 43 | PARTITION* partition; 44 | DIR_ENTRY currentEntry; 45 | uint32_t startCluster; 46 | bool inUse; 47 | bool validEntry; 48 | } DIR_STATE_STRUCT; 49 | 50 | extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st); 51 | 52 | extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink); 53 | 54 | extern int _FAT_unlink_r (struct _reent *r, const char *name); 55 | 56 | extern int _FAT_chdir_r (struct _reent *r, const char *name); 57 | 58 | extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName); 59 | 60 | extern int _FAT_mkdir_r (struct _reent *r, const char *path, int mode); 61 | 62 | extern int _FAT_rmdir_r (struct _reent *r, const char *path); 63 | 64 | extern int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); 65 | 66 | /* 67 | Directory iterator functions 68 | */ 69 | extern DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path); 70 | extern int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState); 71 | extern int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); 72 | extern int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState); 73 | 74 | 75 | #endif // _FATDIR_H 76 | -------------------------------------------------------------------------------- /source/fatfile.h: -------------------------------------------------------------------------------- 1 | /* 2 | fatfile.h 3 | 4 | Functions used by the newlib disc stubs to interface with 5 | this library 6 | 7 | Copyright (c) 2006 Michael "Chishm" Chisholm 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation and/or 16 | other materials provided with the distribution. 17 | 3. The name of the author may not be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 22 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #ifndef _FATFILE_H 33 | #define _FATFILE_H 34 | 35 | #include 36 | #include 37 | 38 | #include "common.h" 39 | #include "partition.h" 40 | #include "directory.h" 41 | 42 | #define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B 43 | 44 | typedef struct { 45 | u32 cluster; 46 | sec_t sector; 47 | s32 byte; 48 | } FILE_POSITION; 49 | 50 | struct _FILE_STRUCT; 51 | 52 | struct _FILE_STRUCT { 53 | uint32_t filesize; 54 | uint32_t startCluster; 55 | uint32_t currentPosition; 56 | FILE_POSITION rwPosition; 57 | FILE_POSITION appendPosition; 58 | DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN 59 | DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry 60 | PARTITION* partition; 61 | struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files 62 | struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files 63 | bool read; 64 | bool write; 65 | bool append; 66 | bool inUse; 67 | bool modified; 68 | }; 69 | 70 | typedef struct _FILE_STRUCT FILE_STRUCT; 71 | 72 | int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); 73 | 74 | int _FAT_close_r (struct _reent *r, void *fd); 75 | 76 | ssize_t _FAT_write_r (struct _reent *r,void *fd, const char *ptr, size_t len); 77 | 78 | ssize_t _FAT_read_r (struct _reent *r, void *fd, char *ptr, size_t len); 79 | 80 | off_t _FAT_seek_r (struct _reent *r, void *fd, off_t pos, int dir); 81 | 82 | int _FAT_fstat_r (struct _reent *r, void *fd, struct stat *st); 83 | 84 | int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st); 85 | 86 | int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink); 87 | 88 | int _FAT_chdir_r (struct _reent *r, const char *name); 89 | 90 | int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName); 91 | 92 | int _FAT_ftruncate_r (struct _reent *r, void *fd, off_t len); 93 | 94 | int _FAT_fsync_r (struct _reent *r, void *fd); 95 | 96 | /* 97 | Synchronizes the file data to disc. 98 | Does no locking of its own -- lock the partition before calling. 99 | Returns 0 on success, an error code on failure. 100 | */ 101 | extern int _FAT_syncToDisc (FILE_STRUCT* file); 102 | 103 | #endif // _FATFILE_H 104 | -------------------------------------------------------------------------------- /source/file_allocation_table.c: -------------------------------------------------------------------------------- 1 | /* 2 | file_allocation_table.c 3 | Reading, writing and manipulation of the FAT structure on 4 | a FAT partition 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #include "file_allocation_table.h" 32 | #include "partition.h" 33 | #include "mem_allocate.h" 34 | #include 35 | 36 | /* 37 | Gets the cluster linked from input cluster 38 | */ 39 | uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster) 40 | { 41 | uint32_t nextCluster = CLUSTER_FREE; 42 | sec_t sector; 43 | int offset; 44 | 45 | if (cluster == CLUSTER_FREE) { 46 | return CLUSTER_FREE; 47 | } 48 | 49 | switch (partition->filesysType) 50 | { 51 | case FS_UNKNOWN: 52 | return CLUSTER_ERROR; 53 | break; 54 | 55 | case FS_FAT12: 56 | { 57 | u32 nextCluster_h; 58 | sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); 59 | offset = ((cluster * 3) / 2) % partition->bytesPerSector; 60 | 61 | 62 | _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8)); 63 | 64 | offset++; 65 | 66 | if (offset >= partition->bytesPerSector) { 67 | offset = 0; 68 | sector++; 69 | } 70 | nextCluster_h = 0; 71 | 72 | _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8)); 73 | nextCluster |= (nextCluster_h << 8); 74 | 75 | if (cluster & 0x01) { 76 | nextCluster = nextCluster >> 4; 77 | } else { 78 | nextCluster &= 0x0FFF; 79 | } 80 | 81 | if (nextCluster >= 0x0FF7) 82 | { 83 | nextCluster = CLUSTER_EOF; 84 | } 85 | 86 | break; 87 | } 88 | case FS_FAT16: 89 | sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); 90 | offset = (cluster % (partition->bytesPerSector >> 1)) << 1; 91 | 92 | _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16)); 93 | 94 | if (nextCluster >= 0xFFF7) { 95 | nextCluster = CLUSTER_EOF; 96 | } 97 | break; 98 | 99 | case FS_FAT32: 100 | sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); 101 | offset = (cluster % (partition->bytesPerSector >> 2)) << 2; 102 | 103 | _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32)); 104 | 105 | if (nextCluster >= 0x0FFFFFF7) { 106 | nextCluster = CLUSTER_EOF; 107 | } 108 | break; 109 | 110 | default: 111 | return CLUSTER_ERROR; 112 | break; 113 | } 114 | 115 | return nextCluster; 116 | } 117 | 118 | /* 119 | writes value into the correct offset within a partition's FAT, based 120 | on the cluster number. 121 | */ 122 | static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint32_t value) { 123 | sec_t sector; 124 | int offset; 125 | uint32_t oldValue; 126 | 127 | if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */)) 128 | { 129 | return false; 130 | } 131 | 132 | switch (partition->filesysType) 133 | { 134 | case FS_UNKNOWN: 135 | return false; 136 | break; 137 | 138 | case FS_FAT12: 139 | sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); 140 | offset = ((cluster * 3) / 2) % partition->bytesPerSector; 141 | 142 | if (cluster & 0x01) { 143 | 144 | _FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8)); 145 | 146 | value = (value << 4) | (oldValue & 0x0F); 147 | 148 | _FAT_cache_writeLittleEndianValue (partition->cache, value & 0xFF, sector, offset, sizeof(u8)); 149 | 150 | offset++; 151 | if (offset >= partition->bytesPerSector) { 152 | offset = 0; 153 | sector++; 154 | } 155 | 156 | _FAT_cache_writeLittleEndianValue (partition->cache, (value >> 8) & 0xFF, sector, offset, sizeof(u8)); 157 | 158 | } else { 159 | 160 | _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8)); 161 | 162 | offset++; 163 | if (offset >= partition->bytesPerSector) { 164 | offset = 0; 165 | sector++; 166 | } 167 | 168 | _FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8)); 169 | 170 | value = ((value >> 8) & 0x0F) | (oldValue & 0xF0); 171 | 172 | _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8)); 173 | } 174 | 175 | break; 176 | 177 | case FS_FAT16: 178 | sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); 179 | offset = (cluster % (partition->bytesPerSector >> 1)) << 1; 180 | 181 | _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u16)); 182 | 183 | break; 184 | 185 | case FS_FAT32: 186 | sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); 187 | offset = (cluster % (partition->bytesPerSector >> 2)) << 2; 188 | 189 | _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u32)); 190 | 191 | break; 192 | 193 | default: 194 | return false; 195 | break; 196 | } 197 | 198 | return true; 199 | } 200 | 201 | /*----------------------------------------------------------------- 202 | gets the first available free cluster, sets it 203 | to end of file, links the input cluster to it then returns the 204 | cluster number 205 | If an error occurs, return CLUSTER_ERROR 206 | -----------------------------------------------------------------*/ 207 | uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster) { 208 | uint32_t firstFree; 209 | uint32_t curLink; 210 | uint32_t lastCluster; 211 | bool loopedAroundFAT = false; 212 | 213 | lastCluster = partition->fat.lastCluster; 214 | 215 | if (cluster > lastCluster) { 216 | return CLUSTER_ERROR; 217 | } 218 | 219 | // Check if the cluster already has a link, and return it if so 220 | curLink = _FAT_fat_nextCluster(partition, cluster); 221 | if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster)) { 222 | return curLink; // Return the current link - don't allocate a new one 223 | } 224 | 225 | // Get a free cluster 226 | firstFree = partition->fat.firstFree; 227 | // Start at first valid cluster 228 | if (firstFree < CLUSTER_FIRST) { 229 | firstFree = CLUSTER_FIRST; 230 | } 231 | 232 | // Search until a free cluster is found 233 | while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) { 234 | firstFree++; 235 | if (firstFree > lastCluster) { 236 | if (loopedAroundFAT) { 237 | // If couldn't get a free cluster then return an error 238 | partition->fat.firstFree = firstFree; 239 | return CLUSTER_ERROR; 240 | } else { 241 | // Try looping back to the beginning of the FAT 242 | // This was suggested by loopy 243 | firstFree = CLUSTER_FIRST; 244 | loopedAroundFAT = true; 245 | } 246 | } 247 | } 248 | partition->fat.firstFree = firstFree; 249 | if(partition->fat.numberFreeCluster) 250 | partition->fat.numberFreeCluster--; 251 | partition->fat.numberLastAllocCluster = firstFree; 252 | 253 | if ((cluster >= CLUSTER_FIRST) && (cluster <= lastCluster)) 254 | { 255 | // Update the linked from FAT entry 256 | _FAT_fat_writeFatEntry (partition, cluster, firstFree); 257 | } 258 | // Create the linked to FAT entry 259 | _FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF); 260 | 261 | return firstFree; 262 | } 263 | 264 | /*----------------------------------------------------------------- 265 | gets the first available free cluster, sets it 266 | to end of file, links the input cluster to it, clears the new 267 | cluster to 0 valued bytes, then returns the cluster number 268 | If an error occurs, return CLUSTER_ERROR 269 | -----------------------------------------------------------------*/ 270 | uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) { 271 | uint32_t newCluster; 272 | uint32_t i; 273 | uint8_t *emptySector; 274 | 275 | // Link the cluster 276 | newCluster = _FAT_fat_linkFreeCluster(partition, cluster); 277 | 278 | if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR) { 279 | return CLUSTER_ERROR; 280 | } 281 | 282 | emptySector = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); 283 | 284 | // Clear all the sectors within the cluster 285 | memset (emptySector, 0, partition->bytesPerSector); 286 | for (i = 0; i < partition->sectorsPerCluster; i++) { 287 | _FAT_cache_writeSectors (partition->cache, 288 | _FAT_fat_clusterToSector (partition, newCluster) + i, 289 | 1, emptySector); 290 | } 291 | 292 | _FAT_mem_free(emptySector); 293 | 294 | return newCluster; 295 | } 296 | 297 | 298 | /*----------------------------------------------------------------- 299 | _FAT_fat_clearLinks 300 | frees any cluster used by a file 301 | -----------------------------------------------------------------*/ 302 | bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster) { 303 | uint32_t nextCluster; 304 | 305 | if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */)) 306 | return false; 307 | 308 | // If this clears up more space in the FAT before the current free pointer, move it backwards 309 | if (cluster < partition->fat.firstFree) { 310 | partition->fat.firstFree = cluster; 311 | } 312 | 313 | while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE) && (cluster != CLUSTER_ERROR)) { 314 | // Store next cluster before erasing the link 315 | nextCluster = _FAT_fat_nextCluster (partition, cluster); 316 | 317 | // Erase the link 318 | _FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE); 319 | 320 | if(partition->fat.numberFreeCluster < (partition->numberOfSectors/partition->sectorsPerCluster)) 321 | partition->fat.numberFreeCluster++; 322 | // Move onto next cluster 323 | cluster = nextCluster; 324 | } 325 | 326 | return true; 327 | } 328 | 329 | /*----------------------------------------------------------------- 330 | _FAT_fat_trimChain 331 | Drop all clusters past the chainLength. 332 | If chainLength is 0, all clusters are dropped. 333 | If chainLength is 1, the first cluster is kept and the rest are 334 | dropped, and so on. 335 | Return the last cluster left in the chain. 336 | -----------------------------------------------------------------*/ 337 | uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength) { 338 | uint32_t nextCluster; 339 | 340 | if (chainLength == 0) { 341 | // Drop the entire chain 342 | _FAT_fat_clearLinks (partition, startCluster); 343 | return CLUSTER_FREE; 344 | } else { 345 | // Find the last cluster in the chain, and the one after it 346 | chainLength--; 347 | nextCluster = _FAT_fat_nextCluster (partition, startCluster); 348 | while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) { 349 | chainLength--; 350 | startCluster = nextCluster; 351 | nextCluster = _FAT_fat_nextCluster (partition, startCluster); 352 | } 353 | 354 | // Drop all clusters after the last in the chain 355 | if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF) { 356 | _FAT_fat_clearLinks (partition, nextCluster); 357 | } 358 | 359 | // Mark the last cluster in the chain as the end of the file 360 | _FAT_fat_writeFatEntry (partition, startCluster, CLUSTER_EOF); 361 | 362 | return startCluster; 363 | } 364 | } 365 | 366 | /*----------------------------------------------------------------- 367 | _FAT_fat_lastCluster 368 | Trace the cluster links until the last one is found 369 | -----------------------------------------------------------------*/ 370 | uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster) { 371 | while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) { 372 | cluster = _FAT_fat_nextCluster(partition, cluster); 373 | } 374 | return cluster; 375 | } 376 | 377 | /*----------------------------------------------------------------- 378 | _FAT_fat_freeClusterCount 379 | Return the number of free clusters available 380 | -----------------------------------------------------------------*/ 381 | unsigned int _FAT_fat_freeClusterCount (PARTITION* partition) { 382 | unsigned int count = 0; 383 | uint32_t curCluster; 384 | 385 | for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) { 386 | if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) { 387 | count++; 388 | } 389 | } 390 | 391 | return count; 392 | } 393 | 394 | -------------------------------------------------------------------------------- /source/file_allocation_table.h: -------------------------------------------------------------------------------- 1 | /* 2 | file_allocation_table.h 3 | Reading, writing and manipulation of the FAT structure on 4 | a FAT partition 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _FAT_H 31 | #define _FAT_H 32 | 33 | #include "common.h" 34 | #include "partition.h" 35 | 36 | #define CLUSTER_EOF_16 0xFFFF 37 | #define CLUSTER_EOF 0x0FFFFFFF 38 | #define CLUSTER_FREE 0x00000000 39 | #define CLUSTER_ROOT 0x00000000 40 | #define CLUSTER_FIRST 0x00000002 41 | #define CLUSTER_ERROR 0xFFFFFFFF 42 | 43 | #define CLUSTERS_PER_FAT12 4085 44 | #define CLUSTERS_PER_FAT16 65525 45 | 46 | 47 | uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster); 48 | 49 | uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster); 50 | uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster); 51 | 52 | bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster); 53 | 54 | uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength); 55 | 56 | uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster); 57 | 58 | unsigned int _FAT_fat_freeClusterCount (PARTITION* partition); 59 | 60 | static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) { 61 | return (cluster >= CLUSTER_FIRST) ? 62 | ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart : 63 | partition->rootDirStart; 64 | } 65 | 66 | static inline bool _FAT_fat_isValidCluster (PARTITION* partition, uint32_t cluster) { 67 | return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */); 68 | } 69 | 70 | #endif // _FAT_H 71 | -------------------------------------------------------------------------------- /source/filetime.c: -------------------------------------------------------------------------------- 1 | /* 2 | filetime.c 3 | Conversion of file time and date values to various other types 4 | 5 | Copyright (c) 2006 Michael "Chishm" Chisholm 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 19 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 20 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | 30 | #include 31 | #include "filetime.h" 32 | #include "common.h" 33 | 34 | #define MAX_HOUR 23 35 | #define MAX_MINUTE 59 36 | #define MAX_SECOND 59 37 | 38 | #define MAX_MONTH 11 39 | #define MIN_MONTH 0 40 | #define MAX_DAY 31 41 | #define MIN_DAY 1 42 | 43 | uint16_t _FAT_filetime_getTimeFromRTC (void) { 44 | #ifdef USE_RTC_TIME 45 | struct tm timeParts; 46 | time_t epochTime; 47 | 48 | if (time(&epochTime) == (time_t)-1) { 49 | return 0; 50 | } 51 | localtime_r(&epochTime, &timeParts); 52 | 53 | // Check that the values are all in range. 54 | // If they are not, return 0 (no timestamp) 55 | if ((timeParts.tm_hour < 0) || (timeParts.tm_hour > MAX_HOUR)) return 0; 56 | if ((timeParts.tm_min < 0) || (timeParts.tm_min > MAX_MINUTE)) return 0; 57 | if ((timeParts.tm_sec < 0) || (timeParts.tm_sec > MAX_SECOND)) return 0; 58 | 59 | return ( 60 | ((timeParts.tm_hour & 0x1F) << 11) | 61 | ((timeParts.tm_min & 0x3F) << 5) | 62 | ((timeParts.tm_sec >> 1) & 0x1F) 63 | ); 64 | #else 65 | return 0; 66 | #endif 67 | } 68 | 69 | 70 | uint16_t _FAT_filetime_getDateFromRTC (void) { 71 | #ifdef USE_RTC_TIME 72 | struct tm timeParts; 73 | time_t epochTime; 74 | 75 | if (time(&epochTime) == (time_t)-1) { 76 | return 0; 77 | } 78 | localtime_r(&epochTime, &timeParts); 79 | 80 | if ((timeParts.tm_mon < MIN_MONTH) || (timeParts.tm_mon > MAX_MONTH)) return 0; 81 | if ((timeParts.tm_mday < MIN_DAY) || (timeParts.tm_mday > MAX_DAY)) return 0; 82 | 83 | return ( 84 | (((timeParts.tm_year - 80) & 0x7F) <<9) | // Adjust for MS-FAT base year (1980 vs 1900 for tm_year) 85 | (((timeParts.tm_mon + 1) & 0xF) << 5) | 86 | (timeParts.tm_mday & 0x1F) 87 | ); 88 | #else 89 | return 0; 90 | #endif 91 | } 92 | 93 | time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) { 94 | struct tm timeParts; 95 | 96 | timeParts.tm_hour = t >> 11; 97 | timeParts.tm_min = (t >> 5) & 0x3F; 98 | timeParts.tm_sec = (t & 0x1F) << 1; 99 | 100 | timeParts.tm_mday = d & 0x1F; 101 | timeParts.tm_mon = ((d >> 5) & 0x0F) - 1; 102 | timeParts.tm_year = (d >> 9) + 80; 103 | 104 | timeParts.tm_isdst = 0; 105 | 106 | return mktime(&timeParts); 107 | } 108 | -------------------------------------------------------------------------------- /source/filetime.h: -------------------------------------------------------------------------------- 1 | /* 2 | filetime.h 3 | Conversion of file time and date values to various other types 4 | 5 | Copyright (c) 2006 Michael "Chishm" Chisholm 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 19 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 20 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef _FILETIME_H 30 | #define _FILETIME_H 31 | 32 | #include "common.h" 33 | #include 34 | 35 | uint16_t _FAT_filetime_getTimeFromRTC (void); 36 | uint16_t _FAT_filetime_getDateFromRTC (void); 37 | 38 | time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d); 39 | 40 | 41 | #endif // _FILETIME_H 42 | -------------------------------------------------------------------------------- /source/libfat.c: -------------------------------------------------------------------------------- 1 | /* 2 | libfat.c 3 | Simple functionality for startup, mounting and unmounting of FAT-based devices. 4 | 5 | Copyright (c) 2006 Michael "Chishm" Chisholm 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 19 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 20 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "common.h" 36 | #include "partition.h" 37 | #include "fatfile.h" 38 | #include "fatdir.h" 39 | #include "lock.h" 40 | #include "mem_allocate.h" 41 | #include "disc.h" 42 | 43 | static const devoptab_t dotab_fat = { 44 | "fat", 45 | sizeof (FILE_STRUCT), 46 | _FAT_open_r, 47 | _FAT_close_r, 48 | _FAT_write_r, 49 | _FAT_read_r, 50 | _FAT_seek_r, 51 | _FAT_fstat_r, 52 | _FAT_stat_r, 53 | _FAT_link_r, 54 | _FAT_unlink_r, 55 | _FAT_chdir_r, 56 | _FAT_rename_r, 57 | _FAT_mkdir_r, 58 | sizeof (DIR_STATE_STRUCT), 59 | _FAT_diropen_r, 60 | _FAT_dirreset_r, 61 | _FAT_dirnext_r, 62 | _FAT_dirclose_r, 63 | _FAT_statvfs_r, 64 | _FAT_ftruncate_r, 65 | _FAT_fsync_r, 66 | NULL, /* Device data */ 67 | NULL, // chmod_r 68 | NULL, // fchmod_r 69 | _FAT_rmdir_r, 70 | _FAT_stat_r, // This is lstat, but we don't support symlinks 71 | }; 72 | 73 | bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage) { 74 | PARTITION* partition; 75 | devoptab_t* devops; 76 | char* nameCopy; 77 | 78 | if(!name || strlen(name) > 8 || !interface) 79 | return false; 80 | 81 | if(!interface->startup()) 82 | return false; 83 | 84 | if(!interface->isInserted()) 85 | return false; 86 | 87 | char devname[10]; 88 | strcpy(devname, name); 89 | strcat(devname, ":"); 90 | if(FindDevice(devname) >= 0) 91 | return true; 92 | 93 | devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1); 94 | if (!devops) { 95 | return false; 96 | } 97 | // Use the space allocated at the end of the devoptab struct for storing the name 98 | nameCopy = (char*)(devops+1); 99 | 100 | // Initialize the file system 101 | partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector); 102 | if (!partition) { 103 | _FAT_mem_free (devops); 104 | return false; 105 | } 106 | 107 | // Add an entry for this device to the devoptab table 108 | memcpy (devops, &dotab_fat, sizeof(dotab_fat)); 109 | strcpy (nameCopy, name); 110 | devops->name = nameCopy; 111 | devops->deviceData = partition; 112 | 113 | AddDevice (devops); 114 | 115 | return true; 116 | } 117 | 118 | bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) { 119 | return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE); 120 | } 121 | 122 | void fatUnmount (const char* name) { 123 | devoptab_t *devops; 124 | PARTITION* partition; 125 | 126 | if(!name) 127 | return; 128 | 129 | devops = (devoptab_t*)GetDeviceOpTab (name); 130 | if (!devops) { 131 | return; 132 | } 133 | 134 | // Perform a quick check to make sure we're dealing with a libfat controlled device 135 | if (devops->open_r != dotab_fat.open_r) { 136 | return; 137 | } 138 | 139 | if (RemoveDevice (name) == -1) { 140 | return; 141 | } 142 | 143 | partition = (PARTITION*)devops->deviceData; 144 | _FAT_partition_destructor (partition); 145 | _FAT_mem_free (devops); 146 | } 147 | 148 | bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) { 149 | int i; 150 | int defaultDevice = -1; 151 | const DISC_INTERFACE *disc; 152 | 153 | for (i = 0; 154 | _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; 155 | i++) 156 | { 157 | disc = _FAT_disc_interfaces[i].getInterface(); 158 | if (!disc) { 159 | continue; 160 | } 161 | if (fatMount (_FAT_disc_interfaces[i].name, disc, 0, cacheSize, DEFAULT_SECTORS_PAGE)) { 162 | // The first device to successfully mount is set as the default 163 | if (defaultDevice < 0) { 164 | defaultDevice = i; 165 | } 166 | } 167 | } 168 | 169 | if (defaultDevice < 0) { 170 | // None of our devices mounted 171 | return false; 172 | } 173 | 174 | if (setAsDefaultDevice) { 175 | char filePath[PATH_MAX]; 176 | strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name); 177 | strcat (filePath, ":/"); 178 | #ifdef ARGV_MAGIC 179 | if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr( __system_argv->argv[0], '/' )!=NULL ) { 180 | // Check the app's path against each of our mounted devices, to see 181 | // if we can support it. If so, change to that path. 182 | for (i = 0; 183 | _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; 184 | i++) 185 | { 186 | if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name, 187 | strlen(_FAT_disc_interfaces[i].name))) 188 | { 189 | char *lastSlash; 190 | strcpy(filePath, __system_argv->argv[0]); 191 | lastSlash = strrchr( filePath, '/' ); 192 | 193 | if ( NULL != lastSlash) { 194 | if ( *(lastSlash - 1) == ':') lastSlash++; 195 | *lastSlash = 0; 196 | } 197 | } 198 | } 199 | } 200 | #endif 201 | chdir (filePath); 202 | } 203 | 204 | return true; 205 | } 206 | 207 | bool fatInitDefault (void) { 208 | return fatInit (DEFAULT_CACHE_PAGES, true); 209 | } 210 | 211 | void fatGetVolumeLabel (const char* name, char *label) { 212 | devoptab_t *devops; 213 | PARTITION* partition; 214 | char *buf; 215 | int namelen,i; 216 | 217 | if(!name || !label) 218 | return; 219 | 220 | namelen = strlen(name); 221 | buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2); 222 | strcpy(buf,name); 223 | 224 | if (name[namelen-1] == '/') { 225 | buf[namelen-1]='\0'; 226 | namelen--; 227 | } 228 | 229 | if (name[namelen-1] != ':') { 230 | buf[namelen]=':'; 231 | buf[namelen+1]='\0'; 232 | } 233 | 234 | devops = (devoptab_t*)GetDeviceOpTab(buf); 235 | 236 | for(i=0;buf[i]!='\0' && buf[i]!=':';i++); 237 | if (!devops || strncasecmp(buf,devops->name,i)) { 238 | _FAT_mem_free(buf); 239 | return; 240 | } 241 | 242 | _FAT_mem_free(buf); 243 | 244 | // Perform a quick check to make sure we're dealing with a libfat controlled device 245 | if (devops->open_r != dotab_fat.open_r) { 246 | return; 247 | } 248 | 249 | partition = (PARTITION*)devops->deviceData; 250 | 251 | if(!_FAT_directory_getVolumeLabel(partition, label)) { 252 | strncpy(label,partition->label,11); 253 | label[11]='\0'; 254 | } 255 | if(!strncmp(label, "NO NAME", 7)) label[0]='\0'; 256 | } 257 | -------------------------------------------------------------------------------- /source/lock.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #ifndef USE_LWP_LOCK 4 | 5 | #ifndef mutex_t 6 | typedef int mutex_t; 7 | #endif 8 | 9 | void __attribute__ ((weak)) _FAT_lock_init(mutex_t *mutex) 10 | { 11 | return; 12 | } 13 | 14 | void __attribute__ ((weak)) _FAT_lock_deinit(mutex_t *mutex) 15 | { 16 | return; 17 | } 18 | 19 | void __attribute__ ((weak)) _FAT_lock(mutex_t *mutex) 20 | { 21 | return; 22 | } 23 | 24 | void __attribute__ ((weak)) _FAT_unlock(mutex_t *mutex) 25 | { 26 | return; 27 | } 28 | 29 | #endif // USE_LWP_LOCK 30 | -------------------------------------------------------------------------------- /source/lock.h: -------------------------------------------------------------------------------- 1 | /* 2 | lock.h 3 | 4 | Copyright (c) 2008 Sven Peter 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 3. The name of the author may not be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 19 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 25 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | */ 28 | 29 | #ifndef _LOCK_H 30 | #define _LOCK_H 31 | 32 | #include "common.h" 33 | 34 | #ifdef USE_LWP_LOCK 35 | 36 | static inline void _FAT_lock_init(mutex_t *mutex) 37 | { 38 | LWP_MutexInit(mutex, false); 39 | } 40 | 41 | static inline void _FAT_lock_deinit(mutex_t *mutex) 42 | { 43 | LWP_MutexDestroy(*mutex); 44 | } 45 | 46 | static inline void _FAT_lock(mutex_t *mutex) 47 | { 48 | LWP_MutexLock(*mutex); 49 | } 50 | 51 | static inline void _FAT_unlock(mutex_t *mutex) 52 | { 53 | LWP_MutexUnlock(*mutex); 54 | } 55 | 56 | #else 57 | 58 | // We still need a blank lock type 59 | #ifndef mutex_t 60 | typedef int mutex_t; 61 | #endif 62 | 63 | void _FAT_lock_init(mutex_t *mutex); 64 | void _FAT_lock_deinit(mutex_t *mutex); 65 | void _FAT_lock(mutex_t *mutex); 66 | void _FAT_unlock(mutex_t *mutex); 67 | 68 | #endif // USE_LWP_LOCK 69 | 70 | 71 | #endif // _LOCK_H 72 | 73 | -------------------------------------------------------------------------------- /source/mem_allocate.h: -------------------------------------------------------------------------------- 1 | /* 2 | mem_allocate.h 3 | Memory allocation and destruction calls 4 | Replace these calls with custom allocators if 5 | malloc is unavailable 6 | 7 | Copyright (c) 2006 Michael "Chishm" Chisholm 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, 13 | this list of conditions and the following disclaimer. 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation and/or 16 | other materials provided with the distribution. 17 | 3. The name of the author may not be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 22 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _MEM_ALLOCATE_H 32 | #define _MEM_ALLOCATE_H 33 | 34 | #include 35 | 36 | static inline void* _FAT_mem_allocate (size_t size) { 37 | return malloc (size); 38 | } 39 | 40 | static inline void* _FAT_mem_align (size_t size) { 41 | #ifdef __wii__ 42 | return memalign (32, size); 43 | #else 44 | return malloc (size); 45 | #endif 46 | } 47 | 48 | static inline void _FAT_mem_free (void* mem) { 49 | free (mem); 50 | } 51 | 52 | #endif // _MEM_ALLOCATE_H 53 | -------------------------------------------------------------------------------- /source/partition.c: -------------------------------------------------------------------------------- 1 | /* 2 | partition.c 3 | Functions for mounting and dismounting partitions 4 | on various block devices. 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "partition.h" 31 | #include "bit_ops.h" 32 | #include "file_allocation_table.h" 33 | #include "directory.h" 34 | #include "mem_allocate.h" 35 | #include "fatfile.h" 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | /* 42 | Data offsets 43 | */ 44 | 45 | // BIOS Parameter Block offsets 46 | enum BPB { 47 | BPB_jmpBoot = 0x00, 48 | BPB_OEMName = 0x03, 49 | // BIOS Parameter Block 50 | BPB_bytesPerSector = 0x0B, 51 | BPB_sectorsPerCluster = 0x0D, 52 | BPB_reservedSectors = 0x0E, 53 | BPB_numFATs = 0x10, 54 | BPB_rootEntries = 0x11, 55 | BPB_numSectorsSmall = 0x13, 56 | BPB_mediaDesc = 0x15, 57 | BPB_sectorsPerFAT = 0x16, 58 | BPB_sectorsPerTrk = 0x18, 59 | BPB_numHeads = 0x1A, 60 | BPB_numHiddenSectors = 0x1C, 61 | BPB_numSectors = 0x20, 62 | // Ext BIOS Parameter Block for FAT16 63 | BPB_FAT16_driveNumber = 0x24, 64 | BPB_FAT16_reserved1 = 0x25, 65 | BPB_FAT16_extBootSig = 0x26, 66 | BPB_FAT16_volumeID = 0x27, 67 | BPB_FAT16_volumeLabel = 0x2B, 68 | BPB_FAT16_fileSysType = 0x36, 69 | // Bootcode 70 | BPB_FAT16_bootCode = 0x3E, 71 | // FAT32 extended block 72 | BPB_FAT32_sectorsPerFAT32 = 0x24, 73 | BPB_FAT32_extFlags = 0x28, 74 | BPB_FAT32_fsVer = 0x2A, 75 | BPB_FAT32_rootClus = 0x2C, 76 | BPB_FAT32_fsInfo = 0x30, 77 | BPB_FAT32_bkBootSec = 0x32, 78 | // Ext BIOS Parameter Block for FAT32 79 | BPB_FAT32_driveNumber = 0x40, 80 | BPB_FAT32_reserved1 = 0x41, 81 | BPB_FAT32_extBootSig = 0x42, 82 | BPB_FAT32_volumeID = 0x43, 83 | BPB_FAT32_volumeLabel = 0x47, 84 | BPB_FAT32_fileSysType = 0x52, 85 | // Bootcode 86 | BPB_FAT32_bootCode = 0x5A, 87 | BPB_bootSig_55 = 0x1FE, 88 | BPB_bootSig_AA = 0x1FF 89 | }; 90 | 91 | // File system information block offsets 92 | enum FSIB 93 | { 94 | FSIB_SIG1 = 0x00, 95 | FSIB_SIG2 = 0x1e4, 96 | FSIB_numberOfFreeCluster = 0x1e8, 97 | FSIB_numberLastAllocCluster = 0x1ec, 98 | FSIB_bootSig_55 = 0x1FE, 99 | FSIB_bootSig_AA = 0x1FF 100 | }; 101 | 102 | static const char FAT_SIG[3] = {'F', 'A', 'T'}; 103 | static const char FS_INFO_SIG1[4] = {'R', 'R', 'a', 'A'}; 104 | static const char FS_INFO_SIG2[4] = {'r', 'r', 'A', 'a'}; 105 | static const char FS_TWL_SIG[8] = { 0xe9, 0x00, 0x00, 0x54, 0x57, 0x4c, 0x20, 0x20 }; 106 | 107 | static bool isValidMBR(uint8_t *sectorBuffer) { 108 | return (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || 109 | !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || 110 | !memcmp(sectorBuffer, FS_TWL_SIG, sizeof(FS_TWL_SIG))); 111 | 112 | } 113 | 114 | sec_t FindFirstValidPartition_buf(const DISC_INTERFACE* disc, uint8_t *sectorBuffer) 115 | { 116 | uint8_t part_table[16*4]; 117 | uint8_t *ptr; 118 | int i; 119 | 120 | // Read first sector of disc 121 | if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) { 122 | return 0; 123 | } 124 | 125 | memcpy(part_table,sectorBuffer+0x1BE,16*4); 126 | ptr = part_table; 127 | 128 | for(i=0;i<4;i++,ptr+=16) { 129 | sec_t part_lba = u8array_to_u32(ptr, 0x8); 130 | 131 | if (isValidMBR(sectorBuffer)) 132 | { 133 | return part_lba; 134 | } 135 | 136 | if(ptr[4]==0) continue; 137 | 138 | if(ptr[4]==0x0F) { 139 | sec_t part_lba2=part_lba; 140 | sec_t next_lba2=0; 141 | int n; 142 | 143 | for(n=0;n<8;n++) // max 8 logic partitions 144 | { 145 | if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0; 146 | 147 | part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ; 148 | next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6); 149 | 150 | if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0; 151 | 152 | if (isValidMBR(sectorBuffer)) 153 | { 154 | return part_lba2; 155 | } 156 | 157 | if(next_lba2==0) break; 158 | } 159 | } else { 160 | if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0; 161 | 162 | 163 | if (isValidMBR(sectorBuffer)) 164 | { 165 | return part_lba; 166 | } 167 | } 168 | } 169 | return 0; 170 | } 171 | 172 | sec_t FindFirstValidPartition(const DISC_INTERFACE* disc) 173 | { 174 | uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); 175 | if (!sectorBuffer) return 0; 176 | sec_t ret = FindFirstValidPartition_buf(disc, sectorBuffer); 177 | _FAT_mem_free(sectorBuffer); 178 | return ret; 179 | } 180 | 181 | 182 | PARTITION* _FAT_partition_constructor_buf (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector, uint8_t *sectorBuffer) 183 | { 184 | PARTITION* partition; 185 | 186 | // Read first sector of disc 187 | if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { 188 | return NULL; 189 | } 190 | 191 | // Make sure it is a valid MBR or boot sector 192 | if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) { 193 | return NULL; 194 | } 195 | 196 | if (startSector != 0) { 197 | // We're told where to start the partition, so just accept it 198 | } else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { 199 | // Check if there is a FAT string, which indicates this is a boot sector 200 | startSector = 0; 201 | } else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { 202 | // Check for FAT32 203 | startSector = 0; 204 | } else { 205 | startSector = FindFirstValidPartition_buf(disc, sectorBuffer); 206 | if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { 207 | return NULL; 208 | } 209 | } 210 | 211 | if (!isValidMBR(sectorBuffer)) 212 | { 213 | return NULL; 214 | } 215 | 216 | partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION)); 217 | if (partition == NULL) { 218 | return NULL; 219 | } 220 | 221 | // Init the partition lock 222 | _FAT_lock_init(&partition->lock); 223 | 224 | if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) 225 | strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11); 226 | else 227 | strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11); 228 | partition->label[11] = '\0'; 229 | 230 | // Set partition's disc interface 231 | partition->disc = disc; 232 | 233 | // Store required information about the file system 234 | partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT); 235 | if (partition->fat.sectorsPerFat == 0) { 236 | partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32); 237 | } 238 | 239 | partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall); 240 | if (partition->numberOfSectors == 0) { 241 | partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors); 242 | } 243 | 244 | partition->bytesPerSector = u8array_to_u16(sectorBuffer, BPB_bytesPerSector); 245 | if(partition->bytesPerSector < MIN_SECTOR_SIZE || partition->bytesPerSector > MAX_SECTOR_SIZE) { 246 | // Unsupported sector size 247 | _FAT_mem_free(partition); 248 | return NULL; 249 | } 250 | 251 | partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster]; 252 | partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster; 253 | partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors); 254 | 255 | partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat); 256 | partition->dataStart = partition->rootDirStart + 257 | (( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector); 258 | 259 | partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector; 260 | 261 | //FS info sector 262 | partition->fsInfoSector = startSector + (u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) ? u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) : 1); 263 | 264 | // Store info about FAT 265 | uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster; 266 | partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1; 267 | partition->fat.firstFree = CLUSTER_FIRST; 268 | partition->fat.numberFreeCluster = 0; 269 | partition->fat.numberLastAllocCluster = 0; 270 | 271 | if (clusterCount < CLUSTERS_PER_FAT12) { 272 | partition->filesysType = FS_FAT12; // FAT12 volume 273 | } else if (clusterCount < CLUSTERS_PER_FAT16) { 274 | partition->filesysType = FS_FAT16; // FAT16 volume 275 | } else { 276 | partition->filesysType = FS_FAT32; // FAT32 volume 277 | } 278 | 279 | if (partition->filesysType != FS_FAT32) { 280 | partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER; 281 | } else { 282 | // Set up for the FAT32 way 283 | partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus); 284 | // Check if FAT mirroring is enabled 285 | if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) { 286 | // Use the active FAT 287 | partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F)); 288 | } 289 | } 290 | 291 | // Create a cache to use 292 | partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors, partition->bytesPerSector); 293 | 294 | // Set current directory to the root 295 | partition->cwdCluster = partition->rootDirCluster; 296 | 297 | // Check if this disc is writable, and set the readOnly property appropriately 298 | partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE); 299 | 300 | // There are currently no open files on this partition 301 | partition->openFileCount = 0; 302 | partition->firstOpenFile = NULL; 303 | 304 | _FAT_partition_readFSinfo(partition); 305 | 306 | return partition; 307 | } 308 | 309 | PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) 310 | { 311 | uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); 312 | if (!sectorBuffer) return NULL; 313 | PARTITION *ret = _FAT_partition_constructor_buf(disc, cacheSize, 314 | sectorsPerPage, startSector, sectorBuffer); 315 | _FAT_mem_free(sectorBuffer); 316 | return ret; 317 | } 318 | 319 | 320 | void _FAT_partition_destructor (PARTITION* partition) { 321 | FILE_STRUCT* nextFile; 322 | 323 | _FAT_lock(&partition->lock); 324 | 325 | // Synchronize open files 326 | nextFile = partition->firstOpenFile; 327 | while (nextFile) { 328 | _FAT_syncToDisc (nextFile); 329 | nextFile = nextFile->nextOpenFile; 330 | } 331 | 332 | // Write out the fs info sector 333 | _FAT_partition_writeFSinfo(partition); 334 | 335 | // Free memory used by the cache, writing it to disc at the same time 336 | _FAT_cache_destructor (partition->cache); 337 | 338 | // Unlock the partition and destroy the lock 339 | _FAT_unlock(&partition->lock); 340 | _FAT_lock_deinit(&partition->lock); 341 | 342 | // Free memory used by the partition 343 | _FAT_mem_free (partition); 344 | } 345 | 346 | PARTITION* _FAT_partition_getPartitionFromPath (const char* path) { 347 | const devoptab_t *devops; 348 | 349 | devops = GetDeviceOpTab (path); 350 | 351 | if (!devops) { 352 | return NULL; 353 | } 354 | 355 | return (PARTITION*)devops->deviceData; 356 | } 357 | 358 | static void _FAT_updateFS_INFO(PARTITION * partition, uint8_t *sectorBuffer) { 359 | partition->fat.numberFreeCluster = _FAT_fat_freeClusterCount(partition); 360 | u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); 361 | u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); 362 | _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); 363 | } 364 | 365 | void _FAT_partition_createFSinfo(PARTITION * partition) 366 | { 367 | if(partition->readOnly || partition->filesysType != FS_FAT32) 368 | return; 369 | 370 | uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); 371 | if (!sectorBuffer) return; 372 | memset(sectorBuffer, 0, partition->bytesPerSector); 373 | 374 | int i; 375 | for(i = 0; i < 4; ++i) 376 | { 377 | sectorBuffer[FSIB_SIG1+i] = FS_INFO_SIG1[i]; 378 | sectorBuffer[FSIB_SIG2+i] = FS_INFO_SIG2[i]; 379 | } 380 | 381 | sectorBuffer[FSIB_bootSig_55] = 0x55; 382 | sectorBuffer[FSIB_bootSig_AA] = 0xAA; 383 | 384 | _FAT_updateFS_INFO(partition,sectorBuffer); 385 | 386 | _FAT_mem_free(sectorBuffer); 387 | } 388 | 389 | void _FAT_partition_readFSinfo(PARTITION * partition) 390 | { 391 | if(partition->filesysType != FS_FAT32) 392 | return; 393 | 394 | uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); 395 | if (!sectorBuffer) return; 396 | memset(sectorBuffer, 0, partition->bytesPerSector); 397 | // Read first sector of disc 398 | if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { 399 | _FAT_mem_free(sectorBuffer); 400 | return; 401 | } 402 | 403 | if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) != 0 || 404 | memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4) != 0 || 405 | u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0) 406 | { 407 | //sector does not yet exist, create one! 408 | _FAT_partition_createFSinfo(partition); 409 | } else { 410 | partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster); 411 | if(partition->fat.numberFreeCluster == 0xffffffff) { 412 | _FAT_updateFS_INFO(partition,sectorBuffer); 413 | partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster); 414 | } 415 | partition->fat.numberLastAllocCluster = u8array_to_u32(sectorBuffer, FSIB_numberLastAllocCluster); 416 | } 417 | _FAT_mem_free(sectorBuffer); 418 | } 419 | 420 | void _FAT_partition_writeFSinfo(PARTITION * partition) 421 | { 422 | if(partition->filesysType != FS_FAT32) 423 | return; 424 | 425 | uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); 426 | if (!sectorBuffer) return; 427 | memset(sectorBuffer, 0, partition->bytesPerSector); 428 | // Read first sector of disc 429 | if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { 430 | _FAT_mem_free(sectorBuffer); 431 | return; 432 | } 433 | 434 | if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4)) { 435 | _FAT_mem_free(sectorBuffer); 436 | return; 437 | } 438 | 439 | u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); 440 | u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); 441 | 442 | // Write first sector of disc 443 | _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); 444 | _FAT_mem_free(sectorBuffer); 445 | } 446 | 447 | uint32_t* _FAT_getCwdClusterPtr(const char* name) { 448 | PARTITION *partition = _FAT_partition_getPartitionFromPath(name); 449 | 450 | if (!partition) { 451 | return NULL; 452 | } 453 | 454 | return &partition->cwdCluster; 455 | } 456 | -------------------------------------------------------------------------------- /source/partition.h: -------------------------------------------------------------------------------- 1 | /* 2 | partition.h 3 | Functions for mounting and dismounting partitions 4 | on various block devices. 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _PARTITION_H 31 | #define _PARTITION_H 32 | 33 | #include "common.h" 34 | #include "cache.h" 35 | #include "lock.h" 36 | 37 | #define MIN_SECTOR_SIZE 512 38 | #define MAX_SECTOR_SIZE 4096 39 | 40 | // Filesystem type 41 | typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; 42 | 43 | typedef struct { 44 | sec_t fatStart; 45 | uint32_t sectorsPerFat; 46 | uint32_t lastCluster; 47 | uint32_t firstFree; 48 | uint32_t numberFreeCluster; 49 | uint32_t numberLastAllocCluster; 50 | } FAT; 51 | 52 | typedef struct { 53 | const DISC_INTERFACE* disc; 54 | CACHE* cache; 55 | // Info about the partition 56 | FS_TYPE filesysType; 57 | uint64_t totalSize; 58 | sec_t rootDirStart; 59 | uint32_t rootDirCluster; 60 | uint32_t numberOfSectors; 61 | sec_t dataStart; 62 | uint32_t bytesPerSector; 63 | uint32_t sectorsPerCluster; 64 | uint32_t bytesPerCluster; 65 | uint32_t fsInfoSector; 66 | FAT fat; 67 | // Values that may change after construction 68 | uint32_t cwdCluster; // Current working directory cluster 69 | int openFileCount; 70 | struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files 71 | mutex_t lock; // A lock for partition operations 72 | bool readOnly; // If this is set, then do not try writing to the disc 73 | char label[12]; // Volume label 74 | } PARTITION; 75 | 76 | /* 77 | Mount the supplied device and return a pointer to the struct necessary to use it 78 | */ 79 | PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector); 80 | 81 | /* 82 | Dismount the device and free all structures used. 83 | Will also attempt to synchronise all open files to disc. 84 | */ 85 | void _FAT_partition_destructor (PARTITION* partition); 86 | 87 | /* 88 | Return the partition specified in a path, as taken from the devoptab. 89 | */ 90 | PARTITION* _FAT_partition_getPartitionFromPath (const char* path); 91 | 92 | /* 93 | Create the fs info sector. 94 | */ 95 | void _FAT_partition_createFSinfo(PARTITION * partition); 96 | 97 | /* 98 | Read the fs info sector data. 99 | */ 100 | void _FAT_partition_readFSinfo(PARTITION * partition); 101 | 102 | /* 103 | Write the fs info sector data. 104 | */ 105 | void _FAT_partition_writeFSinfo(PARTITION * partition); 106 | 107 | #endif // _PARTITION_H 108 | --------------------------------------------------------------------------------