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