Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of FatFileSystem by
ff.c
00001 /*----------------------------------------------------------------------------/ 00002 / FatFs - FAT file system module R0.09 (C)ChaN, 2011 00003 /-----------------------------------------------------------------------------/ 00004 / FatFs module is a generic FAT file system module for small embedded systems. 00005 / This is a free software that opened for education, research and commercial 00006 / developments under license policy of following terms. 00007 / 00008 / Copyright (C) 2011, ChaN, all right reserved. 00009 / 00010 / * The FatFs module is a free software and there is NO WARRANTY. 00011 / * No restriction on use. You can use, modify and redistribute it for 00012 / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 00013 / * Redistributions of source code must retain the above copyright notice. 00014 / 00015 /-----------------------------------------------------------------------------/ 00016 / Feb 26,'06 R0.00 Prototype. 00017 / 00018 / Apr 29,'06 R0.01 First stable version. 00019 / 00020 / Jun 01,'06 R0.02 Added FAT12 support. 00021 / Removed unbuffered mode. 00022 / Fixed a problem on small (<32M) partition. 00023 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). 00024 / 00025 / Sep 22,'06 R0.03 Added f_rename(). 00026 / Changed option _FS_MINIMUM to _FS_MINIMIZE. 00027 / Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast. 00028 / Fixed f_mkdir() creates incorrect directory on FAT32. 00029 / 00030 / Feb 04,'07 R0.04 Supported multiple drive system. 00031 / Changed some interfaces for multiple drive system. 00032 / Changed f_mountdrv() to f_mount(). 00033 / Added f_mkfs(). 00034 / Apr 01,'07 R0.04a Supported multiple partitions on a physical drive. 00035 / Added a capability of extending file size to f_lseek(). 00036 / Added minimization level 3. 00037 / Fixed an endian sensitive code in f_mkfs(). 00038 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. 00039 / Added FSInfo support. 00040 / Fixed DBCS name can result FR_INVALID_NAME. 00041 / Fixed short seek (<= csize) collapses the file object. 00042 / 00043 / Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). 00044 / Fixed f_mkfs() on FAT32 creates incorrect FSInfo. 00045 / Fixed f_mkdir() on FAT32 creates incorrect directory. 00046 / Feb 03,'08 R0.05a Added f_truncate() and f_utime(). 00047 / Fixed off by one error at FAT sub-type determination. 00048 / Fixed btr in f_read() can be mistruncated. 00049 / Fixed cached sector is not flushed when create and close without write. 00050 / 00051 / Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). 00052 / Improved performance of f_lseek() on moving to the same or following cluster. 00053 / 00054 / Apr 01,'09 R0.07 Merged Tiny-FatFs as a configuration option. (_FS_TINY) 00055 / Added long file name feature. 00056 / Added multiple code page feature. 00057 / Added re-entrancy for multitask operation. 00058 / Added auto cluster size selection to f_mkfs(). 00059 / Added rewind option to f_readdir(). 00060 / Changed result code of critical errors. 00061 / Renamed string functions to avoid name collision. 00062 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. 00063 / Added multiple sector size feature. 00064 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. 00065 / Fixed wrong cache control in f_lseek(). 00066 / Added relative path feature. 00067 / Added f_chdir() and f_chdrive(). 00068 / Added proper case conversion to extended char. 00069 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. 00070 / Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. 00071 / Fixed name matching error on the 13 char boundary. 00072 / Added a configuration option, _LFN_UNICODE. 00073 / Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. 00074 / 00075 / May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3) 00076 / Added file lock feature. (_FS_SHARE) 00077 / Added fast seek feature. (_USE_FASTSEEK) 00078 / Changed some types on the API, XCHAR->TCHAR. 00079 / Changed fname member in the FILINFO structure on Unicode cfg. 00080 / String functions support UTF-8 encoding files on Unicode cfg. 00081 / Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) 00082 / Added sector erase feature. (_USE_ERASE) 00083 / Moved file lock semaphore table from fs object to the bss. 00084 / Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. 00085 / Fixed f_mkfs() creates wrong FAT32 volume. 00086 / Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write(). 00087 / f_lseek() reports required table size on creating CLMP. 00088 / Extended format syntax of f_printf function. 00089 / Ignores duplicated directory separators in given path names. 00090 / 00091 / Sep 06,'11 R0.09 f_mkfs() supports multiple partition to finish the multiple partition feature. 00092 / Added f_fdisk(). (_MULTI_PARTITION = 2) 00093 /---------------------------------------------------------------------------*/ 00094 00095 #include "ff.h" /* FatFs configurations and declarations */ 00096 #include "diskio.h" /* Declarations of low level disk I/O functions */ 00097 00098 00099 /*-------------------------------------------------------------------------- 00100 00101 Module Private Definitions 00102 00103 ---------------------------------------------------------------------------*/ 00104 00105 #if _FATFS != 6502 /* Revision ID */ 00106 #error Wrong include file (ff.h). 00107 #endif 00108 00109 00110 /* Definitions on sector size */ 00111 #if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 00112 #error Wrong sector size. 00113 #endif 00114 #if _MAX_SS != 512 00115 #define SS(fs) ((fs)->ssize) /* Variable sector size */ 00116 #else 00117 #define SS(fs) 512U /* Fixed sector size */ 00118 #endif 00119 00120 00121 /* Reentrancy related */ 00122 #if _FS_REENTRANT 00123 #if _USE_LFN == 1 00124 #error Static LFN work area must not be used in re-entrant configuration. 00125 #endif 00126 #define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } 00127 #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } 00128 #else 00129 #define ENTER_FF(fs) 00130 #define LEAVE_FF(fs, res) return res 00131 #endif 00132 00133 #define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } 00134 00135 00136 /* File shareing feature */ 00137 #if _FS_SHARE 00138 #if _FS_READONLY 00139 #error _FS_SHARE must be 0 on read-only cfg. 00140 #endif 00141 typedef struct { 00142 FATFS *fs; /* File ID 1, volume (NULL:blank entry) */ 00143 DWORD clu; /* File ID 2, directory */ 00144 WORD idx; /* File ID 3, directory index */ 00145 WORD ctr; /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */ 00146 } FILESEM; 00147 #endif 00148 00149 00150 /* Misc definitions */ 00151 #define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO)) 00152 #define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);} 00153 00154 00155 /* DBCS code ranges and SBCS extend char conversion table */ 00156 00157 #if _CODE_PAGE == 932 /* Japanese Shift-JIS */ 00158 #define _DF1S 0x81 /* DBC 1st byte range 1 start */ 00159 #define _DF1E 0x9F /* DBC 1st byte range 1 end */ 00160 #define _DF2S 0xE0 /* DBC 1st byte range 2 start */ 00161 #define _DF2E 0xFC /* DBC 1st byte range 2 end */ 00162 #define _DS1S 0x40 /* DBC 2nd byte range 1 start */ 00163 #define _DS1E 0x7E /* DBC 2nd byte range 1 end */ 00164 #define _DS2S 0x80 /* DBC 2nd byte range 2 start */ 00165 #define _DS2E 0xFC /* DBC 2nd byte range 2 end */ 00166 00167 #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ 00168 #define _DF1S 0x81 00169 #define _DF1E 0xFE 00170 #define _DS1S 0x40 00171 #define _DS1E 0x7E 00172 #define _DS2S 0x80 00173 #define _DS2E 0xFE 00174 00175 #elif _CODE_PAGE == 949 /* Korean */ 00176 #define _DF1S 0x81 00177 #define _DF1E 0xFE 00178 #define _DS1S 0x41 00179 #define _DS1E 0x5A 00180 #define _DS2S 0x61 00181 #define _DS2E 0x7A 00182 #define _DS3S 0x81 00183 #define _DS3E 0xFE 00184 00185 #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ 00186 #define _DF1S 0x81 00187 #define _DF1E 0xFE 00188 #define _DS1S 0x40 00189 #define _DS1E 0x7E 00190 #define _DS2S 0xA1 00191 #define _DS2E 0xFE 00192 00193 #elif _CODE_PAGE == 437 /* U.S. (OEM) */ 00194 #define _DF1S 0 00195 #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00196 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00197 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00198 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00199 00200 #elif _CODE_PAGE == 720 /* Arabic (OEM) */ 00201 #define _DF1S 0 00202 #define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00203 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00204 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00205 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00206 00207 #elif _CODE_PAGE == 737 /* Greek (OEM) */ 00208 #define _DF1S 0 00209 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ 00210 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00211 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00212 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00213 00214 #elif _CODE_PAGE == 775 /* Baltic (OEM) */ 00215 #define _DF1S 0 00216 #define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 00217 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00218 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00219 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00220 00221 #elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ 00222 #define _DF1S 0 00223 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 00224 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00225 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00226 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00227 00228 #elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ 00229 #define _DF1S 0 00230 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ 00231 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ 00232 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00233 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} 00234 00235 #elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ 00236 #define _DF1S 0 00237 #define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ 00238 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ 00239 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ 00240 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} 00241 00242 #elif _CODE_PAGE == 857 /* Turkish (OEM) */ 00243 #define _DF1S 0 00244 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ 00245 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00246 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00247 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00248 00249 #elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ 00250 #define _DF1S 0 00251 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 00252 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00253 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00254 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00255 00256 #elif _CODE_PAGE == 862 /* Hebrew (OEM) */ 00257 #define _DF1S 0 00258 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00259 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00260 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00261 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00262 00263 #elif _CODE_PAGE == 866 /* Russian (OEM) */ 00264 #define _DF1S 0 00265 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00266 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00267 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00268 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00269 00270 #elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ 00271 #define _DF1S 0 00272 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00273 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00274 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00275 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00276 00277 #elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ 00278 #define _DF1S 0 00279 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ 00280 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ 00281 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00282 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} 00283 00284 #elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ 00285 #define _DF1S 0 00286 #define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ 00287 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ 00288 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00289 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} 00290 00291 #elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ 00292 #define _DF1S 0 00293 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ 00294 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00295 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00296 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} 00297 00298 #elif _CODE_PAGE == 1253 /* Greek (Windows) */ 00299 #define _DF1S 0 00300 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00301 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00302 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ 00303 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} 00304 00305 #elif _CODE_PAGE == 1254 /* Turkish (Windows) */ 00306 #define _DF1S 0 00307 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ 00308 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00309 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00310 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} 00311 00312 #elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ 00313 #define _DF1S 0 00314 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00315 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00316 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00317 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 00318 00319 #elif _CODE_PAGE == 1256 /* Arabic (Windows) */ 00320 #define _DF1S 0 00321 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ 00322 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00323 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00324 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} 00325 00326 #elif _CODE_PAGE == 1257 /* Baltic (Windows) */ 00327 #define _DF1S 0 00328 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 00329 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ 00330 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00331 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} 00332 00333 #elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ 00334 #define _DF1S 0 00335 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ 00336 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 00337 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 00338 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} 00339 00340 #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ 00341 #if _USE_LFN 00342 #error Cannot use LFN feature without valid code page. 00343 #endif 00344 #define _DF1S 0 00345 00346 #else 00347 #error Unknown code page 00348 00349 #endif 00350 00351 00352 /* Character code support macros */ 00353 #define IsUpper(c) (((c)>='A')&&((c)<='Z')) 00354 #define IsLower(c) (((c)>='a')&&((c)<='z')) 00355 #define IsDigit(c) (((c)>='0')&&((c)<='9')) 00356 00357 #if _DF1S /* Code page is DBCS */ 00358 00359 #ifdef _DF2S /* Two 1st byte areas */ 00360 #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) 00361 #else /* One 1st byte area */ 00362 #define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) 00363 #endif 00364 00365 #ifdef _DS3S /* Three 2nd byte areas */ 00366 #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) 00367 #else /* Two 2nd byte areas */ 00368 #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) 00369 #endif 00370 00371 #else /* Code page is SBCS */ 00372 00373 #define IsDBCS1(c) 0 00374 #define IsDBCS2(c) 0 00375 00376 #endif /* _DF1S */ 00377 00378 00379 /* Name status flags */ 00380 #define NS 11 /* Index of name status byte in fn[] */ 00381 #define NS_LOSS 0x01 /* Out of 8.3 format */ 00382 #define NS_LFN 0x02 /* Force to create LFN entry */ 00383 #define NS_LAST 0x04 /* Last segment */ 00384 #define NS_BODY 0x08 /* Lower case flag (body) */ 00385 #define NS_EXT 0x10 /* Lower case flag (ext) */ 00386 #define NS_DOT 0x20 /* Dot entry */ 00387 00388 00389 /* FAT sub-type boundaries */ 00390 /* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ 00391 #define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */ 00392 #define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */ 00393 00394 00395 /* FatFs refers the members in the FAT structures as byte array instead of 00396 / structure member because the structure is not binary compatible between 00397 / different platforms */ 00398 00399 #define BS_jmpBoot 0 /* Jump instruction (3) */ 00400 #define BS_OEMName 3 /* OEM name (8) */ 00401 #define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ 00402 #define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ 00403 #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ 00404 #define BPB_NumFATs 16 /* Number of FAT copies (1) */ 00405 #define BPB_RootEntCnt 17 /* Number of root dir entries for FAT12/16 (2) */ 00406 #define BPB_TotSec16 19 /* Volume size [sector] (2) */ 00407 #define BPB_Media 21 /* Media descriptor (1) */ 00408 #define BPB_FATSz16 22 /* FAT size [sector] (2) */ 00409 #define BPB_SecPerTrk 24 /* Track size [sector] (2) */ 00410 #define BPB_NumHeads 26 /* Number of heads (2) */ 00411 #define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ 00412 #define BPB_TotSec32 32 /* Volume size [sector] (4) */ 00413 #define BS_DrvNum 36 /* Physical drive number (2) */ 00414 #define BS_BootSig 38 /* Extended boot signature (1) */ 00415 #define BS_VolID 39 /* Volume serial number (4) */ 00416 #define BS_VolLab 43 /* Volume label (8) */ 00417 #define BS_FilSysType 54 /* File system type (1) */ 00418 #define BPB_FATSz32 36 /* FAT size [sector] (4) */ 00419 #define BPB_ExtFlags 40 /* Extended flags (2) */ 00420 #define BPB_FSVer 42 /* File system version (2) */ 00421 #define BPB_RootClus 44 /* Root dir first cluster (4) */ 00422 #define BPB_FSInfo 48 /* Offset of FSInfo sector (2) */ 00423 #define BPB_BkBootSec 50 /* Offset of backup boot sectot (2) */ 00424 #define BS_DrvNum32 64 /* Physical drive number (2) */ 00425 #define BS_BootSig32 66 /* Extended boot signature (1) */ 00426 #define BS_VolID32 67 /* Volume serial number (4) */ 00427 #define BS_VolLab32 71 /* Volume label (8) */ 00428 #define BS_FilSysType32 82 /* File system type (1) */ 00429 #define FSI_LeadSig 0 /* FSI: Leading signature (4) */ 00430 #define FSI_StrucSig 484 /* FSI: Structure signature (4) */ 00431 #define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ 00432 #define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ 00433 #define MBR_Table 446 /* MBR: Partition table offset (2) */ 00434 #define SZ_PTE 16 /* MBR: Size of a partition table entry */ 00435 #define BS_55AA 510 /* Boot sector signature (2) */ 00436 00437 #define DIR_Name 0 /* Short file name (11) */ 00438 #define DIR_Attr 11 /* Attribute (1) */ 00439 #define DIR_NTres 12 /* NT flag (1) */ 00440 #define DIR_CrtTime 14 /* Created time (2) */ 00441 #define DIR_CrtDate 16 /* Created date (2) */ 00442 #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ 00443 #define DIR_WrtTime 22 /* Modified time (2) */ 00444 #define DIR_WrtDate 24 /* Modified date (2) */ 00445 #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ 00446 #define DIR_FileSize 28 /* File size (4) */ 00447 #define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ 00448 #define LDIR_Attr 11 /* LFN attribute (1) */ 00449 #define LDIR_Type 12 /* LFN type (1) */ 00450 #define LDIR_Chksum 13 /* Sum of corresponding SFN entry */ 00451 #define LDIR_FstClusLO 26 /* Filled by zero (0) */ 00452 #define SZ_DIR 32 /* Size of a directory entry */ 00453 #define LLE 0x40 /* Last long entry flag in LDIR_Ord */ 00454 #define DDE 0xE5 /* Deleted directory enrty mark in DIR_Name[0] */ 00455 #define NDDE 0x05 /* Replacement of a character collides with DDE */ 00456 00457 00458 /*------------------------------------------------------------*/ 00459 /* Module private work area */ 00460 /*------------------------------------------------------------*/ 00461 /* Note that uninitialized variables with static duration are 00462 / zeroed/nulled at start-up. If not, the compiler or start-up 00463 / routine is out of ANSI-C standard. 00464 */ 00465 00466 #if _VOLUMES 00467 static 00468 FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ 00469 #else 00470 #error Number of volumes must not be 0. 00471 #endif 00472 00473 static 00474 WORD Fsid; /* File system mount ID */ 00475 00476 #if _FS_RPATH 00477 static 00478 BYTE CurrVol; /* Current drive */ 00479 #endif 00480 00481 #if _FS_SHARE 00482 static 00483 FILESEM Files[_FS_SHARE]; /* File lock semaphores */ 00484 #endif 00485 00486 #if _USE_LFN == 0 /* No LFN feature */ 00487 #define DEF_NAMEBUF BYTE sfn[12] 00488 #define INIT_BUF(dobj) (dobj).fn = sfn 00489 #define FREE_BUF() 00490 00491 #elif _USE_LFN == 1 /* LFN feature with static working buffer */ 00492 static WCHAR LfnBuf[_MAX_LFN+1]; 00493 #define DEF_NAMEBUF BYTE sfn[12] 00494 #define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } 00495 #define FREE_BUF() 00496 00497 #elif _USE_LFN == 2 /* LFN feature with dynamic working buffer on the stack */ 00498 #define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] 00499 #define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } 00500 #define FREE_BUF() 00501 00502 #elif _USE_LFN == 3 /* LFN feature with dynamic working buffer on the heap */ 00503 #define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn 00504 #define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ 00505 if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ 00506 (dobj).lfn = lfn; (dobj).fn = sfn; } 00507 #define FREE_BUF() ff_memfree(lfn) 00508 00509 #else 00510 #error Wrong LFN configuration. 00511 #endif 00512 00513 00514 00515 00516 /*-------------------------------------------------------------------------- 00517 00518 Module Private Functions 00519 00520 ---------------------------------------------------------------------------*/ 00521 00522 00523 /*-----------------------------------------------------------------------*/ 00524 /* String functions */ 00525 /*-----------------------------------------------------------------------*/ 00526 00527 /* Copy memory to memory */ 00528 static 00529 void mem_cpy (void* dst, const void* src, UINT cnt) { 00530 BYTE *d = (BYTE*)dst; 00531 const BYTE *s = (const BYTE*)src; 00532 00533 #if _WORD_ACCESS == 1 00534 while (cnt >= sizeof(int)) { 00535 *(int*)d = *(int*)s; 00536 d += sizeof(int); s += sizeof(int); 00537 cnt -= sizeof(int); 00538 } 00539 #endif 00540 while (cnt--) 00541 *d++ = *s++; 00542 } 00543 00544 /* Fill memory */ 00545 static 00546 void mem_set (void* dst, int val, UINT cnt) { 00547 BYTE *d = (BYTE*)dst; 00548 00549 while (cnt--) 00550 *d++ = (BYTE)val; 00551 } 00552 00553 /* Compare memory to memory */ 00554 static 00555 int mem_cmp (const void* dst, const void* src, UINT cnt) { 00556 const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; 00557 int r = 0; 00558 00559 while (cnt-- && (r = *d++ - *s++) == 0) ; 00560 return r; 00561 } 00562 00563 /* Check if chr is contained in the string */ 00564 static 00565 int chk_chr (const char* str, int chr) { 00566 while (*str && *str != chr) str++; 00567 return *str; 00568 } 00569 00570 00571 00572 /*-----------------------------------------------------------------------*/ 00573 /* Request/Release grant to access the volume */ 00574 /*-----------------------------------------------------------------------*/ 00575 #if _FS_REENTRANT 00576 00577 static 00578 int lock_fs ( 00579 FATFS *fs /* File system object */ 00580 ) 00581 { 00582 return ff_req_grant(fs->sobj); 00583 } 00584 00585 00586 static 00587 void unlock_fs ( 00588 FATFS *fs, /* File system object */ 00589 FRESULT res /* Result code to be returned */ 00590 ) 00591 { 00592 if (res != FR_NOT_ENABLED && 00593 res != FR_INVALID_DRIVE && 00594 res != FR_INVALID_OBJECT && 00595 res != FR_TIMEOUT) { 00596 ff_rel_grant(fs->sobj); 00597 } 00598 } 00599 #endif 00600 00601 00602 00603 /*-----------------------------------------------------------------------*/ 00604 /* File shareing control functions */ 00605 /*-----------------------------------------------------------------------*/ 00606 #if _FS_SHARE 00607 00608 static 00609 FRESULT chk_lock ( /* Check if the file can be accessed */ 00610 FATFS_DIR* dj, /* Directory object pointing the file to be checked */ 00611 int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ 00612 ) 00613 { 00614 UINT i, be; 00615 00616 /* Search file semaphore table */ 00617 for (i = be = 0; i < _FS_SHARE; i++) { 00618 if (Files[i].fs) { /* Existing entry */ 00619 if (Files[i].fs == dj->fs && /* Check if the file matched with an open file */ 00620 Files[i].clu == dj->sclust && 00621 Files[i].idx == dj->index) break; 00622 } else { /* Blank entry */ 00623 be++; 00624 } 00625 } 00626 if (i == _FS_SHARE) /* The file is not opened */ 00627 return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new file? */ 00628 00629 /* The file has been opened. Reject any open against writing file and all write mode open */ 00630 return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; 00631 } 00632 00633 00634 static 00635 int enq_lock (void) /* Check if an entry is available for a new file */ 00636 { 00637 UINT i; 00638 00639 for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; 00640 return (i == _FS_SHARE) ? 0 : 1; 00641 } 00642 00643 00644 static 00645 UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */ 00646 FATFS_DIR* dj, /* Directory object pointing the file to register or increment */ 00647 int acc /* Desired access mode (0:Read, !0:Write) */ 00648 ) 00649 { 00650 UINT i; 00651 00652 00653 for (i = 0; i < _FS_SHARE; i++) { /* Find the file */ 00654 if (Files[i].fs == dj->fs && 00655 Files[i].clu == dj->sclust && 00656 Files[i].idx == dj->index) break; 00657 } 00658 00659 if (i == _FS_SHARE) { /* Not opened. Register it as new. */ 00660 for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; 00661 if (i == _FS_SHARE) return 0; /* No space to register (int err) */ 00662 Files[i].fs = dj->fs; 00663 Files[i].clu = dj->sclust; 00664 Files[i].idx = dj->index; 00665 Files[i].ctr = 0; 00666 } 00667 00668 if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ 00669 00670 Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ 00671 00672 return i + 1; 00673 } 00674 00675 00676 static 00677 FRESULT dec_lock ( /* Decrement file open counter */ 00678 UINT i /* Semaphore index */ 00679 ) 00680 { 00681 WORD n; 00682 FRESULT res; 00683 00684 00685 if (--i < _FS_SHARE) { 00686 n = Files[i].ctr; 00687 if (n == 0x100) n = 0; 00688 if (n) n--; 00689 Files[i].ctr = n; 00690 if (!n) Files[i].fs = 0; 00691 res = FR_OK; 00692 } else { 00693 res = FR_INT_ERR; 00694 } 00695 return res; 00696 } 00697 00698 00699 static 00700 void clear_lock ( /* Clear lock entries of the volume */ 00701 FATFS *fs 00702 ) 00703 { 00704 UINT i; 00705 00706 for (i = 0; i < _FS_SHARE; i++) { 00707 if (Files[i].fs == fs) Files[i].fs = 0; 00708 } 00709 } 00710 #endif 00711 00712 00713 00714 /*-----------------------------------------------------------------------*/ 00715 /* Change window offset */ 00716 /*-----------------------------------------------------------------------*/ 00717 00718 static 00719 FRESULT move_window ( 00720 FATFS *fs, /* File system object */ 00721 DWORD sector /* Sector number to make appearance in the fs->win[] */ 00722 ) /* Move to zero only writes back dirty window */ 00723 { 00724 DWORD wsect; 00725 00726 00727 wsect = fs->winsect; 00728 if (wsect != sector) { /* Changed current window */ 00729 #if !_FS_READONLY 00730 if (fs->wflag) { /* Write back dirty window if needed */ 00731 if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) 00732 return FR_DISK_ERR; 00733 fs->wflag = 0; 00734 if (wsect < (fs->fatbase + fs->fsize)) { /* In FAT area */ 00735 BYTE nf; 00736 for (nf = fs->n_fats; nf > 1; nf--) { /* Reflect the change to all FAT copies */ 00737 wsect += fs->fsize; 00738 disk_write(fs->drv, fs->win, wsect, 1); 00739 } 00740 } 00741 } 00742 #endif 00743 if (sector) { 00744 if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) 00745 return FR_DISK_ERR; 00746 fs->winsect = sector; 00747 } 00748 } 00749 00750 return FR_OK; 00751 } 00752 00753 00754 00755 00756 /*-----------------------------------------------------------------------*/ 00757 /* Clean-up cached data */ 00758 /*-----------------------------------------------------------------------*/ 00759 #if !_FS_READONLY 00760 static 00761 FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ 00762 FATFS *fs /* File system object */ 00763 ) 00764 { 00765 FRESULT res; 00766 00767 00768 res = move_window(fs, 0); 00769 if (res == FR_OK) { 00770 /* Update FSInfo sector if needed */ 00771 if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { 00772 fs->winsect = 0; 00773 /* Create FSInfo structure */ 00774 mem_set(fs->win, 0, 512); 00775 ST_WORD(fs->win+BS_55AA, 0xAA55); 00776 ST_DWORD(fs->win+FSI_LeadSig, 0x41615252); 00777 ST_DWORD(fs->win+FSI_StrucSig, 0x61417272); 00778 ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust); 00779 ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust); 00780 /* Write it into the FSInfo sector */ 00781 disk_write(fs->drv, fs->win, fs->fsi_sector, 1); 00782 fs->fsi_flag = 0; 00783 } 00784 /* Make sure that no pending write process in the physical drive */ 00785 if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) 00786 res = FR_DISK_ERR; 00787 } 00788 00789 return res; 00790 } 00791 #endif 00792 00793 00794 00795 00796 /*-----------------------------------------------------------------------*/ 00797 /* Get sector# from cluster# */ 00798 /*-----------------------------------------------------------------------*/ 00799 00800 00801 DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ 00802 FATFS *fs, /* File system object */ 00803 DWORD clst /* Cluster# to be converted */ 00804 ) 00805 { 00806 clst -= 2; 00807 if (clst >= (fs->n_fatent - 2)) return 0; /* Invalid cluster# */ 00808 return clst * fs->csize + fs->database; 00809 } 00810 00811 00812 00813 00814 /*-----------------------------------------------------------------------*/ 00815 /* FAT access - Read value of a FAT entry */ 00816 /*-----------------------------------------------------------------------*/ 00817 00818 00819 DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ 00820 FATFS *fs, /* File system object */ 00821 DWORD clst /* Cluster# to get the link information */ 00822 ) 00823 { 00824 UINT wc, bc; 00825 BYTE *p; 00826 00827 00828 if (clst < 2 || clst >= fs->n_fatent) /* Chack range */ 00829 return 1; 00830 00831 switch (fs->fs_type) { 00832 case FS_FAT12 : 00833 bc = (UINT)clst; bc += bc / 2; 00834 if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; 00835 wc = fs->win[bc % SS(fs)]; bc++; 00836 if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; 00837 wc |= fs->win[bc % SS(fs)] << 8; 00838 return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); 00839 00840 case FS_FAT16 : 00841 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break; 00842 p = &fs->win[clst * 2 % SS(fs)]; 00843 return LD_WORD(p); 00844 00845 case FS_FAT32 : 00846 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break; 00847 p = &fs->win[clst * 4 % SS(fs)]; 00848 return LD_DWORD(p) & 0x0FFFFFFF; 00849 } 00850 00851 return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */ 00852 } 00853 00854 00855 00856 00857 /*-----------------------------------------------------------------------*/ 00858 /* FAT access - Change value of a FAT entry */ 00859 /*-----------------------------------------------------------------------*/ 00860 #if !_FS_READONLY 00861 00862 FRESULT put_fat ( 00863 FATFS *fs, /* File system object */ 00864 DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ 00865 DWORD val /* New value to mark the cluster */ 00866 ) 00867 { 00868 UINT bc; 00869 BYTE *p; 00870 FRESULT res; 00871 00872 00873 if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ 00874 res = FR_INT_ERR; 00875 00876 } else { 00877 switch (fs->fs_type) { 00878 case FS_FAT12 : 00879 bc = clst; bc += bc / 2; 00880 res = move_window(fs, fs->fatbase + (bc / SS(fs))); 00881 if (res != FR_OK) break; 00882 p = &fs->win[bc % SS(fs)]; 00883 *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; 00884 bc++; 00885 fs->wflag = 1; 00886 res = move_window(fs, fs->fatbase + (bc / SS(fs))); 00887 if (res != FR_OK) break; 00888 p = &fs->win[bc % SS(fs)]; 00889 *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); 00890 break; 00891 00892 case FS_FAT16 : 00893 res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); 00894 if (res != FR_OK) break; 00895 p = &fs->win[clst * 2 % SS(fs)]; 00896 ST_WORD(p, (WORD)val); 00897 break; 00898 00899 case FS_FAT32 : 00900 res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); 00901 if (res != FR_OK) break; 00902 p = &fs->win[clst * 4 % SS(fs)]; 00903 val |= LD_DWORD(p) & 0xF0000000; 00904 ST_DWORD(p, val); 00905 break; 00906 00907 default : 00908 res = FR_INT_ERR; 00909 } 00910 fs->wflag = 1; 00911 } 00912 00913 return res; 00914 } 00915 #endif /* !_FS_READONLY */ 00916 00917 00918 00919 00920 /*-----------------------------------------------------------------------*/ 00921 /* FAT handling - Remove a cluster chain */ 00922 /*-----------------------------------------------------------------------*/ 00923 #if !_FS_READONLY 00924 static 00925 FRESULT remove_chain ( 00926 FATFS *fs, /* File system object */ 00927 DWORD clst /* Cluster# to remove a chain from */ 00928 ) 00929 { 00930 FRESULT res; 00931 DWORD nxt; 00932 #if _USE_ERASE 00933 DWORD scl = clst, ecl = clst, resion[2]; 00934 #endif 00935 00936 if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ 00937 res = FR_INT_ERR; 00938 00939 } else { 00940 res = FR_OK; 00941 while (clst < fs->n_fatent) { /* Not a last link? */ 00942 nxt = get_fat(fs, clst); /* Get cluster status */ 00943 if (nxt == 0) break; /* Empty cluster? */ 00944 if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ 00945 if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ 00946 res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ 00947 if (res != FR_OK) break; 00948 if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ 00949 fs->free_clust++; 00950 fs->fsi_flag = 1; 00951 } 00952 #if _USE_ERASE 00953 if (ecl + 1 == nxt) { /* Next cluster is contiguous */ 00954 ecl = nxt; 00955 } else { /* End of contiguous clusters */ 00956 resion[0] = clust2sect(fs, scl); /* Start sector */ 00957 resion[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ 00958 disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, resion); /* Erase the block */ 00959 scl = ecl = nxt; 00960 } 00961 #endif 00962 clst = nxt; /* Next cluster */ 00963 } 00964 } 00965 00966 return res; 00967 } 00968 #endif 00969 00970 00971 00972 00973 /*-----------------------------------------------------------------------*/ 00974 /* FAT handling - Stretch or Create a cluster chain */ 00975 /*-----------------------------------------------------------------------*/ 00976 #if !_FS_READONLY 00977 static 00978 DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ 00979 FATFS *fs, /* File system object */ 00980 DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ 00981 ) 00982 { 00983 DWORD cs, ncl, scl; 00984 FRESULT res; 00985 00986 00987 if (clst == 0) { /* Create a new chain */ 00988 scl = fs->last_clust; /* Get suggested start point */ 00989 if (!scl || scl >= fs->n_fatent) scl = 1; 00990 } 00991 else { /* Stretch the current chain */ 00992 cs = get_fat(fs, clst); /* Check the cluster status */ 00993 if (cs < 2) return 1; /* It is an invalid cluster */ 00994 if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ 00995 scl = clst; 00996 } 00997 00998 ncl = scl; /* Start cluster */ 00999 for (;;) { 01000 ncl++; /* Next cluster */ 01001 if (ncl >= fs->n_fatent) { /* Wrap around */ 01002 ncl = 2; 01003 if (ncl > scl) return 0; /* No free cluster */ 01004 } 01005 cs = get_fat(fs, ncl); /* Get the cluster status */ 01006 if (cs == 0) break; /* Found a free cluster */ 01007 if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ 01008 return cs; 01009 if (ncl == scl) return 0; /* No free cluster */ 01010 } 01011 01012 res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ 01013 if (res == FR_OK && clst != 0) { 01014 res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ 01015 } 01016 if (res == FR_OK) { 01017 fs->last_clust = ncl; /* Update FSINFO */ 01018 if (fs->free_clust != 0xFFFFFFFF) { 01019 fs->free_clust--; 01020 fs->fsi_flag = 1; 01021 } 01022 } else { 01023 ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; 01024 } 01025 01026 return ncl; /* Return new cluster number or error code */ 01027 } 01028 #endif /* !_FS_READONLY */ 01029 01030 01031 01032 /*-----------------------------------------------------------------------*/ 01033 /* FAT handling - Convert offset into cluster with link map table */ 01034 /*-----------------------------------------------------------------------*/ 01035 01036 #if _USE_FASTSEEK 01037 static 01038 DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ 01039 FIL* fp, /* Pointer to the file object */ 01040 DWORD ofs /* File offset to be converted to cluster# */ 01041 ) 01042 { 01043 DWORD cl, ncl, *tbl; 01044 01045 01046 tbl = fp->cltbl + 1; /* Top of CLMT */ 01047 cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ 01048 for (;;) { 01049 ncl = *tbl++; /* Number of cluters in the fragment */ 01050 if (!ncl) return 0; /* End of table? (error) */ 01051 if (cl < ncl) break; /* In this fragment? */ 01052 cl -= ncl; tbl++; /* Next fragment */ 01053 } 01054 return cl + *tbl; /* Return the cluster number */ 01055 } 01056 #endif /* _USE_FASTSEEK */ 01057 01058 01059 01060 /*-----------------------------------------------------------------------*/ 01061 /* Directory handling - Set directory index */ 01062 /*-----------------------------------------------------------------------*/ 01063 01064 static 01065 FRESULT dir_sdi ( 01066 FATFS_DIR *dj, /* Pointer to directory object */ 01067 WORD idx /* Directory index number */ 01068 ) 01069 { 01070 DWORD clst; 01071 WORD ic; 01072 01073 01074 dj->index = idx; 01075 clst = dj->sclust; 01076 if (clst == 1 || clst >= dj->fs->n_fatent) /* Check start cluster range */ 01077 return FR_INT_ERR; 01078 if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ 01079 clst = dj->fs->dirbase; 01080 01081 if (clst == 0) { /* Static table (root-dir in FAT12/16) */ 01082 dj->clust = clst; 01083 if (idx >= dj->fs->n_rootdir) /* Index is out of range */ 01084 return FR_INT_ERR; 01085 dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / SZ_DIR); /* Sector# */ 01086 } 01087 else { /* Dynamic table (sub-dirs or root-dir in FAT32) */ 01088 ic = SS(dj->fs) / SZ_DIR * dj->fs->csize; /* Entries per cluster */ 01089 while (idx >= ic) { /* Follow cluster chain */ 01090 clst = get_fat(dj->fs, clst); /* Get next cluster */ 01091 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ 01092 if (clst < 2 || clst >= dj->fs->n_fatent) /* Reached to end of table or int error */ 01093 return FR_INT_ERR; 01094 idx -= ic; 01095 } 01096 dj->clust = clst; 01097 dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / SZ_DIR); /* Sector# */ 01098 } 01099 01100 dj->dir = dj->fs->win + (idx % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; /* Ptr to the entry in the sector */ 01101 01102 return FR_OK; /* Seek succeeded */ 01103 } 01104 01105 01106 01107 01108 /*-----------------------------------------------------------------------*/ 01109 /* Directory handling - Move directory index next */ 01110 /*-----------------------------------------------------------------------*/ 01111 01112 static 01113 FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ 01114 FATFS_DIR *dj, /* Pointer to directory object */ 01115 int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ 01116 ) 01117 { 01118 DWORD clst; 01119 WORD i; 01120 01121 01122 stretch = stretch; /* To suppress warning on read-only cfg. */ 01123 i = dj->index + 1; 01124 if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ 01125 return FR_NO_FILE; 01126 01127 if (!(i % (SS(dj->fs) / SZ_DIR))) { /* Sector changed? */ 01128 dj->sect++; /* Next sector */ 01129 01130 if (dj->clust == 0) { /* Static table */ 01131 if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ 01132 return FR_NO_FILE; 01133 } 01134 else { /* Dynamic table */ 01135 if (((i / (SS(dj->fs) / SZ_DIR)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ 01136 clst = get_fat(dj->fs, dj->clust); /* Get next cluster */ 01137 if (clst <= 1) return FR_INT_ERR; 01138 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; 01139 if (clst >= dj->fs->n_fatent) { /* When it reached end of dynamic table */ 01140 #if !_FS_READONLY 01141 BYTE c; 01142 if (!stretch) return FR_NO_FILE; /* When do not stretch, report EOT */ 01143 clst = create_chain(dj->fs, dj->clust); /* Stretch cluster chain */ 01144 if (clst == 0) return FR_DENIED; /* No free cluster */ 01145 if (clst == 1) return FR_INT_ERR; 01146 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; 01147 /* Clean-up stretched table */ 01148 if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */ 01149 mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */ 01150 dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ 01151 for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */ 01152 dj->fs->wflag = 1; 01153 if (move_window(dj->fs, 0)) return FR_DISK_ERR; 01154 dj->fs->winsect++; 01155 } 01156 dj->fs->winsect -= c; /* Rewind window address */ 01157 #else 01158 return FR_NO_FILE; /* Report EOT */ 01159 #endif 01160 } 01161 dj->clust = clst; /* Initialize data for new cluster */ 01162 dj->sect = clust2sect(dj->fs, clst); 01163 } 01164 } 01165 } 01166 01167 dj->index = i; 01168 dj->dir = dj->fs->win + (i % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; 01169 01170 return FR_OK; 01171 } 01172 01173 01174 01175 01176 /*-----------------------------------------------------------------------*/ 01177 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ 01178 /*-----------------------------------------------------------------------*/ 01179 #if _USE_LFN 01180 static 01181 const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */ 01182 01183 01184 static 01185 int cmp_lfn ( /* 1:Matched, 0:Not matched */ 01186 WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ 01187 BYTE *dir /* Pointer to the directory entry containing a part of LFN */ 01188 ) 01189 { 01190 UINT i, s; 01191 WCHAR wc, uc; 01192 01193 01194 i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13; /* Get offset in the LFN buffer */ 01195 s = 0; wc = 1; 01196 do { 01197 uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ 01198 if (wc) { /* Last char has not been processed */ 01199 wc = ff_wtoupper(uc); /* Convert it to upper case */ 01200 if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ 01201 return 0; /* Not matched */ 01202 } else { 01203 if (uc != 0xFFFF) return 0; /* Check filler */ 01204 } 01205 } while (++s < 13); /* Repeat until all chars in the entry are checked */ 01206 01207 if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i]) /* Last segment matched but different length */ 01208 return 0; 01209 01210 return 1; /* The part of LFN matched */ 01211 } 01212 01213 01214 01215 static 01216 int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ 01217 WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ 01218 BYTE *dir /* Pointer to the directory entry */ 01219 ) 01220 { 01221 UINT i, s; 01222 WCHAR wc, uc; 01223 01224 01225 i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ 01226 01227 s = 0; wc = 1; 01228 do { 01229 uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ 01230 if (wc) { /* Last char has not been processed */ 01231 if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ 01232 lfnbuf[i++] = wc = uc; /* Store it */ 01233 } else { 01234 if (uc != 0xFFFF) return 0; /* Check filler */ 01235 } 01236 } while (++s < 13); /* Read all character in the entry */ 01237 01238 if (dir[LDIR_Ord] & LLE) { /* Put terminator if it is the last LFN part */ 01239 if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ 01240 lfnbuf[i] = 0; 01241 } 01242 01243 return 1; 01244 } 01245 01246 01247 #if !_FS_READONLY 01248 static 01249 void fit_lfn ( 01250 const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ 01251 BYTE *dir, /* Pointer to the directory entry */ 01252 BYTE ord, /* LFN order (1-20) */ 01253 BYTE sum /* SFN sum */ 01254 ) 01255 { 01256 UINT i, s; 01257 WCHAR wc; 01258 01259 01260 dir[LDIR_Chksum] = sum; /* Set check sum */ 01261 dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ 01262 dir[LDIR_Type] = 0; 01263 ST_WORD(dir+LDIR_FstClusLO, 0); 01264 01265 i = (ord - 1) * 13; /* Get offset in the LFN buffer */ 01266 s = wc = 0; 01267 do { 01268 if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */ 01269 ST_WORD(dir+LfnOfs[s], wc); /* Put it */ 01270 if (!wc) wc = 0xFFFF; /* Padding chars following last char */ 01271 } while (++s < 13); 01272 if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE; /* Bottom LFN part is the start of LFN sequence */ 01273 dir[LDIR_Ord] = ord; /* Set the LFN order */ 01274 } 01275 01276 #endif 01277 #endif 01278 01279 01280 01281 /*-----------------------------------------------------------------------*/ 01282 /* Create numbered name */ 01283 /*-----------------------------------------------------------------------*/ 01284 #if _USE_LFN 01285 void gen_numname ( 01286 BYTE *dst, /* Pointer to generated SFN */ 01287 const BYTE *src, /* Pointer to source SFN to be modified */ 01288 const WCHAR *lfn, /* Pointer to LFN */ 01289 WORD seq /* Sequence number */ 01290 ) 01291 { 01292 BYTE ns[8], c; 01293 UINT i, j; 01294 01295 01296 mem_cpy(dst, src, 11); 01297 01298 if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ 01299 do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn); 01300 } 01301 01302 /* itoa (hexdecimal) */ 01303 i = 7; 01304 do { 01305 c = (seq % 16) + '0'; 01306 if (c > '9') c += 7; 01307 ns[i--] = c; 01308 seq /= 16; 01309 } while (seq); 01310 ns[i] = '~'; 01311 01312 /* Append the number */ 01313 for (j = 0; j < i && dst[j] != ' '; j++) { 01314 if (IsDBCS1(dst[j])) { 01315 if (j == i - 1) break; 01316 j++; 01317 } 01318 } 01319 do { 01320 dst[j++] = (i < 8) ? ns[i++] : ' '; 01321 } while (j < 8); 01322 } 01323 #endif 01324 01325 01326 01327 01328 /*-----------------------------------------------------------------------*/ 01329 /* Calculate sum of an SFN */ 01330 /*-----------------------------------------------------------------------*/ 01331 #if _USE_LFN 01332 static 01333 BYTE sum_sfn ( 01334 const BYTE *dir /* Ptr to directory entry */ 01335 ) 01336 { 01337 BYTE sum = 0; 01338 UINT n = 11; 01339 01340 do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); 01341 return sum; 01342 } 01343 #endif 01344 01345 01346 01347 01348 /*-----------------------------------------------------------------------*/ 01349 /* Directory handling - Find an object in the directory */ 01350 /*-----------------------------------------------------------------------*/ 01351 01352 static 01353 FRESULT dir_find ( 01354 FATFS_DIR *dj /* Pointer to the directory object linked to the file name */ 01355 ) 01356 { 01357 FRESULT res; 01358 BYTE c, *dir; 01359 #if _USE_LFN 01360 BYTE a, ord, sum; 01361 #endif 01362 01363 res = dir_sdi(dj, 0); /* Rewind directory object */ 01364 if (res != FR_OK) return res; 01365 01366 #if _USE_LFN 01367 ord = sum = 0xFF; 01368 #endif 01369 do { 01370 res = move_window(dj->fs, dj->sect); 01371 if (res != FR_OK) break; 01372 dir = dj->dir; /* Ptr to the directory entry of current index */ 01373 c = dir[DIR_Name]; 01374 if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ 01375 #if _USE_LFN /* LFN configuration */ 01376 a = dir[DIR_Attr] & AM_MASK; 01377 if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ 01378 ord = 0xFF; 01379 } else { 01380 if (a == AM_LFN) { /* An LFN entry is found */ 01381 if (dj->lfn) { 01382 if (c & LLE) { /* Is it start of LFN sequence? */ 01383 sum = dir[LDIR_Chksum]; 01384 c &= ~LLE; ord = c; /* LFN start order */ 01385 dj->lfn_idx = dj->index; 01386 } 01387 /* Check validity of the LFN entry and compare it with given name */ 01388 ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; 01389 } 01390 } else { /* An SFN entry is found */ 01391 if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ 01392 ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */ 01393 if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */ 01394 } 01395 } 01396 #else /* Non LFN configuration */ 01397 if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ 01398 break; 01399 #endif 01400 res = dir_next(dj, 0); /* Next entry */ 01401 } while (res == FR_OK); 01402 01403 return res; 01404 } 01405 01406 01407 01408 01409 /*-----------------------------------------------------------------------*/ 01410 /* Read an object from the directory */ 01411 /*-----------------------------------------------------------------------*/ 01412 #if _FS_MINIMIZE <= 1 01413 static 01414 FRESULT dir_read ( 01415 FATFS_DIR *dj /* Pointer to the directory object that pointing the entry to be read */ 01416 ) 01417 { 01418 FRESULT res; 01419 BYTE c, *dir; 01420 #if _USE_LFN 01421 BYTE a, ord = 0xFF, sum = 0xFF; 01422 #endif 01423 01424 res = FR_NO_FILE; 01425 while (dj->sect) { 01426 res = move_window(dj->fs, dj->sect); 01427 if (res != FR_OK) break; 01428 dir = dj->dir; /* Ptr to the directory entry of current index */ 01429 c = dir[DIR_Name]; 01430 if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ 01431 #if _USE_LFN /* LFN configuration */ 01432 a = dir[DIR_Attr] & AM_MASK; 01433 if (c == DDE || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ 01434 ord = 0xFF; 01435 } else { 01436 if (a == AM_LFN) { /* An LFN entry is found */ 01437 if (c & LLE) { /* Is it start of LFN sequence? */ 01438 sum = dir[LDIR_Chksum]; 01439 c &= ~LLE; ord = c; 01440 dj->lfn_idx = dj->index; 01441 } 01442 /* Check LFN validity and capture it */ 01443 ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; 01444 } else { /* An SFN entry is found */ 01445 if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ 01446 dj->lfn_idx = 0xFFFF; /* It has no LFN. */ 01447 break; 01448 } 01449 } 01450 #else /* Non LFN configuration */ 01451 if (c != DDE && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ 01452 break; 01453 #endif 01454 res = dir_next(dj, 0); /* Next entry */ 01455 if (res != FR_OK) break; 01456 } 01457 01458 if (res != FR_OK) dj->sect = 0; 01459 01460 return res; 01461 } 01462 #endif 01463 01464 01465 01466 /*-----------------------------------------------------------------------*/ 01467 /* Register an object to the directory */ 01468 /*-----------------------------------------------------------------------*/ 01469 #if !_FS_READONLY 01470 static 01471 FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ 01472 FATFS_DIR *dj /* Target directory with object name to be created */ 01473 ) 01474 { 01475 FRESULT res; 01476 BYTE c, *dir; 01477 #if _USE_LFN /* LFN configuration */ 01478 WORD n, ne, is; 01479 BYTE sn[12], *fn, sum; 01480 WCHAR *lfn; 01481 01482 01483 fn = dj->fn; lfn = dj->lfn; 01484 mem_cpy(sn, fn, 12); 01485 01486 if (_FS_RPATH && (sn[NS] & NS_DOT)) /* Cannot create dot entry */ 01487 return FR_INVALID_NAME; 01488 01489 if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ 01490 fn[NS] = 0; dj->lfn = 0; /* Find only SFN */ 01491 for (n = 1; n < 100; n++) { 01492 gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ 01493 res = dir_find(dj); /* Check if the name collides with existing SFN */ 01494 if (res != FR_OK) break; 01495 } 01496 if (n == 100) return FR_DENIED; /* Abort if too many collisions */ 01497 if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ 01498 fn[NS] = sn[NS]; dj->lfn = lfn; 01499 } 01500 01501 if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve an SFN + LFN entries. */ 01502 for (ne = 0; lfn[ne]; ne++) ; 01503 ne = (ne + 25) / 13; 01504 } else { /* Otherwise reserve only an SFN entry. */ 01505 ne = 1; 01506 } 01507 01508 /* Reserve contiguous entries */ 01509 res = dir_sdi(dj, 0); 01510 if (res != FR_OK) return res; 01511 n = is = 0; 01512 do { 01513 res = move_window(dj->fs, dj->sect); 01514 if (res != FR_OK) break; 01515 c = *dj->dir; /* Check the entry status */ 01516 if (c == DDE || c == 0) { /* Is it a blank entry? */ 01517 if (n == 0) is = dj->index; /* First index of the contiguous entry */ 01518 if (++n == ne) break; /* A contiguous entry that required count is found */ 01519 } else { 01520 n = 0; /* Not a blank entry. Restart to search */ 01521 } 01522 res = dir_next(dj, 1); /* Next entry with table stretch */ 01523 } while (res == FR_OK); 01524 01525 if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ 01526 res = dir_sdi(dj, is); 01527 if (res == FR_OK) { 01528 sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ 01529 ne--; 01530 do { /* Store LFN entries in bottom first */ 01531 res = move_window(dj->fs, dj->sect); 01532 if (res != FR_OK) break; 01533 fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); 01534 dj->fs->wflag = 1; 01535 res = dir_next(dj, 0); /* Next entry */ 01536 } while (res == FR_OK && --ne); 01537 } 01538 } 01539 01540 #else /* Non LFN configuration */ 01541 res = dir_sdi(dj, 0); 01542 if (res == FR_OK) { 01543 do { /* Find a blank entry for the SFN */ 01544 res = move_window(dj->fs, dj->sect); 01545 if (res != FR_OK) break; 01546 c = *dj->dir; 01547 if (c == DDE || c == 0) break; /* Is it a blank entry? */ 01548 res = dir_next(dj, 1); /* Next entry with table stretch */ 01549 } while (res == FR_OK); 01550 } 01551 #endif 01552 01553 if (res == FR_OK) { /* Initialize the SFN entry */ 01554 res = move_window(dj->fs, dj->sect); 01555 if (res == FR_OK) { 01556 dir = dj->dir; 01557 mem_set(dir, 0, SZ_DIR); /* Clean the entry */ 01558 mem_cpy(dir, dj->fn, 11); /* Put SFN */ 01559 #if _USE_LFN 01560 dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ 01561 #endif 01562 dj->fs->wflag = 1; 01563 } 01564 } 01565 01566 return res; 01567 } 01568 #endif /* !_FS_READONLY */ 01569 01570 01571 01572 01573 /*-----------------------------------------------------------------------*/ 01574 /* Remove an object from the directory */ 01575 /*-----------------------------------------------------------------------*/ 01576 #if !_FS_READONLY && !_FS_MINIMIZE 01577 static 01578 FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ 01579 FATFS_DIR *dj /* Directory object pointing the entry to be removed */ 01580 ) 01581 { 01582 FRESULT res; 01583 #if _USE_LFN /* LFN configuration */ 01584 WORD i; 01585 01586 i = dj->index; /* SFN index */ 01587 res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */ 01588 if (res == FR_OK) { 01589 do { 01590 res = move_window(dj->fs, dj->sect); 01591 if (res != FR_OK) break; 01592 *dj->dir = DDE; /* Mark the entry "deleted" */ 01593 dj->fs->wflag = 1; 01594 if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ 01595 res = dir_next(dj, 0); /* Next entry */ 01596 } while (res == FR_OK); 01597 if (res == FR_NO_FILE) res = FR_INT_ERR; 01598 } 01599 01600 #else /* Non LFN configuration */ 01601 res = dir_sdi(dj, dj->index); 01602 if (res == FR_OK) { 01603 res = move_window(dj->fs, dj->sect); 01604 if (res == FR_OK) { 01605 *dj->dir = DDE; /* Mark the entry "deleted" */ 01606 dj->fs->wflag = 1; 01607 } 01608 } 01609 #endif 01610 01611 return res; 01612 } 01613 #endif /* !_FS_READONLY */ 01614 01615 01616 01617 01618 /*-----------------------------------------------------------------------*/ 01619 /* Pick a segment and create the object name in directory form */ 01620 /*-----------------------------------------------------------------------*/ 01621 01622 static 01623 FRESULT create_name ( 01624 FATFS_DIR *dj, /* Pointer to the directory object */ 01625 const TCHAR **path /* Pointer to pointer to the segment in the path string */ 01626 ) 01627 { 01628 #ifdef _EXCVT 01629 static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */ 01630 #endif 01631 01632 #if _USE_LFN /* LFN configuration */ 01633 BYTE b, cf; 01634 WCHAR w, *lfn; 01635 UINT i, ni, si, di; 01636 const TCHAR *p; 01637 01638 /* Create LFN in Unicode */ 01639 for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ 01640 lfn = dj->lfn; 01641 si = di = 0; 01642 for (;;) { 01643 w = p[si++]; /* Get a character */ 01644 if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ 01645 if (di >= _MAX_LFN) /* Reject too long name */ 01646 return FR_INVALID_NAME; 01647 #if !_LFN_UNICODE 01648 w &= 0xFF; 01649 if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ 01650 b = (BYTE)p[si++]; /* Get 2nd byte */ 01651 if (!IsDBCS2(b)) 01652 return FR_INVALID_NAME; /* Reject invalid sequence */ 01653 w = (w << 8) + b; /* Create a DBC */ 01654 } 01655 w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ 01656 if (!w) return FR_INVALID_NAME; /* Reject invalid code */ 01657 #endif 01658 if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ 01659 return FR_INVALID_NAME; 01660 lfn[di++] = w; /* Store the Unicode char */ 01661 } 01662 *path = &p[si]; /* Return pointer to the next segment */ 01663 cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ 01664 #if _FS_RPATH 01665 if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */ 01666 (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) { 01667 lfn[di] = 0; 01668 for (i = 0; i < 11; i++) 01669 dj->fn[i] = (i < di) ? '.' : ' '; 01670 dj->fn[i] = cf | NS_DOT; /* This is a dot entry */ 01671 return FR_OK; 01672 } 01673 #endif 01674 while (di) { /* Strip trailing spaces and dots */ 01675 w = lfn[di-1]; 01676 if (w != ' ' && w != '.') break; 01677 di--; 01678 } 01679 if (!di) return FR_INVALID_NAME; /* Reject nul string */ 01680 01681 lfn[di] = 0; /* LFN is created */ 01682 01683 /* Create SFN in directory form */ 01684 mem_set(dj->fn, ' ', 11); 01685 for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ 01686 if (si) cf |= NS_LOSS | NS_LFN; 01687 while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ 01688 01689 b = i = 0; ni = 8; 01690 for (;;) { 01691 w = lfn[si++]; /* Get an LFN char */ 01692 if (!w) break; /* Break on end of the LFN */ 01693 if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ 01694 cf |= NS_LOSS | NS_LFN; continue; 01695 } 01696 01697 if (i >= ni || si == di) { /* Extension or end of SFN */ 01698 if (ni == 11) { /* Long extension */ 01699 cf |= NS_LOSS | NS_LFN; break; 01700 } 01701 if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ 01702 if (si > di) break; /* No extension */ 01703 si = di; i = 8; ni = 11; /* Enter extension section */ 01704 b <<= 2; continue; 01705 } 01706 01707 if (w >= 0x80) { /* Non ASCII char */ 01708 #ifdef _EXCVT 01709 w = ff_convert(w, 0); /* Unicode -> OEM code */ 01710 if (w) w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ 01711 #else 01712 w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ 01713 #endif 01714 cf |= NS_LFN; /* Force create LFN entry */ 01715 } 01716 01717 if (_DF1S && w >= 0x100) { /* Double byte char (always false on SBCS cfg) */ 01718 if (i >= ni - 1) { 01719 cf |= NS_LOSS | NS_LFN; i = ni; continue; 01720 } 01721 dj->fn[i++] = (BYTE)(w >> 8); 01722 } else { /* Single byte char */ 01723 if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal chars for SFN */ 01724 w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ 01725 } else { 01726 if (IsUpper(w)) { /* ASCII large capital */ 01727 b |= 2; 01728 } else { 01729 if (IsLower(w)) { /* ASCII small capital */ 01730 b |= 1; w -= 0x20; 01731 } 01732 } 01733 } 01734 } 01735 dj->fn[i++] = (BYTE)w; 01736 } 01737 01738 if (dj->fn[0] == DDE) dj->fn[0] = NDDE; /* If the first char collides with deleted mark, replace it with 0x05 */ 01739 01740 if (ni == 8) b <<= 2; 01741 if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ 01742 cf |= NS_LFN; 01743 if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ 01744 if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ 01745 if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ 01746 } 01747 01748 dj->fn[NS] = cf; /* SFN is created */ 01749 01750 return FR_OK; 01751 01752 01753 #else /* Non-LFN configuration */ 01754 BYTE b, c, d, *sfn; 01755 UINT ni, si, i; 01756 const char *p; 01757 01758 /* Create file name in directory form */ 01759 for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ 01760 sfn = dj->fn; 01761 mem_set(sfn, ' ', 11); 01762 si = i = b = 0; ni = 8; 01763 #if _FS_RPATH 01764 if (p[si] == '.') { /* Is this a dot entry? */ 01765 for (;;) { 01766 c = (BYTE)p[si++]; 01767 if (c != '.' || si >= 3) break; 01768 sfn[i++] = c; 01769 } 01770 if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; 01771 *path = &p[si]; /* Return pointer to the next segment */ 01772 sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ 01773 return FR_OK; 01774 } 01775 #endif 01776 for (;;) { 01777 c = (BYTE)p[si++]; 01778 if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ 01779 if (c == '.' || i >= ni) { 01780 if (ni != 8 || c != '.') return FR_INVALID_NAME; 01781 i = 8; ni = 11; 01782 b <<= 2; continue; 01783 } 01784 if (c >= 0x80) { /* Extended char? */ 01785 b |= 3; /* Eliminate NT flag */ 01786 #ifdef _EXCVT 01787 c = excvt[c-0x80]; /* Upper conversion (SBCS) */ 01788 #else 01789 #if !_DF1S /* ASCII only cfg */ 01790 return FR_INVALID_NAME; 01791 #endif 01792 #endif 01793 } 01794 if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ 01795 d = (BYTE)p[si++]; /* Get 2nd byte */ 01796 if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ 01797 return FR_INVALID_NAME; 01798 sfn[i++] = c; 01799 sfn[i++] = d; 01800 } else { /* Single byte code */ 01801 if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ 01802 return FR_INVALID_NAME; 01803 if (IsUpper(c)) { /* ASCII large capital? */ 01804 b |= 2; 01805 } else { 01806 if (IsLower(c)) { /* ASCII small capital? */ 01807 b |= 1; c -= 0x20; 01808 } 01809 } 01810 sfn[i++] = c; 01811 } 01812 } 01813 *path = &p[si]; /* Return pointer to the next segment */ 01814 c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ 01815 01816 if (!i) return FR_INVALID_NAME; /* Reject nul string */ 01817 if (sfn[0] == DDE) sfn[0] = NDDE; /* When first char collides with DDE, replace it with 0x05 */ 01818 01819 if (ni == 8) b <<= 2; 01820 if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ 01821 if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ 01822 01823 sfn[NS] = c; /* Store NT flag, File name is created */ 01824 01825 return FR_OK; 01826 #endif 01827 } 01828 01829 01830 01831 01832 /*-----------------------------------------------------------------------*/ 01833 /* Get file information from directory entry */ 01834 /*-----------------------------------------------------------------------*/ 01835 #if _FS_MINIMIZE <= 1 01836 static 01837 void get_fileinfo ( /* No return code */ 01838 FATFS_DIR *dj, /* Pointer to the directory object */ 01839 FILINFO *fno /* Pointer to the file information to be filled */ 01840 ) 01841 { 01842 UINT i; 01843 BYTE nt, *dir; 01844 TCHAR *p, c; 01845 01846 01847 p = fno->fname; 01848 if (dj->sect) { 01849 dir = dj->dir; 01850 nt = dir[DIR_NTres]; /* NT flag */ 01851 for (i = 0; i < 8; i++) { /* Copy name body */ 01852 c = dir[i]; 01853 if (c == ' ') break; 01854 if (c == NDDE) c = (TCHAR)DDE; 01855 if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; 01856 #if _LFN_UNICODE 01857 if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1])) 01858 c = (c << 8) | dir[++i]; 01859 c = ff_convert(c, 1); 01860 if (!c) c = '?'; 01861 #endif 01862 *p++ = c; 01863 } 01864 if (dir[8] != ' ') { /* Copy name extension */ 01865 *p++ = '.'; 01866 for (i = 8; i < 11; i++) { 01867 c = dir[i]; 01868 if (c == ' ') break; 01869 if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20; 01870 #if _LFN_UNICODE 01871 if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1])) 01872 c = (c << 8) | dir[++i]; 01873 c = ff_convert(c, 1); 01874 if (!c) c = '?'; 01875 #endif 01876 *p++ = c; 01877 } 01878 } 01879 fno->fattrib = dir[DIR_Attr]; /* Attribute */ 01880 fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ 01881 fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ 01882 fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ 01883 } 01884 *p = 0; /* Terminate SFN str by a \0 */ 01885 01886 #if _USE_LFN 01887 if (fno->lfname && fno->lfsize) { 01888 TCHAR *tp = fno->lfname; 01889 WCHAR w, *lfn; 01890 01891 i = 0; 01892 if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ 01893 lfn = dj->lfn; 01894 while ((w = *lfn++) != 0) { /* Get an LFN char */ 01895 #if !_LFN_UNICODE 01896 w = ff_convert(w, 0); /* Unicode -> OEM conversion */ 01897 if (!w) { i = 0; break; } /* Could not convert, no LFN */ 01898 if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ 01899 tp[i++] = (TCHAR)(w >> 8); 01900 #endif 01901 if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */ 01902 tp[i++] = (TCHAR)w; 01903 } 01904 } 01905 tp[i] = 0; /* Terminate the LFN str by a \0 */ 01906 } 01907 #endif 01908 } 01909 #endif /* _FS_MINIMIZE <= 1 */ 01910 01911 01912 01913 01914 /*-----------------------------------------------------------------------*/ 01915 /* Follow a file path */ 01916 /*-----------------------------------------------------------------------*/ 01917 01918 static 01919 FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ 01920 FATFS_DIR *dj, /* Directory object to return last directory and found object */ 01921 const TCHAR *path /* Full-path string to find a file or directory */ 01922 ) 01923 { 01924 FRESULT res; 01925 BYTE *dir, ns; 01926 01927 01928 #if _FS_RPATH 01929 if (*path == '/' || *path == '\\') { /* There is a heading separator */ 01930 path++; dj->sclust = 0; /* Strip it and start from the root dir */ 01931 } else { /* No heading separator */ 01932 dj->sclust = dj->fs->cdir; /* Start from the current dir */ 01933 } 01934 #else 01935 if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ 01936 path++; 01937 dj->sclust = 0; /* Start from the root dir */ 01938 #endif 01939 01940 if ((UINT)*path < ' ') { /* Nul path means the start directory itself */ 01941 res = dir_sdi(dj, 0); 01942 dj->dir = 0; 01943 01944 } else { /* Follow path */ 01945 for (;;) { 01946 res = create_name(dj, &path); /* Get a segment */ 01947 if (res != FR_OK) break; 01948 res = dir_find(dj); /* Find it */ 01949 ns = *(dj->fn+NS); 01950 if (res != FR_OK) { /* Failed to find the object */ 01951 if (res != FR_NO_FILE) break; /* Abort if any hard error occured */ 01952 /* Object not found */ 01953 if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exit */ 01954 dj->sclust = 0; dj->dir = 0; /* It is the root dir */ 01955 res = FR_OK; 01956 if (!(ns & NS_LAST)) continue; 01957 } else { /* Could not find the object */ 01958 if (!(ns & NS_LAST)) res = FR_NO_PATH; 01959 } 01960 break; 01961 } 01962 if (ns & NS_LAST) break; /* Last segment match. Function completed. */ 01963 dir = dj->dir; /* There is next segment. Follow the sub directory */ 01964 if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ 01965 res = FR_NO_PATH; break; 01966 } 01967 dj->sclust = LD_CLUST(dir); 01968 } 01969 } 01970 01971 return res; 01972 } 01973 01974 01975 01976 01977 /*-----------------------------------------------------------------------*/ 01978 /* Load a sector and check if it is an FAT Volume Boot Record */ 01979 /*-----------------------------------------------------------------------*/ 01980 01981 static 01982 BYTE check_fs ( /* 0:FAT-VBR, 1:Valid BR but not FAT, 2:Not a BR, 3:Disk error */ 01983 FATFS *fs, /* File system object */ 01984 DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ 01985 ) 01986 { 01987 if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK) /* Load boot record */ 01988 return 3; 01989 if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ 01990 return 2; 01991 01992 if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ 01993 return 0; 01994 if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) 01995 return 0; 01996 01997 return 1; 01998 } 01999 02000 02001 02002 02003 /*-----------------------------------------------------------------------*/ 02004 /* Check if the file system object is valid or not */ 02005 /*-----------------------------------------------------------------------*/ 02006 02007 static 02008 FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */ 02009 const TCHAR **path, /* Pointer to pointer to the path name (drive number) */ 02010 FATFS **rfs, /* Pointer to pointer to the found file system object */ 02011 BYTE chk_wp /* !=0: Check media write protection for write access */ 02012 ) 02013 { 02014 BYTE fmt, b, pi, *tbl; 02015 UINT vol; 02016 DSTATUS stat; 02017 DWORD bsect, fasize, tsect, sysect, nclst, szbfat; 02018 WORD nrsv; 02019 const TCHAR *p = *path; 02020 FATFS *fs; 02021 02022 /* Get logical drive number from the path name */ 02023 vol = p[0] - '0'; /* Is there a drive number? */ 02024 if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */ 02025 p += 2; *path = p; /* Return pointer to the path name */ 02026 } else { /* No drive number is given */ 02027 #if _FS_RPATH 02028 vol = CurrVol; /* Use current drive */ 02029 #else 02030 vol = 0; /* Use drive 0 */ 02031 #endif 02032 } 02033 02034 /* Check if the file system object is valid or not */ 02035 if (vol >= _VOLUMES) /* Is the drive number valid? */ 02036 return FR_INVALID_DRIVE; 02037 *rfs = fs = FatFs[vol]; /* Return pointer to the corresponding file system object */ 02038 if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ 02039 02040 ENTER_FF(fs); /* Lock file system */ 02041 02042 if (fs->fs_type) { /* If the logical drive has been mounted */ 02043 stat = disk_status(fs->drv); 02044 if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized (has not been changed), */ 02045 if (!_FS_READONLY && chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ 02046 return FR_WRITE_PROTECTED; 02047 return FR_OK; /* The file system object is valid */ 02048 } 02049 } 02050 02051 /* The file system object is not valid. */ 02052 /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ 02053 02054 fs->fs_type = 0; /* Clear the file system object */ 02055 fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ 02056 stat = disk_initialize(fs->drv); /* Initialize low level disk I/O layer */ 02057 if (stat & STA_NOINIT) /* Check if the initialization succeeded */ 02058 return FR_NOT_READY; /* Failed to initialize due to no media or hard error */ 02059 if (!_FS_READONLY && chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */ 02060 return FR_WRITE_PROTECTED; 02061 #if _MAX_SS != 512 /* Get disk sector size (variable sector size cfg only) */ 02062 if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK) 02063 return FR_DISK_ERR; 02064 #endif 02065 /* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */ 02066 fmt = check_fs(fs, bsect = 0); /* Load sector 0 and check if it is an FAT-VBR (in SFD) */ 02067 if (LD2PT(vol) && !fmt) fmt = 1; /* Force non-SFD if the volume is forced partition */ 02068 if (fmt == 1) { /* Not an FAT-VBR, the physical drive can be partitioned */ 02069 /* Check the partition listed in the partition table */ 02070 pi = LD2PT(vol); 02071 if (pi) pi--; 02072 tbl = &fs->win[MBR_Table + pi * SZ_PTE];/* Partition table */ 02073 if (tbl[4]) { /* Is the partition existing? */ 02074 bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ 02075 fmt = check_fs(fs, bsect); /* Check the partition */ 02076 } 02077 } 02078 if (fmt == 3) return FR_DISK_ERR; 02079 if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */ 02080 02081 /* An FAT volume is found. Following code initializes the file system object */ 02082 02083 if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ 02084 return FR_NO_FILESYSTEM; 02085 02086 fasize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ 02087 if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32); 02088 fs->fsize = fasize; 02089 02090 fs->n_fats = b = fs->win[BPB_NumFATs]; /* Number of FAT copies */ 02091 if (b != 1 && b != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ 02092 fasize *= b; /* Number of sectors for FAT area */ 02093 02094 fs->csize = b = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ 02095 if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ 02096 02097 fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */ 02098 if (fs->n_rootdir % (SS(fs) / SZ_DIR)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */ 02099 02100 tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ 02101 if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); 02102 02103 nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); /* Number of reserved sectors */ 02104 if (!nrsv) return FR_NO_FILESYSTEM; /* (BPB_RsvdSecCnt must not be 0) */ 02105 02106 /* Determine the FAT sub type */ 02107 sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR); /* RSV+FAT+FATFS_DIR */ 02108 if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ 02109 nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ 02110 if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ 02111 fmt = FS_FAT12; 02112 if (nclst >= MIN_FAT16) fmt = FS_FAT16; 02113 if (nclst >= MIN_FAT32) fmt = FS_FAT32; 02114 02115 /* Boundaries and Limits */ 02116 fs->n_fatent = nclst + 2; /* Number of FAT entries */ 02117 fs->database = bsect + sysect; /* Data start sector */ 02118 fs->fatbase = bsect + nrsv; /* FAT start sector */ 02119 if (fmt == FS_FAT32) { 02120 if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ 02121 fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ 02122 szbfat = fs->n_fatent * 4; /* (Required FAT size) */ 02123 } else { 02124 if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ 02125 fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ 02126 szbfat = (fmt == FS_FAT16) ? /* (Required FAT size) */ 02127 fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); 02128 } 02129 if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than required) */ 02130 return FR_NO_FILESYSTEM; 02131 02132 #if !_FS_READONLY 02133 /* Initialize cluster allocation information */ 02134 fs->free_clust = 0xFFFFFFFF; 02135 fs->last_clust = 0; 02136 02137 /* Get fsinfo if available */ 02138 if (fmt == FS_FAT32) { 02139 fs->fsi_flag = 0; 02140 fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); 02141 if (disk_read(fs->drv, fs->win, fs->fsi_sector, 1) == RES_OK && 02142 LD_WORD(fs->win+BS_55AA) == 0xAA55 && 02143 LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 && 02144 LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) { 02145 fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free); 02146 fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count); 02147 } 02148 } 02149 #endif 02150 fs->fs_type = fmt; /* FAT sub-type */ 02151 fs->id = ++Fsid; /* File system mount ID */ 02152 fs->winsect = 0; /* Invalidate sector cache */ 02153 fs->wflag = 0; 02154 #if _FS_RPATH 02155 fs->cdir = 0; /* Current directory (root dir) */ 02156 #endif 02157 #if _FS_SHARE /* Clear file lock semaphores */ 02158 clear_lock(fs); 02159 #endif 02160 02161 return FR_OK; 02162 } 02163 02164 02165 02166 02167 /*-----------------------------------------------------------------------*/ 02168 /* Check if the file/dir object is valid or not */ 02169 /*-----------------------------------------------------------------------*/ 02170 02171 static 02172 FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ 02173 FATFS *fs, /* Pointer to the file system object */ 02174 WORD id /* Member id of the target object to be checked */ 02175 ) 02176 { 02177 if (!fs || !fs->fs_type || fs->id != id) 02178 return FR_INVALID_OBJECT; 02179 02180 ENTER_FF(fs); /* Lock file system */ 02181 02182 if (disk_status(fs->drv) & STA_NOINIT) 02183 return FR_NOT_READY; 02184 02185 return FR_OK; 02186 } 02187 02188 02189 02190 02191 /*-------------------------------------------------------------------------- 02192 02193 Public Functions 02194 02195 --------------------------------------------------------------------------*/ 02196 02197 02198 02199 /*-----------------------------------------------------------------------*/ 02200 /* Mount/Unmount a Logical Drive */ 02201 /*-----------------------------------------------------------------------*/ 02202 02203 FRESULT f_mount ( 02204 BYTE vol, /* Logical drive number to be mounted/unmounted */ 02205 FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ 02206 ) 02207 { 02208 FATFS *rfs; 02209 02210 02211 if (vol >= _VOLUMES) /* Check if the drive number is valid */ 02212 return FR_INVALID_DRIVE; 02213 rfs = FatFs[vol]; /* Get current fs object */ 02214 02215 if (rfs) { 02216 #if _FS_SHARE 02217 clear_lock(rfs); 02218 #endif 02219 #if _FS_REENTRANT /* Discard sync object of the current volume */ 02220 if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR; 02221 #endif 02222 rfs->fs_type = 0; /* Clear old fs object */ 02223 } 02224 02225 if (fs) { 02226 fs->fs_type = 0; /* Clear new fs object */ 02227 #if _FS_REENTRANT /* Create sync object for the new volume */ 02228 if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR; 02229 #endif 02230 } 02231 FatFs[vol] = fs; /* Register new fs object */ 02232 02233 return FR_OK; 02234 } 02235 02236 02237 02238 02239 /*-----------------------------------------------------------------------*/ 02240 /* Open or Create a File */ 02241 /*-----------------------------------------------------------------------*/ 02242 02243 FRESULT f_open ( 02244 FIL *fp, /* Pointer to the blank file object */ 02245 const TCHAR *path, /* Pointer to the file name */ 02246 BYTE mode /* Access mode and file open mode flags */ 02247 ) 02248 { 02249 FRESULT res; 02250 FATFS_DIR dj; 02251 BYTE *dir; 02252 DEF_NAMEBUF; 02253 02254 02255 fp->fs = 0; /* Clear file object */ 02256 02257 #if !_FS_READONLY 02258 mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; 02259 res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ)); 02260 #else 02261 mode &= FA_READ; 02262 res = chk_mounted(&path, &dj.fs, 0); 02263 #endif 02264 INIT_BUF(dj); 02265 if (res == FR_OK) 02266 res = follow_path(&dj, path); /* Follow the file path */ 02267 dir = dj.dir; 02268 02269 #if !_FS_READONLY /* R/W configuration */ 02270 if (res == FR_OK) { 02271 if (!dir) /* Current dir itself */ 02272 res = FR_INVALID_NAME; 02273 #if _FS_SHARE 02274 else 02275 res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); 02276 #endif 02277 } 02278 /* Create or Open a file */ 02279 if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { 02280 DWORD dw, cl; 02281 02282 if (res != FR_OK) { /* No file, create new */ 02283 if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ 02284 #if _FS_SHARE 02285 res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; 02286 #else 02287 res = dir_register(&dj); 02288 #endif 02289 mode |= FA_CREATE_ALWAYS; /* File is created */ 02290 dir = dj.dir; /* New entry */ 02291 } 02292 else { /* Any object is already existing */ 02293 if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or FATFS_DIR) */ 02294 res = FR_DENIED; 02295 } else { 02296 if (mode & FA_CREATE_NEW) /* Cannot create as new file */ 02297 res = FR_EXIST; 02298 } 02299 } 02300 if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ 02301 dw = get_fattime(); /* Created time */ 02302 ST_DWORD(dir+DIR_CrtTime, dw); 02303 dir[DIR_Attr] = 0; /* Reset attribute */ 02304 ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ 02305 cl = LD_CLUST(dir); /* Get start cluster */ 02306 ST_CLUST(dir, 0); /* cluster = 0 */ 02307 dj.fs->wflag = 1; 02308 if (cl) { /* Remove the cluster chain if exist */ 02309 dw = dj.fs->winsect; 02310 res = remove_chain(dj.fs, cl); 02311 if (res == FR_OK) { 02312 dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ 02313 res = move_window(dj.fs, dw); 02314 } 02315 } 02316 } 02317 } 02318 else { /* Open an existing file */ 02319 if (res == FR_OK) { /* Follow succeeded */ 02320 if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ 02321 res = FR_NO_FILE; 02322 } else { 02323 if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ 02324 res = FR_DENIED; 02325 } 02326 } 02327 } 02328 if (res == FR_OK) { 02329 if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ 02330 mode |= FA__WRITTEN; 02331 fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ 02332 fp->dir_ptr = dir; 02333 #if _FS_SHARE 02334 fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); 02335 if (!fp->lockid) res = FR_INT_ERR; 02336 #endif 02337 } 02338 02339 #else /* R/O configuration */ 02340 if (res == FR_OK) { /* Follow succeeded */ 02341 if (!dir) { /* Current dir itself */ 02342 res = FR_INVALID_NAME; 02343 } else { 02344 if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ 02345 res = FR_NO_FILE; 02346 } 02347 } 02348 #endif 02349 FREE_BUF(); 02350 02351 if (res == FR_OK) { 02352 fp->flag = mode; /* File access mode */ 02353 fp->sclust = LD_CLUST(dir); /* File start cluster */ 02354 fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ 02355 fp->fptr = 0; /* File pointer */ 02356 fp->dsect = 0; 02357 #if _USE_FASTSEEK 02358 fp->cltbl = 0; /* Normal seek mode */ 02359 #endif 02360 fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */ 02361 } 02362 02363 LEAVE_FF(dj.fs, res); 02364 } 02365 02366 02367 02368 02369 /*-----------------------------------------------------------------------*/ 02370 /* Read File */ 02371 /*-----------------------------------------------------------------------*/ 02372 02373 FRESULT f_read ( 02374 FIL *fp, /* Pointer to the file object */ 02375 void *buff, /* Pointer to data buffer */ 02376 UINT btr, /* Number of bytes to read */ 02377 UINT *br /* Pointer to number of bytes read */ 02378 ) 02379 { 02380 FRESULT res; 02381 DWORD clst, sect, remain; 02382 UINT rcnt, cc; 02383 BYTE csect, *rbuff = (BYTE*)buff; 02384 02385 02386 *br = 0; /* Initialize byte counter */ 02387 02388 res = validate(fp->fs, fp->id); /* Check validity */ 02389 if (res != FR_OK) LEAVE_FF(fp->fs, res); 02390 if (fp->flag & FA__ERROR) /* Aborted file? */ 02391 LEAVE_FF(fp->fs, FR_INT_ERR); 02392 if (!(fp->flag & FA_READ)) /* Check access mode */ 02393 LEAVE_FF(fp->fs, FR_DENIED); 02394 remain = fp->fsize - fp->fptr; 02395 if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ 02396 02397 for ( ; btr; /* Repeat until all data read */ 02398 rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { 02399 if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ 02400 csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ 02401 if (!csect) { /* On the cluster boundary? */ 02402 if (fp->fptr == 0) { /* On the top of the file? */ 02403 clst = fp->sclust; /* Follow from the origin */ 02404 } else { /* Middle or end of the file */ 02405 #if _USE_FASTSEEK 02406 if (fp->cltbl) 02407 clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ 02408 else 02409 #endif 02410 clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ 02411 } 02412 if (clst < 2) ABORT(fp->fs, FR_INT_ERR); 02413 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02414 fp->clust = clst; /* Update current cluster */ 02415 } 02416 sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ 02417 if (!sect) ABORT(fp->fs, FR_INT_ERR); 02418 sect += csect; 02419 cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ 02420 if (cc) { /* Read maximum contiguous sectors directly */ 02421 if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ 02422 cc = fp->fs->csize - csect; 02423 if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK) 02424 ABORT(fp->fs, FR_DISK_ERR); 02425 #if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ 02426 #if _FS_TINY 02427 if (fp->fs->wflag && fp->fs->winsect - sect < cc) 02428 mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); 02429 #else 02430 if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) 02431 mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); 02432 #endif 02433 #endif 02434 rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ 02435 continue; 02436 } 02437 #if !_FS_TINY 02438 if (fp->dsect != sect) { /* Load data sector if not in cache */ 02439 #if !_FS_READONLY 02440 if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ 02441 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02442 ABORT(fp->fs, FR_DISK_ERR); 02443 fp->flag &= ~FA__DIRTY; 02444 } 02445 #endif 02446 if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ 02447 ABORT(fp->fs, FR_DISK_ERR); 02448 } 02449 #endif 02450 fp->dsect = sect; 02451 } 02452 rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ 02453 if (rcnt > btr) rcnt = btr; 02454 #if _FS_TINY 02455 if (move_window(fp->fs, fp->dsect)) /* Move sector window */ 02456 ABORT(fp->fs, FR_DISK_ERR); 02457 mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ 02458 #else 02459 mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ 02460 #endif 02461 } 02462 02463 LEAVE_FF(fp->fs, FR_OK); 02464 } 02465 02466 02467 02468 02469 #if !_FS_READONLY 02470 /*-----------------------------------------------------------------------*/ 02471 /* Write File */ 02472 /*-----------------------------------------------------------------------*/ 02473 02474 FRESULT f_write ( 02475 FIL *fp, /* Pointer to the file object */ 02476 const void *buff, /* Pointer to the data to be written */ 02477 UINT btw, /* Number of bytes to write */ 02478 UINT *bw /* Pointer to number of bytes written */ 02479 ) 02480 { 02481 FRESULT res; 02482 DWORD clst, sect; 02483 UINT wcnt, cc; 02484 const BYTE *wbuff = (const BYTE*)buff; 02485 BYTE csect; 02486 02487 02488 *bw = 0; /* Initialize byte counter */ 02489 02490 res = validate(fp->fs, fp->id); /* Check validity */ 02491 if (res != FR_OK) LEAVE_FF(fp->fs, res); 02492 if (fp->flag & FA__ERROR) /* Aborted file? */ 02493 LEAVE_FF(fp->fs, FR_INT_ERR); 02494 if (!(fp->flag & FA_WRITE)) /* Check access mode */ 02495 LEAVE_FF(fp->fs, FR_DENIED); 02496 if ((DWORD)(fp->fsize + btw) < fp->fsize) btw = 0; /* File size cannot reach 4GB */ 02497 02498 for ( ; btw; /* Repeat until all data written */ 02499 wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { 02500 if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ 02501 csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ 02502 if (!csect) { /* On the cluster boundary? */ 02503 if (fp->fptr == 0) { /* On the top of the file? */ 02504 clst = fp->sclust; /* Follow from the origin */ 02505 if (clst == 0) /* When no cluster is allocated, */ 02506 fp->sclust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ 02507 } else { /* Middle or end of the file */ 02508 #if _USE_FASTSEEK 02509 if (fp->cltbl) 02510 clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ 02511 else 02512 #endif 02513 clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ 02514 } 02515 if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ 02516 if (clst == 1) ABORT(fp->fs, FR_INT_ERR); 02517 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02518 fp->clust = clst; /* Update current cluster */ 02519 } 02520 #if _FS_TINY 02521 if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write-back sector cache */ 02522 ABORT(fp->fs, FR_DISK_ERR); 02523 #else 02524 if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ 02525 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02526 ABORT(fp->fs, FR_DISK_ERR); 02527 fp->flag &= ~FA__DIRTY; 02528 } 02529 #endif 02530 sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ 02531 if (!sect) ABORT(fp->fs, FR_INT_ERR); 02532 sect += csect; 02533 cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ 02534 if (cc) { /* Write maximum contiguous sectors directly */ 02535 if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ 02536 cc = fp->fs->csize - csect; 02537 if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK) 02538 ABORT(fp->fs, FR_DISK_ERR); 02539 #if _FS_TINY 02540 if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ 02541 mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); 02542 fp->fs->wflag = 0; 02543 } 02544 #else 02545 if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ 02546 mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); 02547 fp->flag &= ~FA__DIRTY; 02548 } 02549 #endif 02550 wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ 02551 continue; 02552 } 02553 #if _FS_TINY 02554 if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ 02555 if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); 02556 fp->fs->winsect = sect; 02557 } 02558 #else 02559 if (fp->dsect != sect) { /* Fill sector cache with file data */ 02560 if (fp->fptr < fp->fsize && 02561 disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) 02562 ABORT(fp->fs, FR_DISK_ERR); 02563 } 02564 #endif 02565 fp->dsect = sect; 02566 } 02567 wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ 02568 if (wcnt > btw) wcnt = btw; 02569 #if _FS_TINY 02570 if (move_window(fp->fs, fp->dsect)) /* Move sector window */ 02571 ABORT(fp->fs, FR_DISK_ERR); 02572 mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ 02573 fp->fs->wflag = 1; 02574 #else 02575 mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ 02576 fp->flag |= FA__DIRTY; 02577 #endif 02578 } 02579 02580 if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ 02581 fp->flag |= FA__WRITTEN; /* Set file change flag */ 02582 02583 LEAVE_FF(fp->fs, FR_OK); 02584 } 02585 02586 02587 02588 02589 /*-----------------------------------------------------------------------*/ 02590 /* Synchronize the File Object */ 02591 /*-----------------------------------------------------------------------*/ 02592 02593 FRESULT f_sync ( 02594 FIL *fp /* Pointer to the file object */ 02595 ) 02596 { 02597 FRESULT res; 02598 DWORD tim; 02599 BYTE *dir; 02600 02601 02602 res = validate(fp->fs, fp->id); /* Check validity of the object */ 02603 if (res == FR_OK) { 02604 if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ 02605 #if !_FS_TINY /* Write-back dirty buffer */ 02606 if (fp->flag & FA__DIRTY) { 02607 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02608 LEAVE_FF(fp->fs, FR_DISK_ERR); 02609 fp->flag &= ~FA__DIRTY; 02610 } 02611 #endif 02612 /* Update the directory entry */ 02613 res = move_window(fp->fs, fp->dir_sect); 02614 if (res == FR_OK) { 02615 dir = fp->dir_ptr; 02616 dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ 02617 ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ 02618 ST_CLUST(dir, fp->sclust); /* Update start cluster */ 02619 tim = get_fattime(); /* Update updated time */ 02620 ST_DWORD(dir+DIR_WrtTime, tim); 02621 fp->flag &= ~FA__WRITTEN; 02622 fp->fs->wflag = 1; 02623 res = sync(fp->fs); 02624 } 02625 } 02626 } 02627 02628 LEAVE_FF(fp->fs, res); 02629 } 02630 02631 #endif /* !_FS_READONLY */ 02632 02633 02634 02635 02636 /*-----------------------------------------------------------------------*/ 02637 /* Close File */ 02638 /*-----------------------------------------------------------------------*/ 02639 02640 FRESULT f_close ( 02641 FIL *fp /* Pointer to the file object to be closed */ 02642 ) 02643 { 02644 FRESULT res; 02645 02646 #if _FS_READONLY 02647 FATFS *fs = fp->fs; 02648 res = validate(fs, fp->id); 02649 if (res == FR_OK) fp->fs = 0; /* Discard file object */ 02650 LEAVE_FF(fs, res); 02651 02652 #else 02653 res = f_sync(fp); /* Flush cached data */ 02654 #if _FS_SHARE 02655 if (res == FR_OK) { /* Decrement open counter */ 02656 #if _FS_REENTRANT 02657 res = validate(fp->fs, fp->id); 02658 if (res == FR_OK) { 02659 res = dec_lock(fp->lockid); 02660 unlock_fs(fp->fs, FR_OK); 02661 } 02662 #else 02663 res = dec_lock(fp->lockid); 02664 #endif 02665 } 02666 #endif 02667 if (res == FR_OK) fp->fs = 0; /* Discard file object */ 02668 return res; 02669 #endif 02670 } 02671 02672 02673 02674 02675 /*-----------------------------------------------------------------------*/ 02676 /* Current Drive/Directory Handlings */ 02677 /*-----------------------------------------------------------------------*/ 02678 02679 #if _FS_RPATH >= 1 02680 02681 FRESULT f_chdrive ( 02682 BYTE drv /* Drive number */ 02683 ) 02684 { 02685 if (drv >= _VOLUMES) return FR_INVALID_DRIVE; 02686 02687 CurrVol = drv; 02688 02689 return FR_OK; 02690 } 02691 02692 02693 02694 FRESULT f_chdir ( 02695 const TCHAR *path /* Pointer to the directory path */ 02696 ) 02697 { 02698 FRESULT res; 02699 FATFS_DIR dj; 02700 DEF_NAMEBUF; 02701 02702 02703 res = chk_mounted(&path, &dj.fs, 0); 02704 if (res == FR_OK) { 02705 INIT_BUF(dj); 02706 res = follow_path(&dj, path); /* Follow the path */ 02707 FREE_BUF(); 02708 if (res == FR_OK) { /* Follow completed */ 02709 if (!dj.dir) { 02710 dj.fs->cdir = dj.sclust; /* Start directory itself */ 02711 } else { 02712 if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ 02713 dj.fs->cdir = LD_CLUST(dj.dir); 02714 else 02715 res = FR_NO_PATH; /* Reached but a file */ 02716 } 02717 } 02718 if (res == FR_NO_FILE) res = FR_NO_PATH; 02719 } 02720 02721 LEAVE_FF(dj.fs, res); 02722 } 02723 02724 02725 #if _FS_RPATH >= 2 02726 FRESULT f_getcwd ( 02727 TCHAR *path, /* Pointer to the directory path */ 02728 UINT sz_path /* Size of path */ 02729 ) 02730 { 02731 FRESULT res; 02732 FATFS_DIR dj; 02733 UINT i, n; 02734 DWORD ccl; 02735 TCHAR *tp; 02736 FILINFO fno; 02737 DEF_NAMEBUF; 02738 02739 02740 *path = 0; 02741 res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */ 02742 if (res == FR_OK) { 02743 INIT_BUF(dj); 02744 i = sz_path; /* Bottom of buffer (dir stack base) */ 02745 dj.sclust = dj.fs->cdir; /* Start to follow upper dir from current dir */ 02746 while ((ccl = dj.sclust) != 0) { /* Repeat while current dir is a sub-dir */ 02747 res = dir_sdi(&dj, 1); /* Get parent dir */ 02748 if (res != FR_OK) break; 02749 res = dir_read(&dj); 02750 if (res != FR_OK) break; 02751 dj.sclust = LD_CLUST(dj.dir); /* Goto parent dir */ 02752 res = dir_sdi(&dj, 0); 02753 if (res != FR_OK) break; 02754 do { /* Find the entry links to the child dir */ 02755 res = dir_read(&dj); 02756 if (res != FR_OK) break; 02757 if (ccl == LD_CLUST(dj.dir)) break; /* Found the entry */ 02758 res = dir_next(&dj, 0); 02759 } while (res == FR_OK); 02760 if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ 02761 if (res != FR_OK) break; 02762 #if _USE_LFN 02763 fno.lfname = path; 02764 fno.lfsize = i; 02765 #endif 02766 get_fileinfo(&dj, &fno); /* Get the dir name and push it to the buffer */ 02767 tp = fno.fname; 02768 if (_USE_LFN && *path) tp = path; 02769 for (n = 0; tp[n]; n++) ; 02770 if (i < n + 3) { 02771 res = FR_NOT_ENOUGH_CORE; break; 02772 } 02773 while (n) path[--i] = tp[--n]; 02774 path[--i] = '/'; 02775 } 02776 tp = path; 02777 if (res == FR_OK) { 02778 *tp++ = '0' + CurrVol; /* Put drive number */ 02779 *tp++ = ':'; 02780 if (i == sz_path) { /* Root-dir */ 02781 *tp++ = '/'; 02782 } else { /* Sub-dir */ 02783 do /* Add stacked path str */ 02784 *tp++ = path[i++]; 02785 while (i < sz_path); 02786 } 02787 } 02788 *tp = 0; 02789 FREE_BUF(); 02790 } 02791 02792 LEAVE_FF(dj.fs, res); 02793 } 02794 #endif /* _FS_RPATH >= 2 */ 02795 #endif /* _FS_RPATH >= 1 */ 02796 02797 02798 02799 #if _FS_MINIMIZE <= 2 02800 /*-----------------------------------------------------------------------*/ 02801 /* Seek File R/W Pointer */ 02802 /*-----------------------------------------------------------------------*/ 02803 02804 FRESULT f_lseek ( 02805 FIL *fp, /* Pointer to the file object */ 02806 DWORD ofs /* File pointer from top of file */ 02807 ) 02808 { 02809 FRESULT res; 02810 02811 02812 res = validate(fp->fs, fp->id); /* Check validity of the object */ 02813 if (res != FR_OK) LEAVE_FF(fp->fs, res); 02814 if (fp->flag & FA__ERROR) /* Check abort flag */ 02815 LEAVE_FF(fp->fs, FR_INT_ERR); 02816 02817 #if _USE_FASTSEEK 02818 if (fp->cltbl) { /* Fast seek */ 02819 DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; 02820 02821 if (ofs == CREATE_LINKMAP) { /* Create CLMT */ 02822 tbl = fp->cltbl; 02823 tlen = *tbl++; ulen = 2; /* Given table size and required table size */ 02824 cl = fp->sclust; /* Top of the chain */ 02825 if (cl) { 02826 do { 02827 /* Get a fragment */ 02828 tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ 02829 do { 02830 pcl = cl; ncl++; 02831 cl = get_fat(fp->fs, cl); 02832 if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); 02833 if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02834 } while (cl == pcl + 1); 02835 if (ulen <= tlen) { /* Store the length and top of the fragment */ 02836 *tbl++ = ncl; *tbl++ = tcl; 02837 } 02838 } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ 02839 } 02840 *fp->cltbl = ulen; /* Number of items used */ 02841 if (ulen <= tlen) 02842 *tbl = 0; /* Terminate table */ 02843 else 02844 res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ 02845 02846 } else { /* Fast seek */ 02847 if (ofs > fp->fsize) /* Clip offset at the file size */ 02848 ofs = fp->fsize; 02849 fp->fptr = ofs; /* Set file pointer */ 02850 if (ofs) { 02851 fp->clust = clmt_clust(fp, ofs - 1); 02852 dsc = clust2sect(fp->fs, fp->clust); 02853 if (!dsc) ABORT(fp->fs, FR_INT_ERR); 02854 dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); 02855 if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ 02856 #if !_FS_TINY 02857 #if !_FS_READONLY 02858 if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ 02859 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02860 ABORT(fp->fs, FR_DISK_ERR); 02861 fp->flag &= ~FA__DIRTY; 02862 } 02863 #endif 02864 if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ 02865 ABORT(fp->fs, FR_DISK_ERR); 02866 #endif 02867 fp->dsect = dsc; 02868 } 02869 } 02870 } 02871 } else 02872 #endif 02873 02874 /* Normal Seek */ 02875 { 02876 DWORD clst, bcs, nsect, ifptr; 02877 02878 if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ 02879 #if !_FS_READONLY 02880 && !(fp->flag & FA_WRITE) 02881 #endif 02882 ) ofs = fp->fsize; 02883 02884 ifptr = fp->fptr; 02885 fp->fptr = nsect = 0; 02886 if (ofs) { 02887 bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ 02888 if (ifptr > 0 && 02889 (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ 02890 fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ 02891 ofs -= fp->fptr; 02892 clst = fp->clust; 02893 } else { /* When seek to back cluster, */ 02894 clst = fp->sclust; /* start from the first cluster */ 02895 #if !_FS_READONLY 02896 if (clst == 0) { /* If no cluster chain, create a new chain */ 02897 clst = create_chain(fp->fs, 0); 02898 if (clst == 1) ABORT(fp->fs, FR_INT_ERR); 02899 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02900 fp->sclust = clst; 02901 } 02902 #endif 02903 fp->clust = clst; 02904 } 02905 if (clst != 0) { 02906 while (ofs > bcs) { /* Cluster following loop */ 02907 #if !_FS_READONLY 02908 if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ 02909 clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ 02910 if (clst == 0) { /* When disk gets full, clip file size */ 02911 ofs = bcs; break; 02912 } 02913 } else 02914 #endif 02915 clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ 02916 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02917 if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); 02918 fp->clust = clst; 02919 fp->fptr += bcs; 02920 ofs -= bcs; 02921 } 02922 fp->fptr += ofs; 02923 if (ofs % SS(fp->fs)) { 02924 nsect = clust2sect(fp->fs, clst); /* Current sector */ 02925 if (!nsect) ABORT(fp->fs, FR_INT_ERR); 02926 nsect += ofs / SS(fp->fs); 02927 } 02928 } 02929 } 02930 if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ 02931 #if !_FS_TINY 02932 #if !_FS_READONLY 02933 if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ 02934 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02935 ABORT(fp->fs, FR_DISK_ERR); 02936 fp->flag &= ~FA__DIRTY; 02937 } 02938 #endif 02939 if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ 02940 ABORT(fp->fs, FR_DISK_ERR); 02941 #endif 02942 fp->dsect = nsect; 02943 } 02944 #if !_FS_READONLY 02945 if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ 02946 fp->fsize = fp->fptr; 02947 fp->flag |= FA__WRITTEN; 02948 } 02949 #endif 02950 } 02951 02952 LEAVE_FF(fp->fs, res); 02953 } 02954 02955 02956 02957 #if _FS_MINIMIZE <= 1 02958 /*-----------------------------------------------------------------------*/ 02959 /* Create a Directroy Object */ 02960 /*-----------------------------------------------------------------------*/ 02961 02962 FRESULT f_opendir ( 02963 FATFS_DIR *dj, /* Pointer to directory object to create */ 02964 const TCHAR *path /* Pointer to the directory path */ 02965 ) 02966 { 02967 FRESULT res; 02968 DEF_NAMEBUF; 02969 02970 02971 res = chk_mounted(&path, &dj->fs, 0); 02972 if (res == FR_OK) { 02973 INIT_BUF(*dj); 02974 res = follow_path(dj, path); /* Follow the path to the directory */ 02975 FREE_BUF(); 02976 if (res == FR_OK) { /* Follow completed */ 02977 if (dj->dir) { /* It is not the root dir */ 02978 if (dj->dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ 02979 dj->sclust = LD_CLUST(dj->dir); 02980 } else { /* The object is not a directory */ 02981 res = FR_NO_PATH; 02982 } 02983 } 02984 if (res == FR_OK) { 02985 dj->id = dj->fs->id; 02986 res = dir_sdi(dj, 0); /* Rewind dir */ 02987 } 02988 } 02989 if (res == FR_NO_FILE) res = FR_NO_PATH; 02990 } 02991 02992 LEAVE_FF(dj->fs, res); 02993 } 02994 02995 02996 02997 02998 /*-----------------------------------------------------------------------*/ 02999 /* Read Directory Entry in Sequense */ 03000 /*-----------------------------------------------------------------------*/ 03001 03002 FRESULT f_readdir ( 03003 FATFS_DIR *dj, /* Pointer to the open directory object */ 03004 FILINFO *fno /* Pointer to file information to return */ 03005 ) 03006 { 03007 FRESULT res; 03008 DEF_NAMEBUF; 03009 03010 03011 res = validate(dj->fs, dj->id); /* Check validity of the object */ 03012 if (res == FR_OK) { 03013 if (!fno) { 03014 res = dir_sdi(dj, 0); /* Rewind the directory object */ 03015 } else { 03016 INIT_BUF(*dj); 03017 res = dir_read(dj); /* Read an directory item */ 03018 if (res == FR_NO_FILE) { /* Reached end of dir */ 03019 dj->sect = 0; 03020 res = FR_OK; 03021 } 03022 if (res == FR_OK) { /* A valid entry is found */ 03023 get_fileinfo(dj, fno); /* Get the object information */ 03024 res = dir_next(dj, 0); /* Increment index for next */ 03025 if (res == FR_NO_FILE) { 03026 dj->sect = 0; 03027 res = FR_OK; 03028 } 03029 } 03030 FREE_BUF(); 03031 } 03032 } 03033 03034 LEAVE_FF(dj->fs, res); 03035 } 03036 03037 03038 03039 #if _FS_MINIMIZE == 0 03040 /*-----------------------------------------------------------------------*/ 03041 /* Get File Status */ 03042 /*-----------------------------------------------------------------------*/ 03043 03044 FRESULT f_stat ( 03045 const TCHAR *path, /* Pointer to the file path */ 03046 FILINFO *fno /* Pointer to file information to return */ 03047 ) 03048 { 03049 FRESULT res; 03050 FATFS_DIR dj; 03051 DEF_NAMEBUF; 03052 03053 03054 res = chk_mounted(&path, &dj.fs, 0); 03055 if (res == FR_OK) { 03056 INIT_BUF(dj); 03057 res = follow_path(&dj, path); /* Follow the file path */ 03058 if (res == FR_OK) { /* Follow completed */ 03059 if (dj.dir) /* Found an object */ 03060 get_fileinfo(&dj, fno); 03061 else /* It is root dir */ 03062 res = FR_INVALID_NAME; 03063 } 03064 FREE_BUF(); 03065 } 03066 03067 LEAVE_FF(dj.fs, res); 03068 } 03069 03070 03071 03072 #if !_FS_READONLY 03073 /*-----------------------------------------------------------------------*/ 03074 /* Get Number of Free Clusters */ 03075 /*-----------------------------------------------------------------------*/ 03076 03077 FRESULT f_getfree ( 03078 const TCHAR *path, /* Pointer to the logical drive number (root dir) */ 03079 DWORD *nclst, /* Pointer to the variable to return number of free clusters */ 03080 FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ 03081 ) 03082 { 03083 FRESULT res; 03084 DWORD n, clst, sect, stat; 03085 UINT i; 03086 BYTE fat, *p; 03087 03088 03089 /* Get drive number */ 03090 res = chk_mounted(&path, fatfs, 0); 03091 if (res == FR_OK) { 03092 /* If free_clust is valid, return it without full cluster scan */ 03093 if ((*fatfs)->free_clust <= (*fatfs)->n_fatent - 2) { 03094 *nclst = (*fatfs)->free_clust; 03095 } else { 03096 /* Get number of free clusters */ 03097 fat = (*fatfs)->fs_type; 03098 n = 0; 03099 if (fat == FS_FAT12) { 03100 clst = 2; 03101 do { 03102 stat = get_fat(*fatfs, clst); 03103 if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } 03104 if (stat == 1) { res = FR_INT_ERR; break; } 03105 if (stat == 0) n++; 03106 } while (++clst < (*fatfs)->n_fatent); 03107 } else { 03108 clst = (*fatfs)->n_fatent; 03109 sect = (*fatfs)->fatbase; 03110 i = 0; p = 0; 03111 do { 03112 if (!i) { 03113 res = move_window(*fatfs, sect++); 03114 if (res != FR_OK) break; 03115 p = (*fatfs)->win; 03116 i = SS(*fatfs); 03117 } 03118 if (fat == FS_FAT16) { 03119 if (LD_WORD(p) == 0) n++; 03120 p += 2; i -= 2; 03121 } else { 03122 if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; 03123 p += 4; i -= 4; 03124 } 03125 } while (--clst); 03126 } 03127 (*fatfs)->free_clust = n; 03128 if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; 03129 *nclst = n; 03130 } 03131 } 03132 LEAVE_FF(*fatfs, res); 03133 } 03134 03135 03136 03137 03138 /*-----------------------------------------------------------------------*/ 03139 /* Truncate File */ 03140 /*-----------------------------------------------------------------------*/ 03141 03142 FRESULT f_truncate ( 03143 FIL *fp /* Pointer to the file object */ 03144 ) 03145 { 03146 FRESULT res; 03147 DWORD ncl; 03148 03149 03150 res = validate(fp->fs, fp->id); /* Check validity of the object */ 03151 if (res == FR_OK) { 03152 if (fp->flag & FA__ERROR) { /* Check abort flag */ 03153 res = FR_INT_ERR; 03154 } else { 03155 if (!(fp->flag & FA_WRITE)) /* Check access mode */ 03156 res = FR_DENIED; 03157 } 03158 } 03159 if (res == FR_OK) { 03160 if (fp->fsize > fp->fptr) { 03161 fp->fsize = fp->fptr; /* Set file size to current R/W point */ 03162 fp->flag |= FA__WRITTEN; 03163 if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ 03164 res = remove_chain(fp->fs, fp->sclust); 03165 fp->sclust = 0; 03166 } else { /* When truncate a part of the file, remove remaining clusters */ 03167 ncl = get_fat(fp->fs, fp->clust); 03168 res = FR_OK; 03169 if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; 03170 if (ncl == 1) res = FR_INT_ERR; 03171 if (res == FR_OK && ncl < fp->fs->n_fatent) { 03172 res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); 03173 if (res == FR_OK) res = remove_chain(fp->fs, ncl); 03174 } 03175 } 03176 } 03177 if (res != FR_OK) fp->flag |= FA__ERROR; 03178 } 03179 03180 LEAVE_FF(fp->fs, res); 03181 } 03182 03183 03184 03185 03186 /*-----------------------------------------------------------------------*/ 03187 /* Delete a File or Directory */ 03188 /*-----------------------------------------------------------------------*/ 03189 03190 FRESULT f_unlink ( 03191 const TCHAR *path /* Pointer to the file or directory path */ 03192 ) 03193 { 03194 FRESULT res; 03195 FATFS_DIR dj, sdj; 03196 BYTE *dir; 03197 DWORD dclst; 03198 DEF_NAMEBUF; 03199 03200 03201 res = chk_mounted(&path, &dj.fs, 1); 03202 if (res == FR_OK) { 03203 INIT_BUF(dj); 03204 res = follow_path(&dj, path); /* Follow the file path */ 03205 if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) 03206 res = FR_INVALID_NAME; /* Cannot remove dot entry */ 03207 #if _FS_SHARE 03208 if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open file */ 03209 #endif 03210 if (res == FR_OK) { /* The object is accessible */ 03211 dir = dj.dir; 03212 if (!dir) { 03213 res = FR_INVALID_NAME; /* Cannot remove the start directory */ 03214 } else { 03215 if (dir[DIR_Attr] & AM_RDO) 03216 res = FR_DENIED; /* Cannot remove R/O object */ 03217 } 03218 dclst = LD_CLUST(dir); 03219 if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */ 03220 if (dclst < 2) { 03221 res = FR_INT_ERR; 03222 } else { 03223 mem_cpy(&sdj, &dj, sizeof(FATFS_DIR)); /* Check if the sub-dir is empty or not */ 03224 sdj.sclust = dclst; 03225 res = dir_sdi(&sdj, 2); /* Exclude dot entries */ 03226 if (res == FR_OK) { 03227 res = dir_read(&sdj); 03228 if (res == FR_OK /* Not empty dir */ 03229 #if _FS_RPATH 03230 || dclst == sdj.fs->cdir /* Current dir */ 03231 #endif 03232 ) res = FR_DENIED; 03233 if (res == FR_NO_FILE) res = FR_OK; /* Empty */ 03234 } 03235 } 03236 } 03237 if (res == FR_OK) { 03238 res = dir_remove(&dj); /* Remove the directory entry */ 03239 if (res == FR_OK) { 03240 if (dclst) /* Remove the cluster chain if exist */ 03241 res = remove_chain(dj.fs, dclst); 03242 if (res == FR_OK) res = sync(dj.fs); 03243 } 03244 } 03245 } 03246 FREE_BUF(); 03247 } 03248 LEAVE_FF(dj.fs, res); 03249 } 03250 03251 03252 03253 03254 /*-----------------------------------------------------------------------*/ 03255 /* Create a Directory */ 03256 /*-----------------------------------------------------------------------*/ 03257 03258 FRESULT f_mkdir ( 03259 const TCHAR *path /* Pointer to the directory path */ 03260 ) 03261 { 03262 FRESULT res; 03263 FATFS_DIR dj; 03264 BYTE *dir, n; 03265 DWORD dsc, dcl, pcl, tim = get_fattime(); 03266 DEF_NAMEBUF; 03267 03268 03269 res = chk_mounted(&path, &dj.fs, 1); 03270 if (res == FR_OK) { 03271 INIT_BUF(dj); 03272 res = follow_path(&dj, path); /* Follow the file path */ 03273 if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ 03274 if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) 03275 res = FR_INVALID_NAME; 03276 if (res == FR_NO_FILE) { /* Can create a new directory */ 03277 dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ 03278 res = FR_OK; 03279 if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ 03280 if (dcl == 1) res = FR_INT_ERR; 03281 if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; 03282 if (res == FR_OK) /* Flush FAT */ 03283 res = move_window(dj.fs, 0); 03284 if (res == FR_OK) { /* Initialize the new directory table */ 03285 dsc = clust2sect(dj.fs, dcl); 03286 dir = dj.fs->win; 03287 mem_set(dir, 0, SS(dj.fs)); 03288 mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ 03289 dir[DIR_Name] = '.'; 03290 dir[DIR_Attr] = AM_DIR; 03291 ST_DWORD(dir+DIR_WrtTime, tim); 03292 ST_CLUST(dir, dcl); 03293 mem_cpy(dir+SZ_DIR, dir, SZ_DIR); /* Create ".." entry */ 03294 dir[33] = '.'; pcl = dj.sclust; 03295 if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) 03296 pcl = 0; 03297 ST_CLUST(dir+SZ_DIR, pcl); 03298 for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ 03299 dj.fs->winsect = dsc++; 03300 dj.fs->wflag = 1; 03301 res = move_window(dj.fs, 0); 03302 if (res != FR_OK) break; 03303 mem_set(dir, 0, SS(dj.fs)); 03304 } 03305 } 03306 if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ 03307 if (res != FR_OK) { 03308 remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ 03309 } else { 03310 dir = dj.dir; 03311 dir[DIR_Attr] = AM_DIR; /* Attribute */ 03312 ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */ 03313 ST_CLUST(dir, dcl); /* Table start cluster */ 03314 dj.fs->wflag = 1; 03315 res = sync(dj.fs); 03316 } 03317 } 03318 FREE_BUF(); 03319 } 03320 03321 LEAVE_FF(dj.fs, res); 03322 } 03323 03324 03325 03326 03327 /*-----------------------------------------------------------------------*/ 03328 /* Change Attribute */ 03329 /*-----------------------------------------------------------------------*/ 03330 03331 FRESULT f_chmod ( 03332 const TCHAR *path, /* Pointer to the file path */ 03333 BYTE value, /* Attribute bits */ 03334 BYTE mask /* Attribute mask to change */ 03335 ) 03336 { 03337 FRESULT res; 03338 FATFS_DIR dj; 03339 BYTE *dir; 03340 DEF_NAMEBUF; 03341 03342 03343 res = chk_mounted(&path, &dj.fs, 1); 03344 if (res == FR_OK) { 03345 INIT_BUF(dj); 03346 res = follow_path(&dj, path); /* Follow the file path */ 03347 FREE_BUF(); 03348 if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) 03349 res = FR_INVALID_NAME; 03350 if (res == FR_OK) { 03351 dir = dj.dir; 03352 if (!dir) { /* Is it a root directory? */ 03353 res = FR_INVALID_NAME; 03354 } else { /* File or sub directory */ 03355 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ 03356 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ 03357 dj.fs->wflag = 1; 03358 res = sync(dj.fs); 03359 } 03360 } 03361 } 03362 03363 LEAVE_FF(dj.fs, res); 03364 } 03365 03366 03367 03368 03369 /*-----------------------------------------------------------------------*/ 03370 /* Change Timestamp */ 03371 /*-----------------------------------------------------------------------*/ 03372 03373 FRESULT f_utime ( 03374 const TCHAR *path, /* Pointer to the file/directory name */ 03375 const FILINFO *fno /* Pointer to the time stamp to be set */ 03376 ) 03377 { 03378 FRESULT res; 03379 FATFS_DIR dj; 03380 BYTE *dir; 03381 DEF_NAMEBUF; 03382 03383 03384 res = chk_mounted(&path, &dj.fs, 1); 03385 if (res == FR_OK) { 03386 INIT_BUF(dj); 03387 res = follow_path(&dj, path); /* Follow the file path */ 03388 FREE_BUF(); 03389 if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) 03390 res = FR_INVALID_NAME; 03391 if (res == FR_OK) { 03392 dir = dj.dir; 03393 if (!dir) { /* Root directory */ 03394 res = FR_INVALID_NAME; 03395 } else { /* File or sub-directory */ 03396 ST_WORD(dir+DIR_WrtTime, fno->ftime); 03397 ST_WORD(dir+DIR_WrtDate, fno->fdate); 03398 dj.fs->wflag = 1; 03399 res = sync(dj.fs); 03400 } 03401 } 03402 } 03403 03404 LEAVE_FF(dj.fs, res); 03405 } 03406 03407 03408 03409 03410 /*-----------------------------------------------------------------------*/ 03411 /* Rename File/Directory */ 03412 /*-----------------------------------------------------------------------*/ 03413 03414 FRESULT f_rename ( 03415 const TCHAR *path_old, /* Pointer to the old name */ 03416 const TCHAR *path_new /* Pointer to the new name */ 03417 ) 03418 { 03419 FRESULT res; 03420 FATFS_DIR djo, djn; 03421 BYTE buf[21], *dir; 03422 DWORD dw; 03423 DEF_NAMEBUF; 03424 03425 03426 res = chk_mounted(&path_old, &djo.fs, 1); 03427 if (res == FR_OK) { 03428 djn.fs = djo.fs; 03429 INIT_BUF(djo); 03430 res = follow_path(&djo, path_old); /* Check old object */ 03431 if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT)) 03432 res = FR_INVALID_NAME; 03433 #if _FS_SHARE 03434 if (res == FR_OK) res = chk_lock(&djo, 2); 03435 #endif 03436 if (res == FR_OK) { /* Old object is found */ 03437 if (!djo.dir) { /* Is root dir? */ 03438 res = FR_NO_FILE; 03439 } else { 03440 mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */ 03441 mem_cpy(&djn, &djo, sizeof(FATFS_DIR)); /* Check new object */ 03442 res = follow_path(&djn, path_new); 03443 if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ 03444 if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ 03445 /* Start critical section that any interruption or error can cause cross-link */ 03446 res = dir_register(&djn); /* Register the new entry */ 03447 if (res == FR_OK) { 03448 dir = djn.dir; /* Copy object information except for name */ 03449 mem_cpy(dir+13, buf+2, 19); 03450 dir[DIR_Attr] = buf[0] | AM_ARC; 03451 djo.fs->wflag = 1; 03452 if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { /* Update .. entry in the directory if needed */ 03453 dw = clust2sect(djn.fs, LD_CLUST(dir)); 03454 if (!dw) { 03455 res = FR_INT_ERR; 03456 } else { 03457 res = move_window(djn.fs, dw); 03458 dir = djn.fs->win+SZ_DIR; /* .. entry */ 03459 if (res == FR_OK && dir[1] == '.') { 03460 dw = (djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase) ? 0 : djn.sclust; 03461 ST_CLUST(dir, dw); 03462 djn.fs->wflag = 1; 03463 } 03464 } 03465 } 03466 if (res == FR_OK) { 03467 res = dir_remove(&djo); /* Remove old entry */ 03468 if (res == FR_OK) 03469 res = sync(djo.fs); 03470 } 03471 } 03472 /* End critical section */ 03473 } 03474 } 03475 } 03476 FREE_BUF(); 03477 } 03478 LEAVE_FF(djo.fs, res); 03479 } 03480 03481 #endif /* !_FS_READONLY */ 03482 #endif /* _FS_MINIMIZE == 0 */ 03483 #endif /* _FS_MINIMIZE <= 1 */ 03484 #endif /* _FS_MINIMIZE <= 2 */ 03485 03486 03487 03488 /*-----------------------------------------------------------------------*/ 03489 /* Forward data to the stream directly (available on only tiny cfg) */ 03490 /*-----------------------------------------------------------------------*/ 03491 #if _USE_FORWARD && _FS_TINY 03492 03493 FRESULT f_forward ( 03494 FIL *fp, /* Pointer to the file object */ 03495 UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ 03496 UINT btr, /* Number of bytes to forward */ 03497 UINT *bf /* Pointer to number of bytes forwarded */ 03498 ) 03499 { 03500 FRESULT res; 03501 DWORD remain, clst, sect; 03502 UINT rcnt; 03503 BYTE csect; 03504 03505 03506 *bf = 0; /* Initialize byte counter */ 03507 03508 res = validate(fp->fs, fp->id); /* Check validity of the object */ 03509 if (res != FR_OK) LEAVE_FF(fp->fs, res); 03510 if (fp->flag & FA__ERROR) /* Check error flag */ 03511 LEAVE_FF(fp->fs, FR_INT_ERR); 03512 if (!(fp->flag & FA_READ)) /* Check access mode */ 03513 LEAVE_FF(fp->fs, FR_DENIED); 03514 03515 remain = fp->fsize - fp->fptr; 03516 if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ 03517 03518 for ( ; btr && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ 03519 fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { 03520 csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ 03521 if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ 03522 if (!csect) { /* On the cluster boundary? */ 03523 clst = (fp->fptr == 0) ? /* On the top of the file? */ 03524 fp->sclust : get_fat(fp->fs, fp->clust); 03525 if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); 03526 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 03527 fp->clust = clst; /* Update current cluster */ 03528 } 03529 } 03530 sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ 03531 if (!sect) ABORT(fp->fs, FR_INT_ERR); 03532 sect += csect; 03533 if (move_window(fp->fs, sect)) /* Move sector window */ 03534 ABORT(fp->fs, FR_DISK_ERR); 03535 fp->dsect = sect; 03536 rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ 03537 if (rcnt > btr) rcnt = btr; 03538 rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); 03539 if (!rcnt) ABORT(fp->fs, FR_INT_ERR); 03540 } 03541 03542 LEAVE_FF(fp->fs, FR_OK); 03543 } 03544 #endif /* _USE_FORWARD */ 03545 03546 03547 03548 #if _USE_MKFS && !_FS_READONLY 03549 /*-----------------------------------------------------------------------*/ 03550 /* Create File System on the Drive */ 03551 /*-----------------------------------------------------------------------*/ 03552 #define N_ROOTDIR 512 /* Number of root dir entries for FAT12/16 */ 03553 #define N_FATS 1 /* Number of FAT copies (1 or 2) */ 03554 03555 03556 FRESULT f_mkfs ( 03557 BYTE drv, /* Logical drive number */ 03558 BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ 03559 UINT au /* Allocation unit size [bytes] */ 03560 ) 03561 { 03562 static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; 03563 static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; 03564 BYTE fmt, md, sys, *tbl, pdrv, part; 03565 DWORD n_clst, vs, n, wsect; 03566 UINT i; 03567 DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ 03568 DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ 03569 FATFS *fs; 03570 DSTATUS stat; 03571 03572 03573 /* Check mounted drive and clear work area */ 03574 if (drv >= _VOLUMES) return FR_INVALID_DRIVE; 03575 if (sfd > 1) return FR_INVALID_PARAMETER; 03576 if (au & (au - 1)) return FR_INVALID_PARAMETER; 03577 fs = FatFs[drv]; 03578 if (!fs) return FR_NOT_ENABLED; 03579 fs->fs_type = 0; 03580 pdrv = LD2PD(drv); /* Physical drive */ 03581 part = LD2PT(drv); /* Partition (0:auto detect, 1-4:get from partition table)*/ 03582 03583 /* Get disk statics */ 03584 stat = disk_initialize(pdrv); 03585 if (stat & STA_NOINIT) return FR_NOT_READY; 03586 if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; 03587 #if _MAX_SS != 512 /* Get disk sector size */ 03588 if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) 03589 return FR_DISK_ERR; 03590 #endif 03591 if (_MULTI_PARTITION && part) { 03592 /* Get partition information from partition table in the MBR */ 03593 if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; 03594 if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; 03595 tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; 03596 if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ 03597 b_vol = LD_DWORD(tbl+8); /* Volume start sector */ 03598 n_vol = LD_DWORD(tbl+12); /* Volume size */ 03599 } else { 03600 /* Create a partition in this function */ 03601 if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) 03602 return FR_DISK_ERR; 03603 b_vol = (sfd) ? 0 : 63; /* Volume start sector */ 03604 n_vol -= b_vol; /* Volume size */ 03605 } 03606 03607 if (!au) { /* AU auto selection */ 03608 vs = n_vol / (2000 / (SS(fs) / 512)); 03609 for (i = 0; vs < vst[i]; i++) ; 03610 au = cst[i]; 03611 } 03612 au /= SS(fs); /* Number of sectors per cluster */ 03613 if (au == 0) au = 1; 03614 if (au > 128) au = 128; 03615 03616 /* Pre-compute number of clusters and FAT syb-type */ 03617 n_clst = n_vol / au; 03618 fmt = FS_FAT12; 03619 if (n_clst >= MIN_FAT16) fmt = FS_FAT16; 03620 if (n_clst >= MIN_FAT32) fmt = FS_FAT32; 03621 03622 /* Determine offset and size of FAT structure */ 03623 if (fmt == FS_FAT32) { 03624 n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); 03625 n_rsv = 32; 03626 n_dir = 0; 03627 } else { 03628 n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; 03629 n_fat = (n_fat + SS(fs) - 1) / SS(fs); 03630 n_rsv = 1; 03631 n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs); 03632 } 03633 b_fat = b_vol + n_rsv; /* FAT area start sector */ 03634 b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ 03635 b_data = b_dir + n_dir; /* Data area start sector */ 03636 if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ 03637 03638 /* Align data start sector to erase block boundary (for flash memory media) */ 03639 if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; 03640 n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ 03641 n = (n - b_data) / N_FATS; 03642 if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ 03643 n_rsv += n; 03644 b_fat += n; 03645 } else { /* FAT12/16: Expand FAT size */ 03646 n_fat += n; 03647 } 03648 03649 /* Determine number of clusters and final check of validity of the FAT sub-type */ 03650 n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; 03651 if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) 03652 || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) 03653 return FR_MKFS_ABORTED; 03654 03655 switch (fmt) { /* Determine system ID for partition table */ 03656 case FS_FAT12: sys = 0x01; break; 03657 case FS_FAT16: sys = (n_vol < 0x10000) ? 0x04 : 0x06; break; 03658 default: sys = 0x0C; 03659 } 03660 03661 if (_MULTI_PARTITION && part) { 03662 /* Update system ID in the partition table */ 03663 tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; 03664 tbl[4] = sys; 03665 if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; 03666 md = 0xF8; 03667 } else { 03668 if (sfd) { /* No patition table (SFD) */ 03669 md = 0xF0; 03670 } else { /* Create partition table (FDISK) */ 03671 mem_set(fs->win, 0, SS(fs)); 03672 tbl = fs->win+MBR_Table; /* Create partiton table for single partition in the drive */ 03673 tbl[1] = 1; /* Partition start head */ 03674 tbl[2] = 1; /* Partition start sector */ 03675 tbl[3] = 0; /* Partition start cylinder */ 03676 tbl[4] = sys; /* System type */ 03677 tbl[5] = 254; /* Partition end head */ 03678 n = (b_vol + n_vol) / 63 / 255; 03679 tbl[6] = (BYTE)((n >> 2) | 63); /* Partiiton end sector */ 03680 tbl[7] = (BYTE)n; /* End cylinder */ 03681 ST_DWORD(tbl+8, 63); /* Partition start in LBA */ 03682 ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */ 03683 ST_WORD(fs->win+BS_55AA, 0xAA55); /* MBR signature */ 03684 if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR sector */ 03685 return FR_DISK_ERR; 03686 md = 0xF8; 03687 } 03688 } 03689 03690 /* Create BPB in the VBR */ 03691 tbl = fs->win; /* Clear sector */ 03692 mem_set(tbl, 0, SS(fs)); 03693 mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ 03694 i = SS(fs); /* Sector size */ 03695 ST_WORD(tbl+BPB_BytsPerSec, i); 03696 tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ 03697 ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ 03698 tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ 03699 i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of rootdir entries */ 03700 ST_WORD(tbl+BPB_RootEntCnt, i); 03701 if (n_vol < 0x10000) { /* Number of total sectors */ 03702 ST_WORD(tbl+BPB_TotSec16, n_vol); 03703 } else { 03704 ST_DWORD(tbl+BPB_TotSec32, n_vol); 03705 } 03706 tbl[BPB_Media] = md; /* Media descriptor */ 03707 ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ 03708 ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ 03709 ST_DWORD(tbl+BPB_HiddSec, b_vol); /* Hidden sectors */ 03710 n = get_fattime(); /* Use current time as VSN */ 03711 if (fmt == FS_FAT32) { 03712 ST_DWORD(tbl+BS_VolID32, n); /* VSN */ 03713 ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */ 03714 ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */ 03715 ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (VBR+1) */ 03716 ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */ 03717 tbl[BS_DrvNum32] = 0x80; /* Drive number */ 03718 tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ 03719 mem_cpy(tbl+BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ 03720 } else { 03721 ST_DWORD(tbl+BS_VolID, n); /* VSN */ 03722 ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors per FAT */ 03723 tbl[BS_DrvNum] = 0x80; /* Drive number */ 03724 tbl[BS_BootSig] = 0x29; /* Extended boot signature */ 03725 mem_cpy(tbl+BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ 03726 } 03727 ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ 03728 if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */ 03729 return FR_DISK_ERR; 03730 if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR+6) */ 03731 disk_write(pdrv, tbl, b_vol + 6, 1); 03732 03733 /* Initialize FAT area */ 03734 wsect = b_fat; 03735 for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ 03736 mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ 03737 n = md; /* Media descriptor byte */ 03738 if (fmt != FS_FAT32) { 03739 n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; 03740 ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */ 03741 } else { 03742 n |= 0xFFFFFF00; 03743 ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT32) */ 03744 ST_DWORD(tbl+4, 0xFFFFFFFF); 03745 ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ 03746 } 03747 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) 03748 return FR_DISK_ERR; 03749 mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ 03750 for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ 03751 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) 03752 return FR_DISK_ERR; 03753 } 03754 } 03755 03756 /* Initialize root directory */ 03757 i = (fmt == FS_FAT32) ? au : n_dir; 03758 do { 03759 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) 03760 return FR_DISK_ERR; 03761 } while (--i); 03762 03763 #if _USE_ERASE /* Erase data area if needed */ 03764 { 03765 DWORD eb[2]; 03766 03767 eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; 03768 disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb); 03769 } 03770 #endif 03771 03772 /* Create FSInfo if needed */ 03773 if (fmt == FS_FAT32) { 03774 ST_DWORD(tbl+FSI_LeadSig, 0x41615252); 03775 ST_DWORD(tbl+FSI_StrucSig, 0x61417272); 03776 ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); /* Number of free clusters */ 03777 ST_DWORD(tbl+FSI_Nxt_Free, 2); /* Last allocated cluster# */ 03778 ST_WORD(tbl+BS_55AA, 0xAA55); 03779 disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR+1) */ 03780 disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR+7) */ 03781 } 03782 03783 return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; 03784 } 03785 03786 03787 #if _MULTI_PARTITION == 2 03788 /*-----------------------------------------------------------------------*/ 03789 /* Divide Physical Drive */ 03790 /*-----------------------------------------------------------------------*/ 03791 03792 FRESULT f_fdisk ( 03793 BYTE pdrv, /* Physical drive number */ 03794 const DWORD szt[], /* Pointer to the size table for each partitions */ 03795 void* work /* Pointer to the working buffer */ 03796 ) 03797 { 03798 UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; 03799 BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; 03800 DSTATUS stat; 03801 DWORD sz_disk, sz_part, s_part; 03802 03803 03804 stat = disk_initialize(pdrv); 03805 if (stat & STA_NOINIT) return FR_NOT_READY; 03806 if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; 03807 if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; 03808 03809 /* Determine CHS in the table regardless of the drive geometry */ 03810 for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; 03811 if (n == 256) n--; 03812 e_hd = n - 1; 03813 sz_cyl = 63 * n; 03814 tot_cyl = sz_disk / sz_cyl; 03815 03816 /* Create partition table */ 03817 mem_set(buf, 0, _MAX_SS); 03818 p = buf + MBR_Table; b_cyl = 0; 03819 for (i = 0; i < 4; i++, p += SZ_PTE) { 03820 p_cyl = (szt[i] <= 100) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; 03821 if (!p_cyl) continue; 03822 s_part = (DWORD)sz_cyl * b_cyl; 03823 sz_part = (DWORD)sz_cyl * p_cyl; 03824 if (i == 0) { /* Exclude first track of cylinder 0 */ 03825 s_hd = 1; 03826 s_part += 63; sz_part -= 63; 03827 } else { 03828 s_hd = 0; 03829 } 03830 e_cyl = b_cyl + p_cyl - 1; 03831 if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; 03832 03833 /* Set partition table */ 03834 p[1] = s_hd; /* Start head */ 03835 p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ 03836 p[3] = (BYTE)b_cyl; /* Start cylinder */ 03837 p[4] = 0x06; /* System type (temporary setting) */ 03838 p[5] = e_hd; /* End head */ 03839 p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ 03840 p[7] = (BYTE)e_cyl; /* End cylinder */ 03841 ST_DWORD(p + 8, s_part); /* Start sector in LBA */ 03842 ST_DWORD(p + 12, sz_part); /* Partition size */ 03843 03844 /* Next partition */ 03845 b_cyl += p_cyl; 03846 } 03847 ST_WORD(p, 0xAA55); 03848 03849 /* Write it to the MBR */ 03850 return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK; 03851 } 03852 03853 03854 #endif /* _MULTI_PARTITION == 2 */ 03855 #endif /* _USE_MKFS && !_FS_READONLY */ 03856 03857 03858 03859 03860 #if _USE_STRFUNC 03861 /*-----------------------------------------------------------------------*/ 03862 /* Get a string from the file */ 03863 /*-----------------------------------------------------------------------*/ 03864 TCHAR* f_gets ( 03865 TCHAR* buff, /* Pointer to the string buffer to read */ 03866 int len, /* Size of string buffer (characters) */ 03867 FIL* fil /* Pointer to the file object */ 03868 ) 03869 { 03870 int n = 0; 03871 TCHAR c, *p = buff; 03872 BYTE s[2]; 03873 UINT rc; 03874 03875 03876 while (n < len - 1) { /* Read bytes until buffer gets filled */ 03877 f_read(fil, s, 1, &rc); 03878 if (rc != 1) break; /* Break on EOF or error */ 03879 c = s[0]; 03880 #if _LFN_UNICODE /* Read a character in UTF-8 encoding */ 03881 if (c >= 0x80) { 03882 if (c < 0xC0) continue; /* Skip stray trailer */ 03883 if (c < 0xE0) { /* Two-byte sequense */ 03884 f_read(fil, s, 1, &rc); 03885 if (rc != 1) break; 03886 c = ((c & 0x1F) << 6) | (s[0] & 0x3F); 03887 if (c < 0x80) c = '?'; 03888 } else { 03889 if (c < 0xF0) { /* Three-byte sequense */ 03890 f_read(fil, s, 2, &rc); 03891 if (rc != 2) break; 03892 c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F); 03893 if (c < 0x800) c = '?'; 03894 } else { /* Reject four-byte sequense */ 03895 c = '?'; 03896 } 03897 } 03898 } 03899 #endif 03900 #if _USE_STRFUNC >= 2 03901 if (c == '\r') continue; /* Strip '\r' */ 03902 #endif 03903 *p++ = c; 03904 n++; 03905 if (c == '\n') break; /* Break on EOL */ 03906 } 03907 *p = 0; 03908 return n ? buff : 0; /* When no data read (eof or error), return with error. */ 03909 } 03910 03911 03912 03913 #if !_FS_READONLY 03914 #include <stdarg.h> 03915 /*-----------------------------------------------------------------------*/ 03916 /* Put a character to the file */ 03917 /*-----------------------------------------------------------------------*/ 03918 int f_putc ( 03919 TCHAR c, /* A character to be output */ 03920 FIL* fil /* Pointer to the file object */ 03921 ) 03922 { 03923 UINT bw, btw; 03924 BYTE s[3]; 03925 03926 03927 #if _USE_STRFUNC >= 2 03928 if (c == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ 03929 #endif 03930 03931 #if _LFN_UNICODE /* Write the character in UTF-8 encoding */ 03932 if (c < 0x80) { /* 7-bit */ 03933 s[0] = (BYTE)c; 03934 btw = 1; 03935 } else { 03936 if (c < 0x800) { /* 11-bit */ 03937 s[0] = (BYTE)(0xC0 | (c >> 6)); 03938 s[1] = (BYTE)(0x80 | (c & 0x3F)); 03939 btw = 2; 03940 } else { /* 16-bit */ 03941 s[0] = (BYTE)(0xE0 | (c >> 12)); 03942 s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F)); 03943 s[2] = (BYTE)(0x80 | (c & 0x3F)); 03944 btw = 3; 03945 } 03946 } 03947 #else /* Write the character without conversion */ 03948 s[0] = (BYTE)c; 03949 btw = 1; 03950 #endif 03951 f_write(fil, s, btw, &bw); /* Write the char to the file */ 03952 return (bw == btw) ? 1 : EOF; /* Return the result */ 03953 } 03954 03955 03956 03957 03958 /*-----------------------------------------------------------------------*/ 03959 /* Put a string to the file */ 03960 /*-----------------------------------------------------------------------*/ 03961 int f_puts ( 03962 const TCHAR* str, /* Pointer to the string to be output */ 03963 FIL* fil /* Pointer to the file object */ 03964 ) 03965 { 03966 int n; 03967 03968 03969 for (n = 0; *str; str++, n++) { 03970 if (f_putc(*str, fil) == EOF) return EOF; 03971 } 03972 return n; 03973 } 03974 03975 03976 03977 03978 /*-----------------------------------------------------------------------*/ 03979 /* Put a formatted string to the file */ 03980 /*-----------------------------------------------------------------------*/ 03981 int f_printf ( 03982 FIL* fil, /* Pointer to the file object */ 03983 const TCHAR* str, /* Pointer to the format string */ 03984 ... /* Optional arguments... */ 03985 ) 03986 { 03987 va_list arp; 03988 BYTE f, r; 03989 UINT i, j, w; 03990 ULONG v; 03991 TCHAR c, d, s[16], *p; 03992 int res, chc, cc; 03993 03994 03995 va_start(arp, str); 03996 03997 for (cc = res = 0; cc != EOF; res += cc) { 03998 c = *str++; 03999 if (c == 0) break; /* End of string */ 04000 if (c != '%') { /* Non escape character */ 04001 cc = f_putc(c, fil); 04002 if (cc != EOF) cc = 1; 04003 continue; 04004 } 04005 w = f = 0; 04006 c = *str++; 04007 if (c == '0') { /* Flag: '0' padding */ 04008 f = 1; c = *str++; 04009 } else { 04010 if (c == '-') { /* Flag: left justified */ 04011 f = 2; c = *str++; 04012 } 04013 } 04014 while (IsDigit(c)) { /* Precision */ 04015 w = w * 10 + c - '0'; 04016 c = *str++; 04017 } 04018 if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ 04019 f |= 4; c = *str++; 04020 } 04021 if (!c) break; 04022 d = c; 04023 if (IsLower(d)) d -= 0x20; 04024 switch (d) { /* Type is... */ 04025 case 'S' : /* String */ 04026 p = va_arg(arp, TCHAR*); 04027 for (j = 0; p[j]; j++) ; 04028 chc = 0; 04029 if (!(f & 2)) { 04030 while (j++ < w) chc += (cc = f_putc(' ', fil)); 04031 } 04032 chc += (cc = f_puts(p, fil)); 04033 while (j++ < w) chc += (cc = f_putc(' ', fil)); 04034 if (cc != EOF) cc = chc; 04035 continue; 04036 case 'C' : /* Character */ 04037 cc = f_putc((TCHAR)va_arg(arp, int), fil); continue; 04038 case 'B' : /* Binary */ 04039 r = 2; break; 04040 case 'O' : /* Octal */ 04041 r = 8; break; 04042 case 'D' : /* Signed decimal */ 04043 case 'U' : /* Unsigned decimal */ 04044 r = 10; break; 04045 case 'X' : /* Hexdecimal */ 04046 r = 16; break; 04047 default: /* Unknown type (passthrough) */ 04048 cc = f_putc(c, fil); continue; 04049 } 04050 04051 /* Get an argument and put it in numeral */ 04052 v = (f & 4) ? (ULONG)va_arg(arp, long) : ((d == 'D') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int)); 04053 if (d == 'D' && (v & 0x80000000)) { 04054 v = 0 - v; 04055 f |= 8; 04056 } 04057 i = 0; 04058 do { 04059 d = (TCHAR)(v % r); v /= r; 04060 if (d > 9) d += (c == 'x') ? 0x27 : 0x07; 04061 s[i++] = d + '0'; 04062 } while (v && i < sizeof(s) / sizeof(s[0])); 04063 if (f & 8) s[i++] = '-'; 04064 j = i; d = (f & 1) ? '0' : ' '; 04065 res = 0; 04066 while (!(f & 2) && j++ < w) res += (cc = f_putc(d, fil)); 04067 do res += (cc = f_putc(s[--i], fil)); while(i); 04068 while (j++ < w) res += (cc = f_putc(' ', fil)); 04069 if (cc != EOF) cc = res; 04070 } 04071 04072 va_end(arp); 04073 return (cc == EOF) ? cc : res; 04074 } 04075 04076 #endif /* !_FS_READONLY */ 04077 #endif /* _USE_STRFUNC */
Generated on Fri Jul 15 2022 15:34:13 by
1.7.2
