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