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