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