]>
Commit | Line | Data |
---|---|---|
53668523 | 1 | /*----------------------------------------------------------------------------/\r |
7b78a5a2 | 2 | / FatFs - FAT file system module R0.10c (C)ChaN, 2014\r |
53668523 L |
3 | /-----------------------------------------------------------------------------/\r |
4 | / FatFs module is a generic FAT file system module for small embedded systems.\r | |
5 | / This is a free software that opened for education, research and commercial\r | |
6 | / developments under license policy of following terms.\r | |
7 | /\r | |
8 | / Copyright (C) 2014, ChaN, all right reserved.\r | |
9 | /\r | |
10 | / * The FatFs module is a free software and there is NO WARRANTY.\r | |
11 | / * No restriction on use. You can use, modify and redistribute it for\r | |
12 | / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.\r | |
13 | / * Redistributions of source code must retain the above copyright notice.\r | |
14 | /\r | |
15 | /-----------------------------------------------------------------------------/\r | |
16 | / Feb 26,'06 R0.00 Prototype.\r | |
17 | /\r | |
18 | / Apr 29,'06 R0.01 First stable version.\r | |
19 | /\r | |
20 | / Jun 01,'06 R0.02 Added FAT12 support.\r | |
21 | / Removed unbuffered mode.\r | |
22 | / Fixed a problem on small (<32M) partition.\r | |
23 | / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).\r | |
24 | /\r | |
25 | / Sep 22,'06 R0.03 Added f_rename().\r | |
26 | / Changed option _FS_MINIMUM to _FS_MINIMIZE.\r | |
27 | / Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.\r | |
28 | / Fixed f_mkdir() creates incorrect directory on FAT32.\r | |
29 | /\r | |
30 | / Feb 04,'07 R0.04 Supported multiple drive system.\r | |
31 | / Changed some interfaces for multiple drive system.\r | |
32 | / Changed f_mountdrv() to f_mount().\r | |
33 | / Added f_mkfs().\r | |
34 | / Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.\r | |
35 | / Added a capability of extending file size to f_lseek().\r | |
36 | / Added minimization level 3.\r | |
37 | / Fixed an endian sensitive code in f_mkfs().\r | |
38 | / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.\r | |
39 | / Added FSINFO support.\r | |
40 | / Fixed DBCS name can result FR_INVALID_NAME.\r | |
41 | / Fixed short seek (<= csize) collapses the file object.\r | |
42 | /\r | |
43 | / Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs().\r | |
44 | / Fixed f_mkfs() on FAT32 creates incorrect FSINFO.\r | |
45 | / Fixed f_mkdir() on FAT32 creates incorrect directory.\r | |
46 | / Feb 03,'08 R0.05a Added f_truncate() and f_utime().\r | |
47 | / Fixed off by one error at FAT sub-type determination.\r | |
48 | / Fixed btr in f_read() can be mistruncated.\r | |
49 | / Fixed cached sector is not flushed when create and close without write.\r | |
50 | /\r | |
51 | / Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets().\r | |
52 | / Improved performance of f_lseek() on moving to the same or following cluster.\r | |
53 | /\r | |
54 | / Apr 01,'09 R0.07 Merged Tiny-FatFs as a configuration option. (_FS_TINY)\r | |
55 | / Added long file name feature.\r | |
56 | / Added multiple code page feature.\r | |
57 | / Added re-entrancy for multitask operation.\r | |
58 | / Added auto cluster size selection to f_mkfs().\r | |
59 | / Added rewind option to f_readdir().\r | |
60 | / Changed result code of critical errors.\r | |
61 | / Renamed string functions to avoid name collision.\r | |
62 | / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.\r | |
63 | / Added multiple sector size feature.\r | |
64 | / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.\r | |
65 | / Fixed wrong cache control in f_lseek().\r | |
66 | / Added relative path feature.\r | |
67 | / Added f_chdir() and f_chdrive().\r | |
68 | / Added proper case conversion to extended character.\r | |
69 | / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.\r | |
70 | / Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.\r | |
71 | / Fixed name matching error on the 13 character boundary.\r | |
72 | / Added a configuration option, _LFN_UNICODE.\r | |
73 | / Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.\r | |
74 | /\r | |
75 | / May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3)\r | |
76 | / Added file lock feature. (_FS_SHARE)\r | |
77 | / Added fast seek feature. (_USE_FASTSEEK)\r | |
78 | / Changed some types on the API, XCHAR->TCHAR.\r | |
79 | / Changed .fname in the FILINFO structure on Unicode cfg.\r | |
80 | / String functions support UTF-8 encoding files on Unicode cfg.\r | |
81 | / Aug 16,'10 R0.08a Added f_getcwd().\r | |
82 | / Added sector erase feature. (_USE_ERASE)\r | |
83 | / Moved file lock semaphore table from fs object to the bss.\r | |
84 | / Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.\r | |
85 | / Fixed f_mkfs() creates wrong FAT32 volume.\r | |
86 | / Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write().\r | |
87 | / f_lseek() reports required table size on creating CLMP.\r | |
88 | / Extended format syntax of f_printf().\r | |
89 | / Ignores duplicated directory separators in given path name.\r | |
90 | /\r | |
91 | / Sep 06,'11 R0.09 f_mkfs() supports multiple partition to complete the multiple partition feature.\r | |
92 | / Added f_fdisk().\r | |
93 | / Aug 27,'12 R0.09a Changed f_open() and f_opendir() reject null object pointer to avoid crash.\r | |
94 | / Changed option name _FS_SHARE to _FS_LOCK.\r | |
95 | / Fixed assertion failure due to OS/2 EA on FAT12/16 volume.\r | |
96 | / Jan 24,'13 R0.09b Added f_setlabel() and f_getlabel().\r | |
97 | /\r | |
98 | / Oct 02,'13 R0.10 Added selection of character encoding on the file. (_STRF_ENCODE)\r | |
99 | / Added f_closedir().\r | |
100 | / Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)\r | |
101 | / Added forced mount feature with changes of f_mount().\r | |
102 | / Improved behavior of volume auto detection.\r | |
103 | / Improved write throughput of f_puts() and f_printf().\r | |
104 | / Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().\r | |
105 | / Fixed f_write() can be truncated when the file size is close to 4GB.\r | |
106 | / Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.\r | |
107 | / Jan 15,'14 R0.10a Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)\r | |
108 | / Added a configuration option of minimum sector size. (_MIN_SS)\r | |
109 | / 2nd argument of f_rename() can have a drive number and it will be ignored.\r | |
110 | / Fixed f_mount() with forced mount fails when drive number is >= 1.\r | |
111 | / Fixed f_close() invalidates the file object without volume lock.\r | |
112 | / Fixed f_closedir() returns but the volume lock is left acquired.\r | |
113 | / Fixed creation of an entry with LFN fails on too many SFN collisions.\r | |
114 | / May 19,'14 R0.10b Fixed a hard error in the disk I/O layer can collapse the directory entry.\r | |
115 | / Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN.\r | |
7b78a5a2 L |
116 | / Nov 09,'14 R0.10c Added a configuration option for the platforms without RTC. (_FS_NORTC)\r |
117 | / Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel().\r | |
118 | / Fixed a potential problem of FAT access that can appear on disk error.\r | |
119 | / Fixed null pointer dereference on attempting to delete the root direcotry.\r | |
53668523 L |
120 | /---------------------------------------------------------------------------*/\r |
121 | \r | |
122 | #include "ff.h" /* Declarations of FatFs API */\r | |
123 | #include "diskio.h" /* Declarations of disk I/O functions */\r | |
124 | \r | |
125 | \r | |
126 | \r | |
127 | \r | |
128 | /*--------------------------------------------------------------------------\r | |
129 | \r | |
130 | Module Private Definitions\r | |
131 | \r | |
132 | ---------------------------------------------------------------------------*/\r | |
133 | \r | |
7b78a5a2 | 134 | #if _FATFS != 80376 /* Revision ID */\r |
53668523 L |
135 | #error Wrong include file (ff.h).\r |
136 | #endif\r | |
137 | \r | |
138 | \r | |
139 | /* Reentrancy related */\r | |
140 | #if _FS_REENTRANT\r | |
141 | #if _USE_LFN == 1\r | |
7b78a5a2 | 142 | #error Static LFN work area cannot be used at thread-safe configuration\r |
53668523 L |
143 | #endif\r |
144 | #define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; }\r | |
145 | #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }\r | |
146 | #else\r | |
147 | #define ENTER_FF(fs)\r | |
148 | #define LEAVE_FF(fs, res) return res\r | |
149 | #endif\r | |
150 | \r | |
151 | #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }\r | |
152 | \r | |
153 | \r | |
154 | /* Definitions of sector size */\r | |
155 | #if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096)\r | |
7b78a5a2 | 156 | #error Wrong sector size configuration\r |
53668523 L |
157 | #endif\r |
158 | #if _MAX_SS == _MIN_SS\r | |
159 | #define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */\r | |
160 | #else\r | |
161 | #define SS(fs) ((fs)->ssize) /* Variable sector size */\r | |
162 | #endif\r | |
163 | \r | |
164 | \r | |
7b78a5a2 L |
165 | /* Timestamp feature */\r |
166 | #if _FS_NORTC\r | |
167 | #define GET_FATTIME() ((DWORD)_NORTC_YEAR << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16)\r | |
168 | #else\r | |
169 | #define GET_FATTIME() get_fattime()\r | |
170 | #endif\r | |
171 | \r | |
172 | \r | |
53668523 L |
173 | /* File access control feature */\r |
174 | #if _FS_LOCK\r | |
175 | #if _FS_READONLY\r | |
7b78a5a2 | 176 | #error _FS_LOCK must be 0 at read-only configuration\r |
53668523 L |
177 | #endif\r |
178 | typedef struct {\r | |
179 | FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */\r | |
180 | DWORD clu; /* Object ID 2, directory (0:root) */\r | |
181 | WORD idx; /* Object ID 3, directory index */\r | |
182 | WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */\r | |
183 | } FILESEM;\r | |
184 | #endif\r | |
185 | \r | |
186 | \r | |
187 | \r | |
188 | /* DBCS code ranges and SBCS extend character conversion table */\r | |
189 | \r | |
190 | #if _CODE_PAGE == 932 /* Japanese Shift-JIS */\r | |
191 | #define _DF1S 0x81 /* DBC 1st byte range 1 start */\r | |
192 | #define _DF1E 0x9F /* DBC 1st byte range 1 end */\r | |
193 | #define _DF2S 0xE0 /* DBC 1st byte range 2 start */\r | |
194 | #define _DF2E 0xFC /* DBC 1st byte range 2 end */\r | |
195 | #define _DS1S 0x40 /* DBC 2nd byte range 1 start */\r | |
196 | #define _DS1E 0x7E /* DBC 2nd byte range 1 end */\r | |
197 | #define _DS2S 0x80 /* DBC 2nd byte range 2 start */\r | |
198 | #define _DS2E 0xFC /* DBC 2nd byte range 2 end */\r | |
199 | \r | |
200 | #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */\r | |
201 | #define _DF1S 0x81\r | |
202 | #define _DF1E 0xFE\r | |
203 | #define _DS1S 0x40\r | |
204 | #define _DS1E 0x7E\r | |
205 | #define _DS2S 0x80\r | |
206 | #define _DS2E 0xFE\r | |
207 | \r | |
208 | #elif _CODE_PAGE == 949 /* Korean */\r | |
209 | #define _DF1S 0x81\r | |
210 | #define _DF1E 0xFE\r | |
211 | #define _DS1S 0x41\r | |
212 | #define _DS1E 0x5A\r | |
213 | #define _DS2S 0x61\r | |
214 | #define _DS2E 0x7A\r | |
215 | #define _DS3S 0x81\r | |
216 | #define _DS3E 0xFE\r | |
217 | \r | |
218 | #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */\r | |
219 | #define _DF1S 0x81\r | |
220 | #define _DF1E 0xFE\r | |
221 | #define _DS1S 0x40\r | |
222 | #define _DS1E 0x7E\r | |
223 | #define _DS2S 0xA1\r | |
224 | #define _DS2E 0xFE\r | |
225 | \r | |
226 | #elif _CODE_PAGE == 437 /* U.S. (OEM) */\r | |
227 | #define _DF1S 0\r | |
228 | #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, \\r | |
229 | 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, \\r | |
230 | 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, \\r | |
231 | 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}\r | |
232 | \r | |
233 | #elif _CODE_PAGE == 720 /* Arabic (OEM) */\r | |
234 | #define _DF1S 0\r | |
235 | #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, \\r | |
236 | 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, \\r | |
237 | 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, \\r | |
238 | 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}\r | |
239 | \r | |
240 | #elif _CODE_PAGE == 737 /* Greek (OEM) */\r | |
241 | #define _DF1S 0\r | |
242 | #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, \\r | |
243 | 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, \\r | |
244 | 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, \\r | |
245 | 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}\r | |
246 | \r | |
247 | #elif _CODE_PAGE == 775 /* Baltic (OEM) */\r | |
248 | #define _DF1S 0\r | |
249 | #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, \\r | |
250 | 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, \\r | |
251 | 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, \\r | |
252 | 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}\r | |
253 | \r | |
254 | #elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */\r | |
255 | #define _DF1S 0\r | |
256 | #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, \\r | |
257 | 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, \\r | |
258 | 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, \\r | |
259 | 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}\r | |
260 | \r | |
261 | #elif _CODE_PAGE == 852 /* Latin 2 (OEM) */\r | |
262 | #define _DF1S 0\r | |
263 | #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, \\r | |
264 | 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, \\r | |
265 | 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, \\r | |
266 | 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}\r | |
267 | \r | |
268 | #elif _CODE_PAGE == 855 /* Cyrillic (OEM) */\r | |
269 | #define _DF1S 0\r | |
270 | #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, \\r | |
271 | 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, \\r | |
272 | 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, \\r | |
273 | 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}\r | |
274 | \r | |
275 | #elif _CODE_PAGE == 857 /* Turkish (OEM) */\r | |
276 | #define _DF1S 0\r | |
277 | #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, \\r | |
278 | 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, \\r | |
279 | 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, \\r | |
280 | 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}\r | |
281 | \r | |
282 | #elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */\r | |
283 | #define _DF1S 0\r | |
284 | #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, \\r | |
285 | 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, \\r | |
286 | 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, \\r | |
287 | 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}\r | |
288 | \r | |
289 | #elif _CODE_PAGE == 862 /* Hebrew (OEM) */\r | |
290 | #define _DF1S 0\r | |
291 | #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, \\r | |
292 | 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, \\r | |
293 | 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, \\r | |
294 | 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}\r | |
295 | \r | |
296 | #elif _CODE_PAGE == 866 /* Russian (OEM) */\r | |
297 | #define _DF1S 0\r | |
298 | #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, \\r | |
299 | 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, \\r | |
300 | 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, \\r | |
301 | 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}\r | |
302 | \r | |
303 | #elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */\r | |
304 | #define _DF1S 0\r | |
305 | #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, \\r | |
306 | 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, \\r | |
307 | 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, \\r | |
308 | 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}\r | |
309 | \r | |
310 | #elif _CODE_PAGE == 1250 /* Central Europe (Windows) */\r | |
311 | #define _DF1S 0\r | |
312 | #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, \\r | |
313 | 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, \\r | |
314 | 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, \\r | |
315 | 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}\r | |
316 | \r | |
317 | #elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */\r | |
318 | #define _DF1S 0\r | |
319 | #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, \\r | |
320 | 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, \\r | |
321 | 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, \\r | |
322 | 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}\r | |
323 | \r | |
324 | #elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */\r | |
325 | #define _DF1S 0\r | |
326 | #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, \\r | |
327 | 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, \\r | |
328 | 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, \\r | |
329 | 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}\r | |
330 | \r | |
331 | #elif _CODE_PAGE == 1253 /* Greek (Windows) */\r | |
332 | #define _DF1S 0\r | |
333 | #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, \\r | |
334 | 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, \\r | |
335 | 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, \\r | |
336 | 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}\r | |
337 | \r | |
338 | #elif _CODE_PAGE == 1254 /* Turkish (Windows) */\r | |
339 | #define _DF1S 0\r | |
340 | #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, \\r | |
341 | 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, \\r | |
342 | 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, \\r | |
343 | 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}\r | |
344 | \r | |
345 | #elif _CODE_PAGE == 1255 /* Hebrew (Windows) */\r | |
346 | #define _DF1S 0\r | |
347 | #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, \\r | |
348 | 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, \\r | |
349 | 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, \\r | |
350 | 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}\r | |
351 | \r | |
352 | #elif _CODE_PAGE == 1256 /* Arabic (Windows) */\r | |
353 | #define _DF1S 0\r | |
354 | #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, \\r | |
355 | 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, \\r | |
356 | 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, \\r | |
357 | 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}\r | |
358 | \r | |
359 | #elif _CODE_PAGE == 1257 /* Baltic (Windows) */\r | |
360 | #define _DF1S 0\r | |
361 | #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, \\r | |
362 | 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, \\r | |
363 | 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, \\r | |
364 | 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}\r | |
365 | \r | |
366 | #elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */\r | |
367 | #define _DF1S 0\r | |
368 | #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, \\r | |
369 | 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, \\r | |
370 | 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, \\r | |
371 | 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}\r | |
372 | \r | |
373 | #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */\r | |
374 | #if _USE_LFN\r | |
375 | #error Cannot use LFN feature without valid code page.\r | |
376 | #endif\r | |
377 | #define _DF1S 0\r | |
378 | \r | |
379 | #else\r | |
380 | #error Unknown code page\r | |
381 | \r | |
382 | #endif\r | |
383 | \r | |
384 | \r | |
385 | /* Character code support macros */\r | |
386 | #define IsUpper(c) (((c)>='A')&&((c)<='Z'))\r | |
387 | #define IsLower(c) (((c)>='a')&&((c)<='z'))\r | |
388 | #define IsDigit(c) (((c)>='0')&&((c)<='9'))\r | |
389 | \r | |
390 | #if _DF1S /* Code page is DBCS */\r | |
391 | \r | |
392 | #ifdef _DF2S /* Two 1st byte areas */\r | |
393 | #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))\r | |
394 | #else /* One 1st byte area */\r | |
395 | #define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)\r | |
396 | #endif\r | |
397 | \r | |
398 | #ifdef _DS3S /* Three 2nd byte areas */\r | |
399 | #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))\r | |
400 | #else /* Two 2nd byte areas */\r | |
401 | #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))\r | |
402 | #endif\r | |
403 | \r | |
404 | #else /* Code page is SBCS */\r | |
405 | \r | |
406 | #define IsDBCS1(c) 0\r | |
407 | #define IsDBCS2(c) 0\r | |
408 | \r | |
409 | #endif /* _DF1S */\r | |
410 | \r | |
411 | \r | |
412 | /* Name status flags */\r | |
7b78a5a2 | 413 | #define NSFLAG 11 /* Index of name status byte in fn[] */\r |
53668523 L |
414 | #define NS_LOSS 0x01 /* Out of 8.3 format */\r |
415 | #define NS_LFN 0x02 /* Force to create LFN entry */\r | |
416 | #define NS_LAST 0x04 /* Last segment */\r | |
417 | #define NS_BODY 0x08 /* Lower case flag (body) */\r | |
418 | #define NS_EXT 0x10 /* Lower case flag (ext) */\r | |
419 | #define NS_DOT 0x20 /* Dot entry */\r | |
420 | \r | |
421 | \r | |
7b78a5a2 L |
422 | /* FAT sub-type boundaries (Differ from specs but correct for real DOS/Windows) */\r |
423 | #define MIN_FAT16 4086U /* Minimum number of clusters as FAT16 */\r | |
424 | #define MIN_FAT32 65526U /* Minimum number of clusters as FAT32 */\r | |
53668523 L |
425 | \r |
426 | \r | |
427 | /* FatFs refers the members in the FAT structures as byte array instead of\r | |
428 | / structure member because the structure is not binary compatible between\r | |
429 | / different platforms */\r | |
430 | \r | |
7b78a5a2 | 431 | #define BS_jmpBoot 0 /* x86 jump instruction (3) */\r |
53668523 L |
432 | #define BS_OEMName 3 /* OEM name (8) */\r |
433 | #define BPB_BytsPerSec 11 /* Sector size [byte] (2) */\r | |
434 | #define BPB_SecPerClus 13 /* Cluster size [sector] (1) */\r | |
435 | #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */\r | |
436 | #define BPB_NumFATs 16 /* Number of FAT copies (1) */\r | |
437 | #define BPB_RootEntCnt 17 /* Number of root directory entries for FAT12/16 (2) */\r | |
438 | #define BPB_TotSec16 19 /* Volume size [sector] (2) */\r | |
439 | #define BPB_Media 21 /* Media descriptor (1) */\r | |
440 | #define BPB_FATSz16 22 /* FAT size [sector] (2) */\r | |
441 | #define BPB_SecPerTrk 24 /* Track size [sector] (2) */\r | |
442 | #define BPB_NumHeads 26 /* Number of heads (2) */\r | |
443 | #define BPB_HiddSec 28 /* Number of special hidden sectors (4) */\r | |
444 | #define BPB_TotSec32 32 /* Volume size [sector] (4) */\r | |
445 | #define BS_DrvNum 36 /* Physical drive number (2) */\r | |
446 | #define BS_BootSig 38 /* Extended boot signature (1) */\r | |
447 | #define BS_VolID 39 /* Volume serial number (4) */\r | |
448 | #define BS_VolLab 43 /* Volume label (8) */\r | |
449 | #define BS_FilSysType 54 /* File system type (1) */\r | |
450 | #define BPB_FATSz32 36 /* FAT size [sector] (4) */\r | |
451 | #define BPB_ExtFlags 40 /* Extended flags (2) */\r | |
452 | #define BPB_FSVer 42 /* File system version (2) */\r | |
453 | #define BPB_RootClus 44 /* Root directory first cluster (4) */\r | |
454 | #define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */\r | |
455 | #define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */\r | |
456 | #define BS_DrvNum32 64 /* Physical drive number (2) */\r | |
457 | #define BS_BootSig32 66 /* Extended boot signature (1) */\r | |
458 | #define BS_VolID32 67 /* Volume serial number (4) */\r | |
459 | #define BS_VolLab32 71 /* Volume label (8) */\r | |
460 | #define BS_FilSysType32 82 /* File system type (1) */\r | |
461 | #define FSI_LeadSig 0 /* FSI: Leading signature (4) */\r | |
462 | #define FSI_StrucSig 484 /* FSI: Structure signature (4) */\r | |
463 | #define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */\r | |
464 | #define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */\r | |
465 | #define MBR_Table 446 /* MBR: Partition table offset (2) */\r | |
466 | #define SZ_PTE 16 /* MBR: Size of a partition table entry */\r | |
467 | #define BS_55AA 510 /* Signature word (2) */\r | |
468 | \r | |
469 | #define DIR_Name 0 /* Short file name (11) */\r | |
470 | #define DIR_Attr 11 /* Attribute (1) */\r | |
471 | #define DIR_NTres 12 /* NT flag (1) */\r | |
472 | #define DIR_CrtTimeTenth 13 /* Created time sub-second (1) */\r | |
473 | #define DIR_CrtTime 14 /* Created time (2) */\r | |
474 | #define DIR_CrtDate 16 /* Created date (2) */\r | |
475 | #define DIR_LstAccDate 18 /* Last accessed date (2) */\r | |
476 | #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */\r | |
477 | #define DIR_WrtTime 22 /* Modified time (2) */\r | |
478 | #define DIR_WrtDate 24 /* Modified date (2) */\r | |
479 | #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */\r | |
480 | #define DIR_FileSize 28 /* File size (4) */\r | |
481 | #define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */\r | |
482 | #define LDIR_Attr 11 /* LFN attribute (1) */\r | |
483 | #define LDIR_Type 12 /* LFN type (1) */\r | |
484 | #define LDIR_Chksum 13 /* Sum of corresponding SFN entry */\r | |
485 | #define LDIR_FstClusLO 26 /* Filled by zero (0) */\r | |
486 | #define SZ_DIR 32 /* Size of a directory entry */\r | |
487 | #define LLE 0x40 /* Last long entry flag in LDIR_Ord */\r | |
488 | #define DDE 0xE5 /* Deleted directory entry mark in DIR_Name[0] */\r | |
489 | #define NDDE 0x05 /* Replacement of the character collides with DDE */\r | |
490 | \r | |
491 | \r | |
492 | \r | |
493 | \r | |
494 | /*------------------------------------------------------------*/\r | |
495 | /* Module private work area */\r | |
496 | /*------------------------------------------------------------*/\r | |
7b78a5a2 L |
497 | /* Remark: Uninitialized variables with static duration are\r |
498 | / guaranteed zero/null at start-up. If not, either the linker\r | |
499 | / or start-up routine being used is out of ANSI-C standard.\r | |
53668523 L |
500 | */\r |
501 | \r | |
7b78a5a2 L |
502 | #if _VOLUMES < 1 || _VOLUMES > 9\r |
503 | #error Wrong _VOLUMES setting\r | |
53668523 | 504 | #endif\r |
7b78a5a2 L |
505 | static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */\r |
506 | static WORD Fsid; /* File system mount ID */\r | |
53668523 L |
507 | \r |
508 | #if _FS_RPATH && _VOLUMES >= 2\r | |
7b78a5a2 | 509 | static BYTE CurrVol; /* Current drive */\r |
53668523 L |
510 | #endif\r |
511 | \r | |
512 | #if _FS_LOCK\r | |
7b78a5a2 | 513 | static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */\r |
53668523 L |
514 | #endif\r |
515 | \r | |
7b78a5a2 | 516 | #if _USE_LFN == 0 /* Non LFN feature */\r |
53668523 L |
517 | #define DEF_NAMEBUF BYTE sfn[12]\r |
518 | #define INIT_BUF(dobj) (dobj).fn = sfn\r | |
519 | #define FREE_BUF()\r | |
7b78a5a2 L |
520 | #else\r |
521 | #if _MAX_LFN < 12 || _MAX_LFN > 255\r | |
522 | #error Wrong _MAX_LFN setting\r | |
523 | #endif\r | |
524 | #if _USE_LFN == 1 /* LFN feature with static working buffer */\r | |
525 | static WCHAR LfnBuf[_MAX_LFN+1];\r | |
53668523 L |
526 | #define DEF_NAMEBUF BYTE sfn[12]\r |
527 | #define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; }\r | |
528 | #define FREE_BUF()\r | |
53668523 L |
529 | #elif _USE_LFN == 2 /* LFN feature with dynamic working buffer on the stack */\r |
530 | #define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]\r | |
531 | #define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; }\r | |
532 | #define FREE_BUF()\r | |
53668523 L |
533 | #elif _USE_LFN == 3 /* LFN feature with dynamic working buffer on the heap */\r |
534 | #define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn\r | |
7b78a5a2 | 535 | #define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); (dobj).lfn = lfn; (dobj).fn = sfn; }\r |
53668523 | 536 | #define FREE_BUF() ff_memfree(lfn)\r |
53668523 | 537 | #else\r |
7b78a5a2 L |
538 | #error Wrong _USE_LFN setting\r |
539 | #endif\r | |
53668523 | 540 | #endif\r |
53668523 L |
541 | \r |
542 | #ifdef _EXCVT\r | |
7b78a5a2 | 543 | static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for extended characters */\r |
53668523 L |
544 | #endif\r |
545 | \r | |
546 | \r | |
547 | \r | |
548 | \r | |
549 | \r | |
550 | \r | |
551 | /*--------------------------------------------------------------------------\r | |
552 | \r | |
553 | Module Private Functions\r | |
554 | \r | |
555 | ---------------------------------------------------------------------------*/\r | |
556 | \r | |
557 | \r | |
558 | /*-----------------------------------------------------------------------*/\r | |
559 | /* String functions */\r | |
560 | /*-----------------------------------------------------------------------*/\r | |
561 | \r | |
562 | /* Copy memory to memory */\r | |
563 | static\r | |
564 | void mem_cpy (void* dst, const void* src, UINT cnt) {\r | |
565 | BYTE *d = (BYTE*)dst;\r | |
566 | const BYTE *s = (const BYTE*)src;\r | |
567 | \r | |
568 | #if _WORD_ACCESS == 1\r | |
569 | while (cnt >= sizeof (int)) {\r | |
570 | *(int*)d = *(int*)s;\r | |
571 | d += sizeof (int); s += sizeof (int);\r | |
572 | cnt -= sizeof (int);\r | |
573 | }\r | |
574 | #endif\r | |
575 | while (cnt--)\r | |
576 | *d++ = *s++;\r | |
577 | }\r | |
578 | \r | |
579 | /* Fill memory */\r | |
580 | static\r | |
581 | void mem_set (void* dst, int val, UINT cnt) {\r | |
582 | BYTE *d = (BYTE*)dst;\r | |
583 | \r | |
584 | while (cnt--)\r | |
585 | *d++ = (BYTE)val;\r | |
586 | }\r | |
587 | \r | |
588 | /* Compare memory to memory */\r | |
589 | static\r | |
590 | int mem_cmp (const void* dst, const void* src, UINT cnt) {\r | |
591 | const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;\r | |
592 | int r = 0;\r | |
593 | \r | |
594 | while (cnt-- && (r = *d++ - *s++) == 0) ;\r | |
595 | return r;\r | |
596 | }\r | |
597 | \r | |
598 | /* Check if chr is contained in the string */\r | |
599 | static\r | |
600 | int chk_chr (const char* str, int chr) {\r | |
601 | while (*str && *str != chr) str++;\r | |
602 | return *str;\r | |
603 | }\r | |
604 | \r | |
605 | \r | |
606 | \r | |
607 | \r | |
608 | /*-----------------------------------------------------------------------*/\r | |
609 | /* Request/Release grant to access the volume */\r | |
610 | /*-----------------------------------------------------------------------*/\r | |
611 | #if _FS_REENTRANT\r | |
612 | static\r | |
613 | int lock_fs (\r | |
614 | FATFS* fs /* File system object */\r | |
615 | )\r | |
616 | {\r | |
617 | return ff_req_grant(fs->sobj);\r | |
618 | }\r | |
619 | \r | |
620 | \r | |
621 | static\r | |
622 | void unlock_fs (\r | |
623 | FATFS* fs, /* File system object */\r | |
624 | FRESULT res /* Result code to be returned */\r | |
625 | )\r | |
626 | {\r | |
627 | if (fs &&\r | |
628 | res != FR_NOT_ENABLED &&\r | |
629 | res != FR_INVALID_DRIVE &&\r | |
630 | res != FR_INVALID_OBJECT &&\r | |
631 | res != FR_TIMEOUT) {\r | |
632 | ff_rel_grant(fs->sobj);\r | |
633 | }\r | |
634 | }\r | |
635 | #endif\r | |
636 | \r | |
637 | \r | |
638 | \r | |
639 | \r | |
640 | /*-----------------------------------------------------------------------*/\r | |
641 | /* File lock control functions */\r | |
642 | /*-----------------------------------------------------------------------*/\r | |
643 | #if _FS_LOCK\r | |
644 | \r | |
645 | static\r | |
646 | FRESULT chk_lock ( /* Check if the file can be accessed */\r | |
647 | DIR* dp, /* Directory object pointing the file to be checked */\r | |
648 | int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */\r | |
649 | )\r | |
650 | {\r | |
651 | UINT i, be;\r | |
652 | \r | |
653 | /* Search file semaphore table */\r | |
654 | for (i = be = 0; i < _FS_LOCK; i++) {\r | |
655 | if (Files[i].fs) { /* Existing entry */\r | |
656 | if (Files[i].fs == dp->fs && /* Check if the object matched with an open object */\r | |
657 | Files[i].clu == dp->sclust &&\r | |
658 | Files[i].idx == dp->index) break;\r | |
659 | } else { /* Blank entry */\r | |
660 | be = 1;\r | |
661 | }\r | |
662 | }\r | |
663 | if (i == _FS_LOCK) /* The object is not opened */\r | |
664 | return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */\r | |
665 | \r | |
666 | /* The object has been opened. Reject any open against writing file and all write mode open */\r | |
667 | return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;\r | |
668 | }\r | |
669 | \r | |
670 | \r | |
671 | static\r | |
672 | int enq_lock (void) /* Check if an entry is available for a new object */\r | |
673 | {\r | |
674 | UINT i;\r | |
675 | \r | |
676 | for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;\r | |
677 | return (i == _FS_LOCK) ? 0 : 1;\r | |
678 | }\r | |
679 | \r | |
680 | \r | |
681 | static\r | |
682 | UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */\r | |
683 | DIR* dp, /* Directory object pointing the file to register or increment */\r | |
684 | int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */\r | |
685 | )\r | |
686 | {\r | |
687 | UINT i;\r | |
688 | \r | |
689 | \r | |
690 | for (i = 0; i < _FS_LOCK; i++) { /* Find the object */\r | |
691 | if (Files[i].fs == dp->fs &&\r | |
692 | Files[i].clu == dp->sclust &&\r | |
693 | Files[i].idx == dp->index) break;\r | |
694 | }\r | |
695 | \r | |
696 | if (i == _FS_LOCK) { /* Not opened. Register it as new. */\r | |
697 | for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;\r | |
698 | if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */\r | |
699 | Files[i].fs = dp->fs;\r | |
700 | Files[i].clu = dp->sclust;\r | |
701 | Files[i].idx = dp->index;\r | |
702 | Files[i].ctr = 0;\r | |
703 | }\r | |
704 | \r | |
705 | if (acc && Files[i].ctr) return 0; /* Access violation (int err) */\r | |
706 | \r | |
707 | Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */\r | |
708 | \r | |
709 | return i + 1;\r | |
710 | }\r | |
711 | \r | |
712 | \r | |
713 | static\r | |
714 | FRESULT dec_lock ( /* Decrement object open counter */\r | |
715 | UINT i /* Semaphore index (1..) */\r | |
716 | )\r | |
717 | {\r | |
718 | WORD n;\r | |
719 | FRESULT res;\r | |
720 | \r | |
721 | \r | |
722 | if (--i < _FS_LOCK) { /* Shift index number origin from 0 */\r | |
723 | n = Files[i].ctr;\r | |
724 | if (n == 0x100) n = 0; /* If write mode open, delete the entry */\r | |
725 | if (n) n--; /* Decrement read mode open count */\r | |
726 | Files[i].ctr = n;\r | |
727 | if (!n) Files[i].fs = 0; /* Delete the entry if open count gets zero */\r | |
728 | res = FR_OK;\r | |
729 | } else {\r | |
730 | res = FR_INT_ERR; /* Invalid index nunber */\r | |
731 | }\r | |
732 | return res;\r | |
733 | }\r | |
734 | \r | |
735 | \r | |
736 | static\r | |
737 | void clear_lock ( /* Clear lock entries of the volume */\r | |
738 | FATFS *fs\r | |
739 | )\r | |
740 | {\r | |
741 | UINT i;\r | |
742 | \r | |
743 | for (i = 0; i < _FS_LOCK; i++) {\r | |
744 | if (Files[i].fs == fs) Files[i].fs = 0;\r | |
745 | }\r | |
746 | }\r | |
747 | #endif\r | |
748 | \r | |
749 | \r | |
750 | \r | |
751 | \r | |
752 | /*-----------------------------------------------------------------------*/\r | |
753 | /* Move/Flush disk access window in the file system object */\r | |
754 | /*-----------------------------------------------------------------------*/\r | |
755 | #if !_FS_READONLY\r | |
756 | static\r | |
757 | FRESULT sync_window (\r | |
758 | FATFS* fs /* File system object */\r | |
759 | )\r | |
760 | {\r | |
761 | DWORD wsect;\r | |
762 | UINT nf;\r | |
7b78a5a2 | 763 | FRESULT res = FR_OK;\r |
53668523 L |
764 | \r |
765 | \r | |
766 | if (fs->wflag) { /* Write back the sector if it is dirty */\r | |
767 | wsect = fs->winsect; /* Current sector number */\r | |
7b78a5a2 L |
768 | if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) {\r |
769 | res = FR_DISK_ERR;\r | |
770 | } else {\r | |
771 | fs->wflag = 0;\r | |
772 | if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */\r | |
773 | for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */\r | |
774 | wsect += fs->fsize;\r | |
775 | disk_write(fs->drv, fs->win, wsect, 1);\r | |
776 | }\r | |
53668523 L |
777 | }\r |
778 | }\r | |
779 | }\r | |
7b78a5a2 | 780 | return res;\r |
53668523 L |
781 | }\r |
782 | #endif\r | |
783 | \r | |
784 | \r | |
785 | static\r | |
786 | FRESULT move_window (\r | |
787 | FATFS* fs, /* File system object */\r | |
788 | DWORD sector /* Sector number to make appearance in the fs->win[] */\r | |
789 | )\r | |
790 | {\r | |
7b78a5a2 L |
791 | FRESULT res = FR_OK;\r |
792 | \r | |
793 | \r | |
794 | if (sector != fs->winsect) { /* Window offset changed? */\r | |
53668523 | 795 | #if !_FS_READONLY\r |
7b78a5a2 | 796 | res = sync_window(fs); /* Write-back changes */\r |
53668523 | 797 | #endif\r |
7b78a5a2 L |
798 | if (res == FR_OK) { /* Fill sector window with new data */\r |
799 | if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) {\r | |
800 | sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */\r | |
801 | res = FR_DISK_ERR;\r | |
802 | }\r | |
803 | fs->winsect = sector;\r | |
804 | }\r | |
53668523 | 805 | }\r |
7b78a5a2 | 806 | return res;\r |
53668523 L |
807 | }\r |
808 | \r | |
809 | \r | |
810 | \r | |
811 | \r | |
812 | /*-----------------------------------------------------------------------*/\r | |
813 | /* Synchronize file system and strage device */\r | |
814 | /*-----------------------------------------------------------------------*/\r | |
815 | #if !_FS_READONLY\r | |
816 | static\r | |
817 | FRESULT sync_fs ( /* FR_OK: successful, FR_DISK_ERR: failed */\r | |
818 | FATFS* fs /* File system object */\r | |
819 | )\r | |
820 | {\r | |
821 | FRESULT res;\r | |
822 | \r | |
823 | \r | |
824 | res = sync_window(fs);\r | |
825 | if (res == FR_OK) {\r | |
826 | /* Update FSINFO sector if needed */\r | |
827 | if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {\r | |
828 | /* Create FSINFO structure */\r | |
829 | mem_set(fs->win, 0, SS(fs));\r | |
830 | ST_WORD(fs->win+BS_55AA, 0xAA55);\r | |
831 | ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);\r | |
832 | ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);\r | |
833 | ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);\r | |
834 | ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);\r | |
835 | /* Write it into the FSINFO sector */\r | |
836 | fs->winsect = fs->volbase + 1;\r | |
837 | disk_write(fs->drv, fs->win, fs->winsect, 1);\r | |
838 | fs->fsi_flag = 0;\r | |
839 | }\r | |
840 | /* Make sure that no pending write process in the physical drive */\r | |
841 | if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK)\r | |
842 | res = FR_DISK_ERR;\r | |
843 | }\r | |
844 | \r | |
845 | return res;\r | |
846 | }\r | |
847 | #endif\r | |
848 | \r | |
849 | \r | |
850 | \r | |
851 | \r | |
852 | /*-----------------------------------------------------------------------*/\r | |
853 | /* Get sector# from cluster# */\r | |
854 | /*-----------------------------------------------------------------------*/\r | |
7b78a5a2 | 855 | /* Hidden API for hacks and disk tools */\r |
53668523 L |
856 | \r |
857 | DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */\r | |
858 | FATFS* fs, /* File system object */\r | |
859 | DWORD clst /* Cluster# to be converted */\r | |
860 | )\r | |
861 | {\r | |
862 | clst -= 2;\r | |
7b78a5a2 | 863 | if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */\r |
53668523 L |
864 | return clst * fs->csize + fs->database;\r |
865 | }\r | |
866 | \r | |
867 | \r | |
868 | \r | |
869 | \r | |
870 | /*-----------------------------------------------------------------------*/\r | |
871 | /* FAT access - Read value of a FAT entry */\r | |
872 | /*-----------------------------------------------------------------------*/\r | |
7b78a5a2 | 873 | /* Hidden API for hacks and disk tools */\r |
53668523 | 874 | \r |
7b78a5a2 | 875 | DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluster status */\r |
53668523 | 876 | FATFS* fs, /* File system object */\r |
7b78a5a2 | 877 | DWORD clst /* FAT item index (cluster#) to get the value */\r |
53668523 L |
878 | )\r |
879 | {\r | |
880 | UINT wc, bc;\r | |
881 | BYTE *p;\r | |
7b78a5a2 | 882 | DWORD val;\r |
53668523 L |
883 | \r |
884 | \r | |
7b78a5a2 L |
885 | if (clst < 2 || clst >= fs->n_fatent) { /* Check range */\r |
886 | val = 1; /* Internal error */\r | |
53668523 | 887 | \r |
7b78a5a2 L |
888 | } else {\r |
889 | val = 0xFFFFFFFF; /* Default value falls on disk error */\r | |
53668523 | 890 | \r |
7b78a5a2 L |
891 | switch (fs->fs_type) {\r |
892 | case FS_FAT12 :\r | |
893 | bc = (UINT)clst; bc += bc / 2;\r | |
894 | if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;\r | |
895 | wc = fs->win[bc++ % SS(fs)];\r | |
896 | if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;\r | |
897 | wc |= fs->win[bc % SS(fs)] << 8;\r | |
898 | val = clst & 1 ? wc >> 4 : (wc & 0xFFF);\r | |
899 | break;\r | |
900 | \r | |
901 | case FS_FAT16 :\r | |
902 | if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;\r | |
903 | p = &fs->win[clst * 2 % SS(fs)];\r | |
904 | val = LD_WORD(p);\r | |
905 | break;\r | |
53668523 | 906 | \r |
7b78a5a2 L |
907 | case FS_FAT32 :\r |
908 | if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;\r | |
909 | p = &fs->win[clst * 4 % SS(fs)];\r | |
910 | val = LD_DWORD(p) & 0x0FFFFFFF;\r | |
911 | break;\r | |
53668523 | 912 | \r |
7b78a5a2 L |
913 | default:\r |
914 | val = 1; /* Internal error */\r | |
915 | }\r | |
53668523 L |
916 | }\r |
917 | \r | |
7b78a5a2 | 918 | return val;\r |
53668523 L |
919 | }\r |
920 | \r | |
921 | \r | |
922 | \r | |
923 | \r | |
924 | /*-----------------------------------------------------------------------*/\r | |
925 | /* FAT access - Change value of a FAT entry */\r | |
926 | /*-----------------------------------------------------------------------*/\r | |
7b78a5a2 | 927 | /* Hidden API for hacks and disk tools */\r |
53668523 | 928 | \r |
7b78a5a2 | 929 | #if !_FS_READONLY\r |
53668523 L |
930 | FRESULT put_fat (\r |
931 | FATFS* fs, /* File system object */\r | |
7b78a5a2 | 932 | DWORD clst, /* FAT item index (cluster#) to be set */\r |
53668523 L |
933 | DWORD val /* New value to mark the cluster */\r |
934 | )\r | |
935 | {\r | |
936 | UINT bc;\r | |
937 | BYTE *p;\r | |
938 | FRESULT res;\r | |
939 | \r | |
940 | \r | |
941 | if (clst < 2 || clst >= fs->n_fatent) { /* Check range */\r | |
942 | res = FR_INT_ERR;\r | |
943 | \r | |
944 | } else {\r | |
945 | switch (fs->fs_type) {\r | |
946 | case FS_FAT12 :\r | |
947 | bc = (UINT)clst; bc += bc / 2;\r | |
948 | res = move_window(fs, fs->fatbase + (bc / SS(fs)));\r | |
949 | if (res != FR_OK) break;\r | |
7b78a5a2 | 950 | p = &fs->win[bc++ % SS(fs)];\r |
53668523 | 951 | *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;\r |
53668523 L |
952 | fs->wflag = 1;\r |
953 | res = move_window(fs, fs->fatbase + (bc / SS(fs)));\r | |
954 | if (res != FR_OK) break;\r | |
955 | p = &fs->win[bc % SS(fs)];\r | |
956 | *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));\r | |
7b78a5a2 | 957 | fs->wflag = 1;\r |
53668523 L |
958 | break;\r |
959 | \r | |
960 | case FS_FAT16 :\r | |
961 | res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));\r | |
962 | if (res != FR_OK) break;\r | |
963 | p = &fs->win[clst * 2 % SS(fs)];\r | |
964 | ST_WORD(p, (WORD)val);\r | |
7b78a5a2 | 965 | fs->wflag = 1;\r |
53668523 L |
966 | break;\r |
967 | \r | |
968 | case FS_FAT32 :\r | |
969 | res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));\r | |
970 | if (res != FR_OK) break;\r | |
971 | p = &fs->win[clst * 4 % SS(fs)];\r | |
972 | val |= LD_DWORD(p) & 0xF0000000;\r | |
973 | ST_DWORD(p, val);\r | |
7b78a5a2 | 974 | fs->wflag = 1;\r |
53668523 L |
975 | break;\r |
976 | \r | |
977 | default :\r | |
978 | res = FR_INT_ERR;\r | |
979 | }\r | |
53668523 L |
980 | }\r |
981 | \r | |
982 | return res;\r | |
983 | }\r | |
984 | #endif /* !_FS_READONLY */\r | |
985 | \r | |
986 | \r | |
987 | \r | |
988 | \r | |
989 | /*-----------------------------------------------------------------------*/\r | |
990 | /* FAT handling - Remove a cluster chain */\r | |
991 | /*-----------------------------------------------------------------------*/\r | |
992 | #if !_FS_READONLY\r | |
993 | static\r | |
994 | FRESULT remove_chain (\r | |
995 | FATFS* fs, /* File system object */\r | |
996 | DWORD clst /* Cluster# to remove a chain from */\r | |
997 | )\r | |
998 | {\r | |
999 | FRESULT res;\r | |
1000 | DWORD nxt;\r | |
7b78a5a2 | 1001 | #if _USE_TRIM\r |
53668523 L |
1002 | DWORD scl = clst, ecl = clst, rt[2];\r |
1003 | #endif\r | |
1004 | \r | |
1005 | if (clst < 2 || clst >= fs->n_fatent) { /* Check range */\r | |
1006 | res = FR_INT_ERR;\r | |
1007 | \r | |
1008 | } else {\r | |
1009 | res = FR_OK;\r | |
1010 | while (clst < fs->n_fatent) { /* Not a last link? */\r | |
1011 | nxt = get_fat(fs, clst); /* Get cluster status */\r | |
1012 | if (nxt == 0) break; /* Empty cluster? */\r | |
1013 | if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */\r | |
1014 | if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */\r | |
1015 | res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */\r | |
1016 | if (res != FR_OK) break;\r | |
1017 | if (fs->free_clust != 0xFFFFFFFF) { /* Update FSINFO */\r | |
1018 | fs->free_clust++;\r | |
1019 | fs->fsi_flag |= 1;\r | |
1020 | }\r | |
7b78a5a2 | 1021 | #if _USE_TRIM\r |
53668523 L |
1022 | if (ecl + 1 == nxt) { /* Is next cluster contiguous? */\r |
1023 | ecl = nxt;\r | |
1024 | } else { /* End of contiguous clusters */ \r | |
1025 | rt[0] = clust2sect(fs, scl); /* Start sector */\r | |
1026 | rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */\r | |
7b78a5a2 | 1027 | disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Erase the block */\r |
53668523 L |
1028 | scl = ecl = nxt;\r |
1029 | }\r | |
1030 | #endif\r | |
1031 | clst = nxt; /* Next cluster */\r | |
1032 | }\r | |
1033 | }\r | |
1034 | \r | |
1035 | return res;\r | |
1036 | }\r | |
1037 | #endif\r | |
1038 | \r | |
1039 | \r | |
1040 | \r | |
1041 | \r | |
1042 | /*-----------------------------------------------------------------------*/\r | |
1043 | /* FAT handling - Stretch or Create a cluster chain */\r | |
1044 | /*-----------------------------------------------------------------------*/\r | |
1045 | #if !_FS_READONLY\r | |
1046 | static\r | |
1047 | DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */\r | |
1048 | FATFS* fs, /* File system object */\r | |
1049 | DWORD clst /* Cluster# to stretch. 0 means create a new chain. */\r | |
1050 | )\r | |
1051 | {\r | |
1052 | DWORD cs, ncl, scl;\r | |
1053 | FRESULT res;\r | |
1054 | \r | |
1055 | \r | |
1056 | if (clst == 0) { /* Create a new chain */\r | |
1057 | scl = fs->last_clust; /* Get suggested start point */\r | |
1058 | if (!scl || scl >= fs->n_fatent) scl = 1;\r | |
1059 | }\r | |
1060 | else { /* Stretch the current chain */\r | |
1061 | cs = get_fat(fs, clst); /* Check the cluster status */\r | |
1062 | if (cs < 2) return 1; /* Invalid value */\r | |
1063 | if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */\r | |
1064 | if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */\r | |
1065 | scl = clst;\r | |
1066 | }\r | |
1067 | \r | |
1068 | ncl = scl; /* Start cluster */\r | |
1069 | for (;;) {\r | |
1070 | ncl++; /* Next cluster */\r | |
1071 | if (ncl >= fs->n_fatent) { /* Check wrap around */\r | |
1072 | ncl = 2;\r | |
1073 | if (ncl > scl) return 0; /* No free cluster */\r | |
1074 | }\r | |
1075 | cs = get_fat(fs, ncl); /* Get the cluster status */\r | |
1076 | if (cs == 0) break; /* Found a free cluster */\r | |
1077 | if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */\r | |
1078 | return cs;\r | |
1079 | if (ncl == scl) return 0; /* No free cluster */\r | |
1080 | }\r | |
1081 | \r | |
1082 | res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */\r | |
1083 | if (res == FR_OK && clst != 0) {\r | |
1084 | res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */\r | |
1085 | }\r | |
1086 | if (res == FR_OK) {\r | |
1087 | fs->last_clust = ncl; /* Update FSINFO */\r | |
1088 | if (fs->free_clust != 0xFFFFFFFF) {\r | |
1089 | fs->free_clust--;\r | |
1090 | fs->fsi_flag |= 1;\r | |
1091 | }\r | |
1092 | } else {\r | |
1093 | ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;\r | |
1094 | }\r | |
1095 | \r | |
1096 | return ncl; /* Return new cluster number or error code */\r | |
1097 | }\r | |
1098 | #endif /* !_FS_READONLY */\r | |
1099 | \r | |
1100 | \r | |
1101 | \r | |
1102 | \r | |
1103 | /*-----------------------------------------------------------------------*/\r | |
1104 | /* FAT handling - Convert offset into cluster with link map table */\r | |
1105 | /*-----------------------------------------------------------------------*/\r | |
1106 | \r | |
1107 | #if _USE_FASTSEEK\r | |
1108 | static\r | |
1109 | DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */\r | |
1110 | FIL* fp, /* Pointer to the file object */\r | |
1111 | DWORD ofs /* File offset to be converted to cluster# */\r | |
1112 | )\r | |
1113 | {\r | |
1114 | DWORD cl, ncl, *tbl;\r | |
1115 | \r | |
1116 | \r | |
1117 | tbl = fp->cltbl + 1; /* Top of CLMT */\r | |
1118 | cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */\r | |
1119 | for (;;) {\r | |
1120 | ncl = *tbl++; /* Number of cluters in the fragment */\r | |
1121 | if (!ncl) return 0; /* End of table? (error) */\r | |
1122 | if (cl < ncl) break; /* In this fragment? */\r | |
1123 | cl -= ncl; tbl++; /* Next fragment */\r | |
1124 | }\r | |
1125 | return cl + *tbl; /* Return the cluster number */\r | |
1126 | }\r | |
1127 | #endif /* _USE_FASTSEEK */\r | |
1128 | \r | |
1129 | \r | |
1130 | \r | |
1131 | \r | |
1132 | /*-----------------------------------------------------------------------*/\r | |
1133 | /* Directory handling - Set directory index */\r | |
1134 | /*-----------------------------------------------------------------------*/\r | |
1135 | \r | |
1136 | static\r | |
1137 | FRESULT dir_sdi (\r | |
1138 | DIR* dp, /* Pointer to directory object */\r | |
1139 | UINT idx /* Index of directory table */\r | |
1140 | )\r | |
1141 | {\r | |
1142 | DWORD clst, sect;\r | |
1143 | UINT ic;\r | |
1144 | \r | |
1145 | \r | |
1146 | dp->index = (WORD)idx; /* Current index */\r | |
1147 | clst = dp->sclust; /* Table start cluster (0:root) */\r | |
1148 | if (clst == 1 || clst >= dp->fs->n_fatent) /* Check start cluster range */\r | |
1149 | return FR_INT_ERR;\r | |
1150 | if (!clst && dp->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */\r | |
1151 | clst = dp->fs->dirbase;\r | |
1152 | \r | |
1153 | if (clst == 0) { /* Static table (root-directory in FAT12/16) */\r | |
1154 | if (idx >= dp->fs->n_rootdir) /* Is index out of range? */\r | |
1155 | return FR_INT_ERR;\r | |
1156 | sect = dp->fs->dirbase;\r | |
1157 | }\r | |
1158 | else { /* Dynamic table (root-directory in FAT32 or sub-directory) */\r | |
1159 | ic = SS(dp->fs) / SZ_DIR * dp->fs->csize; /* Entries per cluster */\r | |
1160 | while (idx >= ic) { /* Follow cluster chain */\r | |
1161 | clst = get_fat(dp->fs, clst); /* Get next cluster */\r | |
1162 | if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */\r | |
1163 | if (clst < 2 || clst >= dp->fs->n_fatent) /* Reached to end of table or internal error */\r | |
1164 | return FR_INT_ERR;\r | |
1165 | idx -= ic;\r | |
1166 | }\r | |
1167 | sect = clust2sect(dp->fs, clst);\r | |
1168 | }\r | |
1169 | dp->clust = clst; /* Current cluster# */\r | |
1170 | if (!sect) return FR_INT_ERR;\r | |
1171 | dp->sect = sect + idx / (SS(dp->fs) / SZ_DIR); /* Sector# of the directory entry */\r | |
1172 | dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIR)) * SZ_DIR; /* Ptr to the entry in the sector */\r | |
1173 | \r | |
1174 | return FR_OK;\r | |
1175 | }\r | |
1176 | \r | |
1177 | \r | |
1178 | \r | |
1179 | \r | |
1180 | /*-----------------------------------------------------------------------*/\r | |
1181 | /* Directory handling - Move directory table index next */\r | |
1182 | /*-----------------------------------------------------------------------*/\r | |
1183 | \r | |
1184 | static\r | |
1185 | FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */\r | |
1186 | DIR* dp, /* Pointer to the directory object */\r | |
1187 | int stretch /* 0: Do not stretch table, 1: Stretch table if needed */\r | |
1188 | )\r | |
1189 | {\r | |
1190 | DWORD clst;\r | |
1191 | UINT i;\r | |
1192 | \r | |
1193 | \r | |
1194 | i = dp->index + 1;\r | |
1195 | if (!(i & 0xFFFF) || !dp->sect) /* Report EOT when index has reached 65535 */\r | |
1196 | return FR_NO_FILE;\r | |
1197 | \r | |
1198 | if (!(i % (SS(dp->fs) / SZ_DIR))) { /* Sector changed? */\r | |
1199 | dp->sect++; /* Next sector */\r | |
1200 | \r | |
1201 | if (!dp->clust) { /* Static table */\r | |
1202 | if (i >= dp->fs->n_rootdir) /* Report EOT if it reached end of static table */\r | |
1203 | return FR_NO_FILE;\r | |
1204 | }\r | |
1205 | else { /* Dynamic table */\r | |
1206 | if (((i / (SS(dp->fs) / SZ_DIR)) & (dp->fs->csize - 1)) == 0) { /* Cluster changed? */\r | |
1207 | clst = get_fat(dp->fs, dp->clust); /* Get next cluster */\r | |
1208 | if (clst <= 1) return FR_INT_ERR;\r | |
1209 | if (clst == 0xFFFFFFFF) return FR_DISK_ERR;\r | |
1210 | if (clst >= dp->fs->n_fatent) { /* If it reached end of dynamic table, */\r | |
1211 | #if !_FS_READONLY\r | |
1212 | UINT c;\r | |
1213 | if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT */\r | |
1214 | clst = create_chain(dp->fs, dp->clust); /* Stretch cluster chain */\r | |
1215 | if (clst == 0) return FR_DENIED; /* No free cluster */\r | |
1216 | if (clst == 1) return FR_INT_ERR;\r | |
1217 | if (clst == 0xFFFFFFFF) return FR_DISK_ERR;\r | |
1218 | /* Clean-up stretched table */\r | |
1219 | if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */\r | |
1220 | mem_set(dp->fs->win, 0, SS(dp->fs)); /* Clear window buffer */\r | |
1221 | dp->fs->winsect = clust2sect(dp->fs, clst); /* Cluster start sector */\r | |
1222 | for (c = 0; c < dp->fs->csize; c++) { /* Fill the new cluster with 0 */\r | |
1223 | dp->fs->wflag = 1;\r | |
1224 | if (sync_window(dp->fs)) return FR_DISK_ERR;\r | |
1225 | dp->fs->winsect++;\r | |
1226 | }\r | |
1227 | dp->fs->winsect -= c; /* Rewind window offset */\r | |
1228 | #else\r | |
1229 | if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT (this is to suppress warning) */\r | |
1230 | return FR_NO_FILE; /* Report EOT */\r | |
1231 | #endif\r | |
1232 | }\r | |
1233 | dp->clust = clst; /* Initialize data for new cluster */\r | |
1234 | dp->sect = clust2sect(dp->fs, clst);\r | |
1235 | }\r | |
1236 | }\r | |
1237 | }\r | |
1238 | \r | |
1239 | dp->index = (WORD)i; /* Current index */\r | |
1240 | dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIR)) * SZ_DIR; /* Current entry in the window */\r | |
1241 | \r | |
1242 | return FR_OK;\r | |
1243 | }\r | |
1244 | \r | |
1245 | \r | |
1246 | \r | |
1247 | \r | |
1248 | /*-----------------------------------------------------------------------*/\r | |
1249 | /* Directory handling - Reserve directory entry */\r | |
1250 | /*-----------------------------------------------------------------------*/\r | |
1251 | \r | |
1252 | #if !_FS_READONLY\r | |
1253 | static\r | |
1254 | FRESULT dir_alloc (\r | |
1255 | DIR* dp, /* Pointer to the directory object */\r | |
1256 | UINT nent /* Number of contiguous entries to allocate (1-21) */\r | |
1257 | )\r | |
1258 | {\r | |
1259 | FRESULT res;\r | |
1260 | UINT n;\r | |
1261 | \r | |
1262 | \r | |
1263 | res = dir_sdi(dp, 0);\r | |
1264 | if (res == FR_OK) {\r | |
1265 | n = 0;\r | |
1266 | do {\r | |
1267 | res = move_window(dp->fs, dp->sect);\r | |
1268 | if (res != FR_OK) break;\r | |
7b78a5a2 L |
1269 | if (dp->dir[0] == DDE || dp->dir[0] == 0) { /* Is it a free entry? */\r |
1270 | if (++n == nent) break; /* A block of contiguous free entries is found */\r | |
53668523 L |
1271 | } else {\r |
1272 | n = 0; /* Not a blank entry. Restart to search */\r | |
1273 | }\r | |
1274 | res = dir_next(dp, 1); /* Next entry with table stretch enabled */\r | |
1275 | } while (res == FR_OK);\r | |
1276 | }\r | |
1277 | if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */\r | |
1278 | return res;\r | |
1279 | }\r | |
1280 | #endif\r | |
1281 | \r | |
1282 | \r | |
1283 | \r | |
1284 | \r | |
1285 | /*-----------------------------------------------------------------------*/\r | |
1286 | /* Directory handling - Load/Store start cluster number */\r | |
1287 | /*-----------------------------------------------------------------------*/\r | |
1288 | \r | |
1289 | static\r | |
1290 | DWORD ld_clust (\r | |
1291 | FATFS* fs, /* Pointer to the fs object */\r | |
1292 | BYTE* dir /* Pointer to the directory entry */\r | |
1293 | )\r | |
1294 | {\r | |
1295 | DWORD cl;\r | |
1296 | \r | |
1297 | cl = LD_WORD(dir+DIR_FstClusLO);\r | |
1298 | if (fs->fs_type == FS_FAT32)\r | |
1299 | cl |= (DWORD)LD_WORD(dir+DIR_FstClusHI) << 16;\r | |
1300 | \r | |
1301 | return cl;\r | |
1302 | }\r | |
1303 | \r | |
1304 | \r | |
1305 | #if !_FS_READONLY\r | |
1306 | static\r | |
1307 | void st_clust (\r | |
1308 | BYTE* dir, /* Pointer to the directory entry */\r | |
1309 | DWORD cl /* Value to be set */\r | |
1310 | )\r | |
1311 | {\r | |
1312 | ST_WORD(dir+DIR_FstClusLO, cl);\r | |
1313 | ST_WORD(dir+DIR_FstClusHI, cl >> 16);\r | |
1314 | }\r | |
1315 | #endif\r | |
1316 | \r | |
1317 | \r | |
1318 | \r | |
1319 | \r | |
1320 | /*-----------------------------------------------------------------------*/\r | |
1321 | /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */\r | |
1322 | /*-----------------------------------------------------------------------*/\r | |
1323 | #if _USE_LFN\r | |
1324 | static\r | |
1325 | const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */\r | |
1326 | \r | |
1327 | \r | |
1328 | static\r | |
1329 | int cmp_lfn ( /* 1:Matched, 0:Not matched */\r | |
1330 | WCHAR* lfnbuf, /* Pointer to the LFN to be compared */\r | |
1331 | BYTE* dir /* Pointer to the directory entry containing a part of LFN */\r | |
1332 | )\r | |
1333 | {\r | |
1334 | UINT i, s;\r | |
1335 | WCHAR wc, uc;\r | |
1336 | \r | |
1337 | \r | |
1338 | i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13; /* Get offset in the LFN buffer */\r | |
1339 | s = 0; wc = 1;\r | |
1340 | do {\r | |
1341 | uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */\r | |
1342 | if (wc) { /* Last character has not been processed */\r | |
1343 | wc = ff_wtoupper(uc); /* Convert it to upper case */\r | |
1344 | if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */\r | |
1345 | return 0; /* Not matched */\r | |
1346 | } else {\r | |
1347 | if (uc != 0xFFFF) return 0; /* Check filler */\r | |
1348 | }\r | |
1349 | } while (++s < 13); /* Repeat until all characters in the entry are checked */\r | |
1350 | \r | |
1351 | if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i]) /* Last segment matched but different length */\r | |
1352 | return 0;\r | |
1353 | \r | |
1354 | return 1; /* The part of LFN matched */\r | |
1355 | }\r | |
1356 | \r | |
1357 | \r | |
1358 | \r | |
1359 | static\r | |
1360 | int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */\r | |
1361 | WCHAR* lfnbuf, /* Pointer to the Unicode-LFN buffer */\r | |
1362 | BYTE* dir /* Pointer to the directory entry */\r | |
1363 | )\r | |
1364 | {\r | |
1365 | UINT i, s;\r | |
1366 | WCHAR wc, uc;\r | |
1367 | \r | |
1368 | \r | |
1369 | i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */\r | |
1370 | \r | |
1371 | s = 0; wc = 1;\r | |
1372 | do {\r | |
1373 | uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */\r | |
1374 | if (wc) { /* Last character has not been processed */\r | |
1375 | if (i >= _MAX_LFN) return 0; /* Buffer overflow? */\r | |
1376 | lfnbuf[i++] = wc = uc; /* Store it */\r | |
1377 | } else {\r | |
1378 | if (uc != 0xFFFF) return 0; /* Check filler */\r | |
1379 | }\r | |
1380 | } while (++s < 13); /* Read all character in the entry */\r | |
1381 | \r | |
1382 | if (dir[LDIR_Ord] & LLE) { /* Put terminator if it is the last LFN part */\r | |
1383 | if (i >= _MAX_LFN) return 0; /* Buffer overflow? */\r | |
1384 | lfnbuf[i] = 0;\r | |
1385 | }\r | |
1386 | \r | |
1387 | return 1;\r | |
1388 | }\r | |
1389 | \r | |
1390 | \r | |
1391 | #if !_FS_READONLY\r | |
1392 | static\r | |
1393 | void fit_lfn (\r | |
1394 | const WCHAR* lfnbuf, /* Pointer to the LFN buffer */\r | |
1395 | BYTE* dir, /* Pointer to the directory entry */\r | |
1396 | BYTE ord, /* LFN order (1-20) */\r | |
1397 | BYTE sum /* SFN sum */\r | |
1398 | )\r | |
1399 | {\r | |
1400 | UINT i, s;\r | |
1401 | WCHAR wc;\r | |
1402 | \r | |
1403 | \r | |
1404 | dir[LDIR_Chksum] = sum; /* Set check sum */\r | |
1405 | dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */\r | |
1406 | dir[LDIR_Type] = 0;\r | |
1407 | ST_WORD(dir+LDIR_FstClusLO, 0);\r | |
1408 | \r | |
1409 | i = (ord - 1) * 13; /* Get offset in the LFN buffer */\r | |
1410 | s = wc = 0;\r | |
1411 | do {\r | |
1412 | if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective character */\r | |
1413 | ST_WORD(dir+LfnOfs[s], wc); /* Put it */\r | |
1414 | if (!wc) wc = 0xFFFF; /* Padding characters following last character */\r | |
1415 | } while (++s < 13);\r | |
1416 | if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE; /* Bottom LFN part is the start of LFN sequence */\r | |
1417 | dir[LDIR_Ord] = ord; /* Set the LFN order */\r | |
1418 | }\r | |
1419 | \r | |
1420 | #endif\r | |
1421 | #endif\r | |
1422 | \r | |
1423 | \r | |
1424 | \r | |
1425 | \r | |
1426 | /*-----------------------------------------------------------------------*/\r | |
1427 | /* Create numbered name */\r | |
1428 | /*-----------------------------------------------------------------------*/\r | |
1429 | #if _USE_LFN\r | |
1430 | static\r | |
1431 | void gen_numname (\r | |
1432 | BYTE* dst, /* Pointer to the buffer to store numbered SFN */\r | |
1433 | const BYTE* src, /* Pointer to SFN */\r | |
1434 | const WCHAR* lfn, /* Pointer to LFN */\r | |
1435 | UINT seq /* Sequence number */\r | |
1436 | )\r | |
1437 | {\r | |
1438 | BYTE ns[8], c;\r | |
1439 | UINT i, j;\r | |
1440 | \r | |
1441 | \r | |
1442 | mem_cpy(dst, src, 11);\r | |
1443 | \r | |
1444 | if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */\r | |
1445 | WCHAR wc;\r | |
1446 | DWORD sr = seq;\r | |
1447 | \r | |
1448 | while (*lfn) { /* Create a CRC */\r | |
1449 | wc = *lfn++;\r | |
1450 | for (i = 0; i < 16; i++) {\r | |
1451 | sr = (sr << 1) + (wc & 1);\r | |
1452 | wc >>= 1;\r | |
1453 | if (sr & 0x10000) sr ^= 0x11021;\r | |
1454 | }\r | |
1455 | }\r | |
1456 | seq = (UINT)sr;\r | |
1457 | }\r | |
1458 | \r | |
1459 | /* itoa (hexdecimal) */\r | |
1460 | i = 7;\r | |
1461 | do {\r | |
1462 | c = (seq % 16) + '0';\r | |
1463 | if (c > '9') c += 7;\r | |
1464 | ns[i--] = c;\r | |
1465 | seq /= 16;\r | |
1466 | } while (seq);\r | |
1467 | ns[i] = '~';\r | |
1468 | \r | |
1469 | /* Append the number */\r | |
1470 | for (j = 0; j < i && dst[j] != ' '; j++) {\r | |
1471 | if (IsDBCS1(dst[j])) {\r | |
1472 | if (j == i - 1) break;\r | |
1473 | j++;\r | |
1474 | }\r | |
1475 | }\r | |
1476 | do {\r | |
1477 | dst[j++] = (i < 8) ? ns[i++] : ' ';\r | |
1478 | } while (j < 8);\r | |
1479 | }\r | |
1480 | #endif\r | |
1481 | \r | |
1482 | \r | |
1483 | \r | |
1484 | \r | |
1485 | /*-----------------------------------------------------------------------*/\r | |
1486 | /* Calculate sum of an SFN */\r | |
1487 | /*-----------------------------------------------------------------------*/\r | |
1488 | #if _USE_LFN\r | |
1489 | static\r | |
1490 | BYTE sum_sfn (\r | |
1491 | const BYTE* dir /* Pointer to the SFN entry */\r | |
1492 | )\r | |
1493 | {\r | |
1494 | BYTE sum = 0;\r | |
1495 | UINT n = 11;\r | |
1496 | \r | |
1497 | do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);\r | |
1498 | return sum;\r | |
1499 | }\r | |
1500 | #endif\r | |
1501 | \r | |
1502 | \r | |
1503 | \r | |
1504 | \r | |
1505 | /*-----------------------------------------------------------------------*/\r | |
1506 | /* Directory handling - Find an object in the directory */\r | |
1507 | /*-----------------------------------------------------------------------*/\r | |
1508 | \r | |
1509 | static\r | |
1510 | FRESULT dir_find (\r | |
1511 | DIR* dp /* Pointer to the directory object linked to the file name */\r | |
1512 | )\r | |
1513 | {\r | |
1514 | FRESULT res;\r | |
1515 | BYTE c, *dir;\r | |
1516 | #if _USE_LFN\r | |
1517 | BYTE a, ord, sum;\r | |
1518 | #endif\r | |
1519 | \r | |
1520 | res = dir_sdi(dp, 0); /* Rewind directory object */\r | |
1521 | if (res != FR_OK) return res;\r | |
1522 | \r | |
1523 | #if _USE_LFN\r | |
1524 | ord = sum = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */\r | |
1525 | #endif\r | |
1526 | do {\r | |
1527 | res = move_window(dp->fs, dp->sect);\r | |
1528 | if (res != FR_OK) break;\r | |
1529 | dir = dp->dir; /* Ptr to the directory entry of current index */\r | |
1530 | c = dir[DIR_Name];\r | |
1531 | if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */\r | |
1532 | #if _USE_LFN /* LFN configuration */\r | |
1533 | a = dir[DIR_Attr] & AM_MASK;\r | |
1534 | if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */\r | |
1535 | ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */\r | |
1536 | } else {\r | |
1537 | if (a == AM_LFN) { /* An LFN entry is found */\r | |
1538 | if (dp->lfn) {\r | |
1539 | if (c & LLE) { /* Is it start of LFN sequence? */\r | |
1540 | sum = dir[LDIR_Chksum];\r | |
1541 | c &= ~LLE; ord = c; /* LFN start order */\r | |
1542 | dp->lfn_idx = dp->index; /* Start index of LFN */\r | |
1543 | }\r | |
1544 | /* Check validity of the LFN entry and compare it with given name */\r | |
1545 | ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;\r | |
1546 | }\r | |
1547 | } else { /* An SFN entry is found */\r | |
1548 | if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */\r | |
7b78a5a2 | 1549 | if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break; /* SFN matched? */\r |
53668523 L |
1550 | ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */\r |
1551 | }\r | |
1552 | }\r | |
1553 | #else /* Non LFN configuration */\r | |
1554 | if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */\r | |
1555 | break;\r | |
1556 | #endif\r | |
1557 | res = dir_next(dp, 0); /* Next entry */\r | |
1558 | } while (res == FR_OK);\r | |
1559 | \r | |
1560 | return res;\r | |
1561 | }\r | |
1562 | \r | |
1563 | \r | |
1564 | \r | |
1565 | \r | |
1566 | /*-----------------------------------------------------------------------*/\r | |
1567 | /* Read an object from the directory */\r | |
1568 | /*-----------------------------------------------------------------------*/\r | |
1569 | #if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2\r | |
1570 | static\r | |
1571 | FRESULT dir_read (\r | |
1572 | DIR* dp, /* Pointer to the directory object */\r | |
1573 | int vol /* Filtered by 0:file/directory or 1:volume label */\r | |
1574 | )\r | |
1575 | {\r | |
1576 | FRESULT res;\r | |
1577 | BYTE a, c, *dir;\r | |
1578 | #if _USE_LFN\r | |
1579 | BYTE ord = 0xFF, sum = 0xFF;\r | |
1580 | #endif\r | |
1581 | \r | |
1582 | res = FR_NO_FILE;\r | |
1583 | while (dp->sect) {\r | |
1584 | res = move_window(dp->fs, dp->sect);\r | |
1585 | if (res != FR_OK) break;\r | |
1586 | dir = dp->dir; /* Ptr to the directory entry of current index */\r | |
1587 | c = dir[DIR_Name];\r | |
1588 | if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */\r | |
1589 | a = dir[DIR_Attr] & AM_MASK;\r | |
1590 | #if _USE_LFN /* LFN configuration */\r | |
7b78a5a2 | 1591 | if (c == DDE || (!_FS_RPATH && c == '.') || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */\r |
53668523 L |
1592 | ord = 0xFF;\r |
1593 | } else {\r | |
1594 | if (a == AM_LFN) { /* An LFN entry is found */\r | |
1595 | if (c & LLE) { /* Is it start of LFN sequence? */\r | |
1596 | sum = dir[LDIR_Chksum];\r | |
1597 | c &= ~LLE; ord = c;\r | |
1598 | dp->lfn_idx = dp->index;\r | |
1599 | }\r | |
1600 | /* Check LFN validity and capture it */\r | |
1601 | ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;\r | |
1602 | } else { /* An SFN entry is found */\r | |
1603 | if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */\r | |
1604 | dp->lfn_idx = 0xFFFF; /* It has no LFN. */\r | |
1605 | break;\r | |
1606 | }\r | |
1607 | }\r | |
1608 | #else /* Non LFN configuration */\r | |
7b78a5a2 | 1609 | if (c != DDE && (_FS_RPATH || c != '.') && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) /* Is it a valid entry? */\r |
53668523 L |
1610 | break;\r |
1611 | #endif\r | |
1612 | res = dir_next(dp, 0); /* Next entry */\r | |
1613 | if (res != FR_OK) break;\r | |
1614 | }\r | |
1615 | \r | |
1616 | if (res != FR_OK) dp->sect = 0;\r | |
1617 | \r | |
1618 | return res;\r | |
1619 | }\r | |
1620 | #endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */\r | |
1621 | \r | |
1622 | \r | |
1623 | \r | |
1624 | \r | |
1625 | /*-----------------------------------------------------------------------*/\r | |
1626 | /* Register an object to the directory */\r | |
1627 | /*-----------------------------------------------------------------------*/\r | |
1628 | #if !_FS_READONLY\r | |
1629 | static\r | |
1630 | FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */\r | |
1631 | DIR* dp /* Target directory with object name to be created */\r | |
1632 | )\r | |
1633 | {\r | |
1634 | FRESULT res;\r | |
1635 | #if _USE_LFN /* LFN configuration */\r | |
1636 | UINT n, nent;\r | |
1637 | BYTE sn[12], *fn, sum;\r | |
1638 | WCHAR *lfn;\r | |
1639 | \r | |
1640 | \r | |
1641 | fn = dp->fn; lfn = dp->lfn;\r | |
1642 | mem_cpy(sn, fn, 12);\r | |
1643 | \r | |
7b78a5a2 | 1644 | if (_FS_RPATH && (sn[NSFLAG] & NS_DOT)) /* Cannot create dot entry */\r |
53668523 L |
1645 | return FR_INVALID_NAME;\r |
1646 | \r | |
7b78a5a2 L |
1647 | if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */\r |
1648 | fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */\r | |
53668523 L |
1649 | for (n = 1; n < 100; n++) {\r |
1650 | gen_numname(fn, sn, lfn, n); /* Generate a numbered name */\r | |
1651 | res = dir_find(dp); /* Check if the name collides with existing SFN */\r | |
1652 | if (res != FR_OK) break;\r | |
1653 | }\r | |
1654 | if (n == 100) return FR_DENIED; /* Abort if too many collisions */\r | |
1655 | if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */\r | |
7b78a5a2 | 1656 | fn[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn;\r |
53668523 L |
1657 | }\r |
1658 | \r | |
7b78a5a2 | 1659 | if (sn[NSFLAG] & NS_LFN) { /* When LFN is to be created, allocate entries for an SFN + LFNs. */\r |
53668523 L |
1660 | for (n = 0; lfn[n]; n++) ;\r |
1661 | nent = (n + 25) / 13;\r | |
1662 | } else { /* Otherwise allocate an entry for an SFN */\r | |
1663 | nent = 1;\r | |
1664 | }\r | |
1665 | res = dir_alloc(dp, nent); /* Allocate entries */\r | |
1666 | \r | |
1667 | if (res == FR_OK && --nent) { /* Set LFN entry if needed */\r | |
1668 | res = dir_sdi(dp, dp->index - nent);\r | |
1669 | if (res == FR_OK) {\r | |
1670 | sum = sum_sfn(dp->fn); /* Sum value of the SFN tied to the LFN */\r | |
1671 | do { /* Store LFN entries in bottom first */\r | |
1672 | res = move_window(dp->fs, dp->sect);\r | |
1673 | if (res != FR_OK) break;\r | |
1674 | fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum);\r | |
1675 | dp->fs->wflag = 1;\r | |
1676 | res = dir_next(dp, 0); /* Next entry */\r | |
1677 | } while (res == FR_OK && --nent);\r | |
1678 | }\r | |
1679 | }\r | |
1680 | #else /* Non LFN configuration */\r | |
1681 | res = dir_alloc(dp, 1); /* Allocate an entry for SFN */\r | |
1682 | #endif\r | |
1683 | \r | |
1684 | if (res == FR_OK) { /* Set SFN entry */\r | |
1685 | res = move_window(dp->fs, dp->sect);\r | |
1686 | if (res == FR_OK) {\r | |
1687 | mem_set(dp->dir, 0, SZ_DIR); /* Clean the entry */\r | |
1688 | mem_cpy(dp->dir, dp->fn, 11); /* Put SFN */\r | |
1689 | #if _USE_LFN\r | |
7b78a5a2 | 1690 | dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */\r |
53668523 L |
1691 | #endif\r |
1692 | dp->fs->wflag = 1;\r | |
1693 | }\r | |
1694 | }\r | |
1695 | \r | |
1696 | return res;\r | |
1697 | }\r | |
1698 | #endif /* !_FS_READONLY */\r | |
1699 | \r | |
1700 | \r | |
1701 | \r | |
1702 | \r | |
1703 | /*-----------------------------------------------------------------------*/\r | |
1704 | /* Remove an object from the directory */\r | |
1705 | /*-----------------------------------------------------------------------*/\r | |
1706 | #if !_FS_READONLY && !_FS_MINIMIZE\r | |
1707 | static\r | |
1708 | FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */\r | |
1709 | DIR* dp /* Directory object pointing the entry to be removed */\r | |
1710 | )\r | |
1711 | {\r | |
1712 | FRESULT res;\r | |
1713 | #if _USE_LFN /* LFN configuration */\r | |
1714 | UINT i;\r | |
1715 | \r | |
1716 | i = dp->index; /* SFN index */\r | |
1717 | res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx); /* Goto the SFN or top of the LFN entries */\r | |
1718 | if (res == FR_OK) {\r | |
1719 | do {\r | |
1720 | res = move_window(dp->fs, dp->sect);\r | |
1721 | if (res != FR_OK) break;\r | |
1722 | mem_set(dp->dir, 0, SZ_DIR); /* Clear and mark the entry "deleted" */\r | |
1723 | *dp->dir = DDE;\r | |
1724 | dp->fs->wflag = 1;\r | |
1725 | if (dp->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */\r | |
1726 | res = dir_next(dp, 0); /* Next entry */\r | |
1727 | } while (res == FR_OK);\r | |
1728 | if (res == FR_NO_FILE) res = FR_INT_ERR;\r | |
1729 | }\r | |
1730 | \r | |
1731 | #else /* Non LFN configuration */\r | |
1732 | res = dir_sdi(dp, dp->index);\r | |
1733 | if (res == FR_OK) {\r | |
1734 | res = move_window(dp->fs, dp->sect);\r | |
1735 | if (res == FR_OK) {\r | |
1736 | mem_set(dp->dir, 0, SZ_DIR); /* Clear and mark the entry "deleted" */\r | |
1737 | *dp->dir = DDE;\r | |
1738 | dp->fs->wflag = 1;\r | |
1739 | }\r | |
1740 | }\r | |
1741 | #endif\r | |
1742 | \r | |
1743 | return res;\r | |
1744 | }\r | |
1745 | #endif /* !_FS_READONLY */\r | |
1746 | \r | |
1747 | \r | |
1748 | \r | |
1749 | \r | |
1750 | /*-----------------------------------------------------------------------*/\r | |
1751 | /* Get file information from directory entry */\r | |
1752 | /*-----------------------------------------------------------------------*/\r | |
1753 | #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2\r | |
1754 | static\r | |
1755 | void get_fileinfo ( /* No return code */\r | |
1756 | DIR* dp, /* Pointer to the directory object */\r | |
1757 | FILINFO* fno /* Pointer to the file information to be filled */\r | |
1758 | )\r | |
1759 | {\r | |
1760 | UINT i;\r | |
1761 | TCHAR *p, c;\r | |
1762 | \r | |
1763 | \r | |
1764 | p = fno->fname;\r | |
1765 | if (dp->sect) { /* Get SFN */\r | |
1766 | BYTE *dir = dp->dir;\r | |
1767 | \r | |
1768 | i = 0;\r | |
1769 | while (i < 11) { /* Copy name body and extension */\r | |
1770 | c = (TCHAR)dir[i++];\r | |
1771 | if (c == ' ') continue; /* Skip padding spaces */\r | |
1772 | if (c == NDDE) c = (TCHAR)DDE; /* Restore replaced DDE character */\r | |
1773 | if (i == 9) *p++ = '.'; /* Insert a . if extension is exist */\r | |
1774 | #if _USE_LFN\r | |
1775 | if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY)))\r | |
1776 | c += 0x20; /* To lower */\r | |
1777 | #if _LFN_UNICODE\r | |
1778 | if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i]))\r | |
1779 | c = c << 8 | dir[i++];\r | |
1780 | c = ff_convert(c, 1); /* OEM -> Unicode */\r | |
1781 | if (!c) c = '?';\r | |
1782 | #endif\r | |
1783 | #endif\r | |
1784 | *p++ = c;\r | |
1785 | }\r | |
1786 | fno->fattrib = dir[DIR_Attr]; /* Attribute */\r | |
1787 | fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */\r | |
1788 | fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */\r | |
1789 | fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */\r | |
1790 | }\r | |
1791 | *p = 0; /* Terminate SFN string by a \0 */\r | |
1792 | \r | |
1793 | #if _USE_LFN\r | |
1794 | if (fno->lfname) {\r | |
1795 | WCHAR w, *lfn;\r | |
1796 | \r | |
1797 | i = 0; p = fno->lfname;\r | |
1798 | if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */\r | |
1799 | lfn = dp->lfn;\r | |
1800 | while ((w = *lfn++) != 0) { /* Get an LFN character */\r | |
1801 | #if !_LFN_UNICODE\r | |
1802 | w = ff_convert(w, 0); /* Unicode -> OEM */\r | |
1803 | if (!w) { i = 0; break; } /* No LFN if it could not be converted */\r | |
1804 | if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */\r | |
1805 | p[i++] = (TCHAR)(w >> 8);\r | |
1806 | #endif\r | |
1807 | if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */\r | |
1808 | p[i++] = (TCHAR)w;\r | |
1809 | }\r | |
1810 | }\r | |
1811 | p[i] = 0; /* Terminate LFN string by a \0 */\r | |
1812 | }\r | |
1813 | #endif\r | |
1814 | }\r | |
1815 | #endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2*/\r | |
1816 | \r | |
1817 | \r | |
1818 | \r | |
1819 | \r | |
1820 | /*-----------------------------------------------------------------------*/\r | |
1821 | /* Pick a segment and create the object name in directory form */\r | |
1822 | /*-----------------------------------------------------------------------*/\r | |
1823 | \r | |
1824 | static\r | |
1825 | FRESULT create_name (\r | |
1826 | DIR* dp, /* Pointer to the directory object */\r | |
1827 | const TCHAR** path /* Pointer to pointer to the segment in the path string */\r | |
1828 | )\r | |
1829 | {\r | |
1830 | #if _USE_LFN /* LFN configuration */\r | |
1831 | BYTE b, cf;\r | |
1832 | WCHAR w, *lfn;\r | |
1833 | UINT i, ni, si, di;\r | |
1834 | const TCHAR *p;\r | |
1835 | \r | |
1836 | /* Create LFN in Unicode */\r | |
1837 | for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */\r | |
1838 | lfn = dp->lfn;\r | |
1839 | si = di = 0;\r | |
1840 | for (;;) {\r | |
1841 | w = p[si++]; /* Get a character */\r | |
1842 | if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */\r | |
1843 | if (di >= _MAX_LFN) /* Reject too long name */\r | |
1844 | return FR_INVALID_NAME;\r | |
1845 | #if !_LFN_UNICODE\r | |
1846 | w &= 0xFF;\r | |
1847 | if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */\r | |
1848 | b = (BYTE)p[si++]; /* Get 2nd byte */\r | |
1849 | if (!IsDBCS2(b))\r | |
1850 | return FR_INVALID_NAME; /* Reject invalid sequence */\r | |
1851 | w = (w << 8) + b; /* Create a DBC */\r | |
1852 | }\r | |
1853 | w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */\r | |
1854 | if (!w) return FR_INVALID_NAME; /* Reject invalid code */\r | |
1855 | #endif\r | |
1856 | if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */\r | |
1857 | return FR_INVALID_NAME;\r | |
1858 | lfn[di++] = w; /* Store the Unicode character */\r | |
1859 | }\r | |
1860 | *path = &p[si]; /* Return pointer to the next segment */\r | |
1861 | cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */\r | |
1862 | #if _FS_RPATH\r | |
1863 | if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */\r | |
1864 | (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) {\r | |
1865 | lfn[di] = 0;\r | |
1866 | for (i = 0; i < 11; i++)\r | |
1867 | dp->fn[i] = (i < di) ? '.' : ' ';\r | |
1868 | dp->fn[i] = cf | NS_DOT; /* This is a dot entry */\r | |
1869 | return FR_OK;\r | |
1870 | }\r | |
1871 | #endif\r | |
1872 | while (di) { /* Strip trailing spaces and dots */\r | |
1873 | w = lfn[di-1];\r | |
1874 | if (w != ' ' && w != '.') break;\r | |
1875 | di--;\r | |
1876 | }\r | |
1877 | if (!di) return FR_INVALID_NAME; /* Reject nul string */\r | |
1878 | \r | |
1879 | lfn[di] = 0; /* LFN is created */\r | |
1880 | \r | |
1881 | /* Create SFN in directory form */\r | |
1882 | mem_set(dp->fn, ' ', 11);\r | |
1883 | for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */\r | |
1884 | if (si) cf |= NS_LOSS | NS_LFN;\r | |
1885 | while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */\r | |
1886 | \r | |
1887 | b = i = 0; ni = 8;\r | |
1888 | for (;;) {\r | |
1889 | w = lfn[si++]; /* Get an LFN character */\r | |
1890 | if (!w) break; /* Break on end of the LFN */\r | |
1891 | if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */\r | |
1892 | cf |= NS_LOSS | NS_LFN; continue;\r | |
1893 | }\r | |
1894 | \r | |
1895 | if (i >= ni || si == di) { /* Extension or end of SFN */\r | |
1896 | if (ni == 11) { /* Long extension */\r | |
1897 | cf |= NS_LOSS | NS_LFN; break;\r | |
1898 | }\r | |
1899 | if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */\r | |
1900 | if (si > di) break; /* No extension */\r | |
1901 | si = di; i = 8; ni = 11; /* Enter extension section */\r | |
1902 | b <<= 2; continue;\r | |
1903 | }\r | |
1904 | \r | |
1905 | if (w >= 0x80) { /* Non ASCII character */\r | |
1906 | #ifdef _EXCVT\r | |
1907 | w = ff_convert(w, 0); /* Unicode -> OEM code */\r | |
1908 | if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */\r | |
1909 | #else\r | |
1910 | w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */\r | |
1911 | #endif\r | |
1912 | cf |= NS_LFN; /* Force create LFN entry */\r | |
1913 | }\r | |
1914 | \r | |
1915 | if (_DF1S && w >= 0x100) { /* Double byte character (always false on SBCS cfg) */\r | |
1916 | if (i >= ni - 1) {\r | |
1917 | cf |= NS_LOSS | NS_LFN; i = ni; continue;\r | |
1918 | }\r | |
1919 | dp->fn[i++] = (BYTE)(w >> 8);\r | |
1920 | } else { /* Single byte character */\r | |
1921 | if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */\r | |
1922 | w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */\r | |
1923 | } else {\r | |
1924 | if (IsUpper(w)) { /* ASCII large capital */\r | |
1925 | b |= 2;\r | |
1926 | } else {\r | |
1927 | if (IsLower(w)) { /* ASCII small capital */\r | |
1928 | b |= 1; w -= 0x20;\r | |
1929 | }\r | |
1930 | }\r | |
1931 | }\r | |
1932 | }\r | |
1933 | dp->fn[i++] = (BYTE)w;\r | |
1934 | }\r | |
1935 | \r | |
1936 | if (dp->fn[0] == DDE) dp->fn[0] = NDDE; /* If the first character collides with deleted mark, replace it with 0x05 */\r | |
1937 | \r | |
1938 | if (ni == 8) b <<= 2;\r | |
1939 | if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */\r | |
1940 | cf |= NS_LFN;\r | |
1941 | if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */\r | |
1942 | if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */\r | |
1943 | if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */\r | |
1944 | }\r | |
1945 | \r | |
7b78a5a2 | 1946 | dp->fn[NSFLAG] = cf; /* SFN is created */\r |
53668523 L |
1947 | \r |
1948 | return FR_OK;\r | |
1949 | \r | |
1950 | \r | |
1951 | #else /* Non-LFN configuration */\r | |
1952 | BYTE b, c, d, *sfn;\r | |
1953 | UINT ni, si, i;\r | |
1954 | const char *p;\r | |
1955 | \r | |
1956 | /* Create file name in directory form */\r | |
1957 | for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */\r | |
1958 | sfn = dp->fn;\r | |
1959 | mem_set(sfn, ' ', 11);\r | |
1960 | si = i = b = 0; ni = 8;\r | |
1961 | #if _FS_RPATH\r | |
1962 | if (p[si] == '.') { /* Is this a dot entry? */\r | |
1963 | for (;;) {\r | |
1964 | c = (BYTE)p[si++];\r | |
1965 | if (c != '.' || si >= 3) break;\r | |
1966 | sfn[i++] = c;\r | |
1967 | }\r | |
1968 | if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;\r | |
1969 | *path = &p[si]; /* Return pointer to the next segment */\r | |
7b78a5a2 | 1970 | sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */\r |
53668523 L |
1971 | return FR_OK;\r |
1972 | }\r | |
1973 | #endif\r | |
1974 | for (;;) {\r | |
1975 | c = (BYTE)p[si++];\r | |
1976 | if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */\r | |
1977 | if (c == '.' || i >= ni) {\r | |
1978 | if (ni != 8 || c != '.') return FR_INVALID_NAME;\r | |
1979 | i = 8; ni = 11;\r | |
1980 | b <<= 2; continue;\r | |
1981 | }\r | |
1982 | if (c >= 0x80) { /* Extended character? */\r | |
1983 | b |= 3; /* Eliminate NT flag */\r | |
1984 | #ifdef _EXCVT\r | |
1985 | c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */\r | |
1986 | #else\r | |
1987 | #if !_DF1S\r | |
1988 | return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */\r | |
1989 | #endif\r | |
1990 | #endif\r | |
1991 | }\r | |
1992 | if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */\r | |
1993 | d = (BYTE)p[si++]; /* Get 2nd byte */\r | |
1994 | if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */\r | |
1995 | return FR_INVALID_NAME;\r | |
1996 | sfn[i++] = c;\r | |
1997 | sfn[i++] = d;\r | |
1998 | } else { /* Single byte code */\r | |
1999 | if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */\r | |
2000 | return FR_INVALID_NAME;\r | |
2001 | if (IsUpper(c)) { /* ASCII large capital? */\r | |
2002 | b |= 2;\r | |
2003 | } else {\r | |
2004 | if (IsLower(c)) { /* ASCII small capital? */\r | |
2005 | b |= 1; c -= 0x20;\r | |
2006 | }\r | |
2007 | }\r | |
2008 | sfn[i++] = c;\r | |
2009 | }\r | |
2010 | }\r | |
2011 | *path = &p[si]; /* Return pointer to the next segment */\r | |
2012 | c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */\r | |
2013 | \r | |
2014 | if (!i) return FR_INVALID_NAME; /* Reject nul string */\r | |
2015 | if (sfn[0] == DDE) sfn[0] = NDDE; /* When first character collides with DDE, replace it with 0x05 */\r | |
2016 | \r | |
2017 | if (ni == 8) b <<= 2;\r | |
2018 | if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */\r | |
2019 | if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */\r | |
2020 | \r | |
7b78a5a2 | 2021 | sfn[NSFLAG] = c; /* Store NT flag, File name is created */\r |
53668523 L |
2022 | \r |
2023 | return FR_OK;\r | |
2024 | #endif\r | |
2025 | }\r | |
2026 | \r | |
2027 | \r | |
2028 | \r | |
2029 | \r | |
2030 | /*-----------------------------------------------------------------------*/\r | |
2031 | /* Follow a file path */\r | |
2032 | /*-----------------------------------------------------------------------*/\r | |
2033 | \r | |
2034 | static\r | |
2035 | FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */\r | |
2036 | DIR* dp, /* Directory object to return last directory and found object */\r | |
2037 | const TCHAR* path /* Full-path string to find a file or directory */\r | |
2038 | )\r | |
2039 | {\r | |
2040 | FRESULT res;\r | |
2041 | BYTE *dir, ns;\r | |
2042 | \r | |
2043 | \r | |
2044 | #if _FS_RPATH\r | |
2045 | if (*path == '/' || *path == '\\') { /* There is a heading separator */\r | |
2046 | path++; dp->sclust = 0; /* Strip it and start from the root directory */\r | |
2047 | } else { /* No heading separator */\r | |
2048 | dp->sclust = dp->fs->cdir; /* Start from the current directory */\r | |
2049 | }\r | |
2050 | #else\r | |
2051 | if (*path == '/' || *path == '\\') /* Strip heading separator if exist */\r | |
2052 | path++;\r | |
2053 | dp->sclust = 0; /* Always start from the root directory */\r | |
2054 | #endif\r | |
2055 | \r | |
2056 | if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */\r | |
2057 | res = dir_sdi(dp, 0);\r | |
2058 | dp->dir = 0;\r | |
2059 | } else { /* Follow path */\r | |
2060 | for (;;) {\r | |
2061 | res = create_name(dp, &path); /* Get a segment name of the path */\r | |
2062 | if (res != FR_OK) break;\r | |
2063 | res = dir_find(dp); /* Find an object with the sagment name */\r | |
7b78a5a2 | 2064 | ns = dp->fn[NSFLAG];\r |
53668523 L |
2065 | if (res != FR_OK) { /* Failed to find the object */\r |
2066 | if (res == FR_NO_FILE) { /* Object is not found */\r | |
2067 | if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, */\r | |
2068 | dp->sclust = 0; dp->dir = 0; /* it is the root directory and stay there */\r | |
2069 | if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */\r | |
2070 | res = FR_OK; /* Ended at the root directroy. Function completed. */\r | |
2071 | } else { /* Could not find the object */\r | |
2072 | if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */\r | |
2073 | }\r | |
2074 | }\r | |
2075 | break;\r | |
2076 | }\r | |
2077 | if (ns & NS_LAST) break; /* Last segment matched. Function completed. */\r | |
2078 | dir = dp->dir; /* Follow the sub-directory */\r | |
2079 | if (!(dir[DIR_Attr] & AM_DIR)) { /* It is not a sub-directory and cannot follow */\r | |
2080 | res = FR_NO_PATH; break;\r | |
2081 | }\r | |
2082 | dp->sclust = ld_clust(dp->fs, dir);\r | |
2083 | }\r | |
2084 | }\r | |
2085 | \r | |
2086 | return res;\r | |
2087 | }\r | |
2088 | \r | |
2089 | \r | |
2090 | \r | |
2091 | \r | |
2092 | /*-----------------------------------------------------------------------*/\r | |
2093 | /* Get logical drive number from path name */\r | |
2094 | /*-----------------------------------------------------------------------*/\r | |
2095 | \r | |
2096 | static\r | |
2097 | int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */\r | |
2098 | const TCHAR** path /* Pointer to pointer to the path name */\r | |
2099 | )\r | |
2100 | {\r | |
2101 | const TCHAR *tp, *tt;\r | |
2102 | UINT i;\r | |
2103 | int vol = -1;\r | |
2104 | \r | |
2105 | \r | |
2106 | if (*path) { /* If the pointer is not a null */\r | |
2107 | for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */\r | |
2108 | if (*tt == ':') { /* If a ':' is exist in the path name */\r | |
2109 | tp = *path;\r | |
2110 | i = *tp++ - '0'; \r | |
2111 | if (i < 10 && tp == tt) { /* Is there a numeric drive id? */\r | |
2112 | if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */\r | |
2113 | vol = (int)i;\r | |
2114 | *path = ++tt;\r | |
2115 | }\r | |
2116 | } else { /* No numeric drive number */\r | |
2117 | #if _STR_VOLUME_ID /* Find string drive id */\r | |
2118 | static const char* const str[] = {_VOLUME_STRS};\r | |
2119 | const char *sp;\r | |
2120 | char c;\r | |
2121 | TCHAR tc;\r | |
2122 | \r | |
2123 | i = 0; tt++;\r | |
2124 | do {\r | |
2125 | sp = str[i]; tp = *path;\r | |
2126 | do { /* Compare a string drive id with path name */\r | |
2127 | c = *sp++; tc = *tp++;\r | |
2128 | if (IsLower(tc)) tc -= 0x20;\r | |
2129 | } while (c && (TCHAR)c == tc);\r | |
2130 | } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */\r | |
2131 | if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */\r | |
2132 | vol = (int)i;\r | |
2133 | *path = tt;\r | |
2134 | }\r | |
2135 | #endif\r | |
2136 | }\r | |
2137 | return vol;\r | |
2138 | }\r | |
2139 | #if _FS_RPATH && _VOLUMES >= 2\r | |
2140 | vol = CurrVol; /* Current drive */\r | |
2141 | #else\r | |
2142 | vol = 0; /* Drive 0 */\r | |
2143 | #endif\r | |
2144 | }\r | |
2145 | return vol;\r | |
2146 | }\r | |
2147 | \r | |
2148 | \r | |
2149 | \r | |
2150 | \r | |
2151 | /*-----------------------------------------------------------------------*/\r | |
2152 | /* Load a sector and check if it is an FAT boot sector */\r | |
2153 | /*-----------------------------------------------------------------------*/\r | |
2154 | \r | |
2155 | static\r | |
2156 | BYTE check_fs ( /* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a boot sector, 3:Disk error */\r | |
2157 | FATFS* fs, /* File system object */\r | |
2158 | DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */\r | |
2159 | )\r | |
2160 | {\r | |
2161 | fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */\r | |
2162 | if (move_window(fs, sect) != FR_OK) /* Load boot record */\r | |
2163 | return 3;\r | |
2164 | \r | |
2165 | if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */\r | |
2166 | return 2;\r | |
2167 | \r | |
2168 | if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */\r | |
2169 | return 0;\r | |
2170 | if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */\r | |
2171 | return 0;\r | |
2172 | \r | |
2173 | return 1;\r | |
2174 | }\r | |
2175 | \r | |
2176 | \r | |
2177 | \r | |
2178 | \r | |
2179 | /*-----------------------------------------------------------------------*/\r | |
2180 | /* Find logical drive and check if the volume is mounted */\r | |
2181 | /*-----------------------------------------------------------------------*/\r | |
2182 | \r | |
2183 | static\r | |
2184 | FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */\r | |
2185 | FATFS** rfs, /* Pointer to pointer to the found file system object */\r | |
2186 | const TCHAR** path, /* Pointer to pointer to the path name (drive number) */\r | |
2187 | BYTE wmode /* !=0: Check write protection for write access */\r | |
2188 | )\r | |
2189 | {\r | |
2190 | BYTE fmt;\r | |
2191 | int vol;\r | |
2192 | DSTATUS stat;\r | |
2193 | DWORD bsect, fasize, tsect, sysect, nclst, szbfat;\r | |
2194 | WORD nrsv;\r | |
2195 | FATFS *fs;\r | |
2196 | \r | |
2197 | \r | |
2198 | /* Get logical drive number from the path name */\r | |
2199 | *rfs = 0;\r | |
2200 | vol = get_ldnumber(path);\r | |
2201 | if (vol < 0) return FR_INVALID_DRIVE;\r | |
2202 | \r | |
2203 | /* Check if the file system object is valid or not */\r | |
2204 | fs = FatFs[vol]; /* Get pointer to the file system object */\r | |
2205 | if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */\r | |
2206 | \r | |
2207 | ENTER_FF(fs); /* Lock the volume */\r | |
2208 | *rfs = fs; /* Return pointer to the file system object */\r | |
2209 | \r | |
2210 | if (fs->fs_type) { /* If the volume has been mounted */\r | |
2211 | stat = disk_status(fs->drv);\r | |
2212 | if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */\r | |
2213 | if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check write protection if needed */\r | |
2214 | return FR_WRITE_PROTECTED;\r | |
2215 | return FR_OK; /* The file system object is valid */\r | |
2216 | }\r | |
2217 | }\r | |
2218 | \r | |
2219 | /* The file system object is not valid. */\r | |
2220 | /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */\r | |
2221 | \r | |
2222 | fs->fs_type = 0; /* Clear the file system object */\r | |
2223 | fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */\r | |
2224 | stat = disk_initialize(fs->drv); /* Initialize the physical drive */\r | |
2225 | if (stat & STA_NOINIT) /* Check if the initialization succeeded */\r | |
2226 | return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */\r | |
2227 | if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */\r | |
2228 | return FR_WRITE_PROTECTED;\r | |
2229 | #if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */\r | |
2230 | if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK\r | |
2231 | || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR;\r | |
2232 | #endif\r | |
2233 | /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */\r | |
2234 | bsect = 0;\r | |
2235 | fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */\r | |
2236 | if (fmt == 1 || (!fmt && (LD2PT(vol)))) { /* Not an FAT boot sector or forced partition number */\r | |
2237 | UINT i;\r | |
2238 | DWORD br[4];\r | |
2239 | \r | |
2240 | for (i = 0; i < 4; i++) { /* Get partition offset */\r | |
2241 | BYTE *pt = fs->win+MBR_Table + i * SZ_PTE;\r | |
2242 | br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0;\r | |
2243 | }\r | |
2244 | i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */\r | |
2245 | if (i) i--;\r | |
2246 | do { /* Find an FAT volume */\r | |
2247 | bsect = br[i];\r | |
2248 | fmt = bsect ? check_fs(fs, bsect) : 2; /* Check the partition */\r | |
2249 | } while (!LD2PT(vol) && fmt && ++i < 4);\r | |
2250 | }\r | |
2251 | if (fmt == 3) return FR_DISK_ERR; /* An error occured in the disk I/O layer */\r | |
2252 | if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */\r | |
2253 | \r | |
2254 | /* An FAT volume is found. Following code initializes the file system object */\r | |
2255 | \r | |
2256 | if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */\r | |
2257 | return FR_NO_FILESYSTEM;\r | |
2258 | \r | |
2259 | fasize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */\r | |
2260 | if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32);\r | |
2261 | fs->fsize = fasize;\r | |
2262 | \r | |
2263 | fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */\r | |
2264 | if (fs->n_fats != 1 && fs->n_fats != 2) /* (Must be 1 or 2) */\r | |
2265 | return FR_NO_FILESYSTEM;\r | |
2266 | fasize *= fs->n_fats; /* Number of sectors for FAT area */\r | |
2267 | \r | |
2268 | fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */\r | |
2269 | if (!fs->csize || (fs->csize & (fs->csize - 1))) /* (Must be power of 2) */\r | |
2270 | return FR_NO_FILESYSTEM;\r | |
2271 | \r | |
2272 | fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */\r | |
2273 | if (fs->n_rootdir % (SS(fs) / SZ_DIR)) /* (Must be sector aligned) */\r | |
2274 | return FR_NO_FILESYSTEM;\r | |
2275 | \r | |
2276 | tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */\r | |
2277 | if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);\r | |
2278 | \r | |
2279 | nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); /* Number of reserved sectors */\r | |
2280 | if (!nrsv) return FR_NO_FILESYSTEM; /* (Must not be 0) */\r | |
2281 | \r | |
2282 | /* Determine the FAT sub type */\r | |
2283 | sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR); /* RSV+FAT+DIR */\r | |
2284 | if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */\r | |
2285 | nclst = (tsect - sysect) / fs->csize; /* Number of clusters */\r | |
2286 | if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */\r | |
2287 | fmt = FS_FAT12;\r | |
2288 | if (nclst >= MIN_FAT16) fmt = FS_FAT16;\r | |
2289 | if (nclst >= MIN_FAT32) fmt = FS_FAT32;\r | |
2290 | \r | |
2291 | /* Boundaries and Limits */\r | |
2292 | fs->n_fatent = nclst + 2; /* Number of FAT entries */\r | |
2293 | fs->volbase = bsect; /* Volume start sector */\r | |
2294 | fs->fatbase = bsect + nrsv; /* FAT start sector */\r | |
2295 | fs->database = bsect + sysect; /* Data start sector */\r | |
2296 | if (fmt == FS_FAT32) {\r | |
2297 | if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */\r | |
2298 | fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */\r | |
2299 | szbfat = fs->n_fatent * 4; /* (Needed FAT size) */\r | |
2300 | } else {\r | |
2301 | if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */\r | |
2302 | fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */\r | |
2303 | szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */\r | |
2304 | fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);\r | |
2305 | }\r | |
7b78a5a2 | 2306 | if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than the size needed) */\r |
53668523 L |
2307 | return FR_NO_FILESYSTEM;\r |
2308 | \r | |
2309 | #if !_FS_READONLY\r | |
2310 | /* Initialize cluster allocation information */\r | |
2311 | fs->last_clust = fs->free_clust = 0xFFFFFFFF;\r | |
2312 | \r | |
2313 | /* Get fsinfo if available */\r | |
2314 | fs->fsi_flag = 0x80;\r | |
2315 | #if (_FS_NOFSINFO & 3) != 3\r | |
2316 | if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo is 1 */\r | |
2317 | && LD_WORD(fs->win+BPB_FSInfo) == 1\r | |
2318 | && move_window(fs, bsect + 1) == FR_OK)\r | |
2319 | {\r | |
2320 | fs->fsi_flag = 0;\r | |
2321 | if (LD_WORD(fs->win+BS_55AA) == 0xAA55 /* Load FSINFO data if available */\r | |
2322 | && LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252\r | |
2323 | && LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272)\r | |
2324 | {\r | |
2325 | #if (_FS_NOFSINFO & 1) == 0\r | |
2326 | fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);\r | |
2327 | #endif\r | |
2328 | #if (_FS_NOFSINFO & 2) == 0\r | |
2329 | fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);\r | |
2330 | #endif\r | |
2331 | }\r | |
2332 | }\r | |
2333 | #endif\r | |
2334 | #endif\r | |
2335 | fs->fs_type = fmt; /* FAT sub-type */\r | |
2336 | fs->id = ++Fsid; /* File system mount ID */\r | |
2337 | #if _FS_RPATH\r | |
2338 | fs->cdir = 0; /* Set current directory to root */\r | |
2339 | #endif\r | |
2340 | #if _FS_LOCK /* Clear file lock semaphores */\r | |
2341 | clear_lock(fs);\r | |
2342 | #endif\r | |
2343 | \r | |
2344 | return FR_OK;\r | |
2345 | }\r | |
2346 | \r | |
2347 | \r | |
2348 | \r | |
2349 | \r | |
2350 | /*-----------------------------------------------------------------------*/\r | |
2351 | /* Check if the file/directory object is valid or not */\r | |
2352 | /*-----------------------------------------------------------------------*/\r | |
2353 | \r | |
2354 | static\r | |
2355 | FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */\r | |
2356 | void* obj /* Pointer to the object FIL/DIR to check validity */\r | |
2357 | )\r | |
2358 | {\r | |
2359 | FIL *fil = (FIL*)obj; /* Assuming offset of .fs and .id in the FIL/DIR structure is identical */\r | |
2360 | \r | |
2361 | \r | |
2362 | if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id)\r | |
2363 | return FR_INVALID_OBJECT;\r | |
2364 | \r | |
2365 | ENTER_FF(fil->fs); /* Lock file system */\r | |
2366 | \r | |
2367 | if (disk_status(fil->fs->drv) & STA_NOINIT)\r | |
2368 | return FR_NOT_READY;\r | |
2369 | \r | |
2370 | return FR_OK;\r | |
2371 | }\r | |
2372 | \r | |
2373 | \r | |
2374 | \r | |
2375 | \r | |
2376 | /*--------------------------------------------------------------------------\r | |
2377 | \r | |
2378 | Public Functions\r | |
2379 | \r | |
2380 | --------------------------------------------------------------------------*/\r | |
2381 | \r | |
2382 | \r | |
2383 | \r | |
2384 | /*-----------------------------------------------------------------------*/\r | |
2385 | /* Mount/Unmount a Logical Drive */\r | |
2386 | /*-----------------------------------------------------------------------*/\r | |
2387 | \r | |
2388 | FRESULT f_mount (\r | |
2389 | FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/\r | |
2390 | const TCHAR* path, /* Logical drive number to be mounted/unmounted */\r | |
2391 | BYTE opt /* 0:Do not mount (delayed mount), 1:Mount immediately */\r | |
2392 | )\r | |
2393 | {\r | |
2394 | FATFS *cfs;\r | |
2395 | int vol;\r | |
2396 | FRESULT res;\r | |
2397 | const TCHAR *rp = path;\r | |
2398 | \r | |
2399 | \r | |
2400 | vol = get_ldnumber(&rp);\r | |
2401 | if (vol < 0) return FR_INVALID_DRIVE;\r | |
2402 | cfs = FatFs[vol]; /* Pointer to fs object */\r | |
2403 | \r | |
2404 | if (cfs) {\r | |
2405 | #if _FS_LOCK\r | |
2406 | clear_lock(cfs);\r | |
2407 | #endif\r | |
2408 | #if _FS_REENTRANT /* Discard sync object of the current volume */\r | |
2409 | if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;\r | |
2410 | #endif\r | |
2411 | cfs->fs_type = 0; /* Clear old fs object */\r | |
2412 | }\r | |
2413 | \r | |
2414 | if (fs) {\r | |
2415 | fs->fs_type = 0; /* Clear new fs object */\r | |
2416 | #if _FS_REENTRANT /* Create sync object for the new volume */\r | |
2417 | if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;\r | |
2418 | #endif\r | |
2419 | }\r | |
2420 | FatFs[vol] = fs; /* Register new fs object */\r | |
2421 | \r | |
2422 | if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */\r | |
2423 | \r | |
2424 | res = find_volume(&fs, &path, 0); /* Force mounted the volume */\r | |
2425 | LEAVE_FF(fs, res);\r | |
2426 | }\r | |
2427 | \r | |
2428 | \r | |
2429 | \r | |
2430 | \r | |
2431 | /*-----------------------------------------------------------------------*/\r | |
2432 | /* Open or Create a File */\r | |
2433 | /*-----------------------------------------------------------------------*/\r | |
2434 | \r | |
2435 | FRESULT f_open (\r | |
2436 | FIL* fp, /* Pointer to the blank file object */\r | |
2437 | const TCHAR* path, /* Pointer to the file name */\r | |
2438 | BYTE mode /* Access mode and file open mode flags */\r | |
2439 | )\r | |
2440 | {\r | |
2441 | FRESULT res;\r | |
2442 | DIR dj;\r | |
2443 | BYTE *dir;\r | |
2444 | DEF_NAMEBUF;\r | |
2445 | \r | |
2446 | \r | |
2447 | if (!fp) return FR_INVALID_OBJECT;\r | |
2448 | fp->fs = 0; /* Clear file object */\r | |
2449 | \r | |
2450 | /* Get logical drive number */\r | |
2451 | #if !_FS_READONLY\r | |
2452 | mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;\r | |
2453 | res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ));\r | |
2454 | #else\r | |
2455 | mode &= FA_READ;\r | |
2456 | res = find_volume(&dj.fs, &path, 0);\r | |
2457 | #endif\r | |
2458 | if (res == FR_OK) {\r | |
2459 | INIT_BUF(dj);\r | |
2460 | res = follow_path(&dj, path); /* Follow the file path */\r | |
2461 | dir = dj.dir;\r | |
2462 | #if !_FS_READONLY /* R/W configuration */\r | |
2463 | if (res == FR_OK) {\r | |
2464 | if (!dir) /* Default directory itself */\r | |
2465 | res = FR_INVALID_NAME;\r | |
2466 | #if _FS_LOCK\r | |
2467 | else\r | |
2468 | res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);\r | |
2469 | #endif\r | |
2470 | }\r | |
2471 | /* Create or Open a file */\r | |
2472 | if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {\r | |
2473 | DWORD dw, cl;\r | |
2474 | \r | |
2475 | if (res != FR_OK) { /* No file, create new */\r | |
2476 | if (res == FR_NO_FILE) /* There is no file to open, create a new entry */\r | |
2477 | #if _FS_LOCK\r | |
2478 | res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;\r | |
2479 | #else\r | |
2480 | res = dir_register(&dj);\r | |
2481 | #endif\r | |
2482 | mode |= FA_CREATE_ALWAYS; /* File is created */\r | |
2483 | dir = dj.dir; /* New entry */\r | |
2484 | }\r | |
2485 | else { /* Any object is already existing */\r | |
2486 | if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */\r | |
2487 | res = FR_DENIED;\r | |
2488 | } else {\r | |
2489 | if (mode & FA_CREATE_NEW) /* Cannot create as new file */\r | |
2490 | res = FR_EXIST;\r | |
2491 | }\r | |
2492 | }\r | |
2493 | if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */\r | |
7b78a5a2 | 2494 | dw = GET_FATTIME(); /* Created time */\r |
53668523 L |
2495 | ST_DWORD(dir+DIR_CrtTime, dw);\r |
2496 | dir[DIR_Attr] = 0; /* Reset attribute */\r | |
2497 | ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */\r | |
2498 | cl = ld_clust(dj.fs, dir); /* Get start cluster */\r | |
2499 | st_clust(dir, 0); /* cluster = 0 */\r | |
2500 | dj.fs->wflag = 1;\r | |
2501 | if (cl) { /* Remove the cluster chain if exist */\r | |
2502 | dw = dj.fs->winsect;\r | |
2503 | res = remove_chain(dj.fs, cl);\r | |
2504 | if (res == FR_OK) {\r | |
2505 | dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */\r | |
2506 | res = move_window(dj.fs, dw);\r | |
2507 | }\r | |
2508 | }\r | |
2509 | }\r | |
2510 | }\r | |
2511 | else { /* Open an existing file */\r | |
2512 | if (res == FR_OK) { /* Follow succeeded */\r | |
2513 | if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */\r | |
2514 | res = FR_NO_FILE;\r | |
2515 | } else {\r | |
2516 | if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */\r | |
2517 | res = FR_DENIED;\r | |
2518 | }\r | |
2519 | }\r | |
2520 | }\r | |
2521 | if (res == FR_OK) {\r | |
2522 | if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */\r | |
2523 | mode |= FA__WRITTEN;\r | |
2524 | fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */\r | |
2525 | fp->dir_ptr = dir;\r | |
2526 | #if _FS_LOCK\r | |
2527 | fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);\r | |
2528 | if (!fp->lockid) res = FR_INT_ERR;\r | |
2529 | #endif\r | |
2530 | }\r | |
2531 | \r | |
2532 | #else /* R/O configuration */\r | |
2533 | if (res == FR_OK) { /* Follow succeeded */\r | |
2534 | dir = dj.dir;\r | |
2535 | if (!dir) { /* Current directory itself */\r | |
2536 | res = FR_INVALID_NAME;\r | |
2537 | } else {\r | |
2538 | if (dir[DIR_Attr] & AM_DIR) /* It is a directory */\r | |
2539 | res = FR_NO_FILE;\r | |
2540 | }\r | |
2541 | }\r | |
2542 | #endif\r | |
2543 | FREE_BUF();\r | |
2544 | \r | |
2545 | if (res == FR_OK) {\r | |
2546 | fp->flag = mode; /* File access mode */\r | |
2547 | fp->err = 0; /* Clear error flag */\r | |
2548 | fp->sclust = ld_clust(dj.fs, dir); /* File start cluster */\r | |
2549 | fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */\r | |
2550 | fp->fptr = 0; /* File pointer */\r | |
2551 | fp->dsect = 0;\r | |
2552 | #if _USE_FASTSEEK\r | |
2553 | fp->cltbl = 0; /* Normal seek mode */\r | |
2554 | #endif\r | |
2555 | fp->fs = dj.fs; /* Validate file object */\r | |
2556 | fp->id = fp->fs->id;\r | |
2557 | }\r | |
2558 | }\r | |
2559 | \r | |
2560 | LEAVE_FF(dj.fs, res);\r | |
2561 | }\r | |
2562 | \r | |
2563 | \r | |
2564 | \r | |
2565 | \r | |
2566 | /*-----------------------------------------------------------------------*/\r | |
2567 | /* Read File */\r | |
2568 | /*-----------------------------------------------------------------------*/\r | |
2569 | \r | |
2570 | FRESULT f_read (\r | |
2571 | FIL* fp, /* Pointer to the file object */\r | |
2572 | void* buff, /* Pointer to data buffer */\r | |
2573 | UINT btr, /* Number of bytes to read */\r | |
2574 | UINT* br /* Pointer to number of bytes read */\r | |
2575 | )\r | |
2576 | {\r | |
2577 | FRESULT res;\r | |
2578 | DWORD clst, sect, remain;\r | |
2579 | UINT rcnt, cc;\r | |
2580 | BYTE csect, *rbuff = (BYTE*)buff;\r | |
2581 | \r | |
2582 | \r | |
2583 | *br = 0; /* Clear read byte counter */\r | |
2584 | \r | |
2585 | res = validate(fp); /* Check validity */\r | |
2586 | if (res != FR_OK) LEAVE_FF(fp->fs, res);\r | |
2587 | if (fp->err) /* Check error */\r | |
2588 | LEAVE_FF(fp->fs, (FRESULT)fp->err);\r | |
2589 | if (!(fp->flag & FA_READ)) /* Check access mode */\r | |
2590 | LEAVE_FF(fp->fs, FR_DENIED);\r | |
2591 | remain = fp->fsize - fp->fptr;\r | |
2592 | if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */\r | |
2593 | \r | |
2594 | for ( ; btr; /* Repeat until all data read */\r | |
2595 | rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {\r | |
2596 | if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */\r | |
2597 | csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */\r | |
2598 | if (!csect) { /* On the cluster boundary? */\r | |
2599 | if (fp->fptr == 0) { /* On the top of the file? */\r | |
2600 | clst = fp->sclust; /* Follow from the origin */\r | |
2601 | } else { /* Middle or end of the file */\r | |
2602 | #if _USE_FASTSEEK\r | |
2603 | if (fp->cltbl)\r | |
2604 | clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */\r | |
2605 | else\r | |
2606 | #endif\r | |
2607 | clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */\r | |
2608 | }\r | |
2609 | if (clst < 2) ABORT(fp->fs, FR_INT_ERR);\r | |
2610 | if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);\r | |
2611 | fp->clust = clst; /* Update current cluster */\r | |
2612 | }\r | |
2613 | sect = clust2sect(fp->fs, fp->clust); /* Get current sector */\r | |
2614 | if (!sect) ABORT(fp->fs, FR_INT_ERR);\r | |
2615 | sect += csect;\r | |
2616 | cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */\r | |
2617 | if (cc) { /* Read maximum contiguous sectors directly */\r | |
2618 | if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */\r | |
2619 | cc = fp->fs->csize - csect;\r | |
7b78a5a2 | 2620 | if (disk_read(fp->fs->drv, rbuff, sect, cc) != RES_OK)\r |
53668523 L |
2621 | ABORT(fp->fs, FR_DISK_ERR);\r |
2622 | #if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */\r | |
2623 | #if _FS_TINY\r | |
2624 | if (fp->fs->wflag && fp->fs->winsect - sect < cc)\r | |
2625 | mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));\r | |
2626 | #else\r | |
2627 | if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)\r | |
2628 | mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));\r | |
2629 | #endif\r | |
2630 | #endif\r | |
2631 | rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */\r | |
2632 | continue;\r | |
2633 | }\r | |
2634 | #if !_FS_TINY\r | |
2635 | if (fp->dsect != sect) { /* Load data sector if not in cache */\r | |
2636 | #if !_FS_READONLY\r | |
2637 | if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */\r | |
7b78a5a2 | 2638 | if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)\r |
53668523 L |
2639 | ABORT(fp->fs, FR_DISK_ERR);\r |
2640 | fp->flag &= ~FA__DIRTY;\r | |
2641 | }\r | |
2642 | #endif\r | |
7b78a5a2 | 2643 | if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */\r |
53668523 L |
2644 | ABORT(fp->fs, FR_DISK_ERR);\r |
2645 | }\r | |
2646 | #endif\r | |
2647 | fp->dsect = sect;\r | |
2648 | }\r | |
2649 | rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */\r | |
2650 | if (rcnt > btr) rcnt = btr;\r | |
2651 | #if _FS_TINY\r | |
7b78a5a2 | 2652 | if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */\r |
53668523 L |
2653 | ABORT(fp->fs, FR_DISK_ERR);\r |
2654 | mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */\r | |
2655 | #else\r | |
2656 | mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */\r | |
2657 | #endif\r | |
2658 | }\r | |
2659 | \r | |
2660 | LEAVE_FF(fp->fs, FR_OK);\r | |
2661 | }\r | |
2662 | \r | |
2663 | \r | |
2664 | \r | |
2665 | \r | |
2666 | #if !_FS_READONLY\r | |
2667 | /*-----------------------------------------------------------------------*/\r | |
2668 | /* Write File */\r | |
2669 | /*-----------------------------------------------------------------------*/\r | |
2670 | \r | |
2671 | FRESULT f_write (\r | |
2672 | FIL* fp, /* Pointer to the file object */\r | |
2673 | const void *buff, /* Pointer to the data to be written */\r | |
2674 | UINT btw, /* Number of bytes to write */\r | |
2675 | UINT* bw /* Pointer to number of bytes written */\r | |
2676 | )\r | |
2677 | {\r | |
2678 | FRESULT res;\r | |
2679 | DWORD clst, sect;\r | |
2680 | UINT wcnt, cc;\r | |
2681 | const BYTE *wbuff = (const BYTE*)buff;\r | |
2682 | BYTE csect;\r | |
2683 | \r | |
2684 | \r | |
2685 | *bw = 0; /* Clear write byte counter */\r | |
2686 | \r | |
2687 | res = validate(fp); /* Check validity */\r | |
2688 | if (res != FR_OK) LEAVE_FF(fp->fs, res);\r | |
2689 | if (fp->err) /* Check error */\r | |
2690 | LEAVE_FF(fp->fs, (FRESULT)fp->err);\r | |
2691 | if (!(fp->flag & FA_WRITE)) /* Check access mode */\r | |
2692 | LEAVE_FF(fp->fs, FR_DENIED);\r | |
2693 | if (fp->fptr + btw < fp->fptr) btw = 0; /* File size cannot reach 4GB */\r | |
2694 | \r | |
2695 | for ( ; btw; /* Repeat until all data written */\r | |
2696 | wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {\r | |
2697 | if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */\r | |
2698 | csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */\r | |
2699 | if (!csect) { /* On the cluster boundary? */\r | |
2700 | if (fp->fptr == 0) { /* On the top of the file? */\r | |
2701 | clst = fp->sclust; /* Follow from the origin */\r | |
2702 | if (clst == 0) /* When no cluster is allocated, */\r | |
2703 | clst = create_chain(fp->fs, 0); /* Create a new cluster chain */\r | |
2704 | } else { /* Middle or end of the file */\r | |
2705 | #if _USE_FASTSEEK\r | |
2706 | if (fp->cltbl)\r | |
2707 | clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */\r | |
2708 | else\r | |
2709 | #endif\r | |
2710 | clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */\r | |
2711 | }\r | |
2712 | if (clst == 0) break; /* Could not allocate a new cluster (disk full) */\r | |
2713 | if (clst == 1) ABORT(fp->fs, FR_INT_ERR);\r | |
2714 | if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);\r | |
2715 | fp->clust = clst; /* Update current cluster */\r | |
2716 | if (fp->sclust == 0) fp->sclust = clst; /* Set start cluster if the first write */\r | |
2717 | }\r | |
2718 | #if _FS_TINY\r | |
2719 | if (fp->fs->winsect == fp->dsect && sync_window(fp->fs)) /* Write-back sector cache */\r | |
2720 | ABORT(fp->fs, FR_DISK_ERR);\r | |
2721 | #else\r | |
2722 | if (fp->flag & FA__DIRTY) { /* Write-back sector cache */\r | |
7b78a5a2 | 2723 | if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)\r |
53668523 L |
2724 | ABORT(fp->fs, FR_DISK_ERR);\r |
2725 | fp->flag &= ~FA__DIRTY;\r | |
2726 | }\r | |
2727 | #endif\r | |
2728 | sect = clust2sect(fp->fs, fp->clust); /* Get current sector */\r | |
2729 | if (!sect) ABORT(fp->fs, FR_INT_ERR);\r | |
2730 | sect += csect;\r | |
2731 | cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */\r | |
2732 | if (cc) { /* Write maximum contiguous sectors directly */\r | |
2733 | if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */\r | |
2734 | cc = fp->fs->csize - csect;\r | |
7b78a5a2 | 2735 | if (disk_write(fp->fs->drv, wbuff, sect, cc) != RES_OK)\r |
53668523 L |
2736 | ABORT(fp->fs, FR_DISK_ERR);\r |
2737 | #if _FS_MINIMIZE <= 2\r | |
2738 | #if _FS_TINY\r | |
2739 | if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */\r | |
2740 | mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));\r | |
2741 | fp->fs->wflag = 0;\r | |
2742 | }\r | |
2743 | #else\r | |
2744 | if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */\r | |
2745 | mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));\r | |
2746 | fp->flag &= ~FA__DIRTY;\r | |
2747 | }\r | |
2748 | #endif\r | |
2749 | #endif\r | |
2750 | wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */\r | |
2751 | continue;\r | |
2752 | }\r | |
2753 | #if _FS_TINY\r | |
2754 | if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */\r | |
2755 | if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR);\r | |
2756 | fp->fs->winsect = sect;\r | |
2757 | }\r | |
2758 | #else\r | |
2759 | if (fp->dsect != sect) { /* Fill sector cache with file data */\r | |
2760 | if (fp->fptr < fp->fsize &&\r | |
7b78a5a2 | 2761 | disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)\r |
53668523 L |
2762 | ABORT(fp->fs, FR_DISK_ERR);\r |
2763 | }\r | |
2764 | #endif\r | |
2765 | fp->dsect = sect;\r | |
2766 | }\r | |
2767 | wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */\r | |
2768 | if (wcnt > btw) wcnt = btw;\r | |
2769 | #if _FS_TINY\r | |
7b78a5a2 | 2770 | if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */\r |
53668523 L |
2771 | ABORT(fp->fs, FR_DISK_ERR);\r |
2772 | mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */\r | |
2773 | fp->fs->wflag = 1;\r | |
2774 | #else\r | |
2775 | mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */\r | |
2776 | fp->flag |= FA__DIRTY;\r | |
2777 | #endif\r | |
2778 | }\r | |
2779 | \r | |
2780 | if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */\r | |
2781 | fp->flag |= FA__WRITTEN; /* Set file change flag */\r | |
2782 | \r | |
2783 | LEAVE_FF(fp->fs, FR_OK);\r | |
2784 | }\r | |
2785 | \r | |
2786 | \r | |
2787 | \r | |
2788 | \r | |
2789 | /*-----------------------------------------------------------------------*/\r | |
2790 | /* Synchronize the File */\r | |
2791 | /*-----------------------------------------------------------------------*/\r | |
2792 | \r | |
2793 | FRESULT f_sync (\r | |
2794 | FIL* fp /* Pointer to the file object */\r | |
2795 | )\r | |
2796 | {\r | |
2797 | FRESULT res;\r | |
2798 | DWORD tm;\r | |
2799 | BYTE *dir;\r | |
2800 | \r | |
2801 | \r | |
2802 | res = validate(fp); /* Check validity of the object */\r | |
2803 | if (res == FR_OK) {\r | |
2804 | if (fp->flag & FA__WRITTEN) { /* Has the file been written? */\r | |
2805 | /* Write-back dirty buffer */\r | |
2806 | #if !_FS_TINY\r | |
2807 | if (fp->flag & FA__DIRTY) {\r | |
7b78a5a2 | 2808 | if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)\r |
53668523 L |
2809 | LEAVE_FF(fp->fs, FR_DISK_ERR);\r |
2810 | fp->flag &= ~FA__DIRTY;\r | |
2811 | }\r | |
2812 | #endif\r | |
2813 | /* Update the directory entry */\r | |
2814 | res = move_window(fp->fs, fp->dir_sect);\r | |
2815 | if (res == FR_OK) {\r | |
2816 | dir = fp->dir_ptr;\r | |
2817 | dir[DIR_Attr] |= AM_ARC; /* Set archive bit */\r | |
2818 | ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */\r | |
2819 | st_clust(dir, fp->sclust); /* Update start cluster */\r | |
7b78a5a2 | 2820 | tm = GET_FATTIME(); /* Update updated time */\r |
53668523 L |
2821 | ST_DWORD(dir+DIR_WrtTime, tm);\r |
2822 | ST_WORD(dir+DIR_LstAccDate, 0);\r | |
2823 | fp->flag &= ~FA__WRITTEN;\r | |
2824 | fp->fs->wflag = 1;\r | |
2825 | res = sync_fs(fp->fs);\r | |
2826 | }\r | |
2827 | }\r | |
2828 | }\r | |
2829 | \r | |
2830 | LEAVE_FF(fp->fs, res);\r | |
2831 | }\r | |
2832 | \r | |
2833 | #endif /* !_FS_READONLY */\r | |
2834 | \r | |
2835 | \r | |
2836 | \r | |
2837 | \r | |
2838 | /*-----------------------------------------------------------------------*/\r | |
2839 | /* Close File */\r | |
2840 | /*-----------------------------------------------------------------------*/\r | |
2841 | \r | |
2842 | FRESULT f_close (\r | |
2843 | FIL *fp /* Pointer to the file object to be closed */\r | |
2844 | )\r | |
2845 | {\r | |
2846 | FRESULT res;\r | |
2847 | \r | |
2848 | \r | |
2849 | #if !_FS_READONLY\r | |
2850 | res = f_sync(fp); /* Flush cached data */\r | |
2851 | if (res == FR_OK)\r | |
2852 | #endif\r | |
2853 | {\r | |
2854 | res = validate(fp); /* Lock volume */\r | |
2855 | if (res == FR_OK) {\r | |
2856 | #if _FS_REENTRANT\r | |
2857 | FATFS *fs = fp->fs;\r | |
2858 | #endif\r | |
2859 | #if _FS_LOCK\r | |
2860 | res = dec_lock(fp->lockid); /* Decrement file open counter */\r | |
2861 | if (res == FR_OK)\r | |
2862 | #endif\r | |
2863 | fp->fs = 0; /* Invalidate file object */\r | |
2864 | #if _FS_REENTRANT\r | |
2865 | unlock_fs(fs, FR_OK); /* Unlock volume */\r | |
2866 | #endif\r | |
2867 | }\r | |
2868 | }\r | |
2869 | return res;\r | |
2870 | }\r | |
2871 | \r | |
2872 | \r | |
2873 | \r | |
2874 | \r | |
2875 | /*-----------------------------------------------------------------------*/\r | |
2876 | /* Change Current Directory or Current Drive, Get Current Directory */\r | |
2877 | /*-----------------------------------------------------------------------*/\r | |
2878 | \r | |
2879 | #if _FS_RPATH >= 1\r | |
2880 | #if _VOLUMES >= 2\r | |
2881 | FRESULT f_chdrive (\r | |
2882 | const TCHAR* path /* Drive number */\r | |
2883 | )\r | |
2884 | {\r | |
2885 | int vol;\r | |
2886 | \r | |
2887 | \r | |
2888 | vol = get_ldnumber(&path);\r | |
2889 | if (vol < 0) return FR_INVALID_DRIVE;\r | |
2890 | \r | |
2891 | CurrVol = (BYTE)vol;\r | |
2892 | \r | |
2893 | return FR_OK;\r | |
2894 | }\r | |
2895 | #endif\r | |
2896 | \r | |
2897 | \r | |
2898 | FRESULT f_chdir (\r | |
2899 | const TCHAR* path /* Pointer to the directory path */\r | |
2900 | )\r | |
2901 | {\r | |
2902 | FRESULT res;\r | |
2903 | DIR dj;\r | |
2904 | DEF_NAMEBUF;\r | |
2905 | \r | |
2906 | \r | |
2907 | /* Get logical drive number */\r | |
2908 | res = find_volume(&dj.fs, &path, 0);\r | |
2909 | if (res == FR_OK) {\r | |
2910 | INIT_BUF(dj);\r | |
2911 | res = follow_path(&dj, path); /* Follow the path */\r | |
2912 | FREE_BUF();\r | |
2913 | if (res == FR_OK) { /* Follow completed */\r | |
2914 | if (!dj.dir) {\r | |
2915 | dj.fs->cdir = dj.sclust; /* Start directory itself */\r | |
2916 | } else {\r | |
2917 | if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */\r | |
2918 | dj.fs->cdir = ld_clust(dj.fs, dj.dir);\r | |
2919 | else\r | |
2920 | res = FR_NO_PATH; /* Reached but a file */\r | |
2921 | }\r | |
2922 | }\r | |
2923 | if (res == FR_NO_FILE) res = FR_NO_PATH;\r | |
2924 | }\r | |
2925 | \r | |
2926 | LEAVE_FF(dj.fs, res);\r | |
2927 | }\r | |
2928 | \r | |
2929 | \r | |
2930 | #if _FS_RPATH >= 2\r | |
2931 | FRESULT f_getcwd (\r | |
2932 | TCHAR* buff, /* Pointer to the directory path */\r | |
2933 | UINT len /* Size of path */\r | |
2934 | )\r | |
2935 | {\r | |
2936 | FRESULT res;\r | |
2937 | DIR dj;\r | |
2938 | UINT i, n;\r | |
2939 | DWORD ccl;\r | |
2940 | TCHAR *tp;\r | |
2941 | FILINFO fno;\r | |
2942 | DEF_NAMEBUF;\r | |
2943 | \r | |
2944 | \r | |
2945 | *buff = 0;\r | |
2946 | /* Get logical drive number */\r | |
2947 | res = find_volume(&dj.fs, (const TCHAR**)&buff, 0); /* Get current volume */\r | |
2948 | if (res == FR_OK) {\r | |
2949 | INIT_BUF(dj);\r | |
2950 | i = len; /* Bottom of buffer (directory stack base) */\r | |
2951 | dj.sclust = dj.fs->cdir; /* Start to follow upper directory from current directory */\r | |
2952 | while ((ccl = dj.sclust) != 0) { /* Repeat while current directory is a sub-directory */\r | |
2953 | res = dir_sdi(&dj, 1); /* Get parent directory */\r | |
2954 | if (res != FR_OK) break;\r | |
2955 | res = dir_read(&dj, 0);\r | |
2956 | if (res != FR_OK) break;\r | |
2957 | dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent directory */\r | |
2958 | res = dir_sdi(&dj, 0);\r | |
2959 | if (res != FR_OK) break;\r | |
2960 | do { /* Find the entry links to the child directory */\r | |
2961 | res = dir_read(&dj, 0);\r | |
2962 | if (res != FR_OK) break;\r | |
2963 | if (ccl == ld_clust(dj.fs, dj.dir)) break; /* Found the entry */\r | |
2964 | res = dir_next(&dj, 0); \r | |
2965 | } while (res == FR_OK);\r | |
2966 | if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */\r | |
2967 | if (res != FR_OK) break;\r | |
2968 | #if _USE_LFN\r | |
2969 | fno.lfname = buff;\r | |
2970 | fno.lfsize = i;\r | |
2971 | #endif\r | |
2972 | get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */\r | |
2973 | tp = fno.fname;\r | |
2974 | #if _USE_LFN\r | |
2975 | if (*buff) tp = buff;\r | |
2976 | #endif\r | |
2977 | for (n = 0; tp[n]; n++) ;\r | |
2978 | if (i < n + 3) {\r | |
2979 | res = FR_NOT_ENOUGH_CORE; break;\r | |
2980 | }\r | |
2981 | while (n) buff[--i] = tp[--n];\r | |
2982 | buff[--i] = '/';\r | |
2983 | }\r | |
2984 | tp = buff;\r | |
2985 | if (res == FR_OK) {\r | |
2986 | #if _VOLUMES >= 2\r | |
2987 | *tp++ = '0' + CurrVol; /* Put drive number */\r | |
2988 | *tp++ = ':';\r | |
2989 | #endif\r | |
2990 | if (i == len) { /* Root-directory */\r | |
2991 | *tp++ = '/';\r | |
2992 | } else { /* Sub-directroy */\r | |
2993 | do /* Add stacked path str */\r | |
2994 | *tp++ = buff[i++];\r | |
2995 | while (i < len);\r | |
2996 | }\r | |
2997 | }\r | |
2998 | *tp = 0;\r | |
2999 | FREE_BUF();\r | |
3000 | }\r | |
3001 | \r | |
3002 | LEAVE_FF(dj.fs, res);\r | |
3003 | }\r | |
3004 | #endif /* _FS_RPATH >= 2 */\r | |
3005 | #endif /* _FS_RPATH >= 1 */\r | |
3006 | \r | |
3007 | \r | |
3008 | \r | |
3009 | #if _FS_MINIMIZE <= 2\r | |
3010 | /*-----------------------------------------------------------------------*/\r | |
3011 | /* Seek File R/W Pointer */\r | |
3012 | /*-----------------------------------------------------------------------*/\r | |
3013 | \r | |
3014 | FRESULT f_lseek (\r | |
3015 | FIL* fp, /* Pointer to the file object */\r | |
3016 | DWORD ofs /* File pointer from top of file */\r | |
3017 | )\r | |
3018 | {\r | |
3019 | FRESULT res;\r | |
3020 | \r | |
3021 | \r | |
3022 | res = validate(fp); /* Check validity of the object */\r | |
3023 | if (res != FR_OK) LEAVE_FF(fp->fs, res);\r | |
3024 | if (fp->err) /* Check error */\r | |
3025 | LEAVE_FF(fp->fs, (FRESULT)fp->err);\r | |
3026 | \r | |
3027 | #if _USE_FASTSEEK\r | |
3028 | if (fp->cltbl) { /* Fast seek */\r | |
3029 | DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;\r | |
3030 | \r | |
3031 | if (ofs == CREATE_LINKMAP) { /* Create CLMT */\r | |
3032 | tbl = fp->cltbl;\r | |
3033 | tlen = *tbl++; ulen = 2; /* Given table size and required table size */\r | |
3034 | cl = fp->sclust; /* Top of the chain */\r | |
3035 | if (cl) {\r | |
3036 | do {\r | |
3037 | /* Get a fragment */\r | |
3038 | tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */\r | |
3039 | do {\r | |
3040 | pcl = cl; ncl++;\r | |
3041 | cl = get_fat(fp->fs, cl);\r | |
3042 | if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);\r | |
3043 | if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);\r | |
3044 | } while (cl == pcl + 1);\r | |
3045 | if (ulen <= tlen) { /* Store the length and top of the fragment */\r | |
3046 | *tbl++ = ncl; *tbl++ = tcl;\r | |
3047 | }\r | |
3048 | } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */\r | |
3049 | }\r | |
3050 | *fp->cltbl = ulen; /* Number of items used */\r | |
3051 | if (ulen <= tlen)\r | |
3052 | *tbl = 0; /* Terminate table */\r | |
3053 | else\r | |
3054 | res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */\r | |
3055 | \r | |
3056 | } else { /* Fast seek */\r | |
3057 | if (ofs > fp->fsize) /* Clip offset at the file size */\r | |
3058 | ofs = fp->fsize;\r | |
3059 | fp->fptr = ofs; /* Set file pointer */\r | |
3060 | if (ofs) {\r | |
3061 | fp->clust = clmt_clust(fp, ofs - 1);\r | |
3062 | dsc = clust2sect(fp->fs, fp->clust);\r | |
3063 | if (!dsc) ABORT(fp->fs, FR_INT_ERR);\r | |
3064 | dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);\r | |
3065 | if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */\r | |
3066 | #if !_FS_TINY\r | |
3067 | #if !_FS_READONLY\r | |
3068 | if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */\r | |
7b78a5a2 | 3069 | if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)\r |
53668523 L |
3070 | ABORT(fp->fs, FR_DISK_ERR);\r |
3071 | fp->flag &= ~FA__DIRTY;\r | |
3072 | }\r | |
3073 | #endif\r | |
7b78a5a2 | 3074 | if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */\r |
53668523 L |
3075 | ABORT(fp->fs, FR_DISK_ERR);\r |
3076 | #endif\r | |
3077 | fp->dsect = dsc;\r | |
3078 | }\r | |
3079 | }\r | |
3080 | }\r | |
3081 | } else\r | |
3082 | #endif\r | |
3083 | \r | |
3084 | /* Normal Seek */\r | |
3085 | {\r | |
3086 | DWORD clst, bcs, nsect, ifptr;\r | |
3087 | \r | |
3088 | if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */\r | |
3089 | #if !_FS_READONLY\r | |
3090 | && !(fp->flag & FA_WRITE)\r | |
3091 | #endif\r | |
3092 | ) ofs = fp->fsize;\r | |
3093 | \r | |
3094 | ifptr = fp->fptr;\r | |
3095 | fp->fptr = nsect = 0;\r | |
3096 | if (ofs) {\r | |
3097 | bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */\r | |
3098 | if (ifptr > 0 &&\r | |
3099 | (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */\r | |
3100 | fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */\r | |
3101 | ofs -= fp->fptr;\r | |
3102 | clst = fp->clust;\r | |
3103 | } else { /* When seek to back cluster, */\r | |
3104 | clst = fp->sclust; /* start from the first cluster */\r | |
3105 | #if !_FS_READONLY\r | |
3106 | if (clst == 0) { /* If no cluster chain, create a new chain */\r | |
3107 | clst = create_chain(fp->fs, 0);\r | |
3108 | if (clst == 1) ABORT(fp->fs, FR_INT_ERR);\r | |
3109 | if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);\r | |
3110 | fp->sclust = clst;\r | |
3111 | }\r | |
3112 | #endif\r | |
3113 | fp->clust = clst;\r | |
3114 | }\r | |
3115 | if (clst != 0) {\r | |
3116 | while (ofs > bcs) { /* Cluster following loop */\r | |
3117 | #if !_FS_READONLY\r | |
3118 | if (fp->flag & FA_WRITE) { /* Check if in write mode or not */\r | |
3119 | clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */\r | |
3120 | if (clst == 0) { /* When disk gets full, clip file size */\r | |
3121 | ofs = bcs; break;\r | |
3122 | }\r | |
3123 | } else\r | |
3124 | #endif\r | |
3125 | clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */\r | |
3126 | if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);\r | |
3127 | if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);\r | |
3128 | fp->clust = clst;\r | |
3129 | fp->fptr += bcs;\r | |
3130 | ofs -= bcs;\r | |
3131 | }\r | |
3132 | fp->fptr += ofs;\r | |
3133 | if (ofs % SS(fp->fs)) {\r | |
3134 | nsect = clust2sect(fp->fs, clst); /* Current sector */\r | |
3135 | if (!nsect) ABORT(fp->fs, FR_INT_ERR);\r | |
3136 | nsect += ofs / SS(fp->fs);\r | |
3137 | }\r | |
3138 | }\r | |
3139 | }\r | |
3140 | if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */\r | |
3141 | #if !_FS_TINY\r | |
3142 | #if !_FS_READONLY\r | |
3143 | if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */\r | |
7b78a5a2 | 3144 | if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)\r |
53668523 L |
3145 | ABORT(fp->fs, FR_DISK_ERR);\r |
3146 | fp->flag &= ~FA__DIRTY;\r | |
3147 | }\r | |
3148 | #endif\r | |
7b78a5a2 | 3149 | if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */\r |
53668523 L |
3150 | ABORT(fp->fs, FR_DISK_ERR);\r |
3151 | #endif\r | |
3152 | fp->dsect = nsect;\r | |
3153 | }\r | |
3154 | #if !_FS_READONLY\r | |
3155 | if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */\r | |
3156 | fp->fsize = fp->fptr;\r | |
3157 | fp->flag |= FA__WRITTEN;\r | |
3158 | }\r | |
3159 | #endif\r | |
3160 | }\r | |
3161 | \r | |
3162 | LEAVE_FF(fp->fs, res);\r | |
3163 | }\r | |
3164 | \r | |
3165 | \r | |
3166 | \r | |
3167 | #if _FS_MINIMIZE <= 1\r | |
3168 | /*-----------------------------------------------------------------------*/\r | |
3169 | /* Create a Directory Object */\r | |
3170 | /*-----------------------------------------------------------------------*/\r | |
3171 | \r | |
3172 | FRESULT f_opendir (\r | |
3173 | DIR* dp, /* Pointer to directory object to create */\r | |
3174 | const TCHAR* path /* Pointer to the directory path */\r | |
3175 | )\r | |
3176 | {\r | |
3177 | FRESULT res;\r | |
3178 | FATFS* fs;\r | |
3179 | DEF_NAMEBUF;\r | |
3180 | \r | |
3181 | \r | |
3182 | if (!dp) return FR_INVALID_OBJECT;\r | |
3183 | \r | |
3184 | /* Get logical drive number */\r | |
3185 | res = find_volume(&fs, &path, 0);\r | |
3186 | if (res == FR_OK) {\r | |
3187 | dp->fs = fs;\r | |
3188 | INIT_BUF(*dp);\r | |
3189 | res = follow_path(dp, path); /* Follow the path to the directory */\r | |
3190 | FREE_BUF();\r | |
3191 | if (res == FR_OK) { /* Follow completed */\r | |
3192 | if (dp->dir) { /* It is not the origin directory itself */\r | |
3193 | if (dp->dir[DIR_Attr] & AM_DIR) /* The object is a sub directory */\r | |
3194 | dp->sclust = ld_clust(fs, dp->dir);\r | |
3195 | else /* The object is a file */\r | |
3196 | res = FR_NO_PATH;\r | |
3197 | }\r | |
3198 | if (res == FR_OK) {\r | |
3199 | dp->id = fs->id;\r | |
3200 | res = dir_sdi(dp, 0); /* Rewind directory */\r | |
3201 | #if _FS_LOCK\r | |
3202 | if (res == FR_OK) {\r | |
3203 | if (dp->sclust) {\r | |
3204 | dp->lockid = inc_lock(dp, 0); /* Lock the sub directory */\r | |
3205 | if (!dp->lockid)\r | |
3206 | res = FR_TOO_MANY_OPEN_FILES;\r | |
3207 | } else {\r | |
3208 | dp->lockid = 0; /* Root directory need not to be locked */\r | |
3209 | }\r | |
3210 | }\r | |
3211 | #endif\r | |
3212 | }\r | |
3213 | }\r | |
3214 | if (res == FR_NO_FILE) res = FR_NO_PATH;\r | |
3215 | }\r | |
3216 | if (res != FR_OK) dp->fs = 0; /* Invalidate the directory object if function faild */\r | |
3217 | \r | |
3218 | LEAVE_FF(fs, res);\r | |
3219 | }\r | |
3220 | \r | |
3221 | \r | |
3222 | \r | |
3223 | \r | |
3224 | /*-----------------------------------------------------------------------*/\r | |
3225 | /* Close Directory */\r | |
3226 | /*-----------------------------------------------------------------------*/\r | |
3227 | \r | |
3228 | FRESULT f_closedir (\r | |
3229 | DIR *dp /* Pointer to the directory object to be closed */\r | |
3230 | )\r | |
3231 | {\r | |
3232 | FRESULT res;\r | |
3233 | \r | |
3234 | \r | |
3235 | res = validate(dp);\r | |
3236 | if (res == FR_OK) {\r | |
3237 | #if _FS_REENTRANT\r | |
3238 | FATFS *fs = dp->fs;\r | |
3239 | #endif\r | |
3240 | #if _FS_LOCK\r | |
3241 | if (dp->lockid) /* Decrement sub-directory open counter */\r | |
3242 | res = dec_lock(dp->lockid);\r | |
3243 | if (res == FR_OK)\r | |
3244 | #endif\r | |
3245 | dp->fs = 0; /* Invalidate directory object */\r | |
3246 | #if _FS_REENTRANT\r | |
3247 | unlock_fs(fs, FR_OK); /* Unlock volume */\r | |
3248 | #endif\r | |
3249 | }\r | |
3250 | return res;\r | |
3251 | }\r | |
3252 | \r | |
3253 | \r | |
3254 | \r | |
3255 | \r | |
3256 | /*-----------------------------------------------------------------------*/\r | |
3257 | /* Read Directory Entries in Sequence */\r | |
3258 | /*-----------------------------------------------------------------------*/\r | |
3259 | \r | |
3260 | FRESULT f_readdir (\r | |
3261 | DIR* dp, /* Pointer to the open directory object */\r | |
3262 | FILINFO* fno /* Pointer to file information to return */\r | |
3263 | )\r | |
3264 | {\r | |
3265 | FRESULT res;\r | |
3266 | DEF_NAMEBUF;\r | |
3267 | \r | |
3268 | \r | |
3269 | res = validate(dp); /* Check validity of the object */\r | |
3270 | if (res == FR_OK) {\r | |
3271 | if (!fno) {\r | |
3272 | res = dir_sdi(dp, 0); /* Rewind the directory object */\r | |
3273 | } else {\r | |
3274 | INIT_BUF(*dp);\r | |
3275 | res = dir_read(dp, 0); /* Read an item */\r | |
3276 | if (res == FR_NO_FILE) { /* Reached end of directory */\r | |
3277 | dp->sect = 0;\r | |
3278 | res = FR_OK;\r | |
3279 | }\r | |
3280 | if (res == FR_OK) { /* A valid entry is found */\r | |
3281 | get_fileinfo(dp, fno); /* Get the object information */\r | |
3282 | res = dir_next(dp, 0); /* Increment index for next */\r | |
3283 | if (res == FR_NO_FILE) {\r | |
3284 | dp->sect = 0;\r | |
3285 | res = FR_OK;\r | |
3286 | }\r | |
3287 | }\r | |
3288 | FREE_BUF();\r | |
3289 | }\r | |
3290 | }\r | |
3291 | \r | |
3292 | LEAVE_FF(dp->fs, res);\r | |
3293 | }\r | |
3294 | \r | |
3295 | \r | |
3296 | \r | |
3297 | #if _FS_MINIMIZE == 0\r | |
3298 | /*-----------------------------------------------------------------------*/\r | |
3299 | /* Get File Status */\r | |
3300 | /*-----------------------------------------------------------------------*/\r | |
3301 | \r | |
3302 | FRESULT f_stat (\r | |
3303 | const TCHAR* path, /* Pointer to the file path */\r | |
3304 | FILINFO* fno /* Pointer to file information to return */\r | |
3305 | )\r | |
3306 | {\r | |
3307 | FRESULT res;\r | |
3308 | DIR dj;\r | |
3309 | DEF_NAMEBUF;\r | |
3310 | \r | |
3311 | \r | |
3312 | /* Get logical drive number */\r | |
3313 | res = find_volume(&dj.fs, &path, 0);\r | |
3314 | if (res == FR_OK) {\r | |
3315 | INIT_BUF(dj);\r | |
3316 | res = follow_path(&dj, path); /* Follow the file path */\r | |
3317 | if (res == FR_OK) { /* Follow completed */\r | |
3318 | if (dj.dir) { /* Found an object */\r | |
3319 | if (fno) get_fileinfo(&dj, fno);\r | |
3320 | } else { /* It is root directory */\r | |
3321 | res = FR_INVALID_NAME;\r | |
3322 | }\r | |
3323 | }\r | |
3324 | FREE_BUF();\r | |
3325 | }\r | |
3326 | \r | |
3327 | LEAVE_FF(dj.fs, res);\r | |
3328 | }\r | |
3329 | \r | |
3330 | \r | |
3331 | \r | |
3332 | #if !_FS_READONLY\r | |
3333 | /*-----------------------------------------------------------------------*/\r | |
3334 | /* Get Number of Free Clusters */\r | |
3335 | /*-----------------------------------------------------------------------*/\r | |
3336 | \r | |
3337 | FRESULT f_getfree (\r | |
3338 | const TCHAR* path, /* Path name of the logical drive number */\r | |
3339 | DWORD* nclst, /* Pointer to a variable to return number of free clusters */\r | |
3340 | FATFS** fatfs /* Pointer to return pointer to corresponding file system object */\r | |
3341 | )\r | |
3342 | {\r | |
3343 | FRESULT res;\r | |
3344 | FATFS *fs;\r | |
3345 | DWORD n, clst, sect, stat;\r | |
3346 | UINT i;\r | |
3347 | BYTE fat, *p;\r | |
3348 | \r | |
3349 | \r | |
3350 | /* Get logical drive number */\r | |
3351 | res = find_volume(fatfs, &path, 0);\r | |
3352 | fs = *fatfs;\r | |
3353 | if (res == FR_OK) {\r | |
3354 | /* If free_clust is valid, return it without full cluster scan */\r | |
3355 | if (fs->free_clust <= fs->n_fatent - 2) {\r | |
3356 | *nclst = fs->free_clust;\r | |
3357 | } else {\r | |
3358 | /* Get number of free clusters */\r | |
3359 | fat = fs->fs_type;\r | |
3360 | n = 0;\r | |
3361 | if (fat == FS_FAT12) {\r | |
3362 | clst = 2;\r | |
3363 | do {\r | |
3364 | stat = get_fat(fs, clst);\r | |
3365 | if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }\r | |
3366 | if (stat == 1) { res = FR_INT_ERR; break; }\r | |
3367 | if (stat == 0) n++;\r | |
3368 | } while (++clst < fs->n_fatent);\r | |
3369 | } else {\r | |
3370 | clst = fs->n_fatent;\r | |
3371 | sect = fs->fatbase;\r | |
3372 | i = 0; p = 0;\r | |
3373 | do {\r | |
3374 | if (!i) {\r | |
3375 | res = move_window(fs, sect++);\r | |
3376 | if (res != FR_OK) break;\r | |
3377 | p = fs->win;\r | |
3378 | i = SS(fs);\r | |
3379 | }\r | |
3380 | if (fat == FS_FAT16) {\r | |
3381 | if (LD_WORD(p) == 0) n++;\r | |
3382 | p += 2; i -= 2;\r | |
3383 | } else {\r | |
3384 | if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;\r | |
3385 | p += 4; i -= 4;\r | |
3386 | }\r | |
3387 | } while (--clst);\r | |
3388 | }\r | |
3389 | fs->free_clust = n;\r | |
3390 | fs->fsi_flag |= 1;\r | |
3391 | *nclst = n;\r | |
3392 | }\r | |
3393 | }\r | |
3394 | LEAVE_FF(fs, res);\r | |
3395 | }\r | |
3396 | \r | |
3397 | \r | |
3398 | \r | |
3399 | \r | |
3400 | /*-----------------------------------------------------------------------*/\r | |
3401 | /* Truncate File */\r | |
3402 | /*-----------------------------------------------------------------------*/\r | |
3403 | \r | |
3404 | FRESULT f_truncate (\r | |
3405 | FIL* fp /* Pointer to the file object */\r | |
3406 | )\r | |
3407 | {\r | |
3408 | FRESULT res;\r | |
3409 | DWORD ncl;\r | |
3410 | \r | |
3411 | \r | |
3412 | res = validate(fp); /* Check validity of the object */\r | |
3413 | if (res == FR_OK) {\r | |
3414 | if (fp->err) { /* Check error */\r | |
3415 | res = (FRESULT)fp->err;\r | |
3416 | } else {\r | |
3417 | if (!(fp->flag & FA_WRITE)) /* Check access mode */\r | |
3418 | res = FR_DENIED;\r | |
3419 | }\r | |
3420 | }\r | |
3421 | if (res == FR_OK) {\r | |
3422 | if (fp->fsize > fp->fptr) {\r | |
3423 | fp->fsize = fp->fptr; /* Set file size to current R/W point */\r | |
3424 | fp->flag |= FA__WRITTEN;\r | |
3425 | if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */\r | |
3426 | res = remove_chain(fp->fs, fp->sclust);\r | |
3427 | fp->sclust = 0;\r | |
3428 | } else { /* When truncate a part of the file, remove remaining clusters */\r | |
3429 | ncl = get_fat(fp->fs, fp->clust);\r | |
3430 | res = FR_OK;\r | |
3431 | if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;\r | |
3432 | if (ncl == 1) res = FR_INT_ERR;\r | |
3433 | if (res == FR_OK && ncl < fp->fs->n_fatent) {\r | |
3434 | res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF);\r | |
3435 | if (res == FR_OK) res = remove_chain(fp->fs, ncl);\r | |
3436 | }\r | |
3437 | }\r | |
3438 | #if !_FS_TINY\r | |
3439 | if (res == FR_OK && (fp->flag & FA__DIRTY)) {\r | |
7b78a5a2 | 3440 | if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)\r |
53668523 L |
3441 | res = FR_DISK_ERR;\r |
3442 | else\r | |
3443 | fp->flag &= ~FA__DIRTY;\r | |
3444 | }\r | |
3445 | #endif\r | |
3446 | }\r | |
3447 | if (res != FR_OK) fp->err = (FRESULT)res;\r | |
3448 | }\r | |
3449 | \r | |
3450 | LEAVE_FF(fp->fs, res);\r | |
3451 | }\r | |
3452 | \r | |
3453 | \r | |
3454 | \r | |
3455 | \r | |
3456 | /*-----------------------------------------------------------------------*/\r | |
3457 | /* Delete a File or Directory */\r | |
3458 | /*-----------------------------------------------------------------------*/\r | |
3459 | \r | |
3460 | FRESULT f_unlink (\r | |
3461 | const TCHAR* path /* Pointer to the file or directory path */\r | |
3462 | )\r | |
3463 | {\r | |
3464 | FRESULT res;\r | |
3465 | DIR dj, sdj;\r | |
3466 | BYTE *dir;\r | |
7b78a5a2 | 3467 | DWORD dclst = 0;\r |
53668523 L |
3468 | DEF_NAMEBUF;\r |
3469 | \r | |
3470 | \r | |
3471 | /* Get logical drive number */\r | |
3472 | res = find_volume(&dj.fs, &path, 1);\r | |
3473 | if (res == FR_OK) {\r | |
3474 | INIT_BUF(dj);\r | |
3475 | res = follow_path(&dj, path); /* Follow the file path */\r | |
7b78a5a2 | 3476 | if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT))\r |
53668523 L |
3477 | res = FR_INVALID_NAME; /* Cannot remove dot entry */\r |
3478 | #if _FS_LOCK\r | |
7b78a5a2 | 3479 | if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open object */\r |
53668523 L |
3480 | #endif\r |
3481 | if (res == FR_OK) { /* The object is accessible */\r | |
3482 | dir = dj.dir;\r | |
3483 | if (!dir) {\r | |
7b78a5a2 | 3484 | res = FR_INVALID_NAME; /* Cannot remove the origin directory */\r |
53668523 L |
3485 | } else {\r |
3486 | if (dir[DIR_Attr] & AM_RDO)\r | |
3487 | res = FR_DENIED; /* Cannot remove R/O object */\r | |
3488 | }\r | |
53668523 | 3489 | if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */\r |
7b78a5a2 L |
3490 | dclst = ld_clust(dj.fs, dir);\r |
3491 | if (!dclst) {\r | |
53668523 | 3492 | res = FR_INT_ERR;\r |
7b78a5a2 L |
3493 | } else { /* Make sure the sub-directory is empty */\r |
3494 | mem_cpy(&sdj, &dj, sizeof (DIR));\r | |
53668523 L |
3495 | sdj.sclust = dclst;\r |
3496 | res = dir_sdi(&sdj, 2); /* Exclude dot entries */\r | |
3497 | if (res == FR_OK) {\r | |
3498 | res = dir_read(&sdj, 0); /* Read an item */\r | |
3499 | if (res == FR_OK /* Not empty directory */\r | |
3500 | #if _FS_RPATH\r | |
7b78a5a2 | 3501 | || dclst == dj.fs->cdir /* or current directory */\r |
53668523 L |
3502 | #endif\r |
3503 | ) res = FR_DENIED;\r | |
7b78a5a2 | 3504 | if (res == FR_NO_FILE) res = FR_OK; /* It is empty */\r |
53668523 L |
3505 | }\r |
3506 | }\r | |
3507 | }\r | |
3508 | if (res == FR_OK) {\r | |
3509 | res = dir_remove(&dj); /* Remove the directory entry */\r | |
7b78a5a2 L |
3510 | if (res == FR_OK && dclst) /* Remove the cluster chain if exist */\r |
3511 | res = remove_chain(dj.fs, dclst);\r | |
3512 | if (res == FR_OK) res = sync_fs(dj.fs);\r | |
53668523 L |
3513 | }\r |
3514 | }\r | |
3515 | FREE_BUF();\r | |
3516 | }\r | |
3517 | \r | |
3518 | LEAVE_FF(dj.fs, res);\r | |
3519 | }\r | |
3520 | \r | |
3521 | \r | |
3522 | \r | |
3523 | \r | |
3524 | /*-----------------------------------------------------------------------*/\r | |
3525 | /* Create a Directory */\r | |
3526 | /*-----------------------------------------------------------------------*/\r | |
3527 | \r | |
3528 | FRESULT f_mkdir (\r | |
3529 | const TCHAR* path /* Pointer to the directory path */\r | |
3530 | )\r | |
3531 | {\r | |
3532 | FRESULT res;\r | |
3533 | DIR dj;\r | |
3534 | BYTE *dir, n;\r | |
7b78a5a2 | 3535 | DWORD dsc, dcl, pcl, tm = GET_FATTIME();\r |
53668523 L |
3536 | DEF_NAMEBUF;\r |
3537 | \r | |
3538 | \r | |
3539 | /* Get logical drive number */\r | |
3540 | res = find_volume(&dj.fs, &path, 1);\r | |
3541 | if (res == FR_OK) {\r | |
3542 | INIT_BUF(dj);\r | |
3543 | res = follow_path(&dj, path); /* Follow the file path */\r | |
3544 | if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */\r | |
7b78a5a2 | 3545 | if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT))\r |
53668523 L |
3546 | res = FR_INVALID_NAME;\r |
3547 | if (res == FR_NO_FILE) { /* Can create a new directory */\r | |
3548 | dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */\r | |
3549 | res = FR_OK;\r | |
3550 | if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */\r | |
3551 | if (dcl == 1) res = FR_INT_ERR;\r | |
3552 | if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;\r | |
3553 | if (res == FR_OK) /* Flush FAT */\r | |
3554 | res = sync_window(dj.fs);\r | |
3555 | if (res == FR_OK) { /* Initialize the new directory table */\r | |
3556 | dsc = clust2sect(dj.fs, dcl);\r | |
3557 | dir = dj.fs->win;\r | |
3558 | mem_set(dir, 0, SS(dj.fs));\r | |
3559 | mem_set(dir+DIR_Name, ' ', 11); /* Create "." entry */\r | |
3560 | dir[DIR_Name] = '.';\r | |
3561 | dir[DIR_Attr] = AM_DIR;\r | |
3562 | ST_DWORD(dir+DIR_WrtTime, tm);\r | |
3563 | st_clust(dir, dcl);\r | |
3564 | mem_cpy(dir+SZ_DIR, dir, SZ_DIR); /* Create ".." entry */\r | |
3565 | dir[SZ_DIR+1] = '.'; pcl = dj.sclust;\r | |
3566 | if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)\r | |
3567 | pcl = 0;\r | |
3568 | st_clust(dir+SZ_DIR, pcl);\r | |
3569 | for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */\r | |
3570 | dj.fs->winsect = dsc++;\r | |
3571 | dj.fs->wflag = 1;\r | |
3572 | res = sync_window(dj.fs);\r | |
3573 | if (res != FR_OK) break;\r | |
3574 | mem_set(dir, 0, SS(dj.fs));\r | |
3575 | }\r | |
3576 | }\r | |
3577 | if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */\r | |
3578 | if (res != FR_OK) {\r | |
3579 | remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */\r | |
3580 | } else {\r | |
3581 | dir = dj.dir;\r | |
3582 | dir[DIR_Attr] = AM_DIR; /* Attribute */\r | |
3583 | ST_DWORD(dir+DIR_WrtTime, tm); /* Created time */\r | |
3584 | st_clust(dir, dcl); /* Table start cluster */\r | |
3585 | dj.fs->wflag = 1;\r | |
3586 | res = sync_fs(dj.fs);\r | |
3587 | }\r | |
3588 | }\r | |
3589 | FREE_BUF();\r | |
3590 | }\r | |
3591 | \r | |
3592 | LEAVE_FF(dj.fs, res);\r | |
3593 | }\r | |
3594 | \r | |
3595 | \r | |
3596 | \r | |
3597 | \r | |
3598 | /*-----------------------------------------------------------------------*/\r | |
3599 | /* Change Attribute */\r | |
3600 | /*-----------------------------------------------------------------------*/\r | |
3601 | \r | |
3602 | FRESULT f_chmod (\r | |
3603 | const TCHAR* path, /* Pointer to the file path */\r | |
3604 | BYTE value, /* Attribute bits */\r | |
3605 | BYTE mask /* Attribute mask to change */\r | |
3606 | )\r | |
3607 | {\r | |
3608 | FRESULT res;\r | |
3609 | DIR dj;\r | |
3610 | BYTE *dir;\r | |
3611 | DEF_NAMEBUF;\r | |
3612 | \r | |
3613 | \r | |
3614 | /* Get logical drive number */\r | |
3615 | res = find_volume(&dj.fs, &path, 1);\r | |
3616 | if (res == FR_OK) {\r | |
3617 | INIT_BUF(dj);\r | |
3618 | res = follow_path(&dj, path); /* Follow the file path */\r | |
3619 | FREE_BUF();\r | |
7b78a5a2 | 3620 | if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT))\r |
53668523 L |
3621 | res = FR_INVALID_NAME;\r |
3622 | if (res == FR_OK) {\r | |
3623 | dir = dj.dir;\r | |
3624 | if (!dir) { /* Is it a root directory? */\r | |
3625 | res = FR_INVALID_NAME;\r | |
3626 | } else { /* File or sub directory */\r | |
3627 | mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */\r | |
3628 | dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */\r | |
3629 | dj.fs->wflag = 1;\r | |
3630 | res = sync_fs(dj.fs);\r | |
3631 | }\r | |
3632 | }\r | |
3633 | }\r | |
3634 | \r | |
3635 | LEAVE_FF(dj.fs, res);\r | |
3636 | }\r | |
3637 | \r | |
3638 | \r | |
3639 | \r | |
3640 | \r | |
53668523 L |
3641 | /*-----------------------------------------------------------------------*/\r |
3642 | /* Rename File/Directory */\r | |
3643 | /*-----------------------------------------------------------------------*/\r | |
3644 | \r | |
3645 | FRESULT f_rename (\r | |
3646 | const TCHAR* path_old, /* Pointer to the object to be renamed */\r | |
3647 | const TCHAR* path_new /* Pointer to the new name */\r | |
3648 | )\r | |
3649 | {\r | |
3650 | FRESULT res;\r | |
3651 | DIR djo, djn;\r | |
3652 | BYTE buf[21], *dir;\r | |
3653 | DWORD dw;\r | |
3654 | DEF_NAMEBUF;\r | |
3655 | \r | |
3656 | \r | |
3657 | /* Get logical drive number of the source object */\r | |
3658 | res = find_volume(&djo.fs, &path_old, 1);\r | |
3659 | if (res == FR_OK) {\r | |
3660 | djn.fs = djo.fs;\r | |
3661 | INIT_BUF(djo);\r | |
3662 | res = follow_path(&djo, path_old); /* Check old object */\r | |
7b78a5a2 | 3663 | if (_FS_RPATH && res == FR_OK && (djo.fn[NSFLAG] & NS_DOT))\r |
53668523 L |
3664 | res = FR_INVALID_NAME;\r |
3665 | #if _FS_LOCK\r | |
3666 | if (res == FR_OK) res = chk_lock(&djo, 2);\r | |
3667 | #endif\r | |
3668 | if (res == FR_OK) { /* Old object is found */\r | |
3669 | if (!djo.dir) { /* Is root dir? */\r | |
3670 | res = FR_NO_FILE;\r | |
3671 | } else {\r | |
3672 | mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except name */\r | |
3673 | mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */\r | |
3674 | if (get_ldnumber(&path_new) >= 0) /* Snip drive number off and ignore it */\r | |
7b78a5a2 | 3675 | res = follow_path(&djn, path_new); /* and make sure if new object name is not conflicting */\r |
53668523 L |
3676 | else\r |
3677 | res = FR_INVALID_DRIVE;\r | |
3678 | if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */\r | |
7b78a5a2 L |
3679 | if (res == FR_NO_FILE) { /* It is a valid path and no name collision */\r |
3680 | /* Start of critical section that any interruption can cause a cross-link */\r | |
53668523 L |
3681 | res = dir_register(&djn); /* Register the new entry */\r |
3682 | if (res == FR_OK) {\r | |
3683 | dir = djn.dir; /* Copy object information except name */\r | |
3684 | mem_cpy(dir+13, buf+2, 19);\r | |
3685 | dir[DIR_Attr] = buf[0] | AM_ARC;\r | |
3686 | djo.fs->wflag = 1;\r | |
7b78a5a2 | 3687 | if ((dir[DIR_Attr] & AM_DIR) && djo.sclust != djn.sclust) { /* Update .. entry in the directory if needed */\r |
53668523 L |
3688 | dw = clust2sect(djo.fs, ld_clust(djo.fs, dir));\r |
3689 | if (!dw) {\r | |
3690 | res = FR_INT_ERR;\r | |
3691 | } else {\r | |
3692 | res = move_window(djo.fs, dw);\r | |
3693 | dir = djo.fs->win+SZ_DIR; /* .. entry */\r | |
3694 | if (res == FR_OK && dir[1] == '.') {\r | |
7b78a5a2 | 3695 | st_clust(dir, djn.sclust);\r |
53668523 L |
3696 | djo.fs->wflag = 1;\r |
3697 | }\r | |
3698 | }\r | |
3699 | }\r | |
3700 | if (res == FR_OK) {\r | |
3701 | res = dir_remove(&djo); /* Remove old entry */\r | |
3702 | if (res == FR_OK)\r | |
3703 | res = sync_fs(djo.fs);\r | |
3704 | }\r | |
3705 | }\r | |
7b78a5a2 | 3706 | /* End of critical section */\r |
53668523 L |
3707 | }\r |
3708 | }\r | |
3709 | }\r | |
3710 | FREE_BUF();\r | |
3711 | }\r | |
3712 | \r | |
3713 | LEAVE_FF(djo.fs, res);\r | |
3714 | }\r | |
3715 | \r | |
7b78a5a2 L |
3716 | \r |
3717 | \r | |
3718 | \r | |
3719 | /*-----------------------------------------------------------------------*/\r | |
3720 | /* Change Timestamp */\r | |
3721 | /*-----------------------------------------------------------------------*/\r | |
3722 | \r | |
3723 | FRESULT f_utime (\r | |
3724 | const TCHAR* path, /* Pointer to the file/directory name */\r | |
3725 | const FILINFO* fno /* Pointer to the time stamp to be set */\r | |
3726 | )\r | |
3727 | {\r | |
3728 | FRESULT res;\r | |
3729 | DIR dj;\r | |
3730 | BYTE *dir;\r | |
3731 | DEF_NAMEBUF;\r | |
3732 | \r | |
3733 | \r | |
3734 | /* Get logical drive number */\r | |
3735 | res = find_volume(&dj.fs, &path, 1);\r | |
3736 | if (res == FR_OK) {\r | |
3737 | INIT_BUF(dj);\r | |
3738 | res = follow_path(&dj, path); /* Follow the file path */\r | |
3739 | FREE_BUF();\r | |
3740 | if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT))\r | |
3741 | res = FR_INVALID_NAME;\r | |
3742 | if (res == FR_OK) {\r | |
3743 | dir = dj.dir;\r | |
3744 | if (!dir) { /* Root directory */\r | |
3745 | res = FR_INVALID_NAME;\r | |
3746 | } else { /* File or sub-directory */\r | |
3747 | ST_WORD(dir+DIR_WrtTime, fno->ftime);\r | |
3748 | ST_WORD(dir+DIR_WrtDate, fno->fdate);\r | |
3749 | dj.fs->wflag = 1;\r | |
3750 | res = sync_fs(dj.fs);\r | |
3751 | }\r | |
3752 | }\r | |
3753 | }\r | |
3754 | \r | |
3755 | LEAVE_FF(dj.fs, res);\r | |
3756 | }\r | |
3757 | \r | |
53668523 L |
3758 | #endif /* !_FS_READONLY */\r |
3759 | #endif /* _FS_MINIMIZE == 0 */\r | |
3760 | #endif /* _FS_MINIMIZE <= 1 */\r | |
3761 | #endif /* _FS_MINIMIZE <= 2 */\r | |
3762 | \r | |
3763 | \r | |
3764 | \r | |
3765 | #if _USE_LABEL\r | |
3766 | /*-----------------------------------------------------------------------*/\r | |
3767 | /* Get volume label */\r | |
3768 | /*-----------------------------------------------------------------------*/\r | |
3769 | \r | |
3770 | FRESULT f_getlabel (\r | |
3771 | const TCHAR* path, /* Path name of the logical drive number */\r | |
3772 | TCHAR* label, /* Pointer to a buffer to return the volume label */\r | |
3773 | DWORD* vsn /* Pointer to a variable to return the volume serial number */\r | |
3774 | )\r | |
3775 | {\r | |
3776 | FRESULT res;\r | |
3777 | DIR dj;\r | |
3778 | UINT i, j;\r | |
3779 | \r | |
3780 | \r | |
3781 | /* Get logical drive number */\r | |
3782 | res = find_volume(&dj.fs, &path, 0);\r | |
3783 | \r | |
3784 | /* Get volume label */\r | |
3785 | if (res == FR_OK && label) {\r | |
3786 | dj.sclust = 0; /* Open root directory */\r | |
3787 | res = dir_sdi(&dj, 0);\r | |
3788 | if (res == FR_OK) {\r | |
3789 | res = dir_read(&dj, 1); /* Get an entry with AM_VOL */\r | |
3790 | if (res == FR_OK) { /* A volume label is exist */\r | |
3791 | #if _USE_LFN && _LFN_UNICODE\r | |
3792 | WCHAR w;\r | |
3793 | i = j = 0;\r | |
3794 | do {\r | |
3795 | w = (i < 11) ? dj.dir[i++] : ' ';\r | |
3796 | if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i]))\r | |
3797 | w = w << 8 | dj.dir[i++];\r | |
3798 | label[j++] = ff_convert(w, 1); /* OEM -> Unicode */\r | |
3799 | } while (j < 11);\r | |
3800 | #else\r | |
3801 | mem_cpy(label, dj.dir, 11);\r | |
3802 | #endif\r | |
3803 | j = 11;\r | |
3804 | do {\r | |
3805 | label[j] = 0;\r | |
3806 | if (!j) break;\r | |
3807 | } while (label[--j] == ' ');\r | |
3808 | }\r | |
3809 | if (res == FR_NO_FILE) { /* No label, return nul string */\r | |
3810 | label[0] = 0;\r | |
3811 | res = FR_OK;\r | |
3812 | }\r | |
3813 | }\r | |
3814 | }\r | |
3815 | \r | |
3816 | /* Get volume serial number */\r | |
3817 | if (res == FR_OK && vsn) {\r | |
3818 | res = move_window(dj.fs, dj.fs->volbase);\r | |
3819 | if (res == FR_OK) {\r | |
3820 | i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID;\r | |
3821 | *vsn = LD_DWORD(&dj.fs->win[i]);\r | |
3822 | }\r | |
3823 | }\r | |
3824 | \r | |
3825 | LEAVE_FF(dj.fs, res);\r | |
3826 | }\r | |
3827 | \r | |
3828 | \r | |
3829 | \r | |
3830 | #if !_FS_READONLY\r | |
3831 | /*-----------------------------------------------------------------------*/\r | |
3832 | /* Set volume label */\r | |
3833 | /*-----------------------------------------------------------------------*/\r | |
3834 | \r | |
3835 | FRESULT f_setlabel (\r | |
3836 | const TCHAR* label /* Pointer to the volume label to set */\r | |
3837 | )\r | |
3838 | {\r | |
3839 | FRESULT res;\r | |
3840 | DIR dj;\r | |
3841 | BYTE vn[11];\r | |
3842 | UINT i, j, sl;\r | |
3843 | WCHAR w;\r | |
3844 | DWORD tm;\r | |
3845 | \r | |
3846 | \r | |
3847 | /* Get logical drive number */\r | |
3848 | res = find_volume(&dj.fs, &label, 1);\r | |
3849 | if (res) LEAVE_FF(dj.fs, res);\r | |
3850 | \r | |
3851 | /* Create a volume label in directory form */\r | |
3852 | vn[0] = 0;\r | |
3853 | for (sl = 0; label[sl]; sl++) ; /* Get name length */\r | |
3854 | for ( ; sl && label[sl-1] == ' '; sl--) ; /* Remove trailing spaces */\r | |
3855 | if (sl) { /* Create volume label in directory form */\r | |
3856 | i = j = 0;\r | |
3857 | do {\r | |
3858 | #if _USE_LFN && _LFN_UNICODE\r | |
3859 | w = ff_convert(ff_wtoupper(label[i++]), 0);\r | |
3860 | #else\r | |
3861 | w = (BYTE)label[i++];\r | |
3862 | if (IsDBCS1(w))\r | |
3863 | w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;\r | |
3864 | #if _USE_LFN\r | |
3865 | w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0);\r | |
3866 | #else\r | |
3867 | if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */\r | |
3868 | #ifdef _EXCVT\r | |
3869 | if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */\r | |
3870 | #else\r | |
3871 | if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */\r | |
3872 | #endif\r | |
3873 | #endif\r | |
3874 | #endif\r | |
3875 | if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */\r | |
3876 | LEAVE_FF(dj.fs, FR_INVALID_NAME);\r | |
3877 | if (w >= 0x100) vn[j++] = (BYTE)(w >> 8);\r | |
3878 | vn[j++] = (BYTE)w;\r | |
3879 | } while (i < sl);\r | |
3880 | while (j < 11) vn[j++] = ' ';\r | |
3881 | }\r | |
3882 | \r | |
3883 | /* Set volume label */\r | |
3884 | dj.sclust = 0; /* Open root directory */\r | |
3885 | res = dir_sdi(&dj, 0);\r | |
3886 | if (res == FR_OK) {\r | |
3887 | res = dir_read(&dj, 1); /* Get an entry with AM_VOL */\r | |
3888 | if (res == FR_OK) { /* A volume label is found */\r | |
3889 | if (vn[0]) {\r | |
3890 | mem_cpy(dj.dir, vn, 11); /* Change the volume label name */\r | |
7b78a5a2 | 3891 | tm = GET_FATTIME();\r |
53668523 L |
3892 | ST_DWORD(dj.dir+DIR_WrtTime, tm);\r |
3893 | } else {\r | |
3894 | dj.dir[0] = DDE; /* Remove the volume label */\r | |
3895 | }\r | |
3896 | dj.fs->wflag = 1;\r | |
3897 | res = sync_fs(dj.fs);\r | |
3898 | } else { /* No volume label is found or error */\r | |
3899 | if (res == FR_NO_FILE) {\r | |
3900 | res = FR_OK;\r | |
3901 | if (vn[0]) { /* Create volume label as new */\r | |
3902 | res = dir_alloc(&dj, 1); /* Allocate an entry for volume label */\r | |
3903 | if (res == FR_OK) {\r | |
3904 | mem_set(dj.dir, 0, SZ_DIR); /* Set volume label */\r | |
3905 | mem_cpy(dj.dir, vn, 11);\r | |
3906 | dj.dir[DIR_Attr] = AM_VOL;\r | |
7b78a5a2 | 3907 | tm = GET_FATTIME();\r |
53668523 L |
3908 | ST_DWORD(dj.dir+DIR_WrtTime, tm);\r |
3909 | dj.fs->wflag = 1;\r | |
3910 | res = sync_fs(dj.fs);\r | |
3911 | }\r | |
3912 | }\r | |
3913 | }\r | |
3914 | }\r | |
3915 | }\r | |
3916 | \r | |
3917 | LEAVE_FF(dj.fs, res);\r | |
3918 | }\r | |
3919 | \r | |
3920 | #endif /* !_FS_READONLY */\r | |
3921 | #endif /* _USE_LABEL */\r | |
3922 | \r | |
3923 | \r | |
3924 | \r | |
3925 | /*-----------------------------------------------------------------------*/\r | |
3926 | /* Forward data to the stream directly (available on only tiny cfg) */\r | |
3927 | /*-----------------------------------------------------------------------*/\r | |
3928 | #if _USE_FORWARD && _FS_TINY\r | |
3929 | \r | |
3930 | FRESULT f_forward (\r | |
3931 | FIL* fp, /* Pointer to the file object */\r | |
3932 | UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */\r | |
3933 | UINT btf, /* Number of bytes to forward */\r | |
3934 | UINT* bf /* Pointer to number of bytes forwarded */\r | |
3935 | )\r | |
3936 | {\r | |
3937 | FRESULT res;\r | |
3938 | DWORD remain, clst, sect;\r | |
3939 | UINT rcnt;\r | |
3940 | BYTE csect;\r | |
3941 | \r | |
3942 | \r | |
3943 | *bf = 0; /* Clear transfer byte counter */\r | |
3944 | \r | |
3945 | res = validate(fp); /* Check validity of the object */\r | |
3946 | if (res != FR_OK) LEAVE_FF(fp->fs, res);\r | |
3947 | if (fp->err) /* Check error */\r | |
3948 | LEAVE_FF(fp->fs, (FRESULT)fp->err);\r | |
3949 | if (!(fp->flag & FA_READ)) /* Check access mode */\r | |
3950 | LEAVE_FF(fp->fs, FR_DENIED);\r | |
3951 | \r | |
3952 | remain = fp->fsize - fp->fptr;\r | |
3953 | if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */\r | |
3954 | \r | |
3955 | for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */\r | |
3956 | fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {\r | |
3957 | csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */\r | |
3958 | if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */\r | |
3959 | if (!csect) { /* On the cluster boundary? */\r | |
3960 | clst = (fp->fptr == 0) ? /* On the top of the file? */\r | |
3961 | fp->sclust : get_fat(fp->fs, fp->clust);\r | |
3962 | if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);\r | |
3963 | if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);\r | |
3964 | fp->clust = clst; /* Update current cluster */\r | |
3965 | }\r | |
3966 | }\r | |
3967 | sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */\r | |
3968 | if (!sect) ABORT(fp->fs, FR_INT_ERR);\r | |
3969 | sect += csect;\r | |
7b78a5a2 | 3970 | if (move_window(fp->fs, sect) != FR_OK) /* Move sector window */\r |
53668523 L |
3971 | ABORT(fp->fs, FR_DISK_ERR);\r |
3972 | fp->dsect = sect;\r | |
3973 | rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */\r | |
3974 | if (rcnt > btf) rcnt = btf;\r | |
3975 | rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);\r | |
3976 | if (!rcnt) ABORT(fp->fs, FR_INT_ERR);\r | |
3977 | }\r | |
3978 | \r | |
3979 | LEAVE_FF(fp->fs, FR_OK);\r | |
3980 | }\r | |
3981 | #endif /* _USE_FORWARD */\r | |
3982 | \r | |
3983 | \r | |
3984 | \r | |
3985 | #if _USE_MKFS && !_FS_READONLY\r | |
3986 | /*-----------------------------------------------------------------------*/\r | |
3987 | /* Create File System on the Drive */\r | |
3988 | /*-----------------------------------------------------------------------*/\r | |
3989 | #define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */\r | |
7b78a5a2 | 3990 | #define N_FATS 1 /* Number of FATs (1 or 2) */\r |
53668523 L |
3991 | \r |
3992 | \r | |
3993 | FRESULT f_mkfs (\r | |
3994 | const TCHAR* path, /* Logical drive number */\r | |
3995 | BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */\r | |
7b78a5a2 | 3996 | UINT au /* Size of allocation unit in unit of byte or sector */\r |
53668523 L |
3997 | )\r |
3998 | {\r | |
3999 | static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0};\r | |
4000 | static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};\r | |
4001 | int vol;\r | |
4002 | BYTE fmt, md, sys, *tbl, pdrv, part;\r | |
4003 | DWORD n_clst, vs, n, wsect;\r | |
4004 | UINT i;\r | |
4005 | DWORD b_vol, b_fat, b_dir, b_data; /* LBA */\r | |
4006 | DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */\r | |
4007 | FATFS *fs;\r | |
4008 | DSTATUS stat;\r | |
4009 | \r | |
4010 | \r | |
4011 | /* Check mounted drive and clear work area */\r | |
7b78a5a2 | 4012 | if (sfd > 1) return FR_INVALID_PARAMETER;\r |
53668523 L |
4013 | vol = get_ldnumber(&path);\r |
4014 | if (vol < 0) return FR_INVALID_DRIVE;\r | |
53668523 L |
4015 | fs = FatFs[vol];\r |
4016 | if (!fs) return FR_NOT_ENABLED;\r | |
4017 | fs->fs_type = 0;\r | |
4018 | pdrv = LD2PD(vol); /* Physical drive */\r | |
4019 | part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/\r | |
4020 | \r | |
4021 | /* Get disk statics */\r | |
4022 | stat = disk_initialize(pdrv);\r | |
4023 | if (stat & STA_NOINIT) return FR_NOT_READY;\r | |
4024 | if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;\r | |
4025 | #if _MAX_SS != _MIN_SS /* Get disk sector size */\r | |
4026 | if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS)\r | |
4027 | return FR_DISK_ERR;\r | |
4028 | #endif\r | |
4029 | if (_MULTI_PARTITION && part) {\r | |
4030 | /* Get partition information from partition table in the MBR */\r | |
7b78a5a2 | 4031 | if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;\r |
53668523 L |
4032 | if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;\r |
4033 | tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];\r | |
4034 | if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */\r | |
4035 | b_vol = LD_DWORD(tbl+8); /* Volume start sector */\r | |
4036 | n_vol = LD_DWORD(tbl+12); /* Volume size */\r | |
4037 | } else {\r | |
4038 | /* Create a partition in this function */\r | |
4039 | if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)\r | |
4040 | return FR_DISK_ERR;\r | |
4041 | b_vol = (sfd) ? 0 : 63; /* Volume start sector */\r | |
4042 | n_vol -= b_vol; /* Volume size */\r | |
4043 | }\r | |
4044 | \r | |
7b78a5a2 L |
4045 | if (au & (au - 1)) au = 0;\r |
4046 | if (!au) { /* AU auto selection */\r | |
53668523 L |
4047 | vs = n_vol / (2000 / (SS(fs) / 512));\r |
4048 | for (i = 0; vs < vst[i]; i++) ;\r | |
4049 | au = cst[i];\r | |
4050 | }\r | |
7b78a5a2 L |
4051 | if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */\r |
4052 | if (!au) au = 1;\r | |
53668523 L |
4053 | if (au > 128) au = 128;\r |
4054 | \r | |
4055 | /* Pre-compute number of clusters and FAT sub-type */\r | |
4056 | n_clst = n_vol / au;\r | |
4057 | fmt = FS_FAT12;\r | |
4058 | if (n_clst >= MIN_FAT16) fmt = FS_FAT16;\r | |
4059 | if (n_clst >= MIN_FAT32) fmt = FS_FAT32;\r | |
4060 | \r | |
4061 | /* Determine offset and size of FAT structure */\r | |
4062 | if (fmt == FS_FAT32) {\r | |
4063 | n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);\r | |
4064 | n_rsv = 32;\r | |
4065 | n_dir = 0;\r | |
4066 | } else {\r | |
4067 | n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;\r | |
4068 | n_fat = (n_fat + SS(fs) - 1) / SS(fs);\r | |
4069 | n_rsv = 1;\r | |
4070 | n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs);\r | |
4071 | }\r | |
4072 | b_fat = b_vol + n_rsv; /* FAT area start sector */\r | |
4073 | b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */\r | |
4074 | b_data = b_dir + n_dir; /* Data area start sector */\r | |
4075 | if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */\r | |
4076 | \r | |
4077 | /* Align data start sector to erase block boundary (for flash memory media) */\r | |
4078 | if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;\r | |
4079 | n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */\r | |
4080 | n = (n - b_data) / N_FATS;\r | |
4081 | if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */\r | |
4082 | n_rsv += n;\r | |
4083 | b_fat += n;\r | |
4084 | } else { /* FAT12/16: Expand FAT size */\r | |
4085 | n_fat += n;\r | |
4086 | }\r | |
4087 | \r | |
4088 | /* Determine number of clusters and final check of validity of the FAT sub-type */\r | |
4089 | n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;\r | |
4090 | if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16)\r | |
4091 | || (fmt == FS_FAT32 && n_clst < MIN_FAT32))\r | |
4092 | return FR_MKFS_ABORTED;\r | |
4093 | \r | |
4094 | /* Determine system ID in the partition table */\r | |
4095 | if (fmt == FS_FAT32) {\r | |
4096 | sys = 0x0C; /* FAT32X */\r | |
4097 | } else {\r | |
4098 | if (fmt == FS_FAT12 && n_vol < 0x10000) {\r | |
4099 | sys = 0x01; /* FAT12(<65536) */\r | |
4100 | } else {\r | |
4101 | sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */\r | |
4102 | }\r | |
4103 | }\r | |
4104 | \r | |
4105 | if (_MULTI_PARTITION && part) {\r | |
4106 | /* Update system ID in the partition table */\r | |
4107 | tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];\r | |
4108 | tbl[4] = sys;\r | |
7b78a5a2 | 4109 | if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to teh MBR */\r |
53668523 L |
4110 | return FR_DISK_ERR;\r |
4111 | md = 0xF8;\r | |
4112 | } else {\r | |
4113 | if (sfd) { /* No partition table (SFD) */\r | |
4114 | md = 0xF0;\r | |
4115 | } else { /* Create partition table (FDISK) */\r | |
4116 | mem_set(fs->win, 0, SS(fs));\r | |
4117 | tbl = fs->win+MBR_Table; /* Create partition table for single partition in the drive */\r | |
4118 | tbl[1] = 1; /* Partition start head */\r | |
4119 | tbl[2] = 1; /* Partition start sector */\r | |
4120 | tbl[3] = 0; /* Partition start cylinder */\r | |
4121 | tbl[4] = sys; /* System type */\r | |
4122 | tbl[5] = 254; /* Partition end head */\r | |
4123 | n = (b_vol + n_vol) / 63 / 255;\r | |
4124 | tbl[6] = (BYTE)(n >> 2 | 63); /* Partition end sector */\r | |
4125 | tbl[7] = (BYTE)n; /* End cylinder */\r | |
4126 | ST_DWORD(tbl+8, 63); /* Partition start in LBA */\r | |
4127 | ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */\r | |
4128 | ST_WORD(fs->win+BS_55AA, 0xAA55); /* MBR signature */\r | |
7b78a5a2 | 4129 | if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR */\r |
53668523 L |
4130 | return FR_DISK_ERR;\r |
4131 | md = 0xF8;\r | |
4132 | }\r | |
4133 | }\r | |
4134 | \r | |
4135 | /* Create BPB in the VBR */\r | |
4136 | tbl = fs->win; /* Clear sector */\r | |
4137 | mem_set(tbl, 0, SS(fs));\r | |
4138 | mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */\r | |
4139 | i = SS(fs); /* Sector size */\r | |
4140 | ST_WORD(tbl+BPB_BytsPerSec, i);\r | |
4141 | tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */\r | |
4142 | ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */\r | |
4143 | tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */\r | |
4144 | i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of root directory entries */\r | |
4145 | ST_WORD(tbl+BPB_RootEntCnt, i);\r | |
4146 | if (n_vol < 0x10000) { /* Number of total sectors */\r | |
4147 | ST_WORD(tbl+BPB_TotSec16, n_vol);\r | |
4148 | } else {\r | |
4149 | ST_DWORD(tbl+BPB_TotSec32, n_vol);\r | |
4150 | }\r | |
4151 | tbl[BPB_Media] = md; /* Media descriptor */\r | |
4152 | ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */\r | |
4153 | ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */\r | |
4154 | ST_DWORD(tbl+BPB_HiddSec, b_vol); /* Hidden sectors */\r | |
7b78a5a2 | 4155 | n = GET_FATTIME(); /* Use current time as VSN */\r |
53668523 L |
4156 | if (fmt == FS_FAT32) {\r |
4157 | ST_DWORD(tbl+BS_VolID32, n); /* VSN */\r | |
4158 | ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */\r | |
4159 | ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */\r | |
4160 | ST_WORD(tbl+BPB_FSInfo, 1); /* FSINFO record offset (VBR+1) */\r | |
4161 | ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */\r | |
4162 | tbl[BS_DrvNum32] = 0x80; /* Drive number */\r | |
4163 | tbl[BS_BootSig32] = 0x29; /* Extended boot signature */\r | |
4164 | mem_cpy(tbl+BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */\r | |
4165 | } else {\r | |
4166 | ST_DWORD(tbl+BS_VolID, n); /* VSN */\r | |
4167 | ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors per FAT */\r | |
4168 | tbl[BS_DrvNum] = 0x80; /* Drive number */\r | |
4169 | tbl[BS_BootSig] = 0x29; /* Extended boot signature */\r | |
4170 | mem_cpy(tbl+BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */\r | |
4171 | }\r | |
4172 | ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */\r | |
7b78a5a2 | 4173 | if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */\r |
53668523 L |
4174 | return FR_DISK_ERR;\r |
4175 | if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR+6) */\r | |
4176 | disk_write(pdrv, tbl, b_vol + 6, 1);\r | |
4177 | \r | |
4178 | /* Initialize FAT area */\r | |
4179 | wsect = b_fat;\r | |
4180 | for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */\r | |
4181 | mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */\r | |
4182 | n = md; /* Media descriptor byte */\r | |
4183 | if (fmt != FS_FAT32) {\r | |
4184 | n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;\r | |
4185 | ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */\r | |
4186 | } else {\r | |
4187 | n |= 0xFFFFFF00;\r | |
4188 | ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT32) */\r | |
4189 | ST_DWORD(tbl+4, 0xFFFFFFFF);\r | |
4190 | ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root directory */\r | |
4191 | }\r | |
7b78a5a2 | 4192 | if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)\r |
53668523 L |
4193 | return FR_DISK_ERR;\r |
4194 | mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */\r | |
4195 | for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */\r | |
7b78a5a2 | 4196 | if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)\r |
53668523 L |
4197 | return FR_DISK_ERR;\r |
4198 | }\r | |
4199 | }\r | |
4200 | \r | |
4201 | /* Initialize root directory */\r | |
4202 | i = (fmt == FS_FAT32) ? au : (UINT)n_dir;\r | |
4203 | do {\r | |
7b78a5a2 | 4204 | if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)\r |
53668523 L |
4205 | return FR_DISK_ERR;\r |
4206 | } while (--i);\r | |
4207 | \r | |
7b78a5a2 | 4208 | #if _USE_TRIM /* Erase data area if needed */\r |
53668523 L |
4209 | {\r |
4210 | DWORD eb[2];\r | |
4211 | \r | |
4212 | eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;\r | |
7b78a5a2 | 4213 | disk_ioctl(pdrv, CTRL_TRIM, eb);\r |
53668523 L |
4214 | }\r |
4215 | #endif\r | |
4216 | \r | |
4217 | /* Create FSINFO if needed */\r | |
4218 | if (fmt == FS_FAT32) {\r | |
4219 | ST_DWORD(tbl+FSI_LeadSig, 0x41615252);\r | |
4220 | ST_DWORD(tbl+FSI_StrucSig, 0x61417272);\r | |
4221 | ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); /* Number of free clusters */\r | |
4222 | ST_DWORD(tbl+FSI_Nxt_Free, 2); /* Last allocated cluster# */\r | |
4223 | ST_WORD(tbl+BS_55AA, 0xAA55);\r | |
4224 | disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR+1) */\r | |
4225 | disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR+7) */\r | |
4226 | }\r | |
4227 | \r | |
4228 | return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;\r | |
4229 | }\r | |
4230 | \r | |
4231 | \r | |
4232 | \r | |
4233 | #if _MULTI_PARTITION\r | |
4234 | /*-----------------------------------------------------------------------*/\r | |
4235 | /* Divide Physical Drive */\r | |
4236 | /*-----------------------------------------------------------------------*/\r | |
4237 | \r | |
4238 | FRESULT f_fdisk (\r | |
4239 | BYTE pdrv, /* Physical drive number */\r | |
4240 | const DWORD szt[], /* Pointer to the size table for each partitions */\r | |
4241 | void* work /* Pointer to the working buffer */\r | |
4242 | )\r | |
4243 | {\r | |
4244 | UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;\r | |
4245 | BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;\r | |
4246 | DSTATUS stat;\r | |
4247 | DWORD sz_disk, sz_part, s_part;\r | |
4248 | \r | |
4249 | \r | |
4250 | stat = disk_initialize(pdrv);\r | |
4251 | if (stat & STA_NOINIT) return FR_NOT_READY;\r | |
4252 | if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;\r | |
4253 | if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;\r | |
4254 | \r | |
4255 | /* Determine CHS in the table regardless of the drive geometry */\r | |
4256 | for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;\r | |
4257 | if (n == 256) n--;\r | |
4258 | e_hd = n - 1;\r | |
4259 | sz_cyl = 63 * n;\r | |
4260 | tot_cyl = sz_disk / sz_cyl;\r | |
4261 | \r | |
4262 | /* Create partition table */\r | |
4263 | mem_set(buf, 0, _MAX_SS);\r | |
4264 | p = buf + MBR_Table; b_cyl = 0;\r | |
4265 | for (i = 0; i < 4; i++, p += SZ_PTE) {\r | |
4266 | p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;\r | |
4267 | if (!p_cyl) continue;\r | |
4268 | s_part = (DWORD)sz_cyl * b_cyl;\r | |
4269 | sz_part = (DWORD)sz_cyl * p_cyl;\r | |
4270 | if (i == 0) { /* Exclude first track of cylinder 0 */\r | |
4271 | s_hd = 1;\r | |
4272 | s_part += 63; sz_part -= 63;\r | |
4273 | } else {\r | |
4274 | s_hd = 0;\r | |
4275 | }\r | |
4276 | e_cyl = b_cyl + p_cyl - 1;\r | |
4277 | if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;\r | |
4278 | \r | |
4279 | /* Set partition table */\r | |
4280 | p[1] = s_hd; /* Start head */\r | |
4281 | p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */\r | |
4282 | p[3] = (BYTE)b_cyl; /* Start cylinder */\r | |
4283 | p[4] = 0x06; /* System type (temporary setting) */\r | |
4284 | p[5] = e_hd; /* End head */\r | |
4285 | p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */\r | |
4286 | p[7] = (BYTE)e_cyl; /* End cylinder */\r | |
4287 | ST_DWORD(p + 8, s_part); /* Start sector in LBA */\r | |
4288 | ST_DWORD(p + 12, sz_part); /* Partition size */\r | |
4289 | \r | |
4290 | /* Next partition */\r | |
4291 | b_cyl += p_cyl;\r | |
4292 | }\r | |
4293 | ST_WORD(p, 0xAA55);\r | |
4294 | \r | |
4295 | /* Write it to the MBR */\r | |
7b78a5a2 | 4296 | return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK;\r |
53668523 L |
4297 | }\r |
4298 | \r | |
4299 | \r | |
4300 | #endif /* _MULTI_PARTITION */\r | |
4301 | #endif /* _USE_MKFS && !_FS_READONLY */\r | |
4302 | \r | |
4303 | \r | |
4304 | \r | |
4305 | \r | |
4306 | #if _USE_STRFUNC\r | |
4307 | /*-----------------------------------------------------------------------*/\r | |
4308 | /* Get a string from the file */\r | |
4309 | /*-----------------------------------------------------------------------*/\r | |
4310 | \r | |
4311 | TCHAR* f_gets (\r | |
4312 | TCHAR* buff, /* Pointer to the string buffer to read */\r | |
4313 | int len, /* Size of string buffer (characters) */\r | |
4314 | FIL* fp /* Pointer to the file object */\r | |
4315 | )\r | |
4316 | {\r | |
4317 | int n = 0;\r | |
4318 | TCHAR c, *p = buff;\r | |
4319 | BYTE s[2];\r | |
4320 | UINT rc;\r | |
4321 | \r | |
4322 | \r | |
4323 | while (n < len - 1) { /* Read characters until buffer gets filled */\r | |
4324 | #if _USE_LFN && _LFN_UNICODE\r | |
4325 | #if _STRF_ENCODE == 3 /* Read a character in UTF-8 */\r | |
4326 | f_read(fp, s, 1, &rc);\r | |
4327 | if (rc != 1) break;\r | |
4328 | c = s[0];\r | |
4329 | if (c >= 0x80) {\r | |
4330 | if (c < 0xC0) continue; /* Skip stray trailer */\r | |
4331 | if (c < 0xE0) { /* Two-byte sequence */\r | |
4332 | f_read(fp, s, 1, &rc);\r | |
4333 | if (rc != 1) break;\r | |
4334 | c = (c & 0x1F) << 6 | (s[0] & 0x3F);\r | |
4335 | if (c < 0x80) c = '?';\r | |
4336 | } else {\r | |
4337 | if (c < 0xF0) { /* Three-byte sequence */\r | |
4338 | f_read(fp, s, 2, &rc);\r | |
4339 | if (rc != 2) break;\r | |
4340 | c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F);\r | |
4341 | if (c < 0x800) c = '?';\r | |
4342 | } else { /* Reject four-byte sequence */\r | |
4343 | c = '?';\r | |
4344 | }\r | |
4345 | }\r | |
4346 | }\r | |
4347 | #elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */\r | |
4348 | f_read(fp, s, 2, &rc);\r | |
4349 | if (rc != 2) break;\r | |
4350 | c = s[1] + (s[0] << 8);\r | |
4351 | #elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */\r | |
4352 | f_read(fp, s, 2, &rc);\r | |
4353 | if (rc != 2) break;\r | |
4354 | c = s[0] + (s[1] << 8);\r | |
4355 | #else /* Read a character in ANSI/OEM */\r | |
4356 | f_read(fp, s, 1, &rc);\r | |
4357 | if (rc != 1) break;\r | |
4358 | c = s[0];\r | |
4359 | if (IsDBCS1(c)) {\r | |
4360 | f_read(fp, s, 1, &rc);\r | |
4361 | if (rc != 1) break;\r | |
4362 | c = (c << 8) + s[0];\r | |
4363 | }\r | |
4364 | c = ff_convert(c, 1); /* OEM -> Unicode */\r | |
4365 | if (!c) c = '?';\r | |
4366 | #endif\r | |
4367 | #else /* Read a character without conversion */\r | |
4368 | f_read(fp, s, 1, &rc);\r | |
4369 | if (rc != 1) break;\r | |
4370 | c = s[0];\r | |
4371 | #endif\r | |
4372 | if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */\r | |
4373 | *p++ = c;\r | |
4374 | n++;\r | |
4375 | if (c == '\n') break; /* Break on EOL */\r | |
4376 | }\r | |
4377 | *p = 0;\r | |
4378 | return n ? buff : 0; /* When no data read (eof or error), return with error. */\r | |
4379 | }\r | |
4380 | \r | |
4381 | \r | |
4382 | \r | |
4383 | #if !_FS_READONLY\r | |
4384 | #include <stdarg.h>\r | |
4385 | /*-----------------------------------------------------------------------*/\r | |
4386 | /* Put a character to the file */\r | |
4387 | /*-----------------------------------------------------------------------*/\r | |
4388 | \r | |
4389 | typedef struct {\r | |
4390 | FIL* fp;\r | |
4391 | int idx, nchr;\r | |
4392 | BYTE buf[64];\r | |
4393 | } putbuff;\r | |
4394 | \r | |
4395 | \r | |
4396 | static\r | |
4397 | void putc_bfd (\r | |
4398 | putbuff* pb,\r | |
4399 | TCHAR c\r | |
4400 | )\r | |
4401 | {\r | |
4402 | UINT bw;\r | |
4403 | int i;\r | |
4404 | \r | |
4405 | \r | |
4406 | if (_USE_STRFUNC == 2 && c == '\n') /* LF -> CRLF conversion */\r | |
4407 | putc_bfd(pb, '\r');\r | |
4408 | \r | |
4409 | i = pb->idx; /* Buffer write index (-1:error) */\r | |
4410 | if (i < 0) return;\r | |
4411 | \r | |
4412 | #if _USE_LFN && _LFN_UNICODE\r | |
4413 | #if _STRF_ENCODE == 3 /* Write a character in UTF-8 */\r | |
4414 | if (c < 0x80) { /* 7-bit */\r | |
4415 | pb->buf[i++] = (BYTE)c;\r | |
4416 | } else {\r | |
4417 | if (c < 0x800) { /* 11-bit */\r | |
4418 | pb->buf[i++] = (BYTE)(0xC0 | c >> 6);\r | |
4419 | } else { /* 16-bit */\r | |
4420 | pb->buf[i++] = (BYTE)(0xE0 | c >> 12);\r | |
4421 | pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F));\r | |
4422 | }\r | |
4423 | pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F));\r | |
4424 | }\r | |
4425 | #elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */\r | |
4426 | pb->buf[i++] = (BYTE)(c >> 8);\r | |
4427 | pb->buf[i++] = (BYTE)c;\r | |
4428 | #elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */\r | |
4429 | pb->buf[i++] = (BYTE)c;\r | |
4430 | pb->buf[i++] = (BYTE)(c >> 8);\r | |
4431 | #else /* Write a character in ANSI/OEM */\r | |
4432 | c = ff_convert(c, 0); /* Unicode -> OEM */\r | |
4433 | if (!c) c = '?';\r | |
4434 | if (c >= 0x100)\r | |
4435 | pb->buf[i++] = (BYTE)(c >> 8);\r | |
4436 | pb->buf[i++] = (BYTE)c;\r | |
4437 | #endif\r | |
4438 | #else /* Write a character without conversion */\r | |
4439 | pb->buf[i++] = (BYTE)c;\r | |
4440 | #endif\r | |
4441 | \r | |
4442 | if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */\r | |
4443 | f_write(pb->fp, pb->buf, (UINT)i, &bw);\r | |
4444 | i = (bw == (UINT)i) ? 0 : -1;\r | |
4445 | }\r | |
4446 | pb->idx = i;\r | |
4447 | pb->nchr++;\r | |
4448 | }\r | |
4449 | \r | |
4450 | \r | |
4451 | \r | |
4452 | int f_putc (\r | |
4453 | TCHAR c, /* A character to be output */\r | |
4454 | FIL* fp /* Pointer to the file object */\r | |
4455 | )\r | |
4456 | {\r | |
4457 | putbuff pb;\r | |
4458 | UINT nw;\r | |
4459 | \r | |
4460 | \r | |
4461 | pb.fp = fp; /* Initialize output buffer */\r | |
4462 | pb.nchr = pb.idx = 0;\r | |
4463 | \r | |
4464 | putc_bfd(&pb, c); /* Put a character */\r | |
4465 | \r | |
4466 | if ( pb.idx >= 0 /* Flush buffered characters to the file */\r | |
4467 | && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK\r | |
4468 | && (UINT)pb.idx == nw) return pb.nchr;\r | |
4469 | return EOF;\r | |
4470 | }\r | |
4471 | \r | |
4472 | \r | |
4473 | \r | |
4474 | \r | |
4475 | /*-----------------------------------------------------------------------*/\r | |
4476 | /* Put a string to the file */\r | |
4477 | /*-----------------------------------------------------------------------*/\r | |
4478 | \r | |
4479 | int f_puts (\r | |
4480 | const TCHAR* str, /* Pointer to the string to be output */\r | |
4481 | FIL* fp /* Pointer to the file object */\r | |
4482 | )\r | |
4483 | {\r | |
4484 | putbuff pb;\r | |
4485 | UINT nw;\r | |
4486 | \r | |
4487 | \r | |
4488 | pb.fp = fp; /* Initialize output buffer */\r | |
4489 | pb.nchr = pb.idx = 0;\r | |
4490 | \r | |
4491 | while (*str) /* Put the string */\r | |
4492 | putc_bfd(&pb, *str++);\r | |
4493 | \r | |
4494 | if ( pb.idx >= 0 /* Flush buffered characters to the file */\r | |
4495 | && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK\r | |
4496 | && (UINT)pb.idx == nw) return pb.nchr;\r | |
4497 | return EOF;\r | |
4498 | }\r | |
4499 | \r | |
4500 | \r | |
4501 | \r | |
4502 | \r | |
4503 | /*-----------------------------------------------------------------------*/\r | |
4504 | /* Put a formatted string to the file */\r | |
4505 | /*-----------------------------------------------------------------------*/\r | |
4506 | \r | |
4507 | int f_printf (\r | |
4508 | FIL* fp, /* Pointer to the file object */\r | |
4509 | const TCHAR* fmt, /* Pointer to the format string */\r | |
4510 | ... /* Optional arguments... */\r | |
4511 | )\r | |
4512 | {\r | |
4513 | va_list arp;\r | |
4514 | BYTE f, r;\r | |
4515 | UINT nw, i, j, w;\r | |
4516 | DWORD v;\r | |
4517 | TCHAR c, d, s[16], *p;\r | |
4518 | putbuff pb;\r | |
4519 | \r | |
4520 | \r | |
4521 | pb.fp = fp; /* Initialize output buffer */\r | |
4522 | pb.nchr = pb.idx = 0;\r | |
4523 | \r | |
4524 | va_start(arp, fmt);\r | |
4525 | \r | |
4526 | for (;;) {\r | |
4527 | c = *fmt++;\r | |
4528 | if (c == 0) break; /* End of string */\r | |
4529 | if (c != '%') { /* Non escape character */\r | |
4530 | putc_bfd(&pb, c);\r | |
4531 | continue;\r | |
4532 | }\r | |
4533 | w = f = 0;\r | |
4534 | c = *fmt++;\r | |
4535 | if (c == '0') { /* Flag: '0' padding */\r | |
4536 | f = 1; c = *fmt++;\r | |
4537 | } else {\r | |
4538 | if (c == '-') { /* Flag: left justified */\r | |
4539 | f = 2; c = *fmt++;\r | |
4540 | }\r | |
4541 | }\r | |
4542 | while (IsDigit(c)) { /* Precision */\r | |
4543 | w = w * 10 + c - '0';\r | |
4544 | c = *fmt++;\r | |
4545 | }\r | |
4546 | if (c == 'l' || c == 'L') { /* Prefix: Size is long int */\r | |
4547 | f |= 4; c = *fmt++;\r | |
4548 | }\r | |
4549 | if (!c) break;\r | |
4550 | d = c;\r | |
4551 | if (IsLower(d)) d -= 0x20;\r | |
4552 | switch (d) { /* Type is... */\r | |
4553 | case 'S' : /* String */\r | |
4554 | p = va_arg(arp, TCHAR*);\r | |
4555 | for (j = 0; p[j]; j++) ;\r | |
4556 | if (!(f & 2)) {\r | |
4557 | while (j++ < w) putc_bfd(&pb, ' ');\r | |
4558 | }\r | |
4559 | while (*p) putc_bfd(&pb, *p++);\r | |
4560 | while (j++ < w) putc_bfd(&pb, ' ');\r | |
4561 | continue;\r | |
4562 | case 'C' : /* Character */\r | |
4563 | putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;\r | |
4564 | case 'B' : /* Binary */\r | |
4565 | r = 2; break;\r | |
4566 | case 'O' : /* Octal */\r | |
4567 | r = 8; break;\r | |
4568 | case 'D' : /* Signed decimal */\r | |
4569 | case 'U' : /* Unsigned decimal */\r | |
4570 | r = 10; break;\r | |
4571 | case 'X' : /* Hexdecimal */\r | |
4572 | r = 16; break;\r | |
4573 | default: /* Unknown type (pass-through) */\r | |
4574 | putc_bfd(&pb, c); continue;\r | |
4575 | }\r | |
4576 | \r | |
4577 | /* Get an argument and put it in numeral */\r | |
4578 | v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));\r | |
4579 | if (d == 'D' && (v & 0x80000000)) {\r | |
4580 | v = 0 - v;\r | |
4581 | f |= 8;\r | |
4582 | }\r | |
4583 | i = 0;\r | |
4584 | do {\r | |
4585 | d = (TCHAR)(v % r); v /= r;\r | |
4586 | if (d > 9) d += (c == 'x') ? 0x27 : 0x07;\r | |
4587 | s[i++] = d + '0';\r | |
4588 | } while (v && i < sizeof s / sizeof s[0]);\r | |
4589 | if (f & 8) s[i++] = '-';\r | |
4590 | j = i; d = (f & 1) ? '0' : ' ';\r | |
4591 | while (!(f & 2) && j++ < w) putc_bfd(&pb, d);\r | |
4592 | do putc_bfd(&pb, s[--i]); while (i);\r | |
4593 | while (j++ < w) putc_bfd(&pb, d);\r | |
4594 | }\r | |
4595 | \r | |
4596 | va_end(arp);\r | |
4597 | \r | |
4598 | if ( pb.idx >= 0 /* Flush buffered characters to the file */\r | |
4599 | && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK\r | |
4600 | && (UINT)pb.idx == nw) return pb.nchr;\r | |
4601 | return EOF;\r | |
4602 | }\r | |
4603 | \r | |
4604 | #endif /* !_FS_READONLY */\r | |
4605 | #endif /* _USE_STRFUNC */\r |