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 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 #if _FS_TINY 02564 if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write-back sector cache */ 02565 ABORT(fp->fs, FR_DISK_ERR); 02566 #else 02567 if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ 02568 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02569 ABORT(fp->fs, FR_DISK_ERR); 02570 fp->flag &= ~FA__DIRTY; 02571 } 02572 #endif 02573 sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ 02574 if (!sect) ABORT(fp->fs, FR_INT_ERR); 02575 sect += csect; 02576 cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ 02577 if (cc) { /* Write maximum contiguous sectors directly */ 02578 if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ 02579 cc = fp->fs->csize - csect; 02580 if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK) 02581 ABORT(fp->fs, FR_DISK_ERR); 02582 #if _FS_TINY 02583 if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ 02584 mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); 02585 fp->fs->wflag = 0; 02586 } 02587 #else 02588 if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ 02589 mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); 02590 fp->flag &= ~FA__DIRTY; 02591 } 02592 #endif 02593 wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ 02594 continue; 02595 } 02596 #if _FS_TINY 02597 if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ 02598 if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); 02599 fp->fs->winsect = sect; 02600 } 02601 #else 02602 if (fp->dsect != sect) { /* Fill sector cache with file data */ 02603 if (fp->fptr < fp->fsize && 02604 disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) 02605 ABORT(fp->fs, FR_DISK_ERR); 02606 } 02607 #endif 02608 fp->dsect = sect; 02609 } 02610 wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ 02611 if (wcnt > btw) wcnt = btw; 02612 #if _FS_TINY 02613 if (move_window(fp->fs, fp->dsect)) /* Move sector window */ 02614 ABORT(fp->fs, FR_DISK_ERR); 02615 mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ 02616 fp->fs->wflag = 1; 02617 #else 02618 mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ 02619 fp->flag |= FA__DIRTY; 02620 #endif 02621 } 02622 02623 if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ 02624 fp->flag |= FA__WRITTEN; /* Set file change flag */ 02625 02626 LEAVE_FF(fp->fs, FR_OK); 02627 } 02628 02629 02630 02631 02632 /*-----------------------------------------------------------------------*/ 02633 /* Synchronize the File Object */ 02634 /*-----------------------------------------------------------------------*/ 02635 02636 FRESULT f_sync ( 02637 FIL *fp /* Pointer to the file object */ 02638 ) 02639 { 02640 FRESULT res; 02641 DWORD tim; 02642 BYTE *dir; 02643 02644 02645 res = validate(fp); /* Check validity of the object */ 02646 if (res == FR_OK) { 02647 if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ 02648 #if !_FS_TINY /* Write-back dirty buffer */ 02649 if (fp->flag & FA__DIRTY) { 02650 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02651 LEAVE_FF(fp->fs, FR_DISK_ERR); 02652 fp->flag &= ~FA__DIRTY; 02653 } 02654 #endif 02655 /* Update the directory entry */ 02656 res = move_window(fp->fs, fp->dir_sect); 02657 if (res == FR_OK) { 02658 dir = fp->dir_ptr; 02659 dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ 02660 ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ 02661 st_clust(dir, fp->sclust); /* Update start cluster */ 02662 tim = get_fattime(); /* Update updated time */ 02663 ST_DWORD(dir+DIR_WrtTime, tim); 02664 ST_WORD(dir+DIR_LstAccDate, 0); 02665 fp->flag &= ~FA__WRITTEN; 02666 fp->fs->wflag = 1; 02667 res = sync(fp->fs); 02668 } 02669 } 02670 } 02671 02672 LEAVE_FF(fp->fs, res); 02673 } 02674 02675 #endif /* !_FS_READONLY */ 02676 02677 02678 02679 02680 /*-----------------------------------------------------------------------*/ 02681 /* Close File */ 02682 /*-----------------------------------------------------------------------*/ 02683 02684 FRESULT f_close ( 02685 FIL *fp /* Pointer to the file object to be closed */ 02686 ) 02687 { 02688 FRESULT res; 02689 02690 02691 #if _FS_READONLY 02692 res = validate(fp); 02693 { 02694 #if _FS_REENTRANT 02695 FATFS *fs = fp->fs; 02696 #endif 02697 if (res == FR_OK) fp->fs = 0; /* Discard file object */ 02698 LEAVE_FF(fs, res); 02699 } 02700 #else 02701 res = f_sync(fp); /* Flush cached data */ 02702 #if _FS_LOCK 02703 if (res == FR_OK) { /* Decrement open counter */ 02704 #if _FS_REENTRANT 02705 FATFS *fs = fp->fs;; 02706 res = validate(fp); 02707 if (res == FR_OK) { 02708 res = dec_lock(fp->lockid); 02709 unlock_fs(fs, FR_OK); 02710 } 02711 #else 02712 res = dec_lock(fp->lockid); 02713 #endif 02714 } 02715 #endif 02716 if (res == FR_OK) fp->fs = 0; /* Discard file object */ 02717 return res; 02718 #endif 02719 } 02720 02721 02722 02723 02724 /*-----------------------------------------------------------------------*/ 02725 /* Current Drive/Directory Handlings */ 02726 /*-----------------------------------------------------------------------*/ 02727 02728 #if _FS_RPATH >= 1 02729 02730 FRESULT f_chdrive ( 02731 BYTE drv /* Drive number */ 02732 ) 02733 { 02734 if (drv >= _VOLUMES) return FR_INVALID_DRIVE; 02735 02736 CurrVol = drv; 02737 02738 return FR_OK; 02739 } 02740 02741 02742 02743 FRESULT f_chdir ( 02744 const TCHAR *path /* Pointer to the directory path */ 02745 ) 02746 { 02747 FRESULT res; 02748 DIR dj; 02749 DEF_NAMEBUF; 02750 02751 02752 res = chk_mounted(&path, &dj.fs, 0); 02753 if (res == FR_OK) { 02754 INIT_BUF(dj); 02755 res = follow_path(&dj, path); /* Follow the path */ 02756 FREE_BUF(); 02757 if (res == FR_OK) { /* Follow completed */ 02758 if (!dj.dir) { 02759 dj.fs->cdir = dj.sclust; /* Start directory itself */ 02760 } else { 02761 if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ 02762 dj.fs->cdir = ld_clust(dj.fs, dj.dir); 02763 else 02764 res = FR_NO_PATH; /* Reached but a file */ 02765 } 02766 } 02767 if (res == FR_NO_FILE) res = FR_NO_PATH; 02768 } 02769 02770 LEAVE_FF(dj.fs, res); 02771 } 02772 02773 02774 #if _FS_RPATH >= 2 02775 FRESULT f_getcwd ( 02776 TCHAR *path, /* Pointer to the directory path */ 02777 UINT sz_path /* Size of path */ 02778 ) 02779 { 02780 FRESULT res; 02781 DIR dj; 02782 UINT i, n; 02783 DWORD ccl; 02784 TCHAR *tp; 02785 FILINFO fno; 02786 DEF_NAMEBUF; 02787 02788 02789 *path = 0; 02790 res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */ 02791 if (res == FR_OK) { 02792 INIT_BUF(dj); 02793 i = sz_path; /* Bottom of buffer (dir stack base) */ 02794 dj.sclust = dj.fs->cdir; /* Start to follow upper dir from current dir */ 02795 while ((ccl = dj.sclust) != 0) { /* Repeat while current dir is a sub-dir */ 02796 res = dir_sdi(&dj, 1); /* Get parent dir */ 02797 if (res != FR_OK) break; 02798 res = dir_read(&dj); 02799 if (res != FR_OK) break; 02800 dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent dir */ 02801 res = dir_sdi(&dj, 0); 02802 if (res != FR_OK) break; 02803 do { /* Find the entry links to the child dir */ 02804 res = dir_read(&dj); 02805 if (res != FR_OK) break; 02806 if (ccl == ld_clust(dj.fs, dj.dir)) break; /* Found the entry */ 02807 res = dir_next(&dj, 0); 02808 } while (res == FR_OK); 02809 if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ 02810 if (res != FR_OK) break; 02811 #if _USE_LFN 02812 fno.lfname = path; 02813 fno.lfsize = i; 02814 #endif 02815 get_fileinfo(&dj, &fno); /* Get the dir name and push it to the buffer */ 02816 tp = fno.fname; 02817 if (_USE_LFN && *path) tp = path; 02818 for (n = 0; tp[n]; n++) ; 02819 if (i < n + 3) { 02820 res = FR_NOT_ENOUGH_CORE; break; 02821 } 02822 while (n) path[--i] = tp[--n]; 02823 path[--i] = '/'; 02824 } 02825 tp = path; 02826 if (res == FR_OK) { 02827 *tp++ = '0' + CurrVol; /* Put drive number */ 02828 *tp++ = ':'; 02829 if (i == sz_path) { /* Root-dir */ 02830 *tp++ = '/'; 02831 } else { /* Sub-dir */ 02832 do /* Add stacked path str */ 02833 *tp++ = path[i++]; 02834 while (i < sz_path); 02835 } 02836 } 02837 *tp = 0; 02838 FREE_BUF(); 02839 } 02840 02841 LEAVE_FF(dj.fs, res); 02842 } 02843 #endif /* _FS_RPATH >= 2 */ 02844 #endif /* _FS_RPATH >= 1 */ 02845 02846 02847 02848 #if _FS_MINIMIZE <= 2 02849 /*-----------------------------------------------------------------------*/ 02850 /* Seek File R/W Pointer */ 02851 /*-----------------------------------------------------------------------*/ 02852 02853 FRESULT f_lseek ( 02854 FIL *fp, /* Pointer to the file object */ 02855 DWORD ofs /* File pointer from top of file */ 02856 ) 02857 { 02858 FRESULT res; 02859 02860 02861 res = validate(fp); /* Check validity of the object */ 02862 if (res != FR_OK) LEAVE_FF(fp->fs, res); 02863 if (fp->flag & FA__ERROR) /* Check abort flag */ 02864 LEAVE_FF(fp->fs, FR_INT_ERR); 02865 02866 #if _USE_FASTSEEK 02867 if (fp->cltbl) { /* Fast seek */ 02868 DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; 02869 02870 if (ofs == CREATE_LINKMAP) { /* Create CLMT */ 02871 tbl = fp->cltbl; 02872 tlen = *tbl++; ulen = 2; /* Given table size and required table size */ 02873 cl = fp->sclust; /* Top of the chain */ 02874 if (cl) { 02875 do { 02876 /* Get a fragment */ 02877 tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ 02878 do { 02879 pcl = cl; ncl++; 02880 cl = get_fat(fp->fs, cl); 02881 if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); 02882 if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02883 } while (cl == pcl + 1); 02884 if (ulen <= tlen) { /* Store the length and top of the fragment */ 02885 *tbl++ = ncl; *tbl++ = tcl; 02886 } 02887 } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ 02888 } 02889 *fp->cltbl = ulen; /* Number of items used */ 02890 if (ulen <= tlen) 02891 *tbl = 0; /* Terminate table */ 02892 else 02893 res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ 02894 02895 } else { /* Fast seek */ 02896 if (ofs > fp->fsize) /* Clip offset at the file size */ 02897 ofs = fp->fsize; 02898 fp->fptr = ofs; /* Set file pointer */ 02899 if (ofs) { 02900 fp->clust = clmt_clust(fp, ofs - 1); 02901 dsc = clust2sect(fp->fs, fp->clust); 02902 if (!dsc) ABORT(fp->fs, FR_INT_ERR); 02903 dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); 02904 if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ 02905 #if !_FS_TINY 02906 #if !_FS_READONLY 02907 if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ 02908 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02909 ABORT(fp->fs, FR_DISK_ERR); 02910 fp->flag &= ~FA__DIRTY; 02911 } 02912 #endif 02913 if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ 02914 ABORT(fp->fs, FR_DISK_ERR); 02915 #endif 02916 fp->dsect = dsc; 02917 } 02918 } 02919 } 02920 } else 02921 #endif 02922 02923 /* Normal Seek */ 02924 { 02925 DWORD clst, bcs, nsect, ifptr; 02926 02927 if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ 02928 #if !_FS_READONLY 02929 && !(fp->flag & FA_WRITE) 02930 #endif 02931 ) ofs = fp->fsize; 02932 02933 ifptr = fp->fptr; 02934 fp->fptr = nsect = 0; 02935 if (ofs) { 02936 bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ 02937 if (ifptr > 0 && 02938 (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ 02939 fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ 02940 ofs -= fp->fptr; 02941 clst = fp->clust; 02942 } else { /* When seek to back cluster, */ 02943 clst = fp->sclust; /* start from the first cluster */ 02944 #if !_FS_READONLY 02945 if (clst == 0) { /* If no cluster chain, create a new chain */ 02946 clst = create_chain(fp->fs, 0); 02947 if (clst == 1) ABORT(fp->fs, FR_INT_ERR); 02948 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02949 fp->sclust = clst; 02950 } 02951 #endif 02952 fp->clust = clst; 02953 } 02954 if (clst != 0) { 02955 while (ofs > bcs) { /* Cluster following loop */ 02956 #if !_FS_READONLY 02957 if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ 02958 clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ 02959 if (clst == 0) { /* When disk gets full, clip file size */ 02960 ofs = bcs; break; 02961 } 02962 } else 02963 #endif 02964 clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ 02965 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 02966 if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); 02967 fp->clust = clst; 02968 fp->fptr += bcs; 02969 ofs -= bcs; 02970 } 02971 fp->fptr += ofs; 02972 if (ofs % SS(fp->fs)) { 02973 nsect = clust2sect(fp->fs, clst); /* Current sector */ 02974 if (!nsect) ABORT(fp->fs, FR_INT_ERR); 02975 nsect += ofs / SS(fp->fs); 02976 } 02977 } 02978 } 02979 if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ 02980 #if !_FS_TINY 02981 #if !_FS_READONLY 02982 if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ 02983 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) 02984 ABORT(fp->fs, FR_DISK_ERR); 02985 fp->flag &= ~FA__DIRTY; 02986 } 02987 #endif 02988 if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ 02989 ABORT(fp->fs, FR_DISK_ERR); 02990 #endif 02991 fp->dsect = nsect; 02992 } 02993 #if !_FS_READONLY 02994 if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ 02995 fp->fsize = fp->fptr; 02996 fp->flag |= FA__WRITTEN; 02997 } 02998 #endif 02999 } 03000 03001 LEAVE_FF(fp->fs, res); 03002 } 03003 03004 03005 03006 #if _FS_MINIMIZE <= 1 03007 /*-----------------------------------------------------------------------*/ 03008 /* Create a Directory Object */ 03009 /*-----------------------------------------------------------------------*/ 03010 03011 FRESULT f_opendir ( 03012 FATFS_DIR *dj, /* Pointer to directory object to create */ 03013 const TCHAR *path /* Pointer to the directory path */ 03014 ) 03015 { 03016 FRESULT res; 03017 FATFS *fs; 03018 DEF_NAMEBUF; 03019 03020 03021 if (!dj) return FR_INVALID_OBJECT; 03022 03023 res = chk_mounted(&path, &dj->fs, 0); 03024 fs = dj->fs; 03025 if (res == FR_OK) { 03026 INIT_BUF(*dj); 03027 res = follow_path(dj, path); /* Follow the path to the directory */ 03028 FREE_BUF(); 03029 if (res == FR_OK) { /* Follow completed */ 03030 if (dj->dir) { /* It is not the root dir */ 03031 if (dj->dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ 03032 dj->sclust = ld_clust(fs, dj->dir); 03033 } else { /* The object is not a directory */ 03034 res = FR_NO_PATH; 03035 } 03036 } 03037 if (res == FR_OK) { 03038 dj->id = fs->id; 03039 res = dir_sdi(dj, 0); /* Rewind dir */ 03040 } 03041 } 03042 if (res == FR_NO_FILE) res = FR_NO_PATH; 03043 if (res != FR_OK) dj->fs = 0; /* Invalidate the dir object if function faild */ 03044 } else { 03045 dj->fs = 0; 03046 } 03047 03048 LEAVE_FF(fs, res); 03049 } 03050 03051 03052 03053 03054 /*-----------------------------------------------------------------------*/ 03055 /* Read Directory Entry in Sequence */ 03056 /*-----------------------------------------------------------------------*/ 03057 03058 FRESULT f_readdir ( 03059 FATFS_DIR *dj, /* Pointer to the open directory object */ 03060 FILINFO *fno /* Pointer to file information to return */ 03061 ) 03062 { 03063 FRESULT res; 03064 DEF_NAMEBUF; 03065 03066 03067 res = validate(dj); /* Check validity of the object */ 03068 if (res == FR_OK) { 03069 if (!fno) { 03070 res = dir_sdi(dj, 0); /* Rewind the directory object */ 03071 } else { 03072 INIT_BUF(*dj); 03073 res = dir_read(dj); /* Read an directory item */ 03074 if (res == FR_NO_FILE) { /* Reached end of dir */ 03075 dj->sect = 0; 03076 res = FR_OK; 03077 } 03078 if (res == FR_OK) { /* A valid entry is found */ 03079 get_fileinfo(dj, fno); /* Get the object information */ 03080 res = dir_next(dj, 0); /* Increment index for next */ 03081 if (res == FR_NO_FILE) { 03082 dj->sect = 0; 03083 res = FR_OK; 03084 } 03085 } 03086 FREE_BUF(); 03087 } 03088 } 03089 03090 LEAVE_FF(dj->fs, res); 03091 } 03092 03093 03094 03095 #if _FS_MINIMIZE == 0 03096 /*-----------------------------------------------------------------------*/ 03097 /* Get File Status */ 03098 /*-----------------------------------------------------------------------*/ 03099 03100 FRESULT f_stat ( 03101 const TCHAR *path, /* Pointer to the file path */ 03102 FILINFO *fno /* Pointer to file information to return */ 03103 ) 03104 { 03105 FRESULT res; 03106 FATFS_DIR dj; 03107 DEF_NAMEBUF; 03108 03109 03110 res = chk_mounted(&path, &dj.fs, 0); 03111 if (res == FR_OK) { 03112 INIT_BUF(dj); 03113 res = follow_path(&dj, path); /* Follow the file path */ 03114 if (res == FR_OK) { /* Follow completed */ 03115 if (dj.dir) /* Found an object */ 03116 get_fileinfo(&dj, fno); 03117 else /* It is root dir */ 03118 res = FR_INVALID_NAME; 03119 } 03120 FREE_BUF(); 03121 } 03122 03123 LEAVE_FF(dj.fs, res); 03124 } 03125 03126 03127 03128 #if !_FS_READONLY 03129 /*-----------------------------------------------------------------------*/ 03130 /* Get Number of Free Clusters */ 03131 /*-----------------------------------------------------------------------*/ 03132 03133 FRESULT f_getfree ( 03134 const TCHAR *path, /* Pointer to the logical drive number (root dir) */ 03135 DWORD *nclst, /* Pointer to the variable to return number of free clusters */ 03136 FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ 03137 ) 03138 { 03139 FRESULT res; 03140 FATFS *fs; 03141 DWORD n, clst, sect, stat; 03142 UINT i; 03143 BYTE fat, *p; 03144 03145 03146 /* Get drive number */ 03147 res = chk_mounted(&path, fatfs, 0); 03148 fs = *fatfs; 03149 if (res == FR_OK) { 03150 /* If free_clust is valid, return it without full cluster scan */ 03151 if (fs->free_clust <= fs->n_fatent - 2) { 03152 *nclst = fs->free_clust; 03153 } else { 03154 /* Get number of free clusters */ 03155 fat = fs->fs_type; 03156 n = 0; 03157 if (fat == FS_FAT12) { 03158 clst = 2; 03159 do { 03160 stat = get_fat(fs, clst); 03161 if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } 03162 if (stat == 1) { res = FR_INT_ERR; break; } 03163 if (stat == 0) n++; 03164 } while (++clst < fs->n_fatent); 03165 } else { 03166 clst = fs->n_fatent; 03167 sect = fs->fatbase; 03168 i = 0; p = 0; 03169 do { 03170 if (!i) { 03171 res = move_window(fs, sect++); 03172 if (res != FR_OK) break; 03173 p = fs->win; 03174 i = SS(fs); 03175 } 03176 if (fat == FS_FAT16) { 03177 if (LD_WORD(p) == 0) n++; 03178 p += 2; i -= 2; 03179 } else { 03180 if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; 03181 p += 4; i -= 4; 03182 } 03183 } while (--clst); 03184 } 03185 fs->free_clust = n; 03186 if (fat == FS_FAT32) fs->fsi_flag = 1; 03187 *nclst = n; 03188 } 03189 } 03190 LEAVE_FF(fs, res); 03191 } 03192 03193 03194 03195 03196 /*-----------------------------------------------------------------------*/ 03197 /* Truncate File */ 03198 /*-----------------------------------------------------------------------*/ 03199 03200 FRESULT f_truncate ( 03201 FIL *fp /* Pointer to the file object */ 03202 ) 03203 { 03204 FRESULT res; 03205 DWORD ncl; 03206 03207 03208 if (!fp) return FR_INVALID_OBJECT; 03209 03210 res = validate(fp); /* Check validity of the object */ 03211 if (res == FR_OK) { 03212 if (fp->flag & FA__ERROR) { /* Check abort flag */ 03213 res = FR_INT_ERR; 03214 } else { 03215 if (!(fp->flag & FA_WRITE)) /* Check access mode */ 03216 res = FR_DENIED; 03217 } 03218 } 03219 if (res == FR_OK) { 03220 if (fp->fsize > fp->fptr) { 03221 fp->fsize = fp->fptr; /* Set file size to current R/W point */ 03222 fp->flag |= FA__WRITTEN; 03223 if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ 03224 res = remove_chain(fp->fs, fp->sclust); 03225 fp->sclust = 0; 03226 } else { /* When truncate a part of the file, remove remaining clusters */ 03227 ncl = get_fat(fp->fs, fp->clust); 03228 res = FR_OK; 03229 if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; 03230 if (ncl == 1) res = FR_INT_ERR; 03231 if (res == FR_OK && ncl < fp->fs->n_fatent) { 03232 res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); 03233 if (res == FR_OK) res = remove_chain(fp->fs, ncl); 03234 } 03235 } 03236 } 03237 if (res != FR_OK) fp->flag |= FA__ERROR; 03238 } 03239 03240 LEAVE_FF(fp->fs, res); 03241 } 03242 03243 03244 03245 03246 /*-----------------------------------------------------------------------*/ 03247 /* Delete a File or Directory */ 03248 /*-----------------------------------------------------------------------*/ 03249 03250 FRESULT f_unlink ( 03251 const TCHAR *path /* Pointer to the file or directory path */ 03252 ) 03253 { 03254 FRESULT res; 03255 FATFS_DIR dj, sdj; 03256 BYTE *dir; 03257 DWORD dclst; 03258 DEF_NAMEBUF; 03259 03260 03261 res = chk_mounted(&path, &dj.fs, 1); 03262 if (res == FR_OK) { 03263 INIT_BUF(dj); 03264 res = follow_path(&dj, path); /* Follow the file path */ 03265 if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) 03266 res = FR_INVALID_NAME; /* Cannot remove dot entry */ 03267 #if _FS_LOCK 03268 if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open file */ 03269 #endif 03270 if (res == FR_OK) { /* The object is accessible */ 03271 dir = dj.dir; 03272 if (!dir) { 03273 res = FR_INVALID_NAME; /* Cannot remove the start directory */ 03274 } else { 03275 if (dir[DIR_Attr] & AM_RDO) 03276 res = FR_DENIED; /* Cannot remove R/O object */ 03277 } 03278 dclst = ld_clust(dj.fs, dir); 03279 if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */ 03280 if (dclst < 2) { 03281 res = FR_INT_ERR; 03282 } else { 03283 mem_cpy(&sdj, &dj, sizeof (FATFS_DIR)); /* Check if the sub-dir is empty or not */ 03284 sdj.sclust = dclst; 03285 res = dir_sdi(&sdj, 2); /* Exclude dot entries */ 03286 if (res == FR_OK) { 03287 res = dir_read(&sdj); 03288 if (res == FR_OK /* Not empty dir */ 03289 #if _FS_RPATH 03290 || dclst == dj.fs->cdir /* Current dir */ 03291 #endif 03292 ) res = FR_DENIED; 03293 if (res == FR_NO_FILE) res = FR_OK; /* Empty */ 03294 } 03295 } 03296 } 03297 if (res == FR_OK) { 03298 res = dir_remove(&dj); /* Remove the directory entry */ 03299 if (res == FR_OK) { 03300 if (dclst) /* Remove the cluster chain if exist */ 03301 res = remove_chain(dj.fs, dclst); 03302 if (res == FR_OK) res = sync(dj.fs); 03303 } 03304 } 03305 } 03306 FREE_BUF(); 03307 } 03308 LEAVE_FF(dj.fs, res); 03309 } 03310 03311 03312 03313 03314 /*-----------------------------------------------------------------------*/ 03315 /* Create a Directory */ 03316 /*-----------------------------------------------------------------------*/ 03317 03318 FRESULT f_mkdir ( 03319 const TCHAR *path /* Pointer to the directory path */ 03320 ) 03321 { 03322 FRESULT res; 03323 FATFS_DIR dj; 03324 BYTE *dir, n; 03325 DWORD dsc, dcl, pcl, tim = get_fattime(); 03326 DEF_NAMEBUF; 03327 03328 03329 res = chk_mounted(&path, &dj.fs, 1); 03330 if (res == FR_OK) { 03331 INIT_BUF(dj); 03332 res = follow_path(&dj, path); /* Follow the file path */ 03333 if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ 03334 if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) 03335 res = FR_INVALID_NAME; 03336 if (res == FR_NO_FILE) { /* Can create a new directory */ 03337 dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ 03338 res = FR_OK; 03339 if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ 03340 if (dcl == 1) res = FR_INT_ERR; 03341 if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; 03342 if (res == FR_OK) /* Flush FAT */ 03343 res = move_window(dj.fs, 0); 03344 if (res == FR_OK) { /* Initialize the new directory table */ 03345 dsc = clust2sect(dj.fs, dcl); 03346 dir = dj.fs->win; 03347 mem_set(dir, 0, SS(dj.fs)); 03348 mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ 03349 dir[DIR_Name] = '.'; 03350 dir[DIR_Attr] = AM_DIR; 03351 ST_DWORD(dir+DIR_WrtTime, tim); 03352 st_clust(dir, dcl); 03353 mem_cpy(dir+SZ_DIR, dir, SZ_DIR); /* Create ".." entry */ 03354 dir[33] = '.'; pcl = dj.sclust; 03355 if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) 03356 pcl = 0; 03357 st_clust(dir+SZ_DIR, pcl); 03358 for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ 03359 dj.fs->winsect = dsc++; 03360 dj.fs->wflag = 1; 03361 res = move_window(dj.fs, 0); 03362 if (res != FR_OK) break; 03363 mem_set(dir, 0, SS(dj.fs)); 03364 } 03365 } 03366 if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ 03367 if (res != FR_OK) { 03368 remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ 03369 } else { 03370 dir = dj.dir; 03371 dir[DIR_Attr] = AM_DIR; /* Attribute */ 03372 ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */ 03373 st_clust(dir, dcl); /* Table start cluster */ 03374 dj.fs->wflag = 1; 03375 res = sync(dj.fs); 03376 } 03377 } 03378 FREE_BUF(); 03379 } 03380 03381 LEAVE_FF(dj.fs, res); 03382 } 03383 03384 03385 03386 03387 /*-----------------------------------------------------------------------*/ 03388 /* Change Attribute */ 03389 /*-----------------------------------------------------------------------*/ 03390 03391 FRESULT f_chmod ( 03392 const TCHAR *path, /* Pointer to the file path */ 03393 BYTE value, /* Attribute bits */ 03394 BYTE mask /* Attribute mask to change */ 03395 ) 03396 { 03397 FRESULT res; 03398 FATFS_DIR dj; 03399 BYTE *dir; 03400 DEF_NAMEBUF; 03401 03402 03403 res = chk_mounted(&path, &dj.fs, 1); 03404 if (res == FR_OK) { 03405 INIT_BUF(dj); 03406 res = follow_path(&dj, path); /* Follow the file path */ 03407 FREE_BUF(); 03408 if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) 03409 res = FR_INVALID_NAME; 03410 if (res == FR_OK) { 03411 dir = dj.dir; 03412 if (!dir) { /* Is it a root directory? */ 03413 res = FR_INVALID_NAME; 03414 } else { /* File or sub directory */ 03415 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ 03416 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ 03417 dj.fs->wflag = 1; 03418 res = sync(dj.fs); 03419 } 03420 } 03421 } 03422 03423 LEAVE_FF(dj.fs, res); 03424 } 03425 03426 03427 03428 03429 /*-----------------------------------------------------------------------*/ 03430 /* Change Timestamp */ 03431 /*-----------------------------------------------------------------------*/ 03432 03433 FRESULT f_utime ( 03434 const TCHAR *path, /* Pointer to the file/directory name */ 03435 const FILINFO *fno /* Pointer to the time stamp to be set */ 03436 ) 03437 { 03438 FRESULT res; 03439 FATFS_DIR dj; 03440 BYTE *dir; 03441 DEF_NAMEBUF; 03442 03443 03444 res = chk_mounted(&path, &dj.fs, 1); 03445 if (res == FR_OK) { 03446 INIT_BUF(dj); 03447 res = follow_path(&dj, path); /* Follow the file path */ 03448 FREE_BUF(); 03449 if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) 03450 res = FR_INVALID_NAME; 03451 if (res == FR_OK) { 03452 dir = dj.dir; 03453 if (!dir) { /* Root directory */ 03454 res = FR_INVALID_NAME; 03455 } else { /* File or sub-directory */ 03456 ST_WORD(dir+DIR_WrtTime, fno->ftime); 03457 ST_WORD(dir+DIR_WrtDate, fno->fdate); 03458 dj.fs->wflag = 1; 03459 res = sync(dj.fs); 03460 } 03461 } 03462 } 03463 03464 LEAVE_FF(dj.fs, res); 03465 } 03466 03467 03468 03469 03470 /*-----------------------------------------------------------------------*/ 03471 /* Rename File/Directory */ 03472 /*-----------------------------------------------------------------------*/ 03473 03474 FRESULT f_rename ( 03475 const TCHAR *path_old, /* Pointer to the old name */ 03476 const TCHAR *path_new /* Pointer to the new name */ 03477 ) 03478 { 03479 FRESULT res; 03480 FATFS_DIR djo, djn; 03481 BYTE buf[21], *dir; 03482 DWORD dw; 03483 DEF_NAMEBUF; 03484 03485 03486 res = chk_mounted(&path_old, &djo.fs, 1); 03487 if (res == FR_OK) { 03488 djn.fs = djo.fs; 03489 INIT_BUF(djo); 03490 res = follow_path(&djo, path_old); /* Check old object */ 03491 if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT)) 03492 res = FR_INVALID_NAME; 03493 #if _FS_LOCK 03494 if (res == FR_OK) res = chk_lock(&djo, 2); 03495 #endif 03496 if (res == FR_OK) { /* Old object is found */ 03497 if (!djo.dir) { /* Is root dir? */ 03498 res = FR_NO_FILE; 03499 } else { 03500 mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */ 03501 mem_cpy(&djn, &djo, sizeof (FATFS_DIR)); /* Check new object */ 03502 res = follow_path(&djn, path_new); 03503 if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ 03504 if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ 03505 /* Start critical section that an interruption or error can cause cross-link */ 03506 res = dir_register(&djn); /* Register the new entry */ 03507 if (res == FR_OK) { 03508 dir = djn.dir; /* Copy object information except for name */ 03509 mem_cpy(dir+13, buf+2, 19); 03510 dir[DIR_Attr] = buf[0] | AM_ARC; 03511 djo.fs->wflag = 1; 03512 if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { /* Update .. entry in the directory if needed */ 03513 dw = clust2sect(djo.fs, ld_clust(djo.fs, dir)); 03514 if (!dw) { 03515 res = FR_INT_ERR; 03516 } else { 03517 res = move_window(djo.fs, dw); 03518 dir = djo.fs->win+SZ_DIR; /* .. entry */ 03519 if (res == FR_OK && dir[1] == '.') { 03520 dw = (djo.fs->fs_type == FS_FAT32 && djn.sclust == djo.fs->dirbase) ? 0 : djn.sclust; 03521 st_clust(dir, dw); 03522 djo.fs->wflag = 1; 03523 } 03524 } 03525 } 03526 if (res == FR_OK) { 03527 res = dir_remove(&djo); /* Remove old entry */ 03528 if (res == FR_OK) 03529 res = sync(djo.fs); 03530 } 03531 } 03532 /* End critical section */ 03533 } 03534 } 03535 } 03536 FREE_BUF(); 03537 } 03538 LEAVE_FF(djo.fs, res); 03539 } 03540 03541 #endif /* !_FS_READONLY */ 03542 #endif /* _FS_MINIMIZE == 0 */ 03543 #endif /* _FS_MINIMIZE <= 1 */ 03544 #endif /* _FS_MINIMIZE <= 2 */ 03545 03546 03547 03548 /*-----------------------------------------------------------------------*/ 03549 /* Forward data to the stream directly (available on only tiny cfg) */ 03550 /*-----------------------------------------------------------------------*/ 03551 #if _USE_FORWARD && _FS_TINY 03552 03553 FRESULT f_forward ( 03554 FIL *fp, /* Pointer to the file object */ 03555 UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ 03556 UINT btr, /* Number of bytes to forward */ 03557 UINT *bf /* Pointer to number of bytes forwarded */ 03558 ) 03559 { 03560 FRESULT res; 03561 DWORD remain, clst, sect; 03562 UINT rcnt; 03563 BYTE csect; 03564 03565 03566 *bf = 0; /* Clear transfer byte counter */ 03567 03568 if (!fp) return FR_INVALID_OBJECT; 03569 03570 res = validate(fp); /* Check validity of the object */ 03571 if (res != FR_OK) LEAVE_FF(fp->fs, res); 03572 if (fp->flag & FA__ERROR) /* Check error flag */ 03573 LEAVE_FF(fp->fs, FR_INT_ERR); 03574 if (!(fp->flag & FA_READ)) /* Check access mode */ 03575 LEAVE_FF(fp->fs, FR_DENIED); 03576 03577 remain = fp->fsize - fp->fptr; 03578 if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ 03579 03580 for ( ; btr && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ 03581 fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { 03582 csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ 03583 if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ 03584 if (!csect) { /* On the cluster boundary? */ 03585 clst = (fp->fptr == 0) ? /* On the top of the file? */ 03586 fp->sclust : get_fat(fp->fs, fp->clust); 03587 if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); 03588 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); 03589 fp->clust = clst; /* Update current cluster */ 03590 } 03591 } 03592 sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ 03593 if (!sect) ABORT(fp->fs, FR_INT_ERR); 03594 sect += csect; 03595 if (move_window(fp->fs, sect)) /* Move sector window */ 03596 ABORT(fp->fs, FR_DISK_ERR); 03597 fp->dsect = sect; 03598 rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ 03599 if (rcnt > btr) rcnt = btr; 03600 rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); 03601 if (!rcnt) ABORT(fp->fs, FR_INT_ERR); 03602 } 03603 03604 LEAVE_FF(fp->fs, FR_OK); 03605 } 03606 #endif /* _USE_FORWARD */ 03607 03608 03609 03610 #if _USE_MKFS && !_FS_READONLY 03611 /*-----------------------------------------------------------------------*/ 03612 /* Create File System on the Drive */ 03613 /*-----------------------------------------------------------------------*/ 03614 #define N_ROOTDIR 512 /* Number of root dir entries for FAT12/16 */ 03615 #define N_FATS 1 /* Number of FAT copies (1 or 2) */ 03616 03617 03618 FRESULT f_mkfs ( 03619 BYTE drv, /* Logical drive number */ 03620 BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ 03621 UINT au /* Allocation unit size [bytes] */ 03622 ) 03623 { 03624 static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; 03625 static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; 03626 BYTE fmt, md, sys, *tbl, pdrv, part; 03627 DWORD n_clst, vs, n, wsect; 03628 UINT i; 03629 DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ 03630 DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ 03631 FATFS *fs; 03632 DSTATUS stat; 03633 03634 03635 /* Check mounted drive and clear work area */ 03636 if (drv >= _VOLUMES) return FR_INVALID_DRIVE; 03637 if (sfd > 1) return FR_INVALID_PARAMETER; 03638 if (au & (au - 1)) return FR_INVALID_PARAMETER; 03639 fs = FatFs[drv]; 03640 if (!fs) return FR_NOT_ENABLED; 03641 fs->fs_type = 0; 03642 pdrv = LD2PD(drv); /* Physical drive */ 03643 part = LD2PT(drv); /* Partition (0:auto detect, 1-4:get from partition table)*/ 03644 03645 /* Get disk statics */ 03646 stat = disk_initialize(pdrv); 03647 if (stat & STA_NOINIT) return FR_NOT_READY; 03648 if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; 03649 #if _MAX_SS != 512 /* Get disk sector size */ 03650 if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) 03651 return FR_DISK_ERR; 03652 #endif 03653 if (_MULTI_PARTITION && part) { 03654 /* Get partition information from partition table in the MBR */ 03655 if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; 03656 if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; 03657 tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; 03658 if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ 03659 b_vol = LD_DWORD(tbl+8); /* Volume start sector */ 03660 n_vol = LD_DWORD(tbl+12); /* Volume size */ 03661 } else { 03662 /* Create a partition in this function */ 03663 if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) 03664 return FR_DISK_ERR; 03665 b_vol = (sfd) ? 0 : 63; /* Volume start sector */ 03666 n_vol -= b_vol; /* Volume size */ 03667 } 03668 03669 if (!au) { /* AU auto selection */ 03670 vs = n_vol / (2000 / (SS(fs) / 512)); 03671 for (i = 0; vs < vst[i]; i++) ; 03672 au = cst[i]; 03673 } 03674 au /= SS(fs); /* Number of sectors per cluster */ 03675 if (au == 0) au = 1; 03676 if (au > 128) au = 128; 03677 03678 /* Pre-compute number of clusters and FAT sub-type */ 03679 n_clst = n_vol / au; 03680 fmt = FS_FAT12; 03681 if (n_clst >= MIN_FAT16) fmt = FS_FAT16; 03682 if (n_clst >= MIN_FAT32) fmt = FS_FAT32; 03683 03684 /* Determine offset and size of FAT structure */ 03685 if (fmt == FS_FAT32) { 03686 n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); 03687 n_rsv = 32; 03688 n_dir = 0; 03689 } else { 03690 n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; 03691 n_fat = (n_fat + SS(fs) - 1) / SS(fs); 03692 n_rsv = 1; 03693 n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs); 03694 } 03695 b_fat = b_vol + n_rsv; /* FAT area start sector */ 03696 b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ 03697 b_data = b_dir + n_dir; /* Data area start sector */ 03698 if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ 03699 03700 /* Align data start sector to erase block boundary (for flash memory media) */ 03701 if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; 03702 n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ 03703 n = (n - b_data) / N_FATS; 03704 if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ 03705 n_rsv += n; 03706 b_fat += n; 03707 } else { /* FAT12/16: Expand FAT size */ 03708 n_fat += n; 03709 } 03710 03711 /* Determine number of clusters and final check of validity of the FAT sub-type */ 03712 n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; 03713 if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) 03714 || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) 03715 return FR_MKFS_ABORTED; 03716 03717 switch (fmt) { /* Determine system ID for partition table */ 03718 case FS_FAT12: sys = 0x01; break; 03719 case FS_FAT16: sys = (n_vol < 0x10000) ? 0x04 : 0x06; break; 03720 default: sys = 0x0C; 03721 } 03722 03723 if (_MULTI_PARTITION && part) { 03724 /* Update system ID in the partition table */ 03725 tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; 03726 tbl[4] = sys; 03727 if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; 03728 md = 0xF8; 03729 } else { 03730 if (sfd) { /* No partition table (SFD) */ 03731 md = 0xF0; 03732 } else { /* Create partition table (FDISK) */ 03733 mem_set(fs->win, 0, SS(fs)); 03734 tbl = fs->win+MBR_Table; /* Create partition table for single partition in the drive */ 03735 tbl[1] = 1; /* Partition start head */ 03736 tbl[2] = 1; /* Partition start sector */ 03737 tbl[3] = 0; /* Partition start cylinder */ 03738 tbl[4] = sys; /* System type */ 03739 tbl[5] = 254; /* Partition end head */ 03740 n = (b_vol + n_vol) / 63 / 255; 03741 tbl[6] = (BYTE)((n >> 2) | 63); /* Partition end sector */ 03742 tbl[7] = (BYTE)n; /* End cylinder */ 03743 ST_DWORD(tbl+8, 63); /* Partition start in LBA */ 03744 ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */ 03745 ST_WORD(fs->win+BS_55AA, 0xAA55); /* MBR signature */ 03746 if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR sector */ 03747 return FR_DISK_ERR; 03748 md = 0xF8; 03749 } 03750 } 03751 03752 /* Create BPB in the VBR */ 03753 tbl = fs->win; /* Clear sector */ 03754 mem_set(tbl, 0, SS(fs)); 03755 mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ 03756 i = SS(fs); /* Sector size */ 03757 ST_WORD(tbl+BPB_BytsPerSec, i); 03758 tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ 03759 ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ 03760 tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ 03761 i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of rootdir entries */ 03762 ST_WORD(tbl+BPB_RootEntCnt, i); 03763 if (n_vol < 0x10000) { /* Number of total sectors */ 03764 ST_WORD(tbl+BPB_TotSec16, n_vol); 03765 } else { 03766 ST_DWORD(tbl+BPB_TotSec32, n_vol); 03767 } 03768 tbl[BPB_Media] = md; /* Media descriptor */ 03769 ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ 03770 ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ 03771 ST_DWORD(tbl+BPB_HiddSec, b_vol); /* Hidden sectors */ 03772 n = get_fattime(); /* Use current time as VSN */ 03773 if (fmt == FS_FAT32) { 03774 ST_DWORD(tbl+BS_VolID32, n); /* VSN */ 03775 ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */ 03776 ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */ 03777 ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (VBR+1) */ 03778 ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */ 03779 tbl[BS_DrvNum32] = 0x80; /* Drive number */ 03780 tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ 03781 mem_cpy(tbl+BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ 03782 } else { 03783 ST_DWORD(tbl+BS_VolID, n); /* VSN */ 03784 ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors per FAT */ 03785 tbl[BS_DrvNum] = 0x80; /* Drive number */ 03786 tbl[BS_BootSig] = 0x29; /* Extended boot signature */ 03787 mem_cpy(tbl+BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ 03788 } 03789 ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ 03790 if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */ 03791 return FR_DISK_ERR; 03792 if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR+6) */ 03793 disk_write(pdrv, tbl, b_vol + 6, 1); 03794 03795 /* Initialize FAT area */ 03796 wsect = b_fat; 03797 for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ 03798 mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ 03799 n = md; /* Media descriptor byte */ 03800 if (fmt != FS_FAT32) { 03801 n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; 03802 ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */ 03803 } else { 03804 n |= 0xFFFFFF00; 03805 ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT32) */ 03806 ST_DWORD(tbl+4, 0xFFFFFFFF); 03807 ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ 03808 } 03809 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) 03810 return FR_DISK_ERR; 03811 mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ 03812 for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ 03813 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) 03814 return FR_DISK_ERR; 03815 } 03816 } 03817 03818 /* Initialize root directory */ 03819 i = (fmt == FS_FAT32) ? au : n_dir; 03820 do { 03821 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) 03822 return FR_DISK_ERR; 03823 } while (--i); 03824 03825 #if _USE_ERASE /* Erase data area if needed */ 03826 { 03827 DWORD eb[2]; 03828 03829 eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; 03830 disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb); 03831 } 03832 #endif 03833 03834 /* Create FSInfo if needed */ 03835 if (fmt == FS_FAT32) { 03836 ST_DWORD(tbl+FSI_LeadSig, 0x41615252); 03837 ST_DWORD(tbl+FSI_StrucSig, 0x61417272); 03838 ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); /* Number of free clusters */ 03839 ST_DWORD(tbl+FSI_Nxt_Free, 2); /* Last allocated cluster# */ 03840 ST_WORD(tbl+BS_55AA, 0xAA55); 03841 disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR+1) */ 03842 disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR+7) */ 03843 } 03844 03845 return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; 03846 } 03847 03848 03849 #if _MULTI_PARTITION == 2 03850 /*-----------------------------------------------------------------------*/ 03851 /* Divide Physical Drive */ 03852 /*-----------------------------------------------------------------------*/ 03853 03854 FRESULT f_fdisk ( 03855 BYTE pdrv, /* Physical drive number */ 03856 const DWORD szt[], /* Pointer to the size table for each partitions */ 03857 void* work /* Pointer to the working buffer */ 03858 ) 03859 { 03860 UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; 03861 BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; 03862 DSTATUS stat; 03863 DWORD sz_disk, sz_part, s_part; 03864 03865 03866 stat = disk_initialize(pdrv); 03867 if (stat & STA_NOINIT) return FR_NOT_READY; 03868 if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; 03869 if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; 03870 03871 /* Determine CHS in the table regardless of the drive geometry */ 03872 for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; 03873 if (n == 256) n--; 03874 e_hd = n - 1; 03875 sz_cyl = 63 * n; 03876 tot_cyl = sz_disk / sz_cyl; 03877 03878 /* Create partition table */ 03879 mem_set(buf, 0, _MAX_SS); 03880 p = buf + MBR_Table; b_cyl = 0; 03881 for (i = 0; i < 4; i++, p += SZ_PTE) { 03882 p_cyl = (szt[i] <= 100) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; 03883 if (!p_cyl) continue; 03884 s_part = (DWORD)sz_cyl * b_cyl; 03885 sz_part = (DWORD)sz_cyl * p_cyl; 03886 if (i == 0) { /* Exclude first track of cylinder 0 */ 03887 s_hd = 1; 03888 s_part += 63; sz_part -= 63; 03889 } else { 03890 s_hd = 0; 03891 } 03892 e_cyl = b_cyl + p_cyl - 1; 03893 if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; 03894 03895 /* Set partition table */ 03896 p[1] = s_hd; /* Start head */ 03897 p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ 03898 p[3] = (BYTE)b_cyl; /* Start cylinder */ 03899 p[4] = 0x06; /* System type (temporary setting) */ 03900 p[5] = e_hd; /* End head */ 03901 p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ 03902 p[7] = (BYTE)e_cyl; /* End cylinder */ 03903 ST_DWORD(p + 8, s_part); /* Start sector in LBA */ 03904 ST_DWORD(p + 12, sz_part); /* Partition size */ 03905 03906 /* Next partition */ 03907 b_cyl += p_cyl; 03908 } 03909 ST_WORD(p, 0xAA55); 03910 03911 /* Write it to the MBR */ 03912 return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK; 03913 } 03914 03915 03916 #endif /* _MULTI_PARTITION == 2 */ 03917 #endif /* _USE_MKFS && !_FS_READONLY */ 03918 03919 03920 03921 03922 #if _USE_STRFUNC 03923 /*-----------------------------------------------------------------------*/ 03924 /* Get a string from the file */ 03925 /*-----------------------------------------------------------------------*/ 03926 TCHAR* f_gets ( 03927 TCHAR* buff, /* Pointer to the string buffer to read */ 03928 int len, /* Size of string buffer (characters) */ 03929 FIL* fil /* Pointer to the file object */ 03930 ) 03931 { 03932 int n = 0; 03933 TCHAR c, *p = buff; 03934 BYTE s[2]; 03935 UINT rc; 03936 03937 03938 while (n < len - 1) { /* Read bytes until buffer gets filled */ 03939 f_read(fil, s, 1, &rc); 03940 if (rc != 1) break; /* Break on EOF or error */ 03941 c = s[0]; 03942 #if _LFN_UNICODE /* Read a character in UTF-8 encoding */ 03943 if (c >= 0x80) { 03944 if (c < 0xC0) continue; /* Skip stray trailer */ 03945 if (c < 0xE0) { /* Two-byte sequence */ 03946 f_read(fil, s, 1, &rc); 03947 if (rc != 1) break; 03948 c = ((c & 0x1F) << 6) | (s[0] & 0x3F); 03949 if (c < 0x80) c = '?'; 03950 } else { 03951 if (c < 0xF0) { /* Three-byte sequence */ 03952 f_read(fil, s, 2, &rc); 03953 if (rc != 2) break; 03954 c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F); 03955 if (c < 0x800) c = '?'; 03956 } else { /* Reject four-byte sequence */ 03957 c = '?'; 03958 } 03959 } 03960 } 03961 #endif 03962 #if _USE_STRFUNC >= 2 03963 if (c == '\r') continue; /* Strip '\r' */ 03964 #endif 03965 *p++ = c; 03966 n++; 03967 if (c == '\n') break; /* Break on EOL */ 03968 } 03969 *p = 0; 03970 return n ? buff : 0; /* When no data read (eof or error), return with error. */ 03971 } 03972 03973 03974 03975 #if !_FS_READONLY 03976 #include <stdarg.h> 03977 /*-----------------------------------------------------------------------*/ 03978 /* Put a character to the file */ 03979 /*-----------------------------------------------------------------------*/ 03980 int f_putc ( 03981 TCHAR c, /* A character to be output */ 03982 FIL* fil /* Pointer to the file object */ 03983 ) 03984 { 03985 UINT bw, btw; 03986 BYTE s[3]; 03987 03988 03989 #if _USE_STRFUNC >= 2 03990 if (c == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ 03991 #endif 03992 03993 #if _LFN_UNICODE /* Write the character in UTF-8 encoding */ 03994 if (c < 0x80) { /* 7-bit */ 03995 s[0] = (BYTE)c; 03996 btw = 1; 03997 } else { 03998 if (c < 0x800) { /* 11-bit */ 03999 s[0] = (BYTE)(0xC0 | (c >> 6)); 04000 s[1] = (BYTE)(0x80 | (c & 0x3F)); 04001 btw = 2; 04002 } else { /* 16-bit */ 04003 s[0] = (BYTE)(0xE0 | (c >> 12)); 04004 s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F)); 04005 s[2] = (BYTE)(0x80 | (c & 0x3F)); 04006 btw = 3; 04007 } 04008 } 04009 #else /* Write the character without conversion */ 04010 s[0] = (BYTE)c; 04011 btw = 1; 04012 #endif 04013 f_write(fil, s, btw, &bw); /* Write the char to the file */ 04014 return (bw == btw) ? 1 : EOF; /* Return the result */ 04015 } 04016 04017 04018 04019 04020 /*-----------------------------------------------------------------------*/ 04021 /* Put a string to the file */ 04022 /*-----------------------------------------------------------------------*/ 04023 int f_puts ( 04024 const TCHAR* str, /* Pointer to the string to be output */ 04025 FIL* fil /* Pointer to the file object */ 04026 ) 04027 { 04028 int n; 04029 04030 04031 for (n = 0; *str; str++, n++) { 04032 if (f_putc(*str, fil) == EOF) return EOF; 04033 } 04034 return n; 04035 } 04036 04037 04038 04039 04040 /*-----------------------------------------------------------------------*/ 04041 /* Put a formatted string to the file */ 04042 /*-----------------------------------------------------------------------*/ 04043 int f_printf ( 04044 FIL* fil, /* Pointer to the file object */ 04045 const TCHAR* str, /* Pointer to the format string */ 04046 ... /* Optional arguments... */ 04047 ) 04048 { 04049 va_list arp; 04050 BYTE f, r; 04051 UINT i, j, w; 04052 ULONG v; 04053 TCHAR c, d, s[16], *p; 04054 int res, chc, cc; 04055 04056 04057 va_start(arp, str); 04058 04059 for (cc = res = 0; cc != EOF; res += cc) { 04060 c = *str++; 04061 if (c == 0) break; /* End of string */ 04062 if (c != '%') { /* Non escape character */ 04063 cc = f_putc(c, fil); 04064 if (cc != EOF) cc = 1; 04065 continue; 04066 } 04067 w = f = 0; 04068 c = *str++; 04069 if (c == '0') { /* Flag: '0' padding */ 04070 f = 1; c = *str++; 04071 } else { 04072 if (c == '-') { /* Flag: left justified */ 04073 f = 2; c = *str++; 04074 } 04075 } 04076 while (IsDigit(c)) { /* Precision */ 04077 w = w * 10 + c - '0'; 04078 c = *str++; 04079 } 04080 if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ 04081 f |= 4; c = *str++; 04082 } 04083 if (!c) break; 04084 d = c; 04085 if (IsLower(d)) d -= 0x20; 04086 switch (d) { /* Type is... */ 04087 case 'S' : /* String */ 04088 p = va_arg(arp, TCHAR*); 04089 for (j = 0; p[j]; j++) ; 04090 chc = 0; 04091 if (!(f & 2)) { 04092 while (j++ < w) chc += (cc = f_putc(' ', fil)); 04093 } 04094 chc += (cc = f_puts(p, fil)); 04095 while (j++ < w) chc += (cc = f_putc(' ', fil)); 04096 if (cc != EOF) cc = chc; 04097 continue; 04098 case 'C' : /* Character */ 04099 cc = f_putc((TCHAR)va_arg(arp, int), fil); continue; 04100 case 'B' : /* Binary */ 04101 r = 2; break; 04102 case 'O' : /* Octal */ 04103 r = 8; break; 04104 case 'D' : /* Signed decimal */ 04105 case 'U' : /* Unsigned decimal */ 04106 r = 10; break; 04107 case 'X' : /* Hexdecimal */ 04108 r = 16; break; 04109 default: /* Unknown type (pass-through) */ 04110 cc = f_putc(c, fil); continue; 04111 } 04112 04113 /* Get an argument and put it in numeral */ 04114 v = (f & 4) ? (ULONG)va_arg(arp, long) : ((d == 'D') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int)); 04115 if (d == 'D' && (v & 0x80000000)) { 04116 v = 0 - v; 04117 f |= 8; 04118 } 04119 i = 0; 04120 do { 04121 d = (TCHAR)(v % r); v /= r; 04122 if (d > 9) d += (c == 'x') ? 0x27 : 0x07; 04123 s[i++] = d + '0'; 04124 } while (v && i < sizeof s / sizeof s[0]); 04125 if (f & 8) s[i++] = '-'; 04126 j = i; d = (f & 1) ? '0' : ' '; 04127 res = 0; 04128 while (!(f & 2) && j++ < w) res += (cc = f_putc(d, fil)); 04129 do res += (cc = f_putc(s[--i], fil)); while(i); 04130 while (j++ < w) res += (cc = f_putc(' ', fil)); 04131 if (cc != EOF) cc = res; 04132 } 04133 04134 va_end(arp); 04135 return (cc == EOF) ? cc : res; 04136 } 04137 04138 #endif /* !_FS_READONLY */ 04139 #endif /* _USE_STRFUNC */
Generated on Fri Jul 15 2022 23:50:36 by
1.7.2
