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