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