1 /*----------------------------------------------------------------------------/\r
2 / FatFs - FAT file system module R0.12 (C)ChaN, 2016 /\r
3 /-----------------------------------------------------------------------------/\r
4 / FatFs module is a free software that opened under license policy of\r
5 / following conditions.\r
6 /\r
7 / Copyright (C) 2016, ChaN, all right reserved.\r
8 /\r
9 / 1. Redistributions of source code must retain the above copyright notice,\r
10 / this condition and the following disclaimer.\r
11 /\r
12 / This software is provided by the copyright holder and contributors "AS IS"\r
13 / and any warranties related to this software are DISCLAIMED.\r
14 / The copyright owner or contributors be NOT LIABLE for any damages caused\r
15 / by use of this software.\r
16 /----------------------------------------------------------------------------*/\r
17 \r
18 \r
19 #include <ti/csl/tistdtypes.h>\r
20 #include "ff.h" /* Declarations of FatFs API */\r
21 #include "diskio.h" /* Declarations of disk I/O functions */\r
22 \r
23 \r
24 /*--------------------------------------------------------------------------\r
25 \r
26 Module Private Definitions\r
27 \r
28 ---------------------------------------------------------------------------*/\r
29 \r
30 #if _FATFS != 88100 /* Revision ID */\r
31 #error Wrong include file (ff.h).\r
32 #endif\r
33 \r
34 \r
35 /* Reentrancy related */\r
36 #if _FS_REENTRANT\r
37 #if _USE_LFN == 1\r
38 #error Static LFN work area cannot be used at thread-safe configuration\r
39 #endif\r
40 #define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; }\r
41 #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }\r
42 #else\r
43 #define ENTER_FF(fs)\r
44 #define LEAVE_FF(fs, res) return res\r
45 #endif\r
46 \r
47 #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }\r
48 \r
49 \r
50 /* Definitions of sector size */\r
51 #if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096)\r
52 #error Wrong sector size configuration\r
53 #endif\r
54 #if _MAX_SS == _MIN_SS\r
55 #define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */\r
56 #else\r
57 #define SS(fs) ((fs)->ssize) /* Variable sector size */\r
58 #endif\r
59 \r
60 \r
61 /* Timestamp */\r
62 #if _FS_NORTC == 1\r
63 #if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31\r
64 #error Invalid _FS_NORTC settings\r
65 #endif\r
66 #define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16)\r
67 #else\r
68 #define GET_FATTIME() get_fattime()\r
69 #endif\r
70 \r
71 \r
72 /* File lock controls */\r
73 #if _FS_LOCK != 0\r
74 #if _FS_READONLY\r
75 #error _FS_LOCK must be 0 at read-only configuration\r
76 #endif\r
77 typedef struct {\r
78 FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */\r
79 DWORD clu; /* Object ID 2, directory (0:root) */\r
80 DWORD ofs; /* Object ID 3, directory offset */\r
81 WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */\r
82 } FILESEM;\r
83 #endif\r
84 \r
85 \r
86 \r
87 /* DBCS code ranges and SBCS upper conversion tables */\r
88 \r
89 #if _CODE_PAGE == 932 /* Japanese Shift-JIS */\r
90 #define _DF1S 0x81 /* DBC 1st byte range 1 start */\r
91 #define _DF1E 0x9F /* DBC 1st byte range 1 end */\r
92 #define _DF2S 0xE0 /* DBC 1st byte range 2 start */\r
93 #define _DF2E 0xFC /* DBC 1st byte range 2 end */\r
94 #define _DS1S 0x40 /* DBC 2nd byte range 1 start */\r
95 #define _DS1E 0x7E /* DBC 2nd byte range 1 end */\r
96 #define _DS2S 0x80 /* DBC 2nd byte range 2 start */\r
97 #define _DS2E 0xFC /* DBC 2nd byte range 2 end */\r
98 \r
99 #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */\r
100 #define _DF1S 0x81\r
101 #define _DF1E 0xFE\r
102 #define _DS1S 0x40\r
103 #define _DS1E 0x7E\r
104 #define _DS2S 0x80\r
105 #define _DS2E 0xFE\r
106 \r
107 #elif _CODE_PAGE == 949 /* Korean */\r
108 #define _DF1S 0x81\r
109 #define _DF1E 0xFE\r
110 #define _DS1S 0x41\r
111 #define _DS1E 0x5A\r
112 #define _DS2S 0x61\r
113 #define _DS2E 0x7A\r
114 #define _DS3S 0x81\r
115 #define _DS3E 0xFE\r
116 \r
117 #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */\r
118 #define _DF1S 0x81\r
119 #define _DF1E 0xFE\r
120 #define _DS1S 0x40\r
121 #define _DS1E 0x7E\r
122 #define _DS2S 0xA1\r
123 #define _DS2E 0xFE\r
124 \r
125 #elif _CODE_PAGE == 437 /* U.S. */\r
126 #define _DF1S 0\r
127 #define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \\r
128 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
129 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
130 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
131 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
132 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
133 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
134 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
135 \r
136 #elif _CODE_PAGE == 720 /* Arabic */\r
137 #define _DF1S 0\r
138 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
139 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
140 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
141 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
142 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
143 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
144 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
145 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
146 \r
147 #elif _CODE_PAGE == 737 /* Greek */\r
148 #define _DF1S 0\r
149 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
150 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \\r
151 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \\r
152 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
153 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
154 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
155 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
156 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
157 \r
158 #elif _CODE_PAGE == 771 /* KBL */\r
159 #define _DF1S 0\r
160 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
161 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
162 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
163 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
164 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
165 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \\r
166 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
167 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}\r
168 \r
169 #elif _CODE_PAGE == 775 /* Baltic */\r
170 #define _DF1S 0\r
171 #define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \\r
172 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \\r
173 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
174 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
175 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
176 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
177 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \\r
178 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
179 \r
180 #elif _CODE_PAGE == 850 /* Latin 1 */\r
181 #define _DF1S 0\r
182 #define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \\r
183 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \\r
184 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
185 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
186 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
187 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \\r
188 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \\r
189 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
190 \r
191 #elif _CODE_PAGE == 852 /* Latin 2 */\r
192 #define _DF1S 0\r
193 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \\r
194 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \\r
195 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \\r
196 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \\r
197 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
198 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
199 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \\r
200 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}\r
201 \r
202 #elif _CODE_PAGE == 855 /* Cyrillic */\r
203 #define _DF1S 0\r
204 #define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \\r
205 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \\r
206 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \\r
207 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \\r
208 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
209 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \\r
210 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \\r
211 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}\r
212 \r
213 #elif _CODE_PAGE == 857 /* Turkish */\r
214 #define _DF1S 0\r
215 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \\r
216 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \\r
217 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
218 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
219 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
220 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
221 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \\r
222 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
223 \r
224 #elif _CODE_PAGE == 860 /* Portuguese */\r
225 #define _DF1S 0\r
226 #define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \\r
227 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
228 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
229 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
230 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
231 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
232 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
233 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
234 \r
235 #elif _CODE_PAGE == 861 /* Icelandic */\r
236 #define _DF1S 0\r
237 #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \\r
238 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \\r
239 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
240 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
241 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
242 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
243 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
244 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
245 \r
246 #elif _CODE_PAGE == 862 /* Hebrew */\r
247 #define _DF1S 0\r
248 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
249 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
250 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
251 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
252 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
253 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
254 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
255 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
256 \r
257 #elif _CODE_PAGE == 863 /* Canadian-French */\r
258 #define _DF1S 0\r
259 #define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \\r
260 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \\r
261 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
262 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
263 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
264 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
265 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
266 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
267 \r
268 #elif _CODE_PAGE == 864 /* Arabic */\r
269 #define _DF1S 0\r
270 #define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \\r
271 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
272 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
273 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
274 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
275 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
276 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
277 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
278 \r
279 #elif _CODE_PAGE == 865 /* Nordic */\r
280 #define _DF1S 0\r
281 #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \\r
282 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
283 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
284 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
285 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
286 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
287 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \\r
288 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
289 \r
290 #elif _CODE_PAGE == 866 /* Russian */\r
291 #define _DF1S 0\r
292 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
293 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
294 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
295 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
296 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
297 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
298 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
299 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
300 \r
301 #elif _CODE_PAGE == 869 /* Greek 2 */\r
302 #define _DF1S 0\r
303 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \\r
304 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \\r
305 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \\r
306 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
307 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \\r
308 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \\r
309 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \\r
310 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}\r
311 \r
312 #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */\r
313 #if _USE_LFN != 0\r
314 #error Cannot enable LFN without valid code page.\r
315 #endif\r
316 #define _DF1S 0\r
317 \r
318 #else\r
319 #error Unknown code page\r
320 \r
321 #endif\r
322 \r
323 \r
324 /* Character code support macros */\r
325 #define IsUpper(c) (((c)>='A')&&((c)<='Z'))\r
326 #define IsLower(c) (((c)>='a')&&((c)<='z'))\r
327 #define IsDigit(c) (((c)>='0')&&((c)<='9'))\r
328 \r
329 #if _DF1S != 0 /* Code page is DBCS */\r
330 \r
331 #ifdef _DF2S /* Two 1st byte areas */\r
332 #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))\r
333 #else /* One 1st byte area */\r
334 #define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)\r
335 #endif\r
336 \r
337 #ifdef _DS3S /* Three 2nd byte areas */\r
338 #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))\r
339 #else /* Two 2nd byte areas */\r
340 #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))\r
341 #endif\r
342 \r
343 #else /* Code page is SBCS */\r
344 \r
345 #define IsDBCS1(c) 0\r
346 #define IsDBCS2(c) 0\r
347 \r
348 #endif /* _DF1S */\r
349 \r
350 \r
351 /* Name status flags */\r
352 #define NSFLAG 11 /* Index of name status byte in fn[] */\r
353 #define NS_LOSS 0x01 /* Out of 8.3 format */\r
354 #define NS_LFN 0x02 /* Force to create LFN entry */\r
355 #define NS_LAST 0x04 /* Last segment */\r
356 #define NS_BODY 0x08 /* Lower case flag (body) */\r
357 #define NS_EXT 0x10 /* Lower case flag (ext) */\r
358 #define NS_DOT 0x20 /* Dot entry */\r
359 #define NS_NONAME 0x80 /* Not followed */\r
360 \r
361 \r
362 /* Limits and Boundaries (Differ from specs but correct for real DOS/Windows) */\r
363 #define MIN_FAT16 4086U /* Minimum number of clusters of FAT16 */\r
364 #define MIN_FAT32 65526U /* Minimum number of clusters of FAT32 */\r
365 #define MAX_DIR 0x200000 /* Maximum size of FAT directory */\r
366 #define MAX_DIR_EX 0x10000000 /* Maximum size of exFAT directory */\r
367 \r
368 \r
369 /* FatFs refers the members in the FAT structures as byte array instead of\r
370 / structure members because the structure is not binary compatible between\r
371 / different platforms */\r
372 \r
373 #define BS_jmpBoot 0 /* x86 jump instruction (3-byte) */\r
374 #define BS_OEMName 3 /* OEM name (8-byte) */\r
375 #define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */\r
376 #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */\r
377 #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */\r
378 #define BPB_NumFATs 16 /* Number of FATs (BYTE) */\r
379 #define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */\r
380 #define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */\r
381 #define BPB_Media 21 /* Media descriptor (BYTE) */\r
382 #define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */\r
383 #define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */\r
384 #define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */\r
385 #define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */\r
386 #define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */\r
387 #define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */\r
388 #define BS_NTres 37 /* Error flag (BYTE) */\r
389 #define BS_BootSig 38 /* Extended boot signature (BYTE) */\r
390 #define BS_VolID 39 /* Volume serial number (DWORD) */\r
391 #define BS_VolLab 43 /* Volume label string (8-byte) */\r
392 #define BS_FilSysType 54 /* File system type string (8-byte) */\r
393 #define BPB_FATSz32 36 /* FAT size (32-bit) [sector] (DWORD) */\r
394 #define BPB_ExtFlags 40 /* Extended flags (WORD) */\r
395 \r
396 #define BPB_FSVer32 42 /* FAT32: File system version (WORD) */\r
397 #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */\r
398 #define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */\r
399 #define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */\r
400 #define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */\r
401 #define BS_NTres32 65 /* FAT32: Error flag (BYTE) */\r
402 #define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */\r
403 #define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */\r
404 #define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */\r
405 #define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */\r
406 \r
407 #define BPB_ZeroedEx 11 /* exFAT: Must be zero (35-byte) */\r
408 #define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */\r
409 #define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */\r
410 #define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */\r
411 #define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */\r
412 #define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */\r
413 #define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */\r
414 #define BPB_RootClusEx 96 /* exFAT: Root directory cluster (DWORD) */\r
415 #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */\r
416 #define BPB_FSVerEx 104 /* exFAT: File system version (WORD) */\r
417 #define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */\r
418 #define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */\r
419 #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in byte (BYTE) */\r
420 #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in sector (BYTE) */\r
421 #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */\r
422 #define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */\r
423 #define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */\r
424 \r
425 #define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */\r
426 #define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */\r
427 #define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */\r
428 #define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */\r
429 \r
430 #define MBR_Table 446 /* MBR: Partition table offset */\r
431 #define SZ_PTE 16 /* MBR: Size of a partition table entry */\r
432 \r
433 #define BS_55AA 510 /* Signature word (WORD) */\r
434 \r
435 #define DIR_Name 0 /* Short file name (11) */\r
436 #define DIR_Attr 11 /* Attribute (1) */\r
437 #define DIR_NTres 12 /* Lower case flag (1) */\r
438 #define DIR_CrtTime10 13 /* Created time sub-second (1) */\r
439 #define DIR_CrtTime 14 /* Created time (2) */\r
440 #define DIR_CrtDate 16 /* Created date (2) */\r
441 #define DIR_LstAccDate 18 /* Last accessed date (2) */\r
442 #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */\r
443 #define DIR_WrtTime 22 /* Modified time (2) */\r
444 #define DIR_WrtDate 24 /* Modified date (2) */\r
445 #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */\r
446 #define DIR_FileSize 28 /* File size (DWORD) */\r
447 #define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */\r
448 #define LDIR_Attr 11 /* LFN attribute (1) */\r
449 #define LDIR_Type 12 /* LFN type (1) */\r
450 #define LDIR_Chksum 13 /* Checksum of the SFN entry */\r
451 #define LDIR_FstClusLO 26 /* Must be zero (WORD) */\r
452 #define XDIR_Type 0 /* Type of exFAT directory entry (BYTE) */\r
453 #define XDIR_NumLabel 1 /* Number of volume label characters (BYTE) */\r
454 #define XDIR_Label 2 /* Volume label (11-WORD) */\r
455 #define XDIR_CaseSum 4 /* Sum of case conversion table (DWORD) */\r
456 #define XDIR_NumSec 1 /* Number of secondary entries (BYTE) */\r
457 #define XDIR_SetSum 2 /* Sum of the set of directory entries (WORD) */\r
458 #define XDIR_Attr 4 /* File attribute (WORD) */\r
459 #define XDIR_CrtTime 8 /* Created time (4) */\r
460 #define XDIR_ModTime 12 /* Modified time (4) */\r
461 #define XDIR_AccTime 16 /* Last accessed time (4) */\r
462 #define XDIR_CrtTime10 20 /* Created time subsecond (1) */\r
463 #define XDIR_ModTime10 21 /* Modified time subsecond (1) */\r
464 #define XDIR_CrtTZ 22 /* Created timezone (1) */\r
465 #define XDIR_ModTZ 23 /* Modified timezone (1) */\r
466 #define XDIR_AccTZ 24 /* Last accessed timezone (1) */\r
467 #define XDIR_GenFlags 33 /* Gneral flags (1) */\r
468 #define XDIR_NumName 35 /* Number of file name characters (BYTE) */\r
469 #define XDIR_NameHash 36 /* Hash of file name (WORD) */\r
470 #define XDIR_ValidFileSize 40 /* Valid file size (QWORD) */\r
471 #define XDIR_FstClus 52 /* First cluster of the File/Directory (DWORD) */\r
472 #define XDIR_FileSize 56 /* File/Directory size (QWORD) */\r
473 \r
474 #define SZDIRE 32 /* Size of a directory entry */\r
475 #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */\r
476 #define DDEM 0xE5 /* Deleted directory entry mark at DIR_Name[0] */\r
477 #define RDDEM 0x05 /* Replacement of the character collides with DDEM */\r
478 \r
479 \r
480 \r
481 \r
482 \r
483 /*--------------------------------------------------------------------------\r
484 \r
485 Module Private Work Area\r
486 \r
487 ---------------------------------------------------------------------------*/\r
488 \r
489 /* Remark: Variables here without initial value shall be guaranteed zero/null\r
490 / at start-up. If not, either the linker or start-up routine being used is\r
491 / not compliance with C standard. */\r
492 \r
493 #if _VOLUMES < 1 || _VOLUMES > 9\r
494 #error Wrong _VOLUMES setting\r
495 #endif\r
496 static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */\r
497 static WORD Fsid; /* File system mount ID */\r
498 \r
499 #if _FS_RPATH != 0 && _VOLUMES >= 2\r
500 static BYTE CurrVol; /* Current drive */\r
501 #endif\r
502 \r
503 #if _FS_LOCK != 0\r
504 static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */\r
505 #endif\r
506 \r
507 #if _USE_LFN == 0 /* Non-LFN configuration */\r
508 #define DEF_NAMBUF BYTE sfn[12]\r
509 #define INIT_NAMBUF(dobj) (dobj).fn = sfn\r
510 #define FREE_NAMBUF()\r
511 #define DEF_DIRBUF\r
512 #define INIT_DIRBUF(fs)\r
513 #define FREE_DIRBUF()\r
514 #else\r
515 #if _MAX_LFN < 12 || _MAX_LFN > 255\r
516 #error Wrong _MAX_LFN setting\r
517 #endif\r
518 \r
519 #if _USE_LFN == 1 /* LFN enabled with static working buffer */\r
520 #if _FS_EXFAT\r
521 static BYTE DirBuf[SZDIRE*19]; /* Directory entry block scratchpad buffer (19 entries in size) */\r
522 #endif\r
523 static WCHAR LfnBuf[_MAX_LFN+1]; /* LFN enabled with static working buffer */\r
524 #define DEF_NAMBUF BYTE sfn[12]\r
525 #define INIT_NAMBUF(dj) { (dj).fn = sfn; (dj).lfn = LfnBuf; }\r
526 #define FREE_NAMBUF()\r
527 #define DEF_DIRBUF\r
528 #define INIT_DIRBUF(fs)\r
529 #define FREE_DIRBUF()\r
530 \r
531 #elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */\r
532 #if _FS_EXFAT\r
533 #define DEF_NAMBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[SZDIRE*19]\r
534 #define INIT_NAMBUF(dj) { (dj).fn = sfn; (dj).lfn = lbuf; (dj).obj.fs->dirbuf = dbuf; }\r
535 #define FREE_NAMBUF()\r
536 #define DEF_DIRBUF BYTE dbuf[SZDIRE*19]\r
537 #define INIT_DIRBUF(fs) fs->dirbuf = dbuf\r
538 #define FREE_DIRBUF()\r
539 #else\r
540 #define DEF_NAMBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]\r
541 #define INIT_NAMBUF(dj) { (dj).fn = sfn; (dj).lfn = lbuf; }\r
542 #define FREE_NAMBUF()\r
543 #define DEF_DIRBUF\r
544 #define INIT_DIRBUF(fs)\r
545 #define FREE_DIRBUF()\r
546 #endif\r
547 \r
548 #elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */\r
549 #if _FS_EXFAT\r
550 #define DEF_NAMBUF BYTE sfn[12]; WCHAR *lfn\r
551 #define INIT_NAMBUF(dj) { lfn = ff_memalloc((_MAX_LFN+1)*2 + SZDIRE*19); if (!lfn) LEAVE_FF((dj).obj.fs, FR_NOT_ENOUGH_CORE); (dj).fn = sfn; (dj).lfn = lfn; (dj).obj.fs->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); }\r
552 #define FREE_NAMBUF() ff_memfree(lfn)\r
553 #define DEF_DIRBUF BYTE *dirb\r
554 #define INIT_DIRBUF(fs) { dirb = ff_memalloc(SZDIRE*19); if (!dirb) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); fs->dirbuf = dirb; }\r
555 #define FREE_DIRBUF() ff_memfree(dirb)\r
556 #else\r
557 #define DEF_NAMBUF BYTE sfn[12]; WCHAR *lfn\r
558 #define INIT_NAMBUF(dj) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF((dj).obj.fs, FR_NOT_ENOUGH_CORE); (dj).fn = sfn; (dj).lfn = lfn; }\r
559 #define FREE_NAMBUF() ff_memfree(lfn)\r
560 #define DEF_DIRBUF\r
561 #define INIT_DIRBUF(fs)\r
562 #define FREE_DIRBUF()\r
563 #endif\r
564 \r
565 #else\r
566 #error Wrong _USE_LFN setting\r
567 #endif\r
568 #endif\r
569 \r
570 #ifdef _EXCVT\r
571 static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended characters */\r
572 #endif\r
573 \r
574 \r
575 \r
576 \r
577 \r
578 \r
579 /*--------------------------------------------------------------------------\r
580 \r
581 Module Private Functions\r
582 \r
583 ---------------------------------------------------------------------------*/\r
584 \r
585 \r
586 /*-----------------------------------------------------------------------*/\r
587 /* Load/Store multi-byte word in the FAT structure */\r
588 /*-----------------------------------------------------------------------*/\r
589 \r
590 static\r
591 WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */\r
592 {\r
593 WORD rv;\r
594 \r
595 rv = ptr[1];\r
596 rv = rv << 8 | ptr[0];\r
597 return rv;\r
598 }\r
599 \r
600 static\r
601 DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */\r
602 {\r
603 DWORD rv;\r
604 \r
605 rv = ptr[3];\r
606 rv = rv << 8 | ptr[2];\r
607 rv = rv << 8 | ptr[1];\r
608 rv = rv << 8 | ptr[0];\r
609 return rv;\r
610 }\r
611 \r
612 #if _FS_EXFAT\r
613 static\r
614 QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */\r
615 {\r
616 QWORD rv;\r
617 \r
618 rv = ptr[7];\r
619 rv = rv << 8 | ptr[6];\r
620 rv = rv << 8 | ptr[5];\r
621 rv = rv << 8 | ptr[4];\r
622 rv = rv << 8 | ptr[3];\r
623 rv = rv << 8 | ptr[2];\r
624 rv = rv << 8 | ptr[1];\r
625 rv = rv << 8 | ptr[0];\r
626 return rv;\r
627 }\r
628 #endif\r
629 \r
630 #if !_FS_READONLY\r
631 static\r
632 void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */\r
633 {\r
634 *ptr++ = (BYTE)val; val >>= 8;\r
635 *ptr++ = (BYTE)val;\r
636 }\r
637 \r
638 static\r
639 void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */\r
640 {\r
641 *ptr++ = (BYTE)val; val >>= 8;\r
642 *ptr++ = (BYTE)val; val >>= 8;\r
643 *ptr++ = (BYTE)val; val >>= 8;\r
644 *ptr++ = (BYTE)val;\r
645 }\r
646 \r
647 #if _FS_EXFAT\r
648 static\r
649 void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */\r
650 {\r
651 *ptr++ = (BYTE)val; val >>= 8;\r
652 *ptr++ = (BYTE)val; val >>= 8;\r
653 *ptr++ = (BYTE)val; val >>= 8;\r
654 *ptr++ = (BYTE)val; val >>= 8;\r
655 *ptr++ = (BYTE)val; val >>= 8;\r
656 *ptr++ = (BYTE)val; val >>= 8;\r
657 *ptr++ = (BYTE)val; val >>= 8;\r
658 *ptr++ = (BYTE)val;\r
659 }\r
660 #endif\r
661 #endif /* !_FS_READONLY */\r
662 \r
663 \r
664 \r
665 /*-----------------------------------------------------------------------*/\r
666 /* String functions */\r
667 /*-----------------------------------------------------------------------*/\r
668 \r
669 /* Copy memory to memory */\r
670 static\r
671 void mem_cpy (void* dst, const void* src, UINT cnt) {\r
672 BYTE *d = (BYTE*)dst;\r
673 const BYTE *s = (const BYTE*)src;\r
674 \r
675 if (cnt) {\r
676 do *d++ = *s++; while (--cnt);\r
677 }\r
678 }\r
679 \r
680 /* Fill memory block */\r
681 static\r
682 void mem_set (void* dst, int val, UINT cnt) {\r
683 BYTE *d = (BYTE*)dst;\r
684 \r
685 do *d++ = (BYTE)val; while (--cnt);\r
686 }\r
687 \r
688 /* Compare memory block */\r
689 static\r
690 int mem_cmp (const void* dst, const void* src, UINT cnt) { /* ZR:same, NZ:different */\r
691 const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;\r
692 int r = 0;\r
693 \r
694 do {\r
695 r = *d++ - *s++;\r
696 } while (--cnt && r == 0);\r
697 \r
698 return r;\r
699 }\r
700 \r
701 /* Check if chr is contained in the string */\r
702 static\r
703 int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */\r
704 while (*str && *str != chr) str++;\r
705 return *str;\r
706 }\r
707 \r
708 WCHAR ff_convert (WCHAR wch, UINT dir)\r
709 {\r
710 if (wch < 0x80) {\r
711 return wch;\r
712 }\r
713 return 0;\r
714 }\r
715 \r
716 WCHAR ff_wtoupper (WCHAR wch)\r
717 {\r
718 if (wch < 0x80) {\r
719 if (wch >= 'a' && wch <= 'z') {\r
720 wch &= ~0x20;\r
721 }\r
722 return wch;\r
723 }\r
724 else\r
725 return 0;\r
726 }\r
727 \r
728 /*-----------------------------------------------------------------------*/\r
729 /* Request/Release grant to access the volume */\r
730 /*-----------------------------------------------------------------------*/\r
731 #if _FS_REENTRANT\r
732 static\r
733 int lock_fs (\r
734 FATFS* fs /* File system object */\r
735 )\r
736 {\r
737 return ff_req_grant(fs->sobj);\r
738 }\r
739 \r
740 \r
741 static\r
742 void unlock_fs (\r
743 FATFS* fs, /* File system object */\r
744 FRESULT res /* Result code to be returned */\r
745 )\r
746 {\r
747 if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {\r
748 ff_rel_grant(fs->sobj);\r
749 }\r
750 }\r
751 #endif\r
752 \r
753 \r
754 \r
755 \r
756 /*-----------------------------------------------------------------------*/\r
757 /* File lock control functions */\r
758 /*-----------------------------------------------------------------------*/\r
759 #if _FS_LOCK != 0\r
760 \r
761 static\r
762 FRESULT chk_lock ( /* Check if the file can be accessed */\r
763 DIR* dp, /* Directory object pointing the file to be checked */\r
764 int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */\r
765 )\r
766 {\r
767 UINT i, be;\r
768 \r
769 /* Search file semaphore table */\r
770 for (i = be = 0; i < _FS_LOCK; i++) {\r
771 if (Files[i].fs) { /* Existing entry */\r
772 if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */\r
773 Files[i].clu == dp->obj.sclust &&\r
774 Files[i].ofs == dp->dptr) break;\r
775 } else { /* Blank entry */\r
776 be = 1;\r
777 }\r
778 }\r
779 if (i == _FS_LOCK) { /* The object is not opened */\r
780 return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */\r
781 }\r
782 \r
783 /* The object has been opened. Reject any open against writing file and all write mode open */\r
784 return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;\r
785 }\r
786 \r
787 \r
788 static\r
789 int enq_lock (void) /* Check if an entry is available for a new object */\r
790 {\r
791 UINT i;\r
792 \r
793 for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;\r
794 return (i == _FS_LOCK) ? 0 : 1;\r
795 }\r
796 \r
797 \r
798 static\r
799 UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */\r
800 DIR* dp, /* Directory object pointing the file to register or increment */\r
801 int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */\r
802 )\r
803 {\r
804 UINT i;\r
805 \r
806 \r
807 for (i = 0; i < _FS_LOCK; i++) { /* Find the object */\r
808 if (Files[i].fs == dp->obj.fs &&\r
809 Files[i].clu == dp->obj.sclust &&\r
810 Files[i].ofs == dp->dptr) break;\r
811 }\r
812 \r
813 if (i == _FS_LOCK) { /* Not opened. Register it as new. */\r
814 for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;\r
815 if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */\r
816 Files[i].fs = dp->obj.fs;\r
817 Files[i].clu = dp->obj.sclust;\r
818 Files[i].ofs = dp->dptr;\r
819 Files[i].ctr = 0;\r
820 }\r
821 \r
822 if (acc && Files[i].ctr) return 0; /* Access violation (int err) */\r
823 \r
824 Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */\r
825 \r
826 return i + 1;\r
827 }\r
828 \r
829 \r
830 static\r
831 FRESULT dec_lock ( /* Decrement object open counter */\r
832 UINT i /* Semaphore index (1..) */\r
833 )\r
834 {\r
835 WORD n;\r
836 FRESULT res;\r
837 \r
838 \r
839 if (--i < _FS_LOCK) { /* Shift index number origin from 0 */\r
840 n = Files[i].ctr;\r
841 if (n == 0x100) n = 0; /* If write mode open, delete the entry */\r
842 if (n > 0) n--; /* Decrement read mode open count */\r
843 Files[i].ctr = n;\r
844 if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */\r
845 res = FR_OK;\r
846 } else {\r
847 res = FR_INT_ERR; /* Invalid index nunber */\r
848 }\r
849 return res;\r
850 }\r
851 \r
852 \r
853 static\r
854 void clear_lock ( /* Clear lock entries of the volume */\r
855 FATFS *fs\r
856 )\r
857 {\r
858 UINT i;\r
859 \r
860 for (i = 0; i < _FS_LOCK; i++) {\r
861 if (Files[i].fs == fs) Files[i].fs = 0;\r
862 }\r
863 }\r
864 #endif\r
865 \r
866 \r
867 \r
868 \r
869 /*-----------------------------------------------------------------------*/\r
870 /* Move/Flush disk access window in the file system object */\r
871 /*-----------------------------------------------------------------------*/\r
872 #if !_FS_READONLY\r
873 static\r
874 FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERROR */\r
875 FATFS* fs /* File system object */\r
876 )\r
877 {\r
878 DWORD wsect;\r
879 UINT nf;\r
880 FRESULT res = FR_OK;\r
881 \r
882 \r
883 if (fs->wflag) { /* Write back the sector if it is dirty */\r
884 wsect = fs->winsect; /* Current sector number */\r
885 if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) {\r
886 res = FR_DISK_ERR;\r
887 } else {\r
888 fs->wflag = 0;\r
889 if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */\r
890 for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */\r
891 wsect += fs->fsize;\r
892 disk_write(fs->drv, fs->win, wsect, 1);\r
893 }\r
894 }\r
895 }\r
896 }\r
897 return res;\r
898 }\r
899 #endif\r
900 \r
901 \r
902 static\r
903 FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */\r
904 FATFS* fs, /* File system object */\r
905 DWORD sector /* Sector number to make appearance in the fs->win[] */\r
906 )\r
907 {\r
908 FRESULT res = FR_OK;\r
909 \r
910 \r
911 if (sector != fs->winsect) { /* Window offset changed? */\r
912 #if !_FS_READONLY\r
913 res = sync_window(fs); /* Write-back changes */\r
914 #endif\r
915 if (res == FR_OK) { /* Fill sector window with new data */\r
916 if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) {\r
917 sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */\r
918 res = FR_DISK_ERR;\r
919 }\r
920 fs->winsect = sector;\r
921 }\r
922 }\r
923 return res;\r
924 }\r
925 \r
926 \r
927 \r
928 \r
929 /*-----------------------------------------------------------------------*/\r
930 /* Synchronize file system and strage device */\r
931 /*-----------------------------------------------------------------------*/\r
932 #if !_FS_READONLY\r
933 static\r
934 FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */\r
935 FATFS* fs /* File system object */\r
936 )\r
937 {\r
938 FRESULT res;\r
939 \r
940 \r
941 res = sync_window(fs);\r
942 if (res == FR_OK) {\r
943 /* Update FSInfo sector if needed */\r
944 if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {\r
945 /* Create FSInfo structure */\r
946 mem_set(fs->win, 0, SS(fs));\r
947 st_word(fs->win + BS_55AA, 0xAA55);\r
948 st_dword(fs->win + FSI_LeadSig, 0x41615252);\r
949 st_dword(fs->win + FSI_StrucSig, 0x61417272);\r
950 st_dword(fs->win + FSI_Free_Count, fs->free_clst);\r
951 st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);\r
952 /* Write it into the FSInfo sector */\r
953 fs->winsect = fs->volbase + 1;\r
954 disk_write(fs->drv, fs->win, fs->winsect, 1);\r
955 fs->fsi_flag = 0;\r
956 }\r
957 /* Make sure that no pending write process in the physical drive */\r
958 if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;\r
959 }\r
960 \r
961 return res;\r
962 }\r
963 #endif\r
964 \r
965 \r
966 \r
967 \r
968 /*-----------------------------------------------------------------------*/\r
969 /* Get sector# from cluster# */\r
970 /*-----------------------------------------------------------------------*/\r
971 \r
972 static\r
973 DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */\r
974 FATFS* fs, /* File system object */\r
975 DWORD clst /* Cluster# to be converted */\r
976 )\r
977 {\r
978 clst -= 2;\r
979 if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */\r
980 return clst * fs->csize + fs->database;\r
981 }\r
982 \r
983 \r
984 \r
985 \r
986 /*-----------------------------------------------------------------------*/\r
987 /* FAT access - Read value of a FAT entry */\r
988 /*-----------------------------------------------------------------------*/\r
989 \r
990 static\r
991 DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */\r
992 _FDID* obj, /* Corresponding object */\r
993 DWORD clst /* Cluster number to get the value */\r
994 )\r
995 {\r
996 UINT wc, bc;\r
997 DWORD val;\r
998 FATFS *fs = obj->fs;\r
999 \r
1000 \r
1001 if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */\r
1002 val = 1; /* Internal error */\r
1003 \r
1004 } else {\r
1005 val = 0xFFFFFFFF; /* Default value falls on disk error */\r
1006 \r
1007 switch (fs->fs_type) {\r
1008 case FS_FAT12 :\r
1009 bc = (UINT)clst; bc += bc / 2;\r
1010 if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;\r
1011 wc = fs->win[bc++ % SS(fs)];\r
1012 if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;\r
1013 wc |= fs->win[bc % SS(fs)] << 8;\r
1014 val = clst & 1 ? wc >> 4 : (wc & 0xFFF);\r
1015 break;\r
1016 \r
1017 case FS_FAT16 :\r
1018 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;\r
1019 val = ld_word(&fs->win[clst * 2 % SS(fs)]);\r
1020 break;\r
1021 \r
1022 case FS_FAT32 :\r
1023 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;\r
1024 val = ld_dword(&fs->win[clst * 4 % SS(fs)]) & 0x0FFFFFFF;\r
1025 break;\r
1026 #if _FS_EXFAT\r
1027 case FS_EXFAT :\r
1028 if (obj->objsize) {\r
1029 DWORD cofs = clst - obj->sclust; /* Offset from start cluster */\r
1030 DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */\r
1031 \r
1032 if (obj->stat == 2) { /* Is there no valid chain on the FAT? */\r
1033 if (cofs <= clen) {\r
1034 val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* Generate the value */\r
1035 break;\r
1036 }\r
1037 }\r
1038 if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the contiguous part? */\r
1039 val = clst + 1; /* Generate the value */\r
1040 break;\r
1041 }\r
1042 if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */\r
1043 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;\r
1044 val = ld_dword(&fs->win[clst * 4 % SS(fs)]) & 0x7FFFFFFF;\r
1045 break;\r
1046 }\r
1047 }\r
1048 /* Go default */\r
1049 #endif\r
1050 default:\r
1051 val = 1; /* Internal error */\r
1052 }\r
1053 }\r
1054 \r
1055 return val;\r
1056 }\r
1057 \r
1058 \r
1059 \r
1060 \r
1061 /*-----------------------------------------------------------------------*/\r
1062 /* FAT access - Change value of a FAT entry */\r
1063 /*-----------------------------------------------------------------------*/\r
1064 \r
1065 #if !_FS_READONLY\r
1066 static\r
1067 FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */\r
1068 FATFS* fs, /* Corresponding object */\r
1069 DWORD clst, /* FAT index number (cluster number) to be changed */\r
1070 DWORD val /* New value to be set to the entry */\r
1071 )\r
1072 {\r
1073 UINT bc;\r
1074 BYTE *p;\r
1075 FRESULT res = FR_INT_ERR;\r
1076 \r
1077 \r
1078 if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */\r
1079 switch (fs->fs_type) {\r
1080 case FS_FAT12 : /* Bitfield items */\r
1081 bc = (UINT)clst; bc += bc / 2;\r
1082 res = move_window(fs, fs->fatbase + (bc / SS(fs)));\r
1083 if (res != FR_OK) break;\r
1084 p = &fs->win[bc++ % SS(fs)];\r
1085 *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;\r
1086 fs->wflag = 1;\r
1087 res = move_window(fs, fs->fatbase + (bc / SS(fs)));\r
1088 if (res != FR_OK) break;\r
1089 p = &fs->win[bc % SS(fs)];\r
1090 *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));\r
1091 fs->wflag = 1;\r
1092 break;\r
1093 \r
1094 case FS_FAT16 : /* WORD aligned items */\r
1095 res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));\r
1096 if (res != FR_OK) break;\r
1097 st_word(&fs->win[clst * 2 % SS(fs)], (WORD)val);\r
1098 fs->wflag = 1;\r
1099 break;\r
1100 \r
1101 case FS_FAT32 : /* DWORD aligned items */\r
1102 #if _FS_EXFAT\r
1103 case FS_EXFAT :\r
1104 #endif\r
1105 res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));\r
1106 if (res != FR_OK) break;\r
1107 if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {\r
1108 val = (val & 0x0FFFFFFF) | (ld_dword(&fs->win[clst * 4 % SS(fs)]) & 0xF0000000);\r
1109 }\r
1110 st_dword(&fs->win[clst * 4 % SS(fs)], val);\r
1111 fs->wflag = 1;\r
1112 break;\r
1113 }\r
1114 }\r
1115 return res;\r
1116 }\r
1117 #endif /* !_FS_READONLY */\r
1118 \r
1119 \r
1120 \r
1121 \r
1122 #if _FS_EXFAT && !_FS_READONLY\r
1123 /*-----------------------------------------------------------------------*/\r
1124 /* exFAT: Accessing FAT and Allocation Bitmap */\r
1125 /*-----------------------------------------------------------------------*/\r
1126 \r
1127 /*---------------------------------------------*/\r
1128 /* exFAT: Find a contiguous free cluster block */\r
1129 /*---------------------------------------------*/\r
1130 static\r
1131 DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Disk error */\r
1132 FATFS* fs, /* File system object */\r
1133 DWORD clst, /* Cluster number to scan from */\r
1134 DWORD ncl /* Number of contiguous clusters to find (1..) */\r
1135 )\r
1136 {\r
1137 BYTE bm, bv;\r
1138 UINT i;\r
1139 DWORD val, scl, ctr;\r
1140 \r
1141 \r
1142 clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */\r
1143 if (clst >= fs->n_fatent - 2) clst = 0;\r
1144 scl = val = clst; ctr = 0;\r
1145 for (;;) {\r
1146 if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;\r
1147 i = val / 8 & (SS(fs) - 1); bm = 1 << (val % 8);\r
1148 do {\r
1149 do {\r
1150 bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */\r
1151 if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */\r
1152 val = 0; bm = 0; i = 4096;\r
1153 }\r
1154 if (!bv) { /* Is it a free cluster? */\r
1155 if (++ctr == ncl) return scl + 2; /* Check run length */\r
1156 } else {\r
1157 scl = val; ctr = 0; /* Encountered a live cluster, restart to scan */\r
1158 }\r
1159 if (val == clst) return 0; /* All cluster scanned? */\r
1160 } while (bm);\r
1161 bm = 1;\r
1162 } while (++i < SS(fs));\r
1163 }\r
1164 }\r
1165 \r
1166 /*------------------------------------*/\r
1167 /* exFAT: Set/Clear a block of bitmap */\r
1168 /*------------------------------------*/\r
1169 static\r
1170 FRESULT change_bitmap (\r
1171 FATFS* fs, /* File system object */\r
1172 DWORD clst, /* Cluster number to change from */\r
1173 DWORD ncl, /* Number of clusters to be changed */\r
1174 int bv /* bit value to be set (0 or 1) */\r
1175 )\r
1176 {\r
1177 BYTE bm;\r
1178 UINT i;\r
1179 DWORD sect;\r
1180 \r
1181 \r
1182 clst -= 2; /* The first bit corresponds to cluster #2 */\r
1183 sect = fs->database + clst / 8 / SS(fs); /* Sector address */\r
1184 i = clst / 8 & (SS(fs) - 1); /* Byte offset in the sector */\r
1185 bm = 1 << (clst % 8); /* Bit mask in the byte */\r
1186 for (;;) {\r
1187 if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR;\r
1188 do {\r
1189 do {\r
1190 if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */\r
1191 fs->win[i] ^= bm; /* Flip the bit */\r
1192 fs->wflag = 1;\r
1193 if (--ncl == 0) return FR_OK; /* All bits processed? */\r
1194 } while (bm <<= 1); /* Next bit */\r
1195 bm = 1;\r
1196 } while (++i < SS(fs)); /* Next byte */\r
1197 }\r
1198 }\r
1199 \r
1200 \r
1201 /*---------------------------------------------*/\r
1202 /* Complement contiguous part of the FAT chain */\r
1203 /*---------------------------------------------*/\r
1204 static\r
1205 FRESULT fill_fat_chain (\r
1206 _FDID* obj /* Pointer to the corresponding object */\r
1207 )\r
1208 {\r
1209 FRESULT res;\r
1210 DWORD cl, n;\r
1211 \r
1212 if (obj->stat == 3) { /* Has the object got fragmented? */\r
1213 for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */\r
1214 res = put_fat(obj->fs, cl, cl + 1);\r
1215 if (res != FR_OK) return res;\r
1216 }\r
1217 obj->stat = 0; /* Change status 'FAT chain is valid' */\r
1218 }\r
1219 return FR_OK;\r
1220 }\r
1221 \r
1222 #endif\r
1223 \r
1224 \r
1225 \r
1226 /*-----------------------------------------------------------------------*/\r
1227 /* FAT handling - Remove a cluster chain */\r
1228 /*-----------------------------------------------------------------------*/\r
1229 #if !_FS_READONLY\r
1230 static\r
1231 FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */\r
1232 _FDID* obj, /* Corresponding object */\r
1233 DWORD clst, /* Cluster to remove a chain from */\r
1234 DWORD pclst /* Previous cluster of clst (0:an entire chain) */\r
1235 )\r
1236 {\r
1237 FRESULT res = FR_OK;\r
1238 DWORD nxt;\r
1239 FATFS *fs = obj->fs;\r
1240 #if _FS_EXFAT || _USE_TRIM\r
1241 DWORD scl = clst, ecl = clst;\r
1242 #endif\r
1243 #if _USE_TRIM\r
1244 DWORD rt[2];\r
1245 #endif\r
1246 \r
1247 if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */\r
1248 \r
1249 /* Mark the previous cluster 'EOC' on the FAT if it exists */\r
1250 if (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) {\r
1251 res = put_fat(fs, pclst, 0xFFFFFFFF);\r
1252 if (res != FR_OK) return res;\r
1253 }\r
1254 \r
1255 /* Remove the chain */\r
1256 do {\r
1257 nxt = get_fat(obj, clst); /* Get cluster status */\r
1258 if (nxt == 0) break; /* Empty cluster? */\r
1259 if (nxt == 1) return FR_INT_ERR; /* Internal error? */\r
1260 if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */\r
1261 if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {\r
1262 res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */\r
1263 if (res != FR_OK) return res;\r
1264 }\r
1265 if (fs->free_clst != 0xFFFFFFFF) { /* Update FSINFO */\r
1266 fs->free_clst++;\r
1267 fs->fsi_flag |= 1;\r
1268 }\r
1269 #if _FS_EXFAT || _USE_TRIM\r
1270 if (ecl + 1 == nxt) { /* Is next cluster contiguous? */\r
1271 ecl = nxt;\r
1272 } else { /* End of contiguous cluster block */ \r
1273 #if _FS_EXFAT\r
1274 if (fs->fs_type == FS_EXFAT) {\r
1275 res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */\r
1276 if (res != FR_OK) return res;\r
1277 }\r
1278 #endif\r
1279 #if _USE_TRIM\r
1280 rt[0] = clust2sect(fs, scl); /* Start sector */\r
1281 rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */\r
1282 disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Inform device the block can be erased */\r
1283 #endif\r
1284 scl = ecl = nxt;\r
1285 }\r
1286 #endif\r
1287 clst = nxt; /* Next cluster */\r
1288 } while (clst < fs->n_fatent); /* Repeat while not the last link */\r
1289 \r
1290 #if _FS_EXFAT\r
1291 if (fs->fs_type == FS_EXFAT) {\r
1292 if (pclst == 0) { /* Does object have no chain? */\r
1293 obj->stat = 0; /* Change the object status 'initial' */\r
1294 } else {\r
1295 if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Did the chain got contiguous? */\r
1296 obj->stat = 2; /* Change the object status 'contiguous' */\r
1297 }\r
1298 }\r
1299 }\r
1300 #endif\r
1301 return FR_OK;\r
1302 }\r
1303 #endif\r
1304 \r
1305 \r
1306 \r
1307 \r
1308 /*-----------------------------------------------------------------------*/\r
1309 /* FAT handling - Stretch a chain or Create a new chain */\r
1310 /*-----------------------------------------------------------------------*/\r
1311 #if !_FS_READONLY\r
1312 static\r
1313 DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */\r
1314 _FDID* obj, /* Corresponding object */\r
1315 DWORD clst /* Cluster# to stretch, 0:Create a new chain */\r
1316 )\r
1317 {\r
1318 DWORD cs, ncl, scl;\r
1319 FRESULT res;\r
1320 FATFS *fs = obj->fs;\r
1321 \r
1322 \r
1323 if (clst == 0) { /* Create a new chain */\r
1324 scl = fs->last_clst; /* Get suggested cluster to start at */\r
1325 if (scl == 0 || scl >= fs->n_fatent) scl = 1;\r
1326 }\r
1327 else { /* Stretch current chain */\r
1328 cs = get_fat(obj, clst); /* Check the cluster status */\r
1329 if (cs < 2) return 1; /* Invalid value */\r
1330 if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */\r
1331 if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */\r
1332 scl = clst;\r
1333 }\r
1334 \r
1335 #if _FS_EXFAT\r
1336 if (fs->fs_type == FS_EXFAT) { /* At the exFAT */\r
1337 ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */\r
1338 if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */\r
1339 res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */\r
1340 if (res == FR_INT_ERR) return 1;\r
1341 if (res == FR_DISK_ERR) return 0xFFFFFFFF;\r
1342 if (clst == 0) { /* Is it a new chain? */\r
1343 obj->stat = 2; /* Set status 'contiguous chain' */\r
1344 } else { /* This is a stretched chain */\r
1345 if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */\r
1346 obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */\r
1347 obj->stat = 3; /* Change status 'just fragmented' */\r
1348 }\r
1349 }\r
1350 } else\r
1351 #endif\r
1352 { /* At the FAT12/16/32 */\r
1353 ncl = scl; /* Start cluster */\r
1354 for (;;) {\r
1355 ncl++; /* Next cluster */\r
1356 if (ncl >= fs->n_fatent) { /* Check wrap-around */\r
1357 ncl = 2;\r
1358 if (ncl > scl) return 0; /* No free cluster */\r
1359 }\r
1360 cs = get_fat(obj, ncl); /* Get the cluster status */\r
1361 if (cs == 0) break; /* Found a free cluster */\r
1362 if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* An error occurred */\r
1363 if (ncl == scl) return 0; /* No free cluster */\r
1364 }\r
1365 }\r
1366 \r
1367 if (_FS_EXFAT && fs->fs_type == FS_EXFAT && obj->stat == 2) { /* Is it a contiguous chain? */\r
1368 res = FR_OK; /* FAT does not need to be written */\r
1369 } else {\r
1370 res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */\r
1371 if (res == FR_OK && clst) {\r
1372 res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */\r
1373 }\r
1374 }\r
1375 \r
1376 if (res == FR_OK) { /* Update FSINFO if function succeeded. */\r
1377 fs->last_clst = ncl;\r
1378 if (fs->free_clst < fs->n_fatent - 2) fs->free_clst--;\r
1379 fs->fsi_flag |= 1;\r
1380 } else {\r
1381 ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Create error status */\r
1382 }\r
1383 \r
1384 return ncl; /* Return new cluster number or error status */\r
1385 }\r
1386 #endif /* !_FS_READONLY */\r
1387 \r
1388 \r
1389 \r
1390 \r
1391 /*-----------------------------------------------------------------------*/\r
1392 /* FAT handling - Convert offset into cluster with link map table */\r
1393 /*-----------------------------------------------------------------------*/\r
1394 \r
1395 #if _USE_FASTSEEK\r
1396 static\r
1397 DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */\r
1398 FIL* fp, /* Pointer to the file object */\r
1399 DWORD ofs /* File offset to be converted to cluster# */\r
1400 )\r
1401 {\r
1402 DWORD cl, ncl, *tbl;\r
1403 FATFS *fs = fp->obj.fs;\r
1404 \r
1405 \r
1406 tbl = fp->cltbl + 1; /* Top of CLMT */\r
1407 cl = ofs / SS(fs) / fs->csize; /* Cluster order from top of the file */\r
1408 for (;;) {\r
1409 ncl = *tbl++; /* Number of cluters in the fragment */\r
1410 if (ncl == 0) return 0; /* End of table? (error) */\r
1411 if (cl < ncl) break; /* In this fragment? */\r
1412 cl -= ncl; tbl++; /* Next fragment */\r
1413 }\r
1414 return cl + *tbl; /* Return the cluster number */\r
1415 }\r
1416 #endif /* _USE_FASTSEEK */\r
1417 \r
1418 \r
1419 \r
1420 /*-----------------------------------------------------------------------*/\r
1421 /* Directory handling - Set directory index */\r
1422 /*-----------------------------------------------------------------------*/\r
1423 \r
1424 static\r
1425 FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */\r
1426 DIR* dp, /* Pointer to directory object */\r
1427 DWORD ofs /* Offset of directory table */\r
1428 )\r
1429 {\r
1430 DWORD csz, clst;\r
1431 FATFS *fs = dp->obj.fs;\r
1432 \r
1433 \r
1434 if (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */\r
1435 return FR_INT_ERR;\r
1436 }\r
1437 dp->dptr = ofs; /* Set current offset */\r
1438 clst = dp->obj.sclust; /* Table start cluster (0:root) */\r
1439 if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */\r
1440 clst = fs->dirbase;\r
1441 if (_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */\r
1442 }\r
1443 \r
1444 if (clst == 0) { /* Static table (root-directory in FAT12/16) */\r
1445 if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */\r
1446 dp->sect = fs->dirbase;\r
1447 \r
1448 } else { /* Dynamic table (sub-directory or root-directory in FAT32+) */\r
1449 csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */\r
1450 while (ofs >= csz) { /* Follow cluster chain */\r
1451 clst = get_fat(&dp->obj, clst); /* Get next cluster */\r
1452 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */\r
1453 if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */\r
1454 ofs -= csz;\r
1455 }\r
1456 dp->sect = clust2sect(fs, clst);\r
1457 }\r
1458 dp->clust = clst; /* Current cluster# */\r
1459 if (!dp->sect) return FR_INT_ERR;\r
1460 dp->sect += ofs / SS(fs); /* Sector# of the directory entry */\r
1461 dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */\r
1462 \r
1463 return FR_OK;\r
1464 }\r
1465 \r
1466 \r
1467 \r
1468 \r
1469 /*-----------------------------------------------------------------------*/\r
1470 /* Directory handling - Move directory table index next */\r
1471 /*-----------------------------------------------------------------------*/\r
1472 \r
1473 static\r
1474 FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */\r
1475 DIR* dp, /* Pointer to the directory object */\r
1476 int stretch /* 0: Do not stretch table, 1: Stretch table if needed */\r
1477 )\r
1478 {\r
1479 DWORD ofs, clst;\r
1480 FATFS *fs = dp->obj.fs;\r
1481 #if !_FS_READONLY\r
1482 UINT n;\r
1483 #endif\r
1484 \r
1485 ofs = dp->dptr + SZDIRE; /* Next entry */\r
1486 if (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */\r
1487 \r
1488 if (ofs % SS(fs) == 0) { /* Sector changed? */\r
1489 dp->sect++; /* Next sector */\r
1490 \r
1491 if (!dp->clust) { /* Static table */\r
1492 if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */\r
1493 dp->sect = 0; return FR_NO_FILE;\r
1494 }\r
1495 }\r
1496 else { /* Dynamic table */\r
1497 if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */\r
1498 clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */\r
1499 if (clst <= 1) return FR_INT_ERR; /* Internal error */\r
1500 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */\r
1501 if (clst >= fs->n_fatent) { /* Reached end of dynamic table */\r
1502 #if !_FS_READONLY\r
1503 if (!stretch) { /* If no stretch, report EOT */\r
1504 dp->sect = 0; return FR_NO_FILE;\r
1505 }\r
1506 clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */\r
1507 if (clst == 0) return FR_DENIED; /* No free cluster */\r
1508 if (clst == 1) return FR_INT_ERR; /* Internal error */\r
1509 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */\r
1510 /* Clean-up the stretched table */\r
1511 if (_FS_EXFAT) dp->obj.stat |= 4; /* The directory needs to be updated */\r
1512 if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */\r
1513 mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */\r
1514 for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */\r
1515 fs->wflag = 1;\r
1516 if (sync_window(fs) != FR_OK) return FR_DISK_ERR;\r
1517 }\r
1518 fs->winsect -= n; /* Restore window offset */\r
1519 #else\r
1520 if (!stretch) dp->sect = 0; /* If no stretch, report EOT (this is to suppress warning) */\r
1521 dp->sect = 0; return FR_NO_FILE; /* Report EOT */\r
1522 #endif\r
1523 }\r
1524 dp->clust = clst; /* Initialize data for new cluster */\r
1525 dp->sect = clust2sect(fs, clst);\r
1526 }\r
1527 }\r
1528 }\r
1529 dp->dptr = ofs; /* Current entry */\r
1530 dp->dir = &fs->win[ofs % SS(fs)]; /* Pointer to the entry in the win[] */\r
1531 \r
1532 return FR_OK;\r
1533 }\r
1534 \r
1535 \r
1536 \r
1537 \r
1538 /*-----------------------------------------------------------------------*/\r
1539 /* Directory handling - Reserve a block of directory entries */\r
1540 /*-----------------------------------------------------------------------*/\r
1541 \r
1542 #if !_FS_READONLY\r
1543 static\r
1544 FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */\r
1545 DIR* dp, /* Pointer to the directory object */\r
1546 UINT nent /* Number of contiguous entries to allocate */\r
1547 )\r
1548 {\r
1549 FRESULT res;\r
1550 UINT n;\r
1551 FATFS *fs = dp->obj.fs;\r
1552 \r
1553 \r
1554 res = dir_sdi(dp, 0);\r
1555 if (res == FR_OK) {\r
1556 n = 0;\r
1557 do {\r
1558 res = move_window(fs, dp->sect);\r
1559 if (res != FR_OK) break;\r
1560 #if _FS_EXFAT\r
1561 if (fs->fs_type == FS_EXFAT ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {\r
1562 #else\r
1563 if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {\r
1564 #endif\r
1565 if (++n == nent) break; /* A block of contiguous free entries is found */\r
1566 } else {\r
1567 n = 0; /* Not a blank entry. Restart to search */\r
1568 }\r
1569 res = dir_next(dp, 1);\r
1570 } while (res == FR_OK); /* Next entry with table stretch enabled */\r
1571 }\r
1572 \r
1573 if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */\r
1574 return res;\r
1575 }\r
1576 #endif\r
1577 \r
1578 \r
1579 \r
1580 \r
1581 /*-----------------------------------------------------------------------*/\r
1582 /* FAT: Directory handling - Load/Store start cluster number */\r
1583 /*-----------------------------------------------------------------------*/\r
1584 \r
1585 static\r
1586 DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */\r
1587 FATFS* fs, /* Pointer to the fs object */\r
1588 const BYTE* dir /* Pointer to the key entry */\r
1589 )\r
1590 {\r
1591 DWORD cl;\r
1592 \r
1593 cl = ld_word(dir + DIR_FstClusLO);\r
1594 if (fs->fs_type == FS_FAT32) {\r
1595 cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16;\r
1596 }\r
1597 \r
1598 return cl;\r
1599 }\r
1600 \r
1601 \r
1602 #if !_FS_READONLY\r
1603 static\r
1604 void st_clust (\r
1605 FATFS* fs, /* Pointer to the fs object */\r
1606 BYTE* dir, /* Pointer to the key entry */\r
1607 DWORD cl /* Value to be set */\r
1608 )\r
1609 {\r
1610 st_word(dir + DIR_FstClusLO, (WORD)cl);\r
1611 if (fs->fs_type == FS_FAT32) {\r
1612 st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16));\r
1613 }\r
1614 }\r
1615 #endif\r
1616 \r
1617 \r
1618 \r
1619 \r
1620 /*------------------------------------------------------------------------*/\r
1621 /* FAT-LFN: LFN handling */\r
1622 /*------------------------------------------------------------------------*/\r
1623 #if _USE_LFN != 0\r
1624 static\r
1625 const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */\r
1626 \r
1627 /*--------------------------------------------------------*/\r
1628 /* FAT-LFN: Compare a part of file name with an LFN entry */\r
1629 /*--------------------------------------------------------*/\r
1630 static\r
1631 int cmp_lfn ( /* 1:matched, 0:not matched */\r
1632 const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */\r
1633 BYTE* dir /* Pointer to the directory entry containing the part of LFN */\r
1634 )\r
1635 {\r
1636 UINT i, s;\r
1637 WCHAR wc, uc;\r
1638 \r
1639 \r
1640 if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */\r
1641 \r
1642 i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */\r
1643 \r
1644 for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */\r
1645 uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */\r
1646 if (wc) {\r
1647 if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */\r
1648 return 0; /* Not matched */\r
1649 }\r
1650 wc = uc;\r
1651 } else {\r
1652 if (uc != 0xFFFF) return 0; /* Check filler */\r
1653 }\r
1654 }\r
1655 \r
1656 if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */\r
1657 \r
1658 return 1; /* The part of LFN matched */\r
1659 }\r
1660 \r
1661 \r
1662 \r
1663 #if _FS_MINIMIZE <= 1 || _FS_EXFAT\r
1664 /*-----------------------------------------------------*/\r
1665 /* FAT-LFN: Pick a part of file name from an LFN entry */\r
1666 /*-----------------------------------------------------*/\r
1667 static\r
1668 int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */\r
1669 WCHAR* lfnbuf, /* Pointer to the LFN working buffer */\r
1670 BYTE* dir /* Pointer to the LFN entry */\r
1671 )\r
1672 {\r
1673 UINT i, s;\r
1674 WCHAR wc, uc;\r
1675 \r
1676 \r
1677 if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */\r
1678 \r
1679 i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */\r
1680 \r
1681 for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */\r
1682 uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */\r
1683 if (wc) {\r
1684 if (i >= _MAX_LFN) return 0; /* Buffer overflow? */\r
1685 lfnbuf[i++] = wc = uc; /* Store it */\r
1686 } else {\r
1687 if (uc != 0xFFFF) return 0; /* Check filler */\r
1688 }\r
1689 }\r
1690 \r
1691 if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */\r
1692 if (i >= _MAX_LFN) return 0; /* Buffer overflow? */\r
1693 lfnbuf[i] = 0;\r
1694 }\r
1695 \r
1696 return 1; /* The part of LFN is valid */\r
1697 }\r
1698 #endif\r
1699 \r
1700 #if !_FS_READONLY\r
1701 /*-----------------------------------------*/\r
1702 /* FAT-LFN: Create an entry of LFN entries */\r
1703 /*-----------------------------------------*/\r
1704 static\r
1705 void put_lfn (\r
1706 const WCHAR* lfn, /* Pointer to the LFN */\r
1707 BYTE* dir, /* Pointer to the LFN entry to be created */\r
1708 BYTE ord, /* LFN order (1-20) */\r
1709 BYTE sum /* Checksum of the corresponding SFN */\r
1710 )\r
1711 {\r
1712 UINT i, s;\r
1713 WCHAR wc;\r
1714 \r
1715 \r
1716 dir[LDIR_Chksum] = sum; /* Set checksum */\r
1717 dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */\r
1718 dir[LDIR_Type] = 0;\r
1719 st_word(dir + LDIR_FstClusLO, 0);\r
1720 \r
1721 i = (ord - 1) * 13; /* Get offset in the LFN working buffer */\r
1722 s = wc = 0;\r
1723 do {\r
1724 if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */\r
1725 st_word(dir + LfnOfs[s], wc); /* Put it */\r
1726 if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */\r
1727 } while (++s < 13);\r
1728 if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */\r
1729 dir[LDIR_Ord] = ord; /* Set the LFN order */\r
1730 }\r
1731 \r
1732 #endif\r
1733 #endif\r
1734 \r
1735 \r
1736 \r
1737 /*-----------------------------------------------------------------------*/\r
1738 /* FAT-LFN: Create a Numbered SFN */\r
1739 /*-----------------------------------------------------------------------*/\r
1740 #if _USE_LFN != 0 && !_FS_READONLY\r
1741 static\r
1742 void gen_numname (\r
1743 BYTE* dst, /* Pointer to the buffer to store numbered SFN */\r
1744 const BYTE* src, /* Pointer to SFN */\r
1745 const WCHAR* lfn, /* Pointer to LFN */\r
1746 UINT seq /* Sequence number */\r
1747 )\r
1748 {\r
1749 BYTE ns[8], c;\r
1750 UINT i, j;\r
1751 WCHAR wc;\r
1752 DWORD sr;\r
1753 \r
1754 \r
1755 mem_cpy(dst, src, 11);\r
1756 \r
1757 if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */\r
1758 sr = seq;\r
1759 while (*lfn) { /* Create a CRC */\r
1760 wc = *lfn++;\r
1761 for (i = 0; i < 16; i++) {\r
1762 sr = (sr << 1) + (wc & 1);\r
1763 wc >>= 1;\r
1764 if (sr & 0x10000) sr ^= 0x11021;\r
1765 }\r
1766 }\r
1767 seq = (UINT)sr;\r
1768 }\r
1769 \r
1770 /* itoa (hexdecimal) */\r
1771 i = 7;\r
1772 do {\r
1773 c = (seq % 16) + '0';\r
1774 if (c > '9') c += 7;\r
1775 ns[i--] = c;\r
1776 seq /= 16;\r
1777 } while (seq);\r
1778 ns[i] = '~';\r
1779 \r
1780 /* Append the number */\r
1781 for (j = 0; j < i && dst[j] != ' '; j++) {\r
1782 if (IsDBCS1(dst[j])) {\r
1783 if (j == i - 1) break;\r
1784 j++;\r
1785 }\r
1786 }\r
1787 do {\r
1788 dst[j++] = (i < 8) ? ns[i++] : ' ';\r
1789 } while (j < 8);\r
1790 }\r
1791 #endif\r
1792 \r
1793 \r
1794 \r
1795 /*-----------------------------------------------------------------------*/\r
1796 /* FAT-LFN: Calculate checksum of an SFN entry */\r
1797 /*-----------------------------------------------------------------------*/\r
1798 #if _USE_LFN != 0\r
1799 static\r
1800 BYTE sum_sfn (\r
1801 const BYTE* dir /* Pointer to the SFN entry */\r
1802 )\r
1803 {\r
1804 BYTE sum = 0;\r
1805 UINT n = 11;\r
1806 \r
1807 do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);\r
1808 return sum;\r
1809 }\r
1810 #endif\r
1811 \r
1812 \r
1813 \r
1814 \r
1815 #if _FS_EXFAT\r
1816 /*-----------------------------------------------------------------------*/\r
1817 /* exFAT: Directory handling - Load/Store a block of directory entries */\r
1818 /*-----------------------------------------------------------------------*/\r
1819 \r
1820 static\r
1821 WORD xdir_sum ( /* Get checksum of the directoly block */\r
1822 const BYTE* dir /* Directory entry block to be calculated */\r
1823 )\r
1824 {\r
1825 UINT i, szblk;\r
1826 WORD sum;\r
1827 \r
1828 \r
1829 szblk = (dir[XDIR_NumSec] + 1) * SZDIRE;\r
1830 for (i = sum = 0; i < szblk; i++) {\r
1831 if (i == XDIR_SetSum) { /* Skip sum field */\r
1832 i++;\r
1833 } else {\r
1834 sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i];\r
1835 }\r
1836 }\r
1837 return sum;\r
1838 }\r
1839 \r
1840 \r
1841 \r
1842 static\r
1843 WORD xname_sum ( /* Get check sum (to be used as hash) of the name */\r
1844 const WCHAR* name /* File name to be calculated */\r
1845 )\r
1846 {\r
1847 WCHAR chr;\r
1848 WORD sum = 0;\r
1849 \r
1850 \r
1851 while ((chr = *name++) != 0) {\r
1852 chr = ff_wtoupper(chr); /* File name needs to be ignored case */\r
1853 sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF);\r
1854 sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8);\r
1855 }\r
1856 return sum;\r
1857 }\r
1858 \r
1859 \r
1860 /*------------------------------------------------------*/\r
1861 /* exFAT: Get object information from a directory block */\r
1862 /*------------------------------------------------------*/\r
1863 static\r
1864 void get_xdir_info (\r
1865 BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */\r
1866 FILINFO* fno /* Buffer to store the extracted file information */\r
1867 )\r
1868 {\r
1869 UINT di, si, nc;\r
1870 WCHAR w;\r
1871 \r
1872 /* Get file name */\r
1873 #if _LFN_UNICODE\r
1874 if (dirb[XDIR_NumName] <= _MAX_LFN) {\r
1875 for (si = SZDIRE * 2, di = 0; di < dirb[XDIR_NumName]; si += 2, di++) {\r
1876 if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */\r
1877 w = ld_word(dirb + si); /* Get a character */\r
1878 fno->fname[di] = w; /* Store it */\r
1879 }\r
1880 } else {\r
1881 di = 0; /* Buffer overflow and inaccessible object */\r
1882 }\r
1883 #else\r
1884 for (si = SZDIRE * 2, di = nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) {\r
1885 if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */\r
1886 w = ld_word(dirb + si); /* Get a character */\r
1887 w = ff_convert(w, 0); /* Unicode -> OEM */\r
1888 if (w == 0) { di = 0; break; } /* Could not be converted and inaccessible object */\r
1889 if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */\r
1890 fno->fname[di++] = (char)(w >> 8);\r
1891 }\r
1892 if (di >= _MAX_LFN) { di = 0; break; } /* Buffer overflow and inaccessible object */\r
1893 fno->fname[di++] = (char)w;\r
1894 }\r
1895 #endif\r
1896 if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object? */\r
1897 fno->fname[di] = 0; /* Terminate file name */\r
1898 \r
1899 fno->altname[0] = 0; /* No SFN */\r
1900 fno->fattrib = dirb[XDIR_Attr]; /* Attribute */\r
1901 fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */\r
1902 fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */\r
1903 fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */\r
1904 }\r
1905 \r
1906 \r
1907 /*-----------------------------------*/\r
1908 /* exFAT: Get a directry entry block */\r
1909 /*-----------------------------------*/\r
1910 static\r
1911 FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */\r
1912 DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */\r
1913 )\r
1914 {\r
1915 FRESULT res;\r
1916 UINT i, nent;\r
1917 BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */\r
1918 \r
1919 \r
1920 /* Load 85 entry */\r
1921 res = move_window(dp->obj.fs, dp->sect);\r
1922 if (res != FR_OK) return res;\r
1923 if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR;\r
1924 mem_cpy(&dirb[0], dp->dir, SZDIRE);\r
1925 nent = dirb[XDIR_NumSec] + 1;\r
1926 \r
1927 /* Load C0 entry */\r
1928 res = dir_next(dp, 0);\r
1929 if (res != FR_OK) return res;\r
1930 res = move_window(dp->obj.fs, dp->sect);\r
1931 if (res != FR_OK) return res;\r
1932 if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR;\r
1933 mem_cpy(dirb + SZDIRE, dp->dir, SZDIRE);\r
1934 \r
1935 /* Load C1 entries */\r
1936 if (nent < 3 || nent > 19) return FR_NO_FILE;\r
1937 i = SZDIRE * 2; nent *= SZDIRE;\r
1938 do {\r
1939 res = dir_next(dp, 0);\r
1940 if (res != FR_OK) return res;\r
1941 res = move_window(dp->obj.fs, dp->sect);\r
1942 if (res != FR_OK) return res;\r
1943 if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR;\r
1944 mem_cpy(dirb + i, dp->dir, SZDIRE);\r
1945 i += SZDIRE;\r
1946 } while (i < nent);\r
1947 \r
1948 /* Sanity check */\r
1949 if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR;\r
1950 \r
1951 return FR_OK;\r
1952 }\r
1953 \r
1954 \r
1955 #if !_FS_READONLY || _FS_RPATH != 0 \r
1956 /*------------------------------------------------*/\r
1957 /* exFAT: Load the object's directory entry block */\r
1958 /*------------------------------------------------*/\r
1959 static\r
1960 FRESULT load_obj_dir ( \r
1961 DIR* dp, /* Blank directory object to be used to access containing direcotry */\r
1962 const _FDID* obj /* Object with containing directory information */\r
1963 )\r
1964 {\r
1965 FRESULT res;\r
1966 \r
1967 \r
1968 /* Open object containing directory */\r
1969 dp->obj.fs = obj->fs;\r
1970 dp->obj.sclust = obj->c_scl;\r
1971 dp->obj.stat = (BYTE)obj->c_size;\r
1972 dp->obj.objsize = obj->c_size & 0xFFFFFF00;\r
1973 dp->blk_ofs = obj->c_ofs;\r
1974 \r
1975 res = dir_sdi(dp, dp->blk_ofs); /* Goto the block location */\r
1976 if (res == FR_OK) {\r
1977 res = load_xdir(dp); /* Load the object's entry block */\r
1978 }\r
1979 return res;\r
1980 }\r
1981 #endif\r
1982 \r
1983 \r
1984 #if !_FS_READONLY\r
1985 /*-----------------------------------------------*/\r
1986 /* exFAT: Store the directory block to the media */\r
1987 /*-----------------------------------------------*/\r
1988 static\r
1989 FRESULT store_xdir (\r
1990 DIR* dp /* Pointer to the direcotry object */\r
1991 )\r
1992 {\r
1993 FRESULT res;\r
1994 UINT nent;\r
1995 WORD sum;\r
1996 BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */\r
1997 \r
1998 /* Create set sum */\r
1999 sum = xdir_sum(dirb);\r
2000 st_word(dirb + XDIR_SetSum, sum);\r
2001 nent = dirb[XDIR_NumSec] + 1;\r
2002 \r
2003 res = dir_sdi(dp, dp->blk_ofs);\r
2004 while (res == FR_OK && (res = move_window(dp->obj.fs, dp->sect)) == FR_OK) {\r
2005 mem_cpy(dp->dir, dirb, SZDIRE);\r
2006 dp->obj.fs->wflag = 1;\r
2007 if (--nent == 0) break;\r
2008 dirb += SZDIRE;\r
2009 res = dir_next(dp, 0);\r
2010 }\r
2011 return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR;\r
2012 }\r
2013 \r
2014 \r
2015 /*-------------------------------------------*/\r
2016 /* exFAT: Create a new directory enrty block */\r
2017 /*-------------------------------------------*/\r
2018 static\r
2019 void create_xdir (\r
2020 BYTE* dirb, /* Pointer to the direcotry entry block buffer */\r
2021 const WCHAR* lfn /* Pointer to the nul terminated file name */\r
2022 )\r
2023 {\r
2024 UINT i;\r
2025 BYTE nb, nc;\r
2026 WCHAR chr;\r
2027 WORD hash;\r
2028 \r
2029 \r
2030 mem_set(dirb, 0, 2 * SZDIRE); /* Initialize 85+C0 entry */\r
2031 dirb[XDIR_Type] = 0x85;\r
2032 dirb[XDIR_Type + SZDIRE] = 0xC0;\r
2033 hash = xname_sum(lfn);\r
2034 st_word(dirb + XDIR_NameHash, hash); /* Set name hash */\r
2035 \r
2036 i = SZDIRE * 2; /* C1 offset */\r
2037 nc = 0; nb = 1; chr = 1;\r
2038 do {\r
2039 dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */\r
2040 do { /* Fill name field */\r
2041 if (chr && (chr = lfn[nc]) != 0) nc++; /* Get a character if exist */\r
2042 st_word(dirb + i, chr); i += 2; /* Store it */\r
2043 } while (i % SZDIRE);\r
2044 nb++;\r
2045 } while (lfn[nc]); /* Fill next entry if any char follows */\r
2046 \r
2047 dirb[XDIR_NumName] = nc; /* Set name length */\r
2048 dirb[XDIR_NumSec] = nb; /* Set number of C0+C1s */\r
2049 }\r
2050 #endif\r
2051 #endif\r
2052 \r
2053 \r
2054 \r
2055 /*-----------------------------------------------------------------------*/\r
2056 /* Read an object from the directory */\r
2057 /*-----------------------------------------------------------------------*/\r
2058 #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT\r
2059 static\r
2060 FRESULT dir_read (\r
2061 DIR* dp, /* Pointer to the directory object */\r
2062 int vol /* Filtered by 0:file/directory or 1:volume label */\r
2063 )\r
2064 {\r
2065 FRESULT res = FR_NO_FILE;\r
2066 FATFS *fs = dp->obj.fs;\r
2067 BYTE a, c;\r
2068 #if _USE_LFN != 0\r
2069 BYTE ord = 0xFF, sum = 0xFF;\r
2070 #endif\r
2071 \r
2072 while (dp->sect) {\r
2073 res = move_window(fs, dp->sect);\r
2074 if (res != FR_OK) break;\r
2075 c = dp->dir[DIR_Name]; /* Test for the entry type */\r
2076 if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of the directory */\r
2077 #if _FS_EXFAT\r
2078 if (fs->fs_type == FS_EXFAT) { /* At the exFAT */\r
2079 if (_USE_LABEL && vol) {\r
2080 if (c == 0x83) break; /* Volume label entry? */\r
2081 } else {\r
2082 if (c == 0x85) { /* Start of the entry block? */\r
2083 dp->blk_ofs = dp->dptr; /* Set location of block */\r
2084 res = load_xdir(dp); /* Load the entry block */\r
2085 if (res == FR_OK) {\r
2086 dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */\r
2087 }\r
2088 break;\r
2089 }\r
2090 }\r
2091 } else\r
2092 #endif\r
2093 { /* At the FAT12/16/32 */\r
2094 dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */\r
2095 #if _USE_LFN != 0 /* LFN configuration */\r
2096 if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */\r
2097 ord = 0xFF;\r
2098 } else {\r
2099 if (a == AM_LFN) { /* An LFN entry is found */\r
2100 if (c & LLEF) { /* Is it start of an LFN sequence? */\r
2101 sum = dp->dir[LDIR_Chksum];\r
2102 c &= ~LLEF; ord = c;\r
2103 dp->blk_ofs = dp->dptr;\r
2104 }\r
2105 /* Check LFN validity and capture it */\r
2106 ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(dp->lfn, dp->dir)) ? ord - 1 : 0xFF;\r
2107 } else { /* An SFN entry is found */\r
2108 if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */\r
2109 dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */\r
2110 }\r
2111 break;\r
2112 }\r
2113 }\r
2114 #else /* Non LFN configuration */\r
2115 if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */\r
2116 break;\r
2117 }\r
2118 #endif\r
2119 }\r
2120 res = dir_next(dp, 0); /* Next entry */\r
2121 if (res != FR_OK) break;\r
2122 }\r
2123 \r
2124 if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */\r
2125 return res;\r
2126 }\r
2127 #endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */\r
2128 \r
2129 \r
2130 \r
2131 /*-----------------------------------------------------------------------*/\r
2132 /* Directory handling - Find an object in the directory */\r
2133 /*-----------------------------------------------------------------------*/\r
2134 \r
2135 static\r
2136 FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */\r
2137 DIR* dp /* Pointer to the directory object with the file name */\r
2138 )\r
2139 {\r
2140 FRESULT res;\r
2141 FATFS *fs = dp->obj.fs;\r
2142 BYTE c;\r
2143 #if _USE_LFN != 0\r
2144 BYTE a, ord, sum;\r
2145 #endif\r
2146 \r
2147 res = dir_sdi(dp, 0); /* Rewind directory object */\r
2148 if (res != FR_OK) return res;\r
2149 #if _FS_EXFAT\r
2150 if (fs->fs_type == FS_EXFAT) { /* At the exFAT */\r
2151 BYTE nc;\r
2152 UINT di, ni;\r
2153 WORD hash = xname_sum(dp->lfn); /* Hash value of the name to find */\r
2154 \r
2155 while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */\r
2156 if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip the comparison if hash value mismatched */\r
2157 for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */\r
2158 if ((di % SZDIRE) == 0) di += 2;\r
2159 if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(dp->lfn[ni])) break;\r
2160 }\r
2161 if (nc == 0 && !dp->lfn[ni]) break; /* Name matched? */\r
2162 }\r
2163 return res;\r
2164 }\r
2165 #endif\r
2166 /* At the FAT12/16/32 */\r
2167 #if _USE_LFN != 0\r
2168 ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */\r
2169 #endif\r
2170 do {\r
2171 res = move_window(fs, dp->sect);\r
2172 if (res != FR_OK) break;\r
2173 c = dp->dir[DIR_Name];\r
2174 if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */\r
2175 #if _USE_LFN != 0 /* LFN configuration */\r
2176 dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;\r
2177 if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */\r
2178 ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */\r
2179 } else {\r
2180 if (a == AM_LFN) { /* An LFN entry is found */\r
2181 if (dp->lfn) {\r
2182 if (c & LLEF) { /* Is it start of LFN sequence? */\r
2183 sum = dp->dir[LDIR_Chksum];\r
2184 c &= ~LLEF; ord = c; /* LFN start order */\r
2185 dp->blk_ofs = dp->dptr; /* Start offset of LFN */\r
2186 }\r
2187 /* Check validity of the LFN entry and compare it with given name */\r
2188 ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dp->dir)) ? ord - 1 : 0xFF;\r
2189 }\r
2190 } else { /* An SFN entry is found */\r
2191 if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */\r
2192 if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */\r
2193 ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */\r
2194 }\r
2195 }\r
2196 #else /* Non LFN configuration */\r
2197 dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;\r
2198 if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */\r
2199 #endif\r
2200 res = dir_next(dp, 0); /* Next entry */\r
2201 } while (res == FR_OK);\r
2202 \r
2203 return res;\r
2204 }\r
2205 \r
2206 \r
2207 \r
2208 \r
2209 /*-----------------------------------------------------------------------*/\r
2210 /* Register an object to the directory */\r
2211 /*-----------------------------------------------------------------------*/\r
2212 #if !_FS_READONLY\r
2213 static\r
2214 FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */\r
2215 DIR* dp /* Target directory with object name to be created */\r
2216 )\r
2217 {\r
2218 FRESULT res;\r
2219 FATFS *fs = dp->obj.fs;\r
2220 #if _USE_LFN != 0 /* LFN configuration */\r
2221 UINT n, nlen, nent;\r
2222 BYTE sn[12], *fn, sum;\r
2223 WCHAR *lfn;\r
2224 \r
2225 \r
2226 fn = dp->fn; lfn = dp->lfn;\r
2227 if (fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */\r
2228 for (nlen = 0; lfn[nlen]; nlen++) ; /* Get lfn length */\r
2229 \r
2230 #if _FS_EXFAT\r
2231 if (fs->fs_type == FS_EXFAT) { /* At the exFAT */\r
2232 DIR dj;\r
2233 \r
2234 nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */\r
2235 res = dir_alloc(dp, nent); /* Allocate entries */\r
2236 if (res != FR_OK) return res;\r
2237 dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */\r
2238 \r
2239 if (dp->obj.stat & 4) { /* Has the sub-directory been stretched? */\r
2240 dp->obj.stat &= 3;\r
2241 dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */\r
2242 res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */\r
2243 if (res != FR_OK) return res;\r
2244 res = load_obj_dir(&dj, &dp->obj);\r
2245 if (res != FR_OK) return res; /* Load the object status */\r
2246 st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */\r
2247 st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);\r
2248 fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1;\r
2249 res = store_xdir(&dj); /* Store the object status */\r
2250 if (res != FR_OK) return res;\r
2251 }\r
2252 \r
2253 create_xdir(fs->dirbuf, lfn); /* Create on-memory directory block to be written later */\r
2254 return FR_OK;\r
2255 }\r
2256 #endif\r
2257 /* At the FAT12/16/32 */\r
2258 mem_cpy(sn, fn, 12);\r
2259 if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */\r
2260 fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */\r
2261 for (n = 1; n < 100; n++) {\r
2262 gen_numname(fn, sn, lfn, n); /* Generate a numbered name */\r
2263 res = dir_find(dp); /* Check if the name collides with existing SFN */\r
2264 if (res != FR_OK) break;\r
2265 }\r
2266 if (n == 100) return FR_DENIED; /* Abort if too many collisions */\r
2267 if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */\r
2268 fn[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn;\r
2269 }\r
2270 \r
2271 /* Create an SFN with/without LFNs. */\r
2272 nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */\r
2273 res = dir_alloc(dp, nent); /* Allocate entries */\r
2274 if (res == FR_OK && --nent) { /* Set LFN entry if needed */\r
2275 res = dir_sdi(dp, dp->dptr - nent * SZDIRE);\r
2276 if (res == FR_OK) {\r
2277 sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */\r
2278 do { /* Store LFN entries in bottom first */\r
2279 res = move_window(fs, dp->sect);\r
2280 if (res != FR_OK) break;\r
2281 put_lfn(dp->lfn, dp->dir, (BYTE)nent, sum);\r
2282 fs->wflag = 1;\r
2283 res = dir_next(dp, 0); /* Next entry */\r
2284 } while (res == FR_OK && --nent);\r
2285 }\r
2286 }\r
2287 \r
2288 #else /* Non LFN configuration */\r
2289 res = dir_alloc(dp, 1); /* Allocate an entry for SFN */\r
2290 \r
2291 #endif\r
2292 \r
2293 /* Set SFN entry */\r
2294 if (res == FR_OK) {\r
2295 res = move_window(fs, dp->sect);\r
2296 if (res == FR_OK) {\r
2297 mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */\r
2298 mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */\r
2299 #if _USE_LFN != 0\r
2300 dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */\r
2301 #endif\r
2302 fs->wflag = 1;\r
2303 }\r
2304 }\r
2305 \r
2306 return res;\r
2307 }\r
2308 #endif /* !_FS_READONLY */\r
2309 \r
2310 \r
2311 \r
2312 \r
2313 /*-----------------------------------------------------------------------*/\r
2314 /* Remove an object from the directory */\r
2315 /*-----------------------------------------------------------------------*/\r
2316 #if !_FS_READONLY && !_FS_MINIMIZE\r
2317 static\r
2318 FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */\r
2319 DIR* dp /* Directory object pointing the entry to be removed */\r
2320 )\r
2321 {\r
2322 FRESULT res;\r
2323 FATFS *fs = dp->obj.fs;\r
2324 #if _USE_LFN != 0 /* LFN configuration */\r
2325 DWORD last = dp->dptr;\r
2326 \r
2327 res = dp->blk_ofs == 0xFFFFFFFF ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */\r
2328 if (res == FR_OK) {\r
2329 do {\r
2330 res = move_window(fs, dp->sect);\r
2331 if (res != FR_OK) break;\r
2332 /* Mark an entry 'deleted' */\r
2333 if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* At the exFAT */\r
2334 dp->dir[XDIR_Type] &= 0x7F;\r
2335 } else { /* At the FAT12/16/32 */\r
2336 dp->dir[DIR_Name] = DDEM;\r
2337 }\r
2338 fs->wflag = 1;\r
2339 if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */\r
2340 res = dir_next(dp, 0); /* Next entry */\r
2341 } while (res == FR_OK);\r
2342 if (res == FR_NO_FILE) res = FR_INT_ERR;\r
2343 }\r
2344 #else /* Non LFN configuration */\r
2345 \r
2346 res = move_window(fs, dp->sect);\r
2347 if (res == FR_OK) {\r
2348 dp->dir[DIR_Name] = DDEM;\r
2349 fs->wflag = 1;\r
2350 }\r
2351 #endif\r
2352 \r
2353 return res;\r
2354 }\r
2355 #endif /* !_FS_READONLY */\r
2356 \r
2357 \r
2358 \r
2359 \r
2360 /*-----------------------------------------------------------------------*/\r
2361 /* Get file information from directory entry */\r
2362 /*-----------------------------------------------------------------------*/\r
2363 #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2\r
2364 static\r
2365 void get_fileinfo ( /* No return code */\r
2366 DIR* dp, /* Pointer to the directory object */\r
2367 FILINFO* fno /* Pointer to the file information to be filled */\r
2368 )\r
2369 {\r
2370 UINT i, j;\r
2371 TCHAR c;\r
2372 #if _USE_LFN != 0\r
2373 WCHAR w, *lfn;\r
2374 WCHAR lfv;\r
2375 #endif\r
2376 \r
2377 \r
2378 fno->fname[0] = 0; /* Invaidate file info */\r
2379 if (!dp->sect) return; /* Exit if read pointer has reached end of directory */\r
2380 \r
2381 #if _USE_LFN != 0 /* LFN configuration */\r
2382 #if _FS_EXFAT\r
2383 if (dp->obj.fs->fs_type == FS_EXFAT) { /* At the exFAT */\r
2384 get_xdir_info(dp->obj.fs->dirbuf, fno);\r
2385 return;\r
2386 } else\r
2387 #endif\r
2388 { /* At the FAT12/16/32 */\r
2389 if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */\r
2390 i = 0; lfn = dp->lfn;\r
2391 while ((w = *lfn++) != 0) { /* Get an LFN character */\r
2392 #if !_LFN_UNICODE\r
2393 w = ff_convert(w, 0); /* Unicode -> OEM */\r
2394 if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */\r
2395 if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */\r
2396 fno->fname[i++] = (char)(w >> 8);\r
2397 }\r
2398 #endif\r
2399 if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */\r
2400 fno->fname[i++] = (char)w;\r
2401 }\r
2402 fno->fname[i] = 0; /* Terminate the LFN */\r
2403 }\r
2404 }\r
2405 \r
2406 i = j = 0;\r
2407 lfv = fno->fname[i]; /* LFN is exist if non-zero */\r
2408 while (i < 11) { /* Copy name body and extension */\r
2409 c = (TCHAR)dp->dir[i++];\r
2410 if (c == ' ') continue; /* Skip padding spaces */\r
2411 if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */\r
2412 if (i == 9) { /* Insert a . if extension is exist */\r
2413 if (!lfv) fno->fname[j] = '.';\r
2414 fno->altname[j++] = '.';\r
2415 }\r
2416 #if _LFN_UNICODE\r
2417 if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) {\r
2418 c = c << 8 | dp->dir[i++];\r
2419 }\r
2420 c = ff_convert(c, 1); /* OEM -> Unicode */\r
2421 if (!c) c = '?';\r
2422 #endif\r
2423 fno->altname[j] = c;\r
2424 if (!lfv) {\r
2425 if (IsUpper(c) && (dp->dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) {\r
2426 c += 0x20; /* To lower */\r
2427 }\r
2428 fno->fname[j] = c;\r
2429 }\r
2430 j++;\r
2431 }\r
2432 if (!lfv) {\r
2433 fno->fname[j] = 0;\r
2434 if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */\r
2435 }\r
2436 fno->altname[j] = 0; /* Terminate the SFN */\r
2437 \r
2438 #else /* Non-LFN configuration */\r
2439 i = j = 0;\r
2440 while (i < 11) { /* Copy name body and extension */\r
2441 c = (TCHAR)dp->dir[i++];\r
2442 if (c == ' ') continue; /* Skip padding spaces */\r
2443 if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */\r
2444 if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */\r
2445 fno->fname[j++] = c;\r
2446 }\r
2447 fno->fname[j] = 0;\r
2448 #endif\r
2449 \r
2450 fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */\r
2451 fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */\r
2452 fno->fdate = ld_word(dp->dir + DIR_WrtDate); /* Date */\r
2453 fno->ftime = ld_word(dp->dir + DIR_WrtTime); /* Time */\r
2454 }\r
2455 #endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */\r
2456 \r
2457 \r
2458 \r
2459 \r
2460 /*-----------------------------------------------------------------------*/\r
2461 /* Pattern matching */\r
2462 /*-----------------------------------------------------------------------*/\r
2463 #if _USE_FIND && _FS_MINIMIZE <= 1\r
2464 static\r
2465 WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */\r
2466 const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */\r
2467 )\r
2468 {\r
2469 #if !_LFN_UNICODE\r
2470 WCHAR chr;\r
2471 \r
2472 chr = (BYTE)*(*ptr)++; /* Get a byte */\r
2473 if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */\r
2474 #ifdef _EXCVT\r
2475 if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */\r
2476 #else\r
2477 if (IsDBCS1(chr) && IsDBCS2(**ptr)) { /* Get DBC 2nd byte if needed */\r
2478 chr = chr << 8 | (BYTE)*(*ptr)++;\r
2479 }\r
2480 #endif\r
2481 return chr;\r
2482 #else\r
2483 return ff_wtoupper(*(*ptr)++); /* Get a word and to upper */\r
2484 #endif\r
2485 }\r
2486 \r
2487 \r
2488 static\r
2489 int pattern_matching ( /* 0:not matched, 1:matched */\r
2490 const TCHAR* pat, /* Matching pattern */\r
2491 const TCHAR* nam, /* String to be tested */\r
2492 int skip, /* Number of pre-skip chars (number of ?s) */\r
2493 int inf /* Infinite search (* specified) */\r
2494 )\r
2495 {\r
2496 const TCHAR *pp, *np;\r
2497 WCHAR pc, nc;\r
2498 int nm, nx;\r
2499 \r
2500 \r
2501 while (skip--) { /* Pre-skip name chars */\r
2502 if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */\r
2503 }\r
2504 if (!*pat && inf) return 1; /* (short circuit) */\r
2505 \r
2506 do {\r
2507 pp = pat; np = nam; /* Top of pattern and name to match */\r
2508 for (;;) {\r
2509 if (*pp == '?' || *pp == '*') { /* Wildcard? */\r
2510 nm = nx = 0;\r
2511 do { /* Analyze the wildcard chars */\r
2512 if (*pp++ == '?') nm++; else nx = 1;\r
2513 } while (*pp == '?' || *pp == '*');\r
2514 if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recursions upto number of wildcard blocks in the pattern) */\r
2515 nc = *np; break; /* Branch mismatched */\r
2516 }\r
2517 pc = get_achar(&pp); /* Get a pattern char */\r
2518 nc = get_achar(&np); /* Get a name char */\r
2519 if (pc != nc) break; /* Branch mismatched? */\r
2520 if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */\r
2521 }\r
2522 get_achar(&nam); /* nam++ */\r
2523 } while (inf && nc); /* Retry until end of name if infinite search is specified */\r
2524 \r
2525 return 0;\r
2526 }\r
2527 #endif /* _USE_FIND && _FS_MINIMIZE <= 1 */\r
2528 \r
2529 \r
2530 \r
2531 \r
2532 /*-----------------------------------------------------------------------*/\r
2533 /* Pick a top segment and create the object name in directory form */\r
2534 /*-----------------------------------------------------------------------*/\r
2535 \r
2536 static\r
2537 FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */\r
2538 DIR* dp, /* Pointer to the directory object */\r
2539 const TCHAR** path /* Pointer to pointer to the segment in the path string */\r
2540 )\r
2541 {\r
2542 #if _USE_LFN != 0 /* LFN configuration */\r
2543 BYTE b, cf;\r
2544 WCHAR w, *lfn;\r
2545 UINT i, ni, si, di;\r
2546 const TCHAR *p;\r
2547 \r
2548 /* Create LFN in Unicode */\r
2549 p = *path; lfn = dp->lfn; si = di = 0;\r
2550 for (;;) {\r
2551 w = p[si++]; /* Get a character */\r
2552 if (w < ' ' || w == '/' || w == '\\') { /* Break on end of segment */\r
2553 while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator */\r
2554 break;\r
2555 }\r
2556 if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */\r
2557 #if !_LFN_UNICODE\r
2558 w &= 0xFF;\r
2559 if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */\r
2560 b = (BYTE)p[si++]; /* Get 2nd byte */\r
2561 w = (w << 8) + b; /* Create a DBC */\r
2562 if (!IsDBCS2(b)) return FR_INVALID_NAME; /* Reject invalid sequence */\r
2563 }\r
2564 w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */\r
2565 if (!w) return FR_INVALID_NAME; /* Reject invalid code */\r
2566 #endif\r
2567 if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */\r
2568 lfn[di++] = w; /* Store the Unicode character */\r
2569 }\r
2570 *path = &p[si]; /* Return pointer to the next segment */\r
2571 cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */\r
2572 #if _FS_RPATH != 0\r
2573 if ((di == 1 && lfn[di - 1] == '.') ||\r
2574 (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */\r
2575 lfn[di] = 0;\r
2576 for (i = 0; i < 11; i++) /* Create dot name for SFN entry */\r
2577 dp->fn[i] = (i < di) ? '.' : ' ';\r
2578 dp->fn[i] = cf | NS_DOT; /* This is a dot entry */\r
2579 return FR_OK;\r
2580 }\r
2581 #endif\r
2582 while (di) { /* Snip off trailing spaces and dots if exist */\r
2583 w = lfn[di - 1];\r
2584 if (w != ' ' && w != '.') break;\r
2585 di--;\r
2586 }\r
2587 lfn[di] = 0; /* LFN is created */\r
2588 if (di == 0) return FR_INVALID_NAME; /* Reject nul name */\r
2589 \r
2590 /* Create SFN in directory form */\r
2591 mem_set(dp->fn, ' ', 11);\r
2592 for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */\r
2593 if (si) cf |= NS_LOSS | NS_LFN;\r
2594 while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */\r
2595 \r
2596 b = i = 0; ni = 8;\r
2597 for (;;) {\r
2598 w = lfn[si++]; /* Get an LFN character */\r
2599 if (!w) break; /* Break on end of the LFN */\r
2600 if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */\r
2601 cf |= NS_LOSS | NS_LFN; continue;\r
2602 }\r
2603 \r
2604 if (i >= ni || si == di) { /* Extension or end of SFN */\r
2605 if (ni == 11) { /* Long extension */\r
2606 cf |= NS_LOSS | NS_LFN; break;\r
2607 }\r
2608 if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */\r
2609 if (si > di) break; /* No extension */\r
2610 si = di; i = 8; ni = 11; /* Enter extension section */\r
2611 b <<= 2; continue;\r
2612 }\r
2613 \r
2614 if (w >= 0x80) { /* Non ASCII character */\r
2615 #ifdef _EXCVT\r
2616 w = ff_convert(w, 0); /* Unicode -> OEM code */\r
2617 if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */\r
2618 #else\r
2619 w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */\r
2620 #endif\r
2621 cf |= NS_LFN; /* Force create LFN entry */\r
2622 }\r
2623 \r
2624 if (_DF1S && w >= 0x100) { /* Is this DBC? (always false at SBCS cfg) */\r
2625 if (i >= ni - 1) {\r
2626 cf |= NS_LOSS | NS_LFN; i = ni; continue;\r
2627 }\r
2628 dp->fn[i++] = (BYTE)(w >> 8);\r
2629 } else { /* SBC */\r
2630 if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */\r
2631 w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */\r
2632 } else {\r
2633 if (IsUpper(w)) { /* ASCII large capital */\r
2634 b |= 2;\r
2635 } else {\r
2636 if (IsLower(w)) { /* ASCII small capital */\r
2637 b |= 1; w -= 0x20;\r
2638 }\r
2639 }\r
2640 }\r
2641 }\r
2642 dp->fn[i++] = (BYTE)w;\r
2643 }\r
2644 \r
2645 if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */\r
2646 \r
2647 if (ni == 8) b <<= 2;\r
2648 if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */\r
2649 if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */\r
2650 if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */\r
2651 if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */\r
2652 }\r
2653 \r
2654 dp->fn[NSFLAG] = cf; /* SFN is created */\r
2655 \r
2656 return FR_OK;\r
2657 \r
2658 \r
2659 #else /* Non-LFN configuration */\r
2660 BYTE b, c, d, *sfn;\r
2661 UINT ni, si, i;\r
2662 const char *p;\r
2663 \r
2664 /* Create file name in directory form */\r
2665 p = *path; sfn = dp->fn;\r
2666 mem_set(sfn, ' ', 11);\r
2667 si = i = b = 0; ni = 8;\r
2668 #if _FS_RPATH != 0\r
2669 if (p[si] == '.') { /* Is this a dot entry? */\r
2670 for (;;) {\r
2671 c = (BYTE)p[si++];\r
2672 if (c != '.' || si >= 3) break;\r
2673 sfn[i++] = c;\r
2674 }\r
2675 if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;\r
2676 *path = &p[si]; /* Return pointer to the next segment */\r
2677 sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */\r
2678 return FR_OK;\r
2679 }\r
2680 #endif\r
2681 for (;;) {\r
2682 c = (BYTE)p[si++];\r
2683 if (c <= ' ' || c == '/' || c == '\\') { /* Break on end of segment */\r
2684 while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator */\r
2685 break;\r
2686 }\r
2687 if (c == '.' || i >= ni) {\r
2688 if (ni != 8 || c != '.') return FR_INVALID_NAME;\r
2689 i = 8; ni = 11;\r
2690 b <<= 2; continue;\r
2691 }\r
2692 if (c >= 0x80) { /* Extended character? */\r
2693 b |= 3; /* Eliminate NT flag */\r
2694 #ifdef _EXCVT\r
2695 c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */\r
2696 #else\r
2697 #if !_DF1S\r
2698 return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */\r
2699 #endif\r
2700 #endif\r
2701 }\r
2702 if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at SBCS cfg.) */\r
2703 d = (BYTE)p[si++]; /* Get 2nd byte */\r
2704 if (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */\r
2705 sfn[i++] = c;\r
2706 sfn[i++] = d;\r
2707 } else { /* SBC */\r
2708 if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */\r
2709 if (IsUpper(c)) { /* ASCII large capital? */\r
2710 b |= 2;\r
2711 } else {\r
2712 if (IsLower(c)) { /* ASCII small capital? */\r
2713 b |= 1; c -= 0x20;\r
2714 }\r
2715 }\r
2716 sfn[i++] = c;\r
2717 }\r
2718 }\r
2719 *path = &p[si]; /* Return pointer to the next segment */\r
2720 c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */\r
2721 \r
2722 if (i == 0) return FR_INVALID_NAME; /* Reject nul string */\r
2723 if (sfn[0] == DDEM) sfn[0] = RDDEM; /* When first character collides with DDEM, replace it with RDDEM */\r
2724 \r
2725 if (ni == 8) b <<= 2;\r
2726 if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */\r
2727 if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */\r
2728 \r
2729 sfn[NSFLAG] = c; /* Store NT flag, File name is created */\r
2730 \r
2731 return FR_OK;\r
2732 #endif\r
2733 }\r
2734 \r
2735 \r
2736 \r
2737 \r
2738 /*-----------------------------------------------------------------------*/\r
2739 /* Follow a file path */\r
2740 /*-----------------------------------------------------------------------*/\r
2741 \r
2742 static\r
2743 FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */\r
2744 DIR* dp, /* Directory object to return last directory and found object */\r
2745 const TCHAR* path /* Full-path string to find a file or directory */\r
2746 )\r
2747 {\r
2748 FRESULT res;\r
2749 BYTE ns;\r
2750 _FDID *obj = &dp->obj;\r
2751 FATFS *fs = obj->fs;\r
2752 \r
2753 \r
2754 #if _FS_RPATH != 0\r
2755 if (*path != '/' && *path != '\\') { /* Without heading separator */\r
2756 obj->sclust = fs->cdir; /* Start from the current directory */\r
2757 } else\r
2758 #endif\r
2759 { /* With heading separator */\r
2760 while (*path == '/' || *path == '\\') path++; /* Strip heading separator */\r
2761 obj->sclust = 0; /* Start from the root directory */\r
2762 }\r
2763 #if _FS_EXFAT && _FS_RPATH != 0\r
2764 if (fs->fs_type == FS_EXFAT && obj->sclust) { /* Retrieve the sub-directory status if needed */\r
2765 DIR dj;\r
2766 \r
2767 obj->c_scl = fs->cdc_scl;\r
2768 obj->c_size = fs->cdc_size;\r
2769 obj->c_ofs = fs->cdc_ofs;\r
2770 res = load_obj_dir(&dj, obj);\r
2771 if (res != FR_OK) return res;\r
2772 obj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize);\r
2773 obj->stat = fs->dirbuf[XDIR_GenFlags] & 2;\r
2774 }\r
2775 #endif\r
2776 \r
2777 if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */\r
2778 dp->fn[NSFLAG] = NS_NONAME;\r
2779 res = dir_sdi(dp, 0);\r
2780 \r
2781 } else { /* Follow path */\r
2782 for (;;) {\r
2783 res = create_name(dp, &path); /* Get a segment name of the path */\r
2784 if (res != FR_OK) break;\r
2785 res = dir_find(dp); /* Find an object with the segment name */\r
2786 ns = dp->fn[NSFLAG];\r
2787 if (res != FR_OK) { /* Failed to find the object */\r
2788 if (res == FR_NO_FILE) { /* Object is not found */\r
2789 if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */\r
2790 if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */\r
2791 dp->fn[NSFLAG] = NS_NONAME;\r
2792 res = FR_OK;\r
2793 } else { /* Could not find the object */\r
2794 if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */\r
2795 }\r
2796 }\r
2797 break;\r
2798 }\r
2799 if (ns & NS_LAST) break; /* Last segment matched. Function completed. */\r
2800 /* Get into the sub-directory */\r
2801 if (!(obj->attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */\r
2802 res = FR_NO_PATH; break;\r
2803 }\r
2804 #if _FS_EXFAT\r
2805 if (fs->fs_type == FS_EXFAT) {\r
2806 obj->c_scl = obj->sclust; /* Save containing directory information for next dir */\r
2807 obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat;\r
2808 obj->c_ofs = dp->blk_ofs;\r
2809 obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */\r
2810 obj->stat = fs->dirbuf[XDIR_GenFlags] & 2;\r
2811 obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\r
2812 } else\r
2813 #endif\r
2814 {\r
2815 obj->sclust = ld_clust(fs, &fs->win[dp->dptr % SS(fs)]); /* Open next directory */\r
2816 }\r
2817 }\r
2818 }\r
2819 \r
2820 return res;\r
2821 }\r
2822 \r
2823 \r
2824 \r
2825 \r
2826 /*-----------------------------------------------------------------------*/\r
2827 /* Get logical drive number from path name */\r
2828 /*-----------------------------------------------------------------------*/\r
2829 \r
2830 static\r
2831 int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */\r
2832 const TCHAR** path /* Pointer to pointer to the path name */\r
2833 )\r
2834 {\r
2835 const TCHAR *tp, *tt;\r
2836 UINT i;\r
2837 int vol = -1;\r
2838 #if _STR_VOLUME_ID /* Find string drive id */\r
2839 static const char* const str[] = {_VOLUME_STRS};\r
2840 const char *sp;\r
2841 char c;\r
2842 TCHAR tc;\r
2843 #endif\r
2844 \r
2845 \r
2846 if (*path) { /* If the pointer is not a null */\r
2847 for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */\r
2848 if (*tt == ':') { /* If a ':' is exist in the path name */\r
2849 tp = *path;\r
2850 i = *tp++ - '0'; \r
2851 if (i < 10 && tp == tt) { /* Is there a numeric drive id? */\r
2852 if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */\r
2853 vol = (int)i;\r
2854 *path = ++tt;\r
2855 }\r
2856 }\r
2857 #if _STR_VOLUME_ID\r
2858 else { /* No numeric drive number, find string drive id */\r
2859 i = 0; tt++;\r
2860 do {\r
2861 sp = str[i]; tp = *path;\r
2862 do { /* Compare a string drive id with path name */\r
2863 c = *sp++; tc = *tp++;\r
2864 if (IsLower(tc)) tc -= 0x20;\r
2865 } while (c && (TCHAR)c == tc);\r
2866 } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */\r
2867 if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */\r
2868 vol = (int)i;\r
2869 *path = tt;\r
2870 }\r
2871 }\r
2872 #endif\r
2873 return vol;\r
2874 }\r
2875 #if _FS_RPATH != 0 && _VOLUMES >= 2\r
2876 vol = CurrVol; /* Current drive */\r
2877 #else\r
2878 vol = 0; /* Drive 0 */\r
2879 #endif\r
2880 }\r
2881 return vol;\r
2882 }\r
2883 \r
2884 \r
2885 \r
2886 \r
2887 /*-----------------------------------------------------------------------*/\r
2888 /* Load a sector and check if it is an FAT boot sector */\r
2889 /*-----------------------------------------------------------------------*/\r
2890 \r
2891 static\r
2892 BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */\r
2893 FATFS* fs, /* File system object */\r
2894 DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */\r
2895 )\r
2896 {\r
2897 fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */\r
2898 if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */\r
2899 \r
2900 if (ld_word(&fs->win[BS_55AA]) != 0xAA55) return 3; /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */\r
2901 \r
2902 if ((ld_dword(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */\r
2903 if ((ld_dword(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */\r
2904 #if _FS_EXFAT\r
2905 if (!mem_cmp(&fs->win[BS_OEMName], "EXFAT ", 8)) return 1;\r
2906 #endif\r
2907 return 2;\r
2908 }\r
2909 \r
2910 \r
2911 \r
2912 \r
2913 /*-----------------------------------------------------------------------*/\r
2914 /* Find logical drive and check if the volume is mounted */\r
2915 /*-----------------------------------------------------------------------*/\r
2916 \r
2917 static\r
2918 FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */\r
2919 const TCHAR** path, /* Pointer to pointer to the path name (drive number) */\r
2920 FATFS** rfs, /* Pointer to pointer to the found file system object */\r
2921 BYTE mode /* !=0: Check write protection for write access */\r
2922 )\r
2923 {\r
2924 BYTE fmt, *pt;\r
2925 int vol;\r
2926 DSTATUS stat;\r
2927 DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4];\r
2928 WORD nrsv;\r
2929 FATFS *fs;\r
2930 UINT i;\r
2931 \r
2932 \r
2933 /* Get logical drive number from the path name */\r
2934 *rfs = 0;\r
2935 vol = get_ldnumber(path);\r
2936 if (vol < 0) return FR_INVALID_DRIVE;\r
2937 \r
2938 /* Check if the file system object is valid or not */\r
2939 fs = FatFs[vol]; /* Get pointer to the file system object */\r
2940 if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */\r
2941 \r
2942 ENTER_FF(fs); /* Lock the volume */\r
2943 *rfs = fs; /* Return pointer to the file system object */\r
2944 \r
2945 mode &= ~FA_READ; /* Desired access mode, write access or not */\r
2946 if (fs->fs_type) { /* If the volume has been mounted */\r
2947 stat = disk_status(fs->drv);\r
2948 if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */\r
2949 if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */\r
2950 return FR_WRITE_PROTECTED;\r
2951 }\r
2952 return FR_OK; /* The file system object is valid */\r
2953 }\r
2954 }\r
2955 \r
2956 /* The file system object is not valid. */\r
2957 /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */\r
2958 \r
2959 fs->fs_type = 0; /* Clear the file system object */\r
2960 fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */\r
2961 stat = disk_initialize(fs->drv); /* Initialize the physical drive */\r
2962 if (stat & STA_NOINIT) { /* Check if the initialization succeeded */\r
2963 return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */\r
2964 }\r
2965 if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */\r
2966 return FR_WRITE_PROTECTED;\r
2967 }\r
2968 #if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */\r
2969 if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK\r
2970 || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR;\r
2971 #endif\r
2972 /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */\r
2973 bsect = 0;\r
2974 fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */\r
2975 if (fmt == 2 || (fmt < 2 && LD2PT(vol))) { /* Not an FAT boot sector or forced partition number */\r
2976 for (i = 0; i < 4; i++) { /* Get partition offset */\r
2977 pt = fs->win + MBR_Table + i * SZ_PTE;\r
2978 br[i] = pt[4] ? ld_dword(&pt[8]) : 0;\r
2979 }\r
2980 i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */\r
2981 if (i) i--;\r
2982 do { /* Find an FAT volume */\r
2983 bsect = br[i];\r
2984 fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */\r
2985 } while (!LD2PT(vol) && fmt >= 2 && ++i < 4);\r
2986 }\r
2987 if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */\r
2988 if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */\r
2989 \r
2990 /* An FAT volume is found. Following code initializes the file system object */\r
2991 \r
2992 #if _FS_EXFAT\r
2993 if (fmt == 1) {\r
2994 QWORD maxlba;\r
2995 \r
2996 for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && !fs->win[i]; i++) ; /* Check zero filler */\r
2997 if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;\r
2998 \r
2999 if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */\r
3000 \r
3001 if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) /* (BPB_BytsPerSecEx must be equal to the physical sector size) */\r
3002 return FR_NO_FILESYSTEM;\r
3003 \r
3004 maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Number of sectors on the volume */\r
3005 if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */\r
3006 \r
3007 fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */\r
3008 \r
3009 fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */\r
3010 if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Must be 1) */\r
3011 \r
3012 fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */\r
3013 if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */\r
3014 \r
3015 nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */\r
3016 fs->n_fatent = nclst + 2;\r
3017 if (fs->n_fatent >= 0x80000000) return FR_NO_FILESYSTEM; /* (Must be <= 0x7FFFFFFF) */\r
3018 \r
3019 /* Boundaries and Limits */\r
3020 fs->volbase = bsect;\r
3021 fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);\r
3022 fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);\r
3023 if (maxlba < fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */\r
3024 fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);\r
3025 \r
3026 /* Check if bitmap location is in assumption (at the first cluster) */\r
3027 if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR;\r
3028 for (i = 0; i < SS(fs); i += SZDIRE) {\r
3029 if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */\r
3030 }\r
3031 if (i == SS(fs)) return FR_NO_FILESYSTEM;\r
3032 #if !_FS_READONLY\r
3033 fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */\r
3034 #endif\r
3035 #if _USE_LFN == 1\r
3036 fs->dirbuf = DirBuf; /* Static directory block working buuffer */\r
3037 #endif\r
3038 fmt = FS_EXFAT; /* FAT sub-type */\r
3039 } else\r
3040 #endif\r
3041 {\r
3042 if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */\r
3043 \r
3044 fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */\r
3045 if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);\r
3046 fs->fsize = fasize;\r
3047 \r
3048 fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */\r
3049 if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */\r
3050 fasize *= fs->n_fats; /* Number of sectors for FAT area */\r
3051 \r
3052 fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */\r
3053 if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */\r
3054 \r
3055 fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */\r
3056 if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */\r
3057 \r
3058 tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */\r
3059 if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);\r
3060 \r
3061 nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */\r
3062 if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */\r
3063 \r
3064 /* Determine the FAT sub type */\r
3065 sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */\r
3066 if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */\r
3067 nclst = (tsect - sysect) / fs->csize; /* Number of clusters */\r
3068 if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */\r
3069 fmt = FS_FAT12;\r
3070 if (nclst >= MIN_FAT16) fmt = FS_FAT16;\r
3071 if (nclst >= MIN_FAT32) fmt = FS_FAT32;\r
3072 \r
3073 /* Boundaries and Limits */\r
3074 fs->n_fatent = nclst + 2; /* Number of FAT entries */\r
3075 fs->volbase = bsect; /* Volume start sector */\r
3076 fs->fatbase = bsect + nrsv; /* FAT start sector */\r
3077 fs->database = bsect + sysect; /* Data start sector */\r
3078 if (fmt == FS_FAT32) {\r
3079 if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */\r
3080 if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */\r
3081 fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */\r
3082 szbfat = fs->n_fatent * 4; /* (Needed FAT size) */\r
3083 } else {\r
3084 if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM;/* (BPB_RootEntCnt must not be 0) */\r
3085 fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */\r
3086 szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */\r
3087 fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);\r
3088 }\r
3089 if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */\r
3090 \r
3091 #if !_FS_READONLY\r
3092 /* Initialize cluster allocation information */\r
3093 fs->last_clst = fs->free_clst = 0xFFFFFFFF;\r
3094 \r
3095 /* Get FSINFO if available */\r
3096 fs->fsi_flag = 0x80;\r
3097 #if (_FS_NOFSINFO & 3) != 3\r
3098 if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */\r
3099 && ld_word(fs->win + BPB_FSInfo32) == 1\r
3100 && move_window(fs, bsect + 1) == FR_OK)\r
3101 {\r
3102 fs->fsi_flag = 0;\r
3103 if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */\r
3104 && ld_dword(fs->win + FSI_LeadSig) == 0x41615252\r
3105 && ld_dword(fs->win + FSI_StrucSig) == 0x61417272)\r
3106 {\r
3107 #if (_FS_NOFSINFO & 1) == 0\r
3108 fs->free_clst = ld_dword(fs->win + FSI_Free_Count);\r
3109 #endif\r
3110 #if (_FS_NOFSINFO & 2) == 0\r
3111 fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);\r
3112 #endif\r
3113 }\r
3114 }\r
3115 #endif\r
3116 #endif\r
3117 }\r
3118 \r
3119 fs->fs_type = fmt; /* FAT sub-type */\r
3120 fs->id = ++Fsid; /* File system mount ID */\r
3121 #if _FS_RPATH != 0\r
3122 fs->cdir = 0; /* Initialize current directory */\r
3123 #endif\r
3124 #if _FS_LOCK != 0 /* Clear file lock semaphores */\r
3125 clear_lock(fs);\r
3126 #endif\r
3127 return FR_OK;\r
3128 }\r
3129 \r
3130 \r
3131 \r
3132 \r
3133 /*-----------------------------------------------------------------------*/\r
3134 /* Check if the file/directory object is valid or not */\r
3135 /*-----------------------------------------------------------------------*/\r
3136 \r
3137 static\r
3138 FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */\r
3139 void* dfp, /* Pointer to the FIL/DIR object to check validity */\r
3140 FATFS** fs /* Pointer to pointer to the owner file system object to return */\r
3141 )\r
3142 {\r
3143 _FDID *obj = (_FDID*)dfp; /* Assuming .obj in the FIL/DIR is the first member */\r
3144 FRESULT res;\r
3145 \r
3146 \r
3147 if (!dfp || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) {\r
3148 *fs = 0; /* The object is invalid */\r
3149 res = FR_INVALID_OBJECT;\r
3150 } else {\r
3151 *fs = obj->fs; /* Owner file sytem object */\r
3152 ENTER_FF(obj->fs); /* Lock file system */\r
3153 res = FR_OK;\r
3154 }\r
3155 return res;\r
3156 }\r
3157 \r
3158 \r
3159 \r
3160 \r
3161 /*---------------------------------------------------------------------------\r
3162 \r
3163 Public Functions (FatFs API)\r
3164 \r
3165 ----------------------------------------------------------------------------*/\r
3166 \r
3167 \r
3168 \r
3169 /*-----------------------------------------------------------------------*/\r
3170 /* Mount/Unmount a Logical Drive */\r
3171 /*-----------------------------------------------------------------------*/\r
3172 \r
3173 FRESULT f_mount (\r
3174 FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/\r
3175 const TCHAR* path, /* Logical drive number to be mounted/unmounted */\r
3176 BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */\r
3177 )\r
3178 {\r
3179 FATFS *cfs;\r
3180 int vol;\r
3181 FRESULT res;\r
3182 const TCHAR *rp = path;\r
3183 \r
3184 \r
3185 vol = get_ldnumber(&rp);\r
3186 if (vol < 0) return FR_INVALID_DRIVE;\r
3187 cfs = FatFs[vol]; /* Pointer to fs object */\r
3188 \r
3189 if (cfs) {\r
3190 #if _FS_LOCK != 0\r
3191 clear_lock(cfs);\r
3192 #endif\r
3193 #if _FS_REENTRANT /* Discard sync object of the current volume */\r
3194 if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;\r
3195 #endif\r
3196 cfs->fs_type = 0; /* Clear old fs object */\r
3197 }\r
3198 \r
3199 if (fs) {\r
3200 fs->fs_type = 0; /* Clear new fs object */\r
3201 #if _FS_REENTRANT /* Create sync object for the new volume */\r
3202 if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;\r
3203 #endif\r
3204 }\r
3205 FatFs[vol] = fs; /* Register new fs object */\r
3206 \r
3207 if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */\r
3208 \r
3209 res = find_volume(&path, &fs, 0); /* Force mounted the volume */\r
3210 LEAVE_FF(fs, res);\r
3211 }\r
3212 \r
3213 \r
3214 \r
3215 \r
3216 /*-----------------------------------------------------------------------*/\r
3217 /* Open or Create a File */\r
3218 /*-----------------------------------------------------------------------*/\r
3219 \r
3220 FRESULT f_open (\r
3221 FIL* fp, /* Pointer to the blank file object */\r
3222 const TCHAR* path, /* Pointer to the file name */\r
3223 BYTE mode /* Access mode and file open mode flags */\r
3224 )\r
3225 {\r
3226 FRESULT res;\r
3227 DIR dj;\r
3228 FATFS *fs;\r
3229 #if !_FS_READONLY\r
3230 DWORD dw, cl;\r
3231 #endif\r
3232 DEF_NAMBUF;\r
3233 \r
3234 \r
3235 if (!fp) return FR_INVALID_OBJECT;\r
3236 fp->obj.fs = 0; /* Clear file object */\r
3237 \r
3238 /* Get logical drive number */\r
3239 mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;\r
3240 res = find_volume(&path, &fs, mode);\r
3241 if (res == FR_OK) {\r
3242 dj.obj.fs = fs;\r
3243 INIT_NAMBUF(dj);\r
3244 res = follow_path(&dj, path); /* Follow the file path */\r
3245 #if !_FS_READONLY /* R/W configuration */\r
3246 if (res == FR_OK) {\r
3247 if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */\r
3248 res = FR_INVALID_NAME;\r
3249 }\r
3250 #if _FS_LOCK != 0\r
3251 else {\r
3252 res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);\r
3253 }\r
3254 #endif\r
3255 }\r
3256 /* Create or Open a file */\r
3257 if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {\r
3258 if (res != FR_OK) { /* No file, create new */\r
3259 if (res == FR_NO_FILE) /* There is no file to open, create a new entry */\r
3260 #if _FS_LOCK != 0\r
3261 res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;\r
3262 #else\r
3263 res = dir_register(&dj);\r
3264 #endif\r
3265 mode |= FA_CREATE_ALWAYS; /* File is created */\r
3266 }\r
3267 else { /* Any object is already existing */\r
3268 if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */\r
3269 res = FR_DENIED;\r
3270 } else {\r
3271 if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */\r
3272 }\r
3273 }\r
3274 if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */\r
3275 dw = GET_FATTIME();\r
3276 #if _FS_EXFAT\r
3277 if (fs->fs_type == FS_EXFAT) {\r
3278 /* Get current allocation info */\r
3279 fp->obj.fs = fs;\r
3280 fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus);\r
3281 fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\r
3282 fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;\r
3283 /* Initialize directory entry block */\r
3284 st_dword(fs->dirbuf + XDIR_CrtTime, dw); /* Set created time */\r
3285 fs->dirbuf[XDIR_CrtTime10] = 0;\r
3286 st_dword(fs->dirbuf + XDIR_ModTime, dw); /* Set modified time */\r
3287 fs->dirbuf[XDIR_ModTime10] = 0;\r
3288 fs->dirbuf[XDIR_Attr] = AM_ARC; /* Reset attribute */\r
3289 st_dword(fs->dirbuf + XDIR_FstClus, 0); /* Reset file allocation info */\r
3290 st_qword(fs->dirbuf + XDIR_FileSize, 0);\r
3291 st_qword(fs->dirbuf + XDIR_ValidFileSize, 0);\r
3292 fs->dirbuf[XDIR_GenFlags] = 1;\r
3293 res = store_xdir(&dj);\r
3294 if (res == FR_OK && fp->obj.sclust) { /* Remove the cluster chain if exist */\r
3295 res = remove_chain(&fp->obj, fp->obj.sclust, 0);\r
3296 fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */\r
3297 }\r
3298 } else\r
3299 #endif\r
3300 {\r
3301 /* Clean directory info */\r
3302 st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */\r
3303 st_dword(dj.dir + DIR_WrtTime, dw); /* Set modified time */\r
3304 dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */\r
3305 cl = ld_clust(fs, dj.dir); /* Get cluster chain */\r
3306 st_clust(fs, dj.dir, 0); /* Reset file allocation info */\r
3307 st_dword(dj.dir + DIR_FileSize, 0);\r
3308 fs->wflag = 1;\r
3309 \r
3310 if (cl) { /* Remove the cluster chain if exist */\r
3311 dw = fs->winsect;\r
3312 res = remove_chain(&dj.obj, cl, 0);\r
3313 if (res == FR_OK) {\r
3314 res = move_window(fs, dw);\r
3315 fs->last_clst = cl - 1; /* Reuse the cluster hole */\r
3316 }\r
3317 }\r
3318 }\r
3319 }\r
3320 }\r
3321 else { /* Open an existing file */\r
3322 if (res == FR_OK) { /* Following succeeded */\r
3323 if (dj.obj.attr & AM_DIR) { /* It is a directory */\r
3324 res = FR_NO_FILE;\r
3325 } else {\r
3326 if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */\r
3327 res = FR_DENIED;\r
3328 }\r
3329 }\r
3330 }\r
3331 }\r
3332 if (res == FR_OK) {\r
3333 if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */\r
3334 mode |= _FA_MODIFIED;\r
3335 fp->dir_sect = fs->winsect; /* Pointer to the directory entry */\r
3336 fp->dir_ptr = dj.dir;\r
3337 #if _FS_LOCK != 0\r
3338 fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);\r
3339 if (!fp->obj.lockid) res = FR_INT_ERR;\r
3340 #endif\r
3341 }\r
3342 #else /* R/O configuration */\r
3343 if (res == FR_OK) {\r
3344 if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */\r
3345 res = FR_INVALID_NAME;\r
3346 } else {\r
3347 if (dj.obj.attr & AM_DIR) { /* It is a directory */\r
3348 res = FR_NO_FILE;\r
3349 }\r
3350 }\r
3351 }\r
3352 #endif\r
3353 \r
3354 if (res == FR_OK) {\r
3355 #if _FS_EXFAT\r
3356 if (fs->fs_type == FS_EXFAT) {\r
3357 fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get allocation info */\r
3358 fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\r
3359 fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;\r
3360 fp->obj.c_scl = dj.obj.sclust;\r
3361 fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;\r
3362 fp->obj.c_ofs = dj.blk_ofs;\r
3363 } else\r
3364 #endif\r
3365 {\r
3366 fp->obj.sclust = ld_clust(fs, dj.dir); /* Get allocation info */\r
3367 fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);\r
3368 }\r
3369 #if _USE_FASTSEEK\r
3370 fp->cltbl = 0; /* Normal seek mode */\r
3371 #endif\r
3372 fp->err = 0; /* Clear error flag */\r
3373 fp->fptr = 0; /* Set file pointer */\r
3374 fp->sect = 0; /* Invalidate current data sector */\r
3375 fp->flag = mode; /* File access mode */\r
3376 fp->obj.fs = fs; /* Validate the file object */\r
3377 fp->obj.id = fs->id;\r
3378 }\r
3379 \r
3380 FREE_NAMBUF();\r
3381 }\r
3382 \r
3383 LEAVE_FF(dj.obj.fs, res);\r
3384 }\r
3385 \r
3386 \r
3387 \r
3388 \r
3389 /*-----------------------------------------------------------------------*/\r
3390 /* Read File */\r
3391 /*-----------------------------------------------------------------------*/\r
3392 \r
3393 FRESULT f_read (\r
3394 FIL* fp, /* Pointer to the file object */\r
3395 void* buff, /* Pointer to data buffer */\r
3396 UINT btr, /* Number of bytes to read */\r
3397 UINT* br /* Pointer to number of bytes read */\r
3398 )\r
3399 {\r
3400 FRESULT res;\r
3401 FATFS *fs;\r
3402 DWORD clst, sect;\r
3403 FSIZE_t remain;\r
3404 UINT rcnt, cc, csect;\r
3405 BYTE *rbuff = (BYTE*)buff;\r
3406 \r
3407 \r
3408 *br = 0; /* Clear read byte counter */\r
3409 res = validate(fp, &fs);\r
3410 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */\r
3411 if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */\r
3412 remain = fp->obj.objsize - fp->fptr;\r
3413 if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */\r
3414 \r
3415 for ( ; btr; /* Repeat until all data read */\r
3416 rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {\r
3417 if ((fp->fptr % SS(fs)) == 0) { /* On the sector boundary? */\r
3418 csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */\r
3419 if (csect == 0) { /* On the cluster boundary? */\r
3420 if (fp->fptr == 0) { /* On the top of the file? */\r
3421 clst = fp->obj.sclust; /* Follow cluster chain from the origin */\r
3422 } else { /* Middle or end of the file */\r
3423 #if _USE_FASTSEEK\r
3424 if (fp->cltbl) {\r
3425 clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */\r
3426 } else\r
3427 #endif\r
3428 {\r
3429 clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */\r
3430 }\r
3431 }\r
3432 if (clst < 2) ABORT(fs, FR_INT_ERR);\r
3433 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\r
3434 fp->clust = clst; /* Update current cluster */\r
3435 }\r
3436 sect = clust2sect(fs, fp->clust); /* Get current sector */\r
3437 if (!sect) ABORT(fs, FR_INT_ERR);\r
3438 sect += csect;\r
3439 cc = btr / SS(fs); /* When remaining bytes >= sector size, */\r
3440 if (cc) { /* Read maximum contiguous sectors directly */\r
3441 if (csect + cc > fs->csize) { /* Clip at cluster boundary */\r
3442 cc = fs->csize - csect;\r
3443 }\r
3444 if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) {\r
3445 ABORT(fs, FR_DISK_ERR);\r
3446 }\r
3447 #if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */\r
3448 #if _FS_TINY\r
3449 if (fs->wflag && fs->winsect - sect < cc) {\r
3450 mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));\r
3451 }\r
3452 #else\r
3453 if ((fp->flag & _FA_DIRTY) && fp->sect - sect < cc) {\r
3454 mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));\r
3455 }\r
3456 #endif\r
3457 #endif\r
3458 rcnt = SS(fs) * cc; /* Number of bytes transferred */\r
3459 continue;\r
3460 }\r
3461 #if !_FS_TINY\r
3462 if (fp->sect != sect) { /* Load data sector if not in cache */\r
3463 #if !_FS_READONLY\r
3464 if (fp->flag & _FA_DIRTY) { /* Write-back dirty sector cache */\r
3465 if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {\r
3466 ABORT(fs, FR_DISK_ERR);\r
3467 }\r
3468 fp->flag &= ~_FA_DIRTY;\r
3469 }\r
3470 #endif\r
3471 if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { /* Fill sector cache */\r
3472 ABORT(fs, FR_DISK_ERR);\r
3473 }\r
3474 }\r
3475 #endif\r
3476 fp->sect = sect;\r
3477 }\r
3478 rcnt = SS(fs) - ((UINT)fp->fptr % SS(fs)); /* Get partial sector data from sector buffer */\r
3479 if (rcnt > btr) rcnt = btr;\r
3480 #if _FS_TINY\r
3481 if (move_window(fs, fp->sect) != FR_OK) { /* Move sector window */\r
3482 ABORT(fs, FR_DISK_ERR);\r
3483 }\r
3484 mem_cpy(rbuff, &fs->win[fp->fptr % SS(fs)], rcnt); /* Pick partial sector */\r
3485 #else\r
3486 mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fs)], rcnt); /* Pick partial sector */\r
3487 #endif\r
3488 }\r
3489 \r
3490 LEAVE_FF(fs, FR_OK);\r
3491 }\r
3492 \r
3493 \r
3494 \r
3495 \r
3496 #if !_FS_READONLY\r
3497 /*-----------------------------------------------------------------------*/\r
3498 /* Write File */\r
3499 /*-----------------------------------------------------------------------*/\r
3500 \r
3501 FRESULT f_write (\r
3502 FIL* fp, /* Pointer to the file object */\r
3503 const void* buff, /* Pointer to the data to be written */\r
3504 UINT btw, /* Number of bytes to write */\r
3505 UINT* bw /* Pointer to number of bytes written */\r
3506 )\r
3507 {\r
3508 FRESULT res;\r
3509 FATFS *fs;\r
3510 DWORD clst, sect;\r
3511 UINT wcnt, cc, csect;\r
3512 const BYTE *wbuff = (const BYTE*)buff;\r
3513 \r
3514 \r
3515 *bw = 0; /* Clear write byte counter */\r
3516 res = validate(fp, &fs);\r
3517 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */\r
3518 if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */\r
3519 \r
3520 /* Check fptr wrap-around (file size cannot exceed the limit on each FAT specs) */\r
3521 if ((_FS_EXFAT && fs->fs_type == FS_EXFAT && fp->fptr + btw < fp->fptr)\r
3522 || (DWORD)fp->fptr + btw < (DWORD)fp->fptr) {\r
3523 btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);\r
3524 }\r
3525 \r
3526 for ( ; btw; /* Repeat until all data written */\r
3527 wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) {\r
3528 if ((fp->fptr % SS(fs)) == 0) { /* On the sector boundary? */\r
3529 csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */\r
3530 if (csect == 0) { /* On the cluster boundary? */\r
3531 if (fp->fptr == 0) { /* On the top of the file? */\r
3532 clst = fp->obj.sclust; /* Follow from the origin */\r
3533 if (clst == 0) { /* If no cluster is allocated, */\r
3534 clst = create_chain(&fp->obj, 0); /* create a new cluster chain */\r
3535 }\r
3536 } else { /* On the middle or end of the file */\r
3537 #if _USE_FASTSEEK\r
3538 if (fp->cltbl) {\r
3539 clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */\r
3540 } else\r
3541 #endif\r
3542 {\r
3543 clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */\r
3544 }\r
3545 }\r
3546 if (clst == 0) break; /* Could not allocate a new cluster (disk full) */\r
3547 if (clst == 1) ABORT(fs, FR_INT_ERR);\r
3548 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\r
3549 fp->clust = clst; /* Update current cluster */\r
3550 if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */\r
3551 }\r
3552 #if _FS_TINY\r
3553 if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) { /* Write-back sector cache */\r
3554 ABORT(fs, FR_DISK_ERR);\r
3555 }\r
3556 #else\r
3557 if (fp->flag & _FA_DIRTY) { /* Write-back sector cache */\r
3558 if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {\r
3559 ABORT(fs, FR_DISK_ERR);\r
3560 }\r
3561 fp->flag &= ~_FA_DIRTY;\r
3562 }\r
3563 #endif\r
3564 sect = clust2sect(fs, fp->clust); /* Get current sector */\r
3565 if (!sect) ABORT(fs, FR_INT_ERR);\r
3566 sect += csect;\r
3567 cc = btw / SS(fs); /* When remaining bytes >= sector size, */\r
3568 if (cc) { /* Write maximum contiguous sectors directly */\r
3569 if (csect + cc > fs->csize) { /* Clip at cluster boundary */\r
3570 cc = fs->csize - csect;\r
3571 }\r
3572 if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) {\r
3573 ABORT(fs, FR_DISK_ERR);\r
3574 }\r
3575 #if _FS_MINIMIZE <= 2\r
3576 #if _FS_TINY\r
3577 if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */\r
3578 mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));\r
3579 fs->wflag = 0;\r
3580 }\r
3581 #else\r
3582 if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */\r
3583 mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));\r
3584 fp->flag &= ~_FA_DIRTY;\r
3585 }\r
3586 #endif\r
3587 #endif\r
3588 wcnt = SS(fs) * cc; /* Number of bytes transferred */\r
3589 continue;\r
3590 }\r
3591 #if _FS_TINY\r
3592 if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling at growing edge */\r
3593 if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);\r
3594 fs->winsect = sect;\r
3595 }\r
3596 #else\r
3597 if (fp->sect != sect) { /* Fill sector cache with file data */\r
3598 if (fp->fptr < fp->obj.objsize &&\r
3599 disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) {\r
3600 ABORT(fs, FR_DISK_ERR);\r
3601 }\r
3602 }\r
3603 #endif\r
3604 fp->sect = sect;\r
3605 }\r
3606 wcnt = SS(fs) - ((UINT)fp->fptr % SS(fs)); /* Put partial sector into file I/O buffer */\r
3607 if (wcnt > btw) wcnt = btw;\r
3608 #if _FS_TINY\r
3609 if (move_window(fs, fp->sect) != FR_OK) { /* Move sector window */\r
3610 ABORT(fs, FR_DISK_ERR);\r
3611 }\r
3612 mem_cpy(&fs->win[fp->fptr % SS(fs)], wbuff, wcnt); /* Fit partial sector */\r
3613 fs->wflag = 1;\r
3614 #else\r
3615 mem_cpy(&fp->buf[fp->fptr % SS(fs)], wbuff, wcnt); /* Fit partial sector */\r
3616 fp->flag |= _FA_DIRTY;\r
3617 #endif\r
3618 }\r
3619 \r
3620 fp->flag |= _FA_MODIFIED; /* Set file change flag */\r
3621 \r
3622 LEAVE_FF(fs, FR_OK);\r
3623 }\r
3624 \r
3625 \r
3626 \r
3627 \r
3628 /*-----------------------------------------------------------------------*/\r
3629 /* Synchronize the File */\r
3630 /*-----------------------------------------------------------------------*/\r
3631 \r
3632 FRESULT f_sync (\r
3633 FIL* fp /* Pointer to the file object */\r
3634 )\r
3635 {\r
3636 FRESULT res;\r
3637 FATFS *fs;\r
3638 DWORD tm;\r
3639 BYTE *dir;\r
3640 DEF_DIRBUF;\r
3641 \r
3642 \r
3643 res = validate(fp, &fs); /* Check validity of the object */\r
3644 if (res == FR_OK) {\r
3645 if (fp->flag & _FA_MODIFIED) { /* Is there any change to the file? */\r
3646 #if !_FS_TINY\r
3647 if (fp->flag & _FA_DIRTY) { /* Write-back cached data if needed */\r
3648 if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {\r
3649 LEAVE_FF(fs, FR_DISK_ERR);\r
3650 }\r
3651 fp->flag &= ~_FA_DIRTY;\r
3652 }\r
3653 #endif\r
3654 /* Update the directory entry */\r
3655 tm = GET_FATTIME(); /* Modified time */\r
3656 #if _FS_EXFAT\r
3657 if (fs->fs_type == FS_EXFAT) {\r
3658 res = fill_fat_chain(&fp->obj); /* Create FAT chain if needed */\r
3659 if (res == FR_OK) {\r
3660 DIR dj;\r
3661 \r
3662 INIT_DIRBUF(fs);\r
3663 res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */\r
3664 if (res == FR_OK) {\r
3665 fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */\r
3666 fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */\r
3667 st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust);\r
3668 st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize);\r
3669 st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize);\r
3670 st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */\r
3671 fs->dirbuf[XDIR_ModTime10] = 0;\r
3672 st_dword(fs->dirbuf + XDIR_AccTime, 0);\r
3673 res = store_xdir(&dj); /* Restore it to the directory */\r
3674 if (res == FR_OK) {\r
3675 res = sync_fs(fs);\r
3676 fp->flag &= ~_FA_MODIFIED;\r
3677 }\r
3678 }\r
3679 FREE_DIRBUF();\r
3680 }\r
3681 } else\r
3682 #endif\r
3683 {\r
3684 res = move_window(fs, fp->dir_sect);\r
3685 if (res == FR_OK) {\r
3686 dir = fp->dir_ptr;\r
3687 dir[DIR_Attr] |= AM_ARC; /* Set archive bit */\r
3688 st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */\r
3689 st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */\r
3690 st_dword(dir + DIR_WrtTime, tm); /* Update modified time */\r
3691 st_word(dir + DIR_LstAccDate, 0);\r
3692 fs->wflag = 1;\r
3693 res = sync_fs(fs); /* Restore it to the directory */\r
3694 fp->flag &= ~_FA_MODIFIED;\r
3695 }\r
3696 }\r
3697 }\r
3698 }\r
3699 \r
3700 LEAVE_FF(fs, res);\r
3701 }\r
3702 \r
3703 #endif /* !_FS_READONLY */\r
3704 \r
3705 \r
3706 \r
3707 \r
3708 /*-----------------------------------------------------------------------*/\r
3709 /* Close File */\r
3710 /*-----------------------------------------------------------------------*/\r
3711 \r
3712 FRESULT f_close (\r
3713 FIL* fp /* Pointer to the file object to be closed */\r
3714 )\r
3715 {\r
3716 FRESULT res;\r
3717 FATFS *fs;\r
3718 \r
3719 #if !_FS_READONLY\r
3720 res = f_sync(fp); /* Flush cached data */\r
3721 if (res == FR_OK)\r
3722 #endif\r
3723 {\r
3724 res = validate(fp, &fs); /* Lock volume */\r
3725 if (res == FR_OK) {\r
3726 #if _FS_LOCK != 0\r
3727 res = dec_lock(fp->obj.lockid); /* Decrement file open counter */\r
3728 if (res == FR_OK)\r
3729 #endif\r
3730 {\r
3731 fp->obj.fs = 0; /* Invalidate file object */\r
3732 }\r
3733 #if _FS_REENTRANT\r
3734 unlock_fs(fs, FR_OK); /* Unlock volume */\r
3735 #endif\r
3736 }\r
3737 }\r
3738 return res;\r
3739 }\r
3740 \r
3741 \r
3742 \r
3743 \r
3744 /*-----------------------------------------------------------------------*/\r
3745 /* Change Current Directory or Current Drive, Get Current Directory */\r
3746 /*-----------------------------------------------------------------------*/\r
3747 \r
3748 #if _FS_RPATH >= 1\r
3749 #if _VOLUMES >= 2\r
3750 FRESULT f_chdrive (\r
3751 const TCHAR* path /* Drive number */\r
3752 )\r
3753 {\r
3754 int vol;\r
3755 \r
3756 \r
3757 vol = get_ldnumber(&path);\r
3758 if (vol < 0) return FR_INVALID_DRIVE;\r
3759 \r
3760 CurrVol = (BYTE)vol;\r
3761 \r
3762 return FR_OK;\r
3763 }\r
3764 #endif\r
3765 \r
3766 \r
3767 FRESULT f_chdir (\r
3768 const TCHAR* path /* Pointer to the directory path */\r
3769 )\r
3770 {\r
3771 FRESULT res;\r
3772 DIR dj;\r
3773 FATFS *fs;\r
3774 DEF_NAMBUF;\r
3775 \r
3776 /* Get logical drive number */\r
3777 res = find_volume(&path, &fs, 0);\r
3778 if (res == FR_OK) {\r
3779 dj.obj.fs = fs;\r
3780 INIT_NAMBUF(dj);\r
3781 res = follow_path(&dj, path); /* Follow the path */\r
3782 if (res == FR_OK) { /* Follow completed */\r
3783 if (dj.fn[NSFLAG] & NS_NONAME) {\r
3784 fs->cdir = dj.obj.sclust; /* It is the start directory itself */\r
3785 #if _FS_EXFAT\r
3786 if (fs->fs_type == FS_EXFAT) {\r
3787 fs->cdc_scl = dj.obj.c_scl;\r
3788 fs->cdc_size = dj.obj.c_size;\r
3789 fs->cdc_ofs = dj.obj.c_ofs;\r
3790 }\r
3791 #endif\r
3792 } else {\r
3793 if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */\r
3794 #if _FS_EXFAT\r
3795 if (fs->fs_type == FS_EXFAT) {\r
3796 fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */\r
3797 fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */\r
3798 fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;\r
3799 fs->cdc_ofs = dj.blk_ofs;\r
3800 } else\r
3801 #endif\r
3802 {\r
3803 fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */\r
3804 }\r
3805 } else {\r
3806 res = FR_NO_PATH; /* Reached but a file */\r
3807 }\r
3808 }\r
3809 }\r
3810 FREE_NAMBUF();\r
3811 if (res == FR_NO_FILE) res = FR_NO_PATH;\r
3812 }\r
3813 \r
3814 LEAVE_FF(fs, res);\r
3815 }\r
3816 \r
3817 \r
3818 #if _FS_RPATH >= 2\r
3819 FRESULT f_getcwd (\r
3820 TCHAR* buff, /* Pointer to the directory path */\r
3821 UINT len /* Size of path */\r
3822 )\r
3823 {\r
3824 FRESULT res;\r
3825 DIR dj;\r
3826 FATFS *fs;\r
3827 UINT i, n;\r
3828 DWORD ccl;\r
3829 TCHAR *tp;\r
3830 FILINFO fno;\r
3831 DEF_NAMBUF;\r
3832 \r
3833 \r
3834 *buff = 0;\r
3835 /* Get logical drive number */\r
3836 res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */\r
3837 if (res == FR_OK) {\r
3838 dj.obj.fs = fs;\r
3839 INIT_NAMBUF(dj);\r
3840 i = len; /* Bottom of buffer (directory stack base) */\r
3841 if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */\r
3842 dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */\r
3843 while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */\r
3844 res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */\r
3845 if (res != FR_OK) break;\r
3846 res = move_window(fs, dj.sect);\r
3847 if (res != FR_OK) break;\r
3848 dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */\r
3849 res = dir_sdi(&dj, 0);\r
3850 if (res != FR_OK) break;\r
3851 do { /* Find the entry links to the child directory */\r
3852 res = dir_read(&dj, 0);\r
3853 if (res != FR_OK) break;\r
3854 if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */\r
3855 res = dir_next(&dj, 0);\r
3856 } while (res == FR_OK);\r
3857 if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */\r
3858 if (res != FR_OK) break;\r
3859 get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */\r
3860 for (n = 0; fno.fname[n]; n++) ;\r
3861 if (i < n + 3) {\r
3862 res = FR_NOT_ENOUGH_CORE; break;\r
3863 }\r
3864 while (n) buff[--i] = fno.fname[--n];\r
3865 buff[--i] = '/';\r
3866 }\r
3867 }\r
3868 tp = buff;\r
3869 if (res == FR_OK) {\r
3870 #if _VOLUMES >= 2\r
3871 *tp++ = '0' + CurrVol; /* Put drive number */\r
3872 *tp++ = ':';\r
3873 #endif\r
3874 if (i == len) { /* Root-directory */\r
3875 *tp++ = '/';\r
3876 } else { /* Sub-directroy */\r
3877 do /* Add stacked path str */\r
3878 *tp++ = buff[i++];\r
3879 while (i < len);\r
3880 }\r
3881 }\r
3882 *tp = 0;\r
3883 FREE_NAMBUF();\r
3884 }\r
3885 \r
3886 LEAVE_FF(fs, res);\r
3887 }\r
3888 #endif /* _FS_RPATH >= 2 */\r
3889 #endif /* _FS_RPATH >= 1 */\r
3890 \r
3891 \r
3892 \r
3893 #if _FS_MINIMIZE <= 2\r
3894 /*-----------------------------------------------------------------------*/\r
3895 /* Seek File R/W Pointer */\r
3896 /*-----------------------------------------------------------------------*/\r
3897 \r
3898 FRESULT f_lseek (\r
3899 FIL* fp, /* Pointer to the file object */\r
3900 FSIZE_t ofs /* File pointer from top of file */\r
3901 )\r
3902 {\r
3903 FRESULT res;\r
3904 FATFS *fs;\r
3905 DWORD clst, bcs, nsect;\r
3906 FSIZE_t ifptr;\r
3907 #if _USE_FASTSEEK\r
3908 DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;\r
3909 #endif\r
3910 \r
3911 res = validate(fp, &fs); /* Check validity of the object */\r
3912 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */\r
3913 #if _USE_FASTSEEK\r
3914 if (fp->cltbl) { /* Fast seek */\r
3915 if (ofs == CREATE_LINKMAP) { /* Create CLMT */\r
3916 tbl = fp->cltbl;\r
3917 tlen = *tbl++; ulen = 2; /* Given table size and required table size */\r
3918 cl = fp->sclust; /* Top of the chain */\r
3919 if (cl) {\r
3920 do {\r
3921 /* Get a fragment */\r
3922 tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */\r
3923 do {\r
3924 pcl = cl; ncl++;\r
3925 cl = get_fat(fs, cl);\r
3926 if (cl <= 1) ABORT(fs, FR_INT_ERR);\r
3927 if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\r
3928 } while (cl == pcl + 1);\r
3929 if (ulen <= tlen) { /* Store the length and top of the fragment */\r
3930 *tbl++ = ncl; *tbl++ = tcl;\r
3931 }\r
3932 } while (cl < fs->n_fatent); /* Repeat until end of chain */\r
3933 }\r
3934 *fp->cltbl = ulen; /* Number of items used */\r
3935 if (ulen <= tlen) {\r
3936 *tbl = 0; /* Terminate table */\r
3937 } else {\r
3938 res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */\r
3939 }\r
3940 } else { /* Fast seek */\r
3941 if (ofs > fp->fsize) { /* Clip offset at the file size */\r
3942 ofs = fp->fsize;\r
3943 }\r
3944 fp->fptr = ofs; /* Set file pointer */\r
3945 if (ofs) {\r
3946 fp->clust = clmt_clust(fp, ofs - 1);\r
3947 dsc = clust2sect(fs, fp->clust);\r
3948 if (!dsc) ABORT(fs, FR_INT_ERR);\r
3949 dsc += (ofs - 1) / SS(fs) & (fs->csize - 1);\r
3950 if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */\r
3951 #if !_FS_TINY\r
3952 #if !_FS_READONLY\r
3953 if (fp->flag & _FA_DIRTY) { /* Write-back dirty sector cache */\r
3954 if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {\r
3955 ABORT(fp, FR_DISK_ERR);\r
3956 }\r
3957 fp->flag &= ~_FA_DIRTY;\r
3958 }\r
3959 #endif\r
3960 if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) { /* Load current sector */\r
3961 ABORT(fs, FR_DISK_ERR);\r
3962 }\r
3963 #endif\r
3964 fp->sect = dsc;\r
3965 }\r
3966 }\r
3967 }\r
3968 } else\r
3969 #endif\r
3970 \r
3971 /* Normal Seek */\r
3972 {\r
3973 if (ofs > fp->obj.objsize /* In read-only mode, clip offset with the file size */\r
3974 #if !_FS_READONLY\r
3975 && !(fp->flag & FA_WRITE)\r
3976 #endif\r
3977 ) ofs = fp->obj.objsize;\r
3978 \r
3979 ifptr = fp->fptr;\r
3980 fp->fptr = nsect = 0;\r
3981 if (ofs) {\r
3982 bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */\r
3983 if (ifptr > 0 &&\r
3984 (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */\r
3985 fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */\r
3986 ofs -= fp->fptr;\r
3987 clst = fp->clust;\r
3988 } else { /* When seek to back cluster, */\r
3989 clst = fp->obj.sclust; /* start from the first cluster */\r
3990 #if !_FS_READONLY\r
3991 if (clst == 0) { /* If no cluster chain, create a new chain */\r
3992 clst = create_chain(&fp->obj, 0);\r
3993 if (clst == 1) ABORT(fs, FR_INT_ERR);\r
3994 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\r
3995 fp->obj.sclust = clst;\r
3996 }\r
3997 #endif\r
3998 fp->clust = clst;\r
3999 }\r
4000 if (clst != 0) {\r
4001 while (ofs > bcs) { /* Cluster following loop */\r
4002 #if !_FS_READONLY\r
4003 if (fp->flag & FA_WRITE) { /* Check if in write mode or not */\r
4004 clst = create_chain(&fp->obj, clst); /* Force stretch if in write mode */\r
4005 if (clst == 0) { /* When disk gets full, clip file size */\r
4006 ofs = bcs; break;\r
4007 }\r
4008 } else\r
4009 #endif\r
4010 clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */\r
4011 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\r
4012 if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);\r
4013 fp->clust = clst;\r
4014 fp->fptr += bcs;\r
4015 ofs -= bcs;\r
4016 }\r
4017 fp->fptr += ofs;\r
4018 if (ofs % SS(fs)) {\r
4019 nsect = clust2sect(fs, clst); /* Current sector */\r
4020 if (!nsect) ABORT(fs, FR_INT_ERR);\r
4021 nsect += (DWORD)(ofs / SS(fs));\r
4022 }\r
4023 }\r
4024 }\r
4025 if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */\r
4026 #if !_FS_TINY\r
4027 #if !_FS_READONLY\r
4028 if (fp->flag & _FA_DIRTY) { /* Write-back dirty sector cache */\r
4029 if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {\r
4030 ABORT(fs, FR_DISK_ERR);\r
4031 }\r
4032 fp->flag &= ~_FA_DIRTY;\r
4033 }\r
4034 #endif\r
4035 if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) { /* Fill sector cache */\r
4036 ABORT(fs, FR_DISK_ERR);\r
4037 }\r
4038 #endif\r
4039 fp->sect = nsect;\r
4040 }\r
4041 #if !_FS_READONLY\r
4042 if (fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */\r
4043 fp->obj.objsize = fp->fptr;\r
4044 fp->flag |= _FA_MODIFIED;\r
4045 }\r
4046 #endif\r
4047 }\r
4048 \r
4049 LEAVE_FF(fs, res);\r
4050 }\r
4051 \r
4052 \r
4053 \r
4054 #if _FS_MINIMIZE <= 1\r
4055 /*-----------------------------------------------------------------------*/\r
4056 /* Create a Directory Object */\r
4057 /*-----------------------------------------------------------------------*/\r
4058 \r
4059 FRESULT f_opendir (\r
4060 DIR* dp, /* Pointer to directory object to create */\r
4061 const TCHAR* path /* Pointer to the directory path */\r
4062 )\r
4063 {\r
4064 FRESULT res;\r
4065 FATFS *fs;\r
4066 _FDID *obj;\r
4067 DEF_NAMBUF;\r
4068 \r
4069 \r
4070 if (!dp) return FR_INVALID_OBJECT;\r
4071 \r
4072 /* Get logical drive number */\r
4073 obj = &dp->obj;\r
4074 res = find_volume(&path, &fs, 0);\r
4075 if (res == FR_OK) {\r
4076 obj->fs = fs;\r
4077 INIT_NAMBUF(*dp);\r
4078 res = follow_path(dp, path); /* Follow the path to the directory */\r
4079 if (res == FR_OK) { /* Follow completed */\r
4080 if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */\r
4081 if (obj->attr & AM_DIR) { /* This object is a sub-directory */\r
4082 #if _FS_EXFAT\r
4083 if (fs->fs_type == FS_EXFAT) {\r
4084 obj->c_scl = obj->sclust; /* Save containing directory inforamation */\r
4085 obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat;\r
4086 obj->c_ofs = dp->blk_ofs;\r
4087 obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object location and status */\r
4088 obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\r
4089 obj->stat = fs->dirbuf[XDIR_GenFlags] & 2;\r
4090 } else\r
4091 #endif\r
4092 {\r
4093 obj->sclust = ld_clust(fs, dp->dir); /* Get object location */\r
4094 }\r
4095 } else { /* This object is a file */\r
4096 res = FR_NO_PATH;\r
4097 }\r
4098 }\r
4099 if (res == FR_OK) {\r
4100 obj->id = fs->id;\r
4101 res = dir_sdi(dp, 0); /* Rewind directory */\r
4102 #if _FS_LOCK != 0\r
4103 if (res == FR_OK) {\r
4104 if (obj->sclust) {\r
4105 obj->lockid = inc_lock(dp, 0); /* Lock the sub directory */\r
4106 if (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES;\r
4107 } else {\r
4108 obj->lockid = 0; /* Root directory need not to be locked */\r
4109 }\r
4110 }\r
4111 #endif\r
4112 }\r
4113 }\r
4114 FREE_NAMBUF();\r
4115 if (res == FR_NO_FILE) res = FR_NO_PATH;\r
4116 }\r
4117 if (res != FR_OK) obj->fs = 0; /* Invalidate the directory object if function faild */\r
4118 \r
4119 LEAVE_FF(fs, res);\r
4120 }\r
4121 \r
4122 \r
4123 \r
4124 \r
4125 /*-----------------------------------------------------------------------*/\r
4126 /* Close Directory */\r
4127 /*-----------------------------------------------------------------------*/\r
4128 \r
4129 FRESULT f_closedir (\r
4130 DIR *dp /* Pointer to the directory object to be closed */\r
4131 )\r
4132 {\r
4133 FRESULT res;\r
4134 FATFS *fs;\r
4135 \r
4136 \r
4137 res = validate(dp, &fs);\r
4138 if (res == FR_OK) {\r
4139 #if _FS_LOCK != 0\r
4140 if (dp->obj.lockid) { /* Decrement sub-directory open counter */\r
4141 res = dec_lock(dp->obj.lockid);\r
4142 }\r
4143 if (res == FR_OK)\r
4144 #endif\r
4145 {\r
4146 dp->obj.fs = 0; /* Invalidate directory object */\r
4147 }\r
4148 #if _FS_REENTRANT\r
4149 unlock_fs(fs, FR_OK); /* Unlock volume */\r
4150 #endif\r
4151 }\r
4152 return res;\r
4153 }\r
4154 \r
4155 \r
4156 \r
4157 \r
4158 /*-----------------------------------------------------------------------*/\r
4159 /* Read Directory Entries in Sequence */\r
4160 /*-----------------------------------------------------------------------*/\r
4161 \r
4162 FRESULT f_readdir (\r
4163 DIR* dp, /* Pointer to the open directory object */\r
4164 FILINFO* fno /* Pointer to file information to return */\r
4165 )\r
4166 {\r
4167 FRESULT res;\r
4168 FATFS *fs;\r
4169 DEF_NAMBUF;\r
4170 \r
4171 \r
4172 res = validate(dp, &fs); /* Check validity of the object */\r
4173 if (res == FR_OK) {\r
4174 if (!fno) {\r
4175 res = dir_sdi(dp, 0); /* Rewind the directory object */\r
4176 } else {\r
4177 INIT_NAMBUF(*dp);\r
4178 res = dir_read(dp, 0); /* Read an item */\r
4179 if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */\r
4180 if (res == FR_OK) { /* A valid entry is found */\r
4181 get_fileinfo(dp, fno); /* Get the object information */\r
4182 res = dir_next(dp, 0); /* Increment index for next */\r
4183 if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */\r
4184 }\r
4185 FREE_NAMBUF();\r
4186 }\r
4187 }\r
4188 LEAVE_FF(fs, res);\r
4189 }\r
4190 \r
4191 \r
4192 \r
4193 #if _USE_FIND\r
4194 /*-----------------------------------------------------------------------*/\r
4195 /* Find Next File */\r
4196 /*-----------------------------------------------------------------------*/\r
4197 \r
4198 FRESULT f_findnext (\r
4199 DIR* dp, /* Pointer to the open directory object */\r
4200 FILINFO* fno /* Pointer to the file information structure */\r
4201 )\r
4202 {\r
4203 FRESULT res;\r
4204 \r
4205 \r
4206 for (;;) {\r
4207 res = f_readdir(dp, fno); /* Get a directory item */\r
4208 if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */\r
4209 if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */\r
4210 #if _USE_LFN != 0 && _USE_FIND == 2\r
4211 if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */\r
4212 #endif\r
4213 }\r
4214 return res;\r
4215 }\r
4216 \r
4217 \r
4218 \r
4219 /*-----------------------------------------------------------------------*/\r
4220 /* Find First File */\r
4221 /*-----------------------------------------------------------------------*/\r
4222 \r
4223 FRESULT f_findfirst (\r
4224 DIR* dp, /* Pointer to the blank directory object */\r
4225 FILINFO* fno, /* Pointer to the file information structure */\r
4226 const TCHAR* path, /* Pointer to the directory to open */\r
4227 const TCHAR* pattern /* Pointer to the matching pattern */\r
4228 )\r
4229 {\r
4230 FRESULT res;\r
4231 \r
4232 \r
4233 dp->pat = pattern; /* Save pointer to pattern string */\r
4234 res = f_opendir(dp, path); /* Open the target directory */\r
4235 if (res == FR_OK) {\r
4236 res = f_findnext(dp, fno); /* Find the first item */\r
4237 }\r
4238 return res;\r
4239 }\r
4240 \r
4241 #endif /* _USE_FIND */\r
4242 \r
4243 \r
4244 \r
4245 #if _FS_MINIMIZE == 0\r
4246 /*-----------------------------------------------------------------------*/\r
4247 /* Get File Status */\r
4248 /*-----------------------------------------------------------------------*/\r
4249 \r
4250 FRESULT f_stat (\r
4251 const TCHAR* path, /* Pointer to the file path */\r
4252 FILINFO* fno /* Pointer to file information to return */\r
4253 )\r
4254 {\r
4255 FRESULT res;\r
4256 DIR dj;\r
4257 DEF_NAMBUF;\r
4258 \r
4259 \r
4260 /* Get logical drive number */\r
4261 res = find_volume(&path, &dj.obj.fs, 0);\r
4262 if (res == FR_OK) {\r
4263 INIT_NAMBUF(dj);\r
4264 res = follow_path(&dj, path); /* Follow the file path */\r
4265 if (res == FR_OK) { /* Follow completed */\r
4266 if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */\r
4267 res = FR_INVALID_NAME;\r
4268 } else { /* Found an object */\r
4269 if (fno) get_fileinfo(&dj, fno);\r
4270 }\r
4271 }\r
4272 FREE_NAMBUF();\r
4273 }\r
4274 \r
4275 LEAVE_FF(dj.obj.fs, res);\r
4276 }\r
4277 \r
4278 \r
4279 \r
4280 #if !_FS_READONLY\r
4281 /*-----------------------------------------------------------------------*/\r
4282 /* Get Number of Free Clusters */\r
4283 /*-----------------------------------------------------------------------*/\r
4284 \r
4285 FRESULT f_getfree (\r
4286 const TCHAR* path, /* Path name of the logical drive number */\r
4287 DWORD* nclst, /* Pointer to a variable to return number of free clusters */\r
4288 FATFS** fatfs /* Pointer to return pointer to corresponding file system object */\r
4289 )\r
4290 {\r
4291 FRESULT res;\r
4292 FATFS *fs;\r
4293 DWORD nfree, clst, sect, stat;\r
4294 UINT i;\r
4295 BYTE *p;\r
4296 _FDID obj;\r
4297 \r
4298 \r
4299 /* Get logical drive number */\r
4300 res = find_volume(&path, &fs, 0);\r
4301 if (res == FR_OK) {\r
4302 *fatfs = fs; /* Return ptr to the fs object */\r
4303 /* If free_clst is valid, return it without full cluster scan */\r
4304 if (fs->free_clst <= fs->n_fatent - 2) {\r
4305 *nclst = fs->free_clst;\r
4306 } else {\r
4307 /* Get number of free clusters */\r
4308 nfree = 0;\r
4309 if (fs->fs_type == FS_FAT12) { /* FAT12: Sector unalighed FAT entries */\r
4310 clst = 2; obj.fs = fs;\r
4311 do {\r
4312 stat = get_fat(&obj, clst);\r
4313 if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }\r
4314 if (stat == 1) { res = FR_INT_ERR; break; }\r
4315 if (stat == 0) nfree++;\r
4316 } while (++clst < fs->n_fatent);\r
4317 } else {\r
4318 #if _FS_EXFAT\r
4319 if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan bitmap table */\r
4320 BYTE bm;\r
4321 UINT b;\r
4322 \r
4323 clst = fs->n_fatent - 2;\r
4324 sect = fs->database;\r
4325 i = 0;\r
4326 do {\r
4327 if (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break;\r
4328 for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) {\r
4329 if (!(bm & 1)) nfree++;\r
4330 bm >>= 1;\r
4331 }\r
4332 i = (i + 1) & (SS(fs) - 1);\r
4333 } while (clst);\r
4334 } else\r
4335 #endif\r
4336 { /* FAT16/32: Sector alighed FAT entries */\r
4337 clst = fs->n_fatent; sect = fs->fatbase;\r
4338 i = 0; p = 0;\r
4339 do {\r
4340 if (i == 0) {\r
4341 res = move_window(fs, sect++);\r
4342 if (res != FR_OK) break;\r
4343 p = fs->win;\r
4344 i = SS(fs);\r
4345 }\r
4346 if (fs->fs_type == FS_FAT16) {\r
4347 if (ld_word(p) == 0) nfree++;\r
4348 p += 2; i -= 2;\r
4349 } else {\r
4350 if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++;\r
4351 p += 4; i -= 4;\r
4352 }\r
4353 } while (--clst);\r
4354 }\r
4355 }\r
4356 *nclst = nfree; /* Return the free clusters */\r
4357 fs->free_clst = nfree; /* Now free_clst is valid */\r
4358 fs->fsi_flag |= 1; /* FSInfo is to be updated */\r
4359 }\r
4360 }\r
4361 \r
4362 LEAVE_FF(fs, res);\r
4363 }\r
4364 \r
4365 \r
4366 \r
4367 \r
4368 /*-----------------------------------------------------------------------*/\r
4369 /* Truncate File */\r
4370 /*-----------------------------------------------------------------------*/\r
4371 \r
4372 FRESULT f_truncate (\r
4373 FIL* fp /* Pointer to the file object */\r
4374 )\r
4375 {\r
4376 FRESULT res;\r
4377 FATFS *fs;\r
4378 DWORD ncl;\r
4379 \r
4380 \r
4381 res = validate(fp, &fs); /* Check validity of the object */\r
4382 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */\r
4383 if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */\r
4384 \r
4385 if (fp->obj.objsize > fp->fptr) {\r
4386 if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */\r
4387 res = remove_chain(&fp->obj, fp->obj.sclust, 0);\r
4388 fp->obj.sclust = 0;\r
4389 } else { /* When truncate a part of the file, remove remaining clusters */\r
4390 ncl = get_fat(&fp->obj, fp->clust);\r
4391 res = FR_OK;\r
4392 if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;\r
4393 if (ncl == 1) res = FR_INT_ERR;\r
4394 if (res == FR_OK && ncl < fs->n_fatent) {\r
4395 res = remove_chain(&fp->obj, ncl, fp->clust);\r
4396 }\r
4397 }\r
4398 fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */\r
4399 fp->flag |= _FA_MODIFIED;\r
4400 #if !_FS_TINY\r
4401 if (res == FR_OK && (fp->flag & _FA_DIRTY)) {\r
4402 if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {\r
4403 res = FR_DISK_ERR;\r
4404 } else {\r
4405 fp->flag &= ~_FA_DIRTY;\r
4406 }\r
4407 }\r
4408 #endif\r
4409 if (res != FR_OK) ABORT(fs, res);\r
4410 }\r
4411 \r
4412 LEAVE_FF(fs, res);\r
4413 }\r
4414 \r
4415 \r
4416 \r
4417 \r
4418 /*-----------------------------------------------------------------------*/\r
4419 /* Delete a File/Directory */\r
4420 /*-----------------------------------------------------------------------*/\r
4421 \r
4422 FRESULT f_unlink (\r
4423 const TCHAR* path /* Pointer to the file or directory path */\r
4424 )\r
4425 {\r
4426 FRESULT res;\r
4427 DIR dj, sdj;\r
4428 DWORD dclst = 0;\r
4429 FATFS *fs;\r
4430 #if _FS_EXFAT\r
4431 _FDID obj;\r
4432 #endif\r
4433 DEF_NAMBUF;\r
4434 \r
4435 \r
4436 /* Get logical drive number */\r
4437 res = find_volume(&path, &fs, FA_WRITE);\r
4438 dj.obj.fs = fs;\r
4439 if (res == FR_OK) {\r
4440 INIT_NAMBUF(dj);\r
4441 res = follow_path(&dj, path); /* Follow the file path */\r
4442 if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {\r
4443 res = FR_INVALID_NAME; /* Cannot remove dot entry */\r
4444 }\r
4445 #if _FS_LOCK != 0\r
4446 if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */\r
4447 #endif\r
4448 if (res == FR_OK) { /* The object is accessible */\r
4449 if (dj.fn[NSFLAG] & NS_NONAME) {\r
4450 res = FR_INVALID_NAME; /* Cannot remove the origin directory */\r
4451 } else {\r
4452 if (dj.obj.attr & AM_RDO) {\r
4453 res = FR_DENIED; /* Cannot remove R/O object */\r
4454 }\r
4455 }\r
4456 if (res == FR_OK) {\r
4457 #if _FS_EXFAT\r
4458 obj.fs = fs;\r
4459 if (fs->fs_type == FS_EXFAT) {\r
4460 obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus);\r
4461 obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);\r
4462 obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;\r
4463 } else\r
4464 #endif\r
4465 {\r
4466 dclst = ld_clust(fs, dj.dir);\r
4467 }\r
4468 if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory ? */\r
4469 #if _FS_RPATH != 0\r
4470 if (dclst == fs->cdir) { /* Is it the current directory? */\r
4471 res = FR_DENIED;\r
4472 } else\r
4473 #endif\r
4474 {\r
4475 sdj.obj.fs = fs; /* Open the sub-directory */\r
4476 sdj.obj.sclust = dclst;\r
4477 #if _FS_EXFAT\r
4478 if (fs->fs_type == FS_EXFAT) {\r
4479 sdj.obj.objsize = obj.objsize;\r
4480 sdj.obj.stat = obj.stat;\r
4481 }\r
4482 #endif\r
4483 res = dir_sdi(&sdj, 0);\r
4484 if (res == FR_OK) {\r
4485 res = dir_read(&sdj, 0); /* Read an item */\r
4486 if (res == FR_OK) res = FR_DENIED; /* Not empty? */\r
4487 if (res == FR_NO_FILE) res = FR_OK; /* Empty? */\r
4488 }\r
4489 }\r
4490 }\r
4491 }\r
4492 if (res == FR_OK) {\r
4493 res = dir_remove(&dj); /* Remove the directory entry */\r
4494 if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */\r
4495 #if _FS_EXFAT\r
4496 res = remove_chain(&obj, dclst, 0);\r
4497 #else\r
4498 res = remove_chain(&dj.obj, dclst, 0);\r
4499 #endif\r
4500 }\r
4501 if (res == FR_OK) res = sync_fs(fs);\r
4502 }\r
4503 }\r
4504 FREE_NAMBUF();\r
4505 }\r
4506 \r
4507 LEAVE_FF(fs, res);\r
4508 }\r
4509 \r
4510 \r
4511 \r
4512 \r
4513 /*-----------------------------------------------------------------------*/\r
4514 /* Create a Directory */\r
4515 /*-----------------------------------------------------------------------*/\r
4516 \r
4517 FRESULT f_mkdir (\r
4518 const TCHAR* path /* Pointer to the directory path */\r
4519 )\r
4520 {\r
4521 FRESULT res;\r
4522 DIR dj;\r
4523 FATFS *fs;\r
4524 BYTE *dir;\r
4525 UINT n;\r
4526 DWORD dsc, dcl, pcl, tm;\r
4527 DEF_NAMBUF;\r
4528 \r
4529 \r
4530 /* Get logical drive number */\r
4531 res = find_volume(&path, &fs, FA_WRITE);\r
4532 dj.obj.fs = fs;\r
4533 if (res == FR_OK) {\r
4534 INIT_NAMBUF(dj);\r
4535 res = follow_path(&dj, path); /* Follow the file path */\r
4536 if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */\r
4537 if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) {\r
4538 res = FR_INVALID_NAME;\r
4539 }\r
4540 if (res == FR_NO_FILE) { /* Can create a new directory */\r
4541 dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */\r
4542 dj.obj.objsize = (DWORD)fs->csize * SS(fs);\r
4543 res = FR_OK;\r
4544 if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */\r
4545 if (dcl == 1) res = FR_INT_ERR;\r
4546 if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;\r
4547 if (res == FR_OK) res = sync_window(fs); /* Flush FAT */\r
4548 tm = GET_FATTIME();\r
4549 if (res == FR_OK) { /* Initialize the new directory table */\r
4550 dsc = clust2sect(fs, dcl);\r
4551 dir = fs->win;\r
4552 mem_set(dir, 0, SS(fs));\r
4553 if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {\r
4554 mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */\r
4555 dir[DIR_Name] = '.';\r
4556 dir[DIR_Attr] = AM_DIR;\r
4557 st_dword(dir + DIR_WrtTime, tm);\r
4558 st_clust(fs, dir, dcl);\r
4559 mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */\r
4560 dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;\r
4561 if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase)\r
4562 pcl = 0;\r
4563 st_clust(fs, dir + SZDIRE, pcl);\r
4564 }\r
4565 for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */\r
4566 fs->winsect = dsc++;\r
4567 fs->wflag = 1;\r
4568 res = sync_window(fs);\r
4569 if (res != FR_OK) break;\r
4570 mem_set(dir, 0, SS(fs));\r
4571 }\r
4572 }\r
4573 if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */\r
4574 if (res == FR_OK) {\r
4575 #if _FS_EXFAT\r
4576 if (fs->fs_type == FS_EXFAT) {\r
4577 st_dword(fs->dirbuf + XDIR_ModTime, tm);\r
4578 st_dword(fs->dirbuf + XDIR_FstClus, dcl);\r
4579 st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize);\r
4580 st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize);\r
4581 fs->dirbuf[XDIR_GenFlags] = 3;\r
4582 fs->dirbuf[XDIR_Attr] = AM_DIR;\r
4583 res = store_xdir(&dj);\r
4584 } else\r
4585 #endif\r
4586 {\r
4587 dir = dj.dir;\r
4588 st_dword(dir + DIR_WrtTime, tm); /* Created time */\r
4589 st_clust(fs, dir, dcl); /* Table start cluster */\r
4590 dir[DIR_Attr] = AM_DIR; /* Attribute */\r
4591 fs->wflag = 1;\r
4592 }\r
4593 res = sync_fs(fs);\r
4594 } else {\r
4595 remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */\r
4596 }\r
4597 }\r
4598 FREE_NAMBUF();\r
4599 }\r
4600 \r
4601 LEAVE_FF(fs, res);\r
4602 }\r
4603 \r
4604 \r
4605 \r
4606 \r
4607 /*-----------------------------------------------------------------------*/\r
4608 /* Rename a File/Directory */\r
4609 /*-----------------------------------------------------------------------*/\r
4610 \r
4611 FRESULT f_rename (\r
4612 const TCHAR* path_old, /* Pointer to the object name to be renamed */\r
4613 const TCHAR* path_new /* Pointer to the new name */\r
4614 )\r
4615 {\r
4616 FRESULT res;\r
4617 DIR djo, djn;\r
4618 FATFS *fs;\r
4619 BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir;\r
4620 DWORD dw;\r
4621 DEF_NAMBUF;\r
4622 \r
4623 \r
4624 get_ldnumber(&path_new); /* Ignore drive number of new name */\r
4625 res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive number of the old object */\r
4626 if (res == FR_OK) {\r
4627 djo.obj.fs = fs;\r
4628 INIT_NAMBUF(djo);\r
4629 res = follow_path(&djo, path_old); /* Check old object */\r
4630 if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */\r
4631 #if _FS_LOCK != 0\r
4632 if (res == FR_OK) res = chk_lock(&djo, 2);\r
4633 #endif\r
4634 if (res == FR_OK) { /* Object to be renamed is found */\r
4635 #if _FS_EXFAT\r
4636 if (fs->fs_type == FS_EXFAT) { /* At exFAT */\r
4637 BYTE nf, nn;\r
4638 WORD nh;\r
4639 \r
4640 mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */\r
4641 mem_cpy(&djn, &djo, sizeof djo);\r
4642 res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */\r
4643 if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */\r
4644 if (res == FR_NO_FILE) { /* It is a valid path and no name collision */\r
4645 res = dir_register(&djn); /* Register the new entry */\r
4646 if (res == FR_OK) {\r
4647 nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName];\r
4648 nh = ld_word(fs->dirbuf + XDIR_NameHash);\r
4649 mem_cpy(fs->dirbuf, buf, SZDIRE * 2);\r
4650 fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn;\r
4651 st_word(fs->dirbuf + XDIR_NameHash, nh);\r
4652 /* Start of critical section where any interruption can cause a cross-link */\r
4653 res = store_xdir(&djn);\r
4654 }\r
4655 }\r
4656 } else\r
4657 #endif\r
4658 { /* At FAT12/FAT16/FAT32 */\r
4659 mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */\r
4660 mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */\r
4661 res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */\r
4662 if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */\r
4663 if (res == FR_NO_FILE) { /* It is a valid path and no name collision */\r
4664 res = dir_register(&djn); /* Register the new entry */\r
4665 if (res == FR_OK) {\r
4666 dir = djn.dir; /* Copy information about object except name */\r
4667 mem_cpy(dir + 13, buf + 2, 19);\r
4668 dir[DIR_Attr] = buf[0] | AM_ARC;\r
4669 fs->wflag = 1;\r
4670 if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */\r
4671 dw = clust2sect(fs, ld_clust(fs, dir));\r
4672 if (!dw) {\r
4673 res = FR_INT_ERR;\r
4674 } else {\r
4675 /* Start of critical section where any interruption can cause a cross-link */\r
4676 res = move_window(fs, dw);\r
4677 dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */\r
4678 if (res == FR_OK && dir[1] == '.') {\r
4679 st_clust(fs, dir, djn.obj.sclust);\r
4680 fs->wflag = 1;\r
4681 }\r
4682 }\r
4683 }\r
4684 }\r
4685 }\r
4686 }\r
4687 if (res == FR_OK) {\r
4688 res = dir_remove(&djo); /* Remove old entry */\r
4689 if (res == FR_OK) {\r
4690 res = sync_fs(fs);\r
4691 }\r
4692 }\r
4693 /* End of critical section */\r
4694 }\r
4695 FREE_NAMBUF();\r
4696 }\r
4697 \r
4698 LEAVE_FF(fs, res);\r
4699 }\r
4700 \r
4701 \r
4702 \r
4703 #endif /* !_FS_READONLY */\r
4704 #endif /* _FS_MINIMIZE == 0 */\r
4705 #endif /* _FS_MINIMIZE <= 1 */\r
4706 #endif /* _FS_MINIMIZE <= 2 */\r
4707 \r
4708 \r
4709 \r
4710 #if _USE_CHMOD && !_FS_READONLY\r
4711 /*-----------------------------------------------------------------------*/\r
4712 /* Change Attribute */\r
4713 /*-----------------------------------------------------------------------*/\r
4714 \r
4715 FRESULT f_chmod (\r
4716 const TCHAR* path, /* Pointer to the file path */\r
4717 BYTE attr, /* Attribute bits */\r
4718 BYTE mask /* Attribute mask to change */\r
4719 )\r
4720 {\r
4721 FRESULT res;\r
4722 DIR dj;\r
4723 FATFS *fs;\r
4724 DEF_NAMBUF;\r
4725 \r
4726 \r
4727 res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */\r
4728 dj.obj.fs = fs;\r
4729 if (res == FR_OK) {\r
4730 INIT_NAMBUF(dj);\r
4731 res = follow_path(&dj, path); /* Follow the file path */\r
4732 if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */\r
4733 if (res == FR_OK) {\r
4734 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */\r
4735 #if _FS_EXFAT\r
4736 if (fs->fs_type == FS_EXFAT) {\r
4737 fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */\r
4738 res = store_xdir(&dj);\r
4739 } else\r
4740 #endif\r
4741 {\r
4742 dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */\r
4743 fs->wflag = 1;\r
4744 }\r
4745 res = sync_fs(fs);\r
4746 }\r
4747 FREE_NAMBUF();\r
4748 }\r
4749 \r
4750 LEAVE_FF(fs, res);\r
4751 }\r
4752 \r
4753 \r
4754 \r
4755 \r
4756 /*-----------------------------------------------------------------------*/\r
4757 /* Change Timestamp */\r
4758 /*-----------------------------------------------------------------------*/\r
4759 \r
4760 FRESULT f_utime (\r
4761 const TCHAR* path, /* Pointer to the file/directory name */\r
4762 const FILINFO* fno /* Pointer to the time stamp to be set */\r
4763 )\r
4764 {\r
4765 FRESULT res;\r
4766 DIR dj;\r
4767 FATFS *fs;\r
4768 DEF_NAMBUF;\r
4769 \r
4770 \r
4771 res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */\r
4772 dj.obj.fs = fs;\r
4773 if (res == FR_OK) {\r
4774 INIT_NAMBUF(dj);\r
4775 res = follow_path(&dj, path); /* Follow the file path */\r
4776 if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */\r
4777 if (res == FR_OK) {\r
4778 #if _FS_EXFAT\r
4779 if (fs->fs_type == FS_EXFAT) {\r
4780 st_word(fs->dirbuf + XDIR_ModTime, fno->ftime);\r
4781 st_word(fs->dirbuf + XDIR_ModTime + 2, fno->fdate);\r
4782 res = store_xdir(&dj);\r
4783 } else\r
4784 #endif\r
4785 {\r
4786 st_word(dj.dir + DIR_WrtTime, fno->ftime);\r
4787 st_word(dj.dir + DIR_WrtDate, fno->fdate);\r
4788 fs->wflag = 1;\r
4789 }\r
4790 if (res == FR_OK) res = sync_fs(fs);\r
4791 }\r
4792 FREE_NAMBUF();\r
4793 }\r
4794 \r
4795 LEAVE_FF(fs, res);\r
4796 }\r
4797 \r
4798 #endif /* _USE_CHMOD && !_FS_READONLY */\r
4799 \r
4800 \r
4801 \r
4802 #if _USE_LABEL\r
4803 /*-----------------------------------------------------------------------*/\r
4804 /* Get Volume Label */\r
4805 /*-----------------------------------------------------------------------*/\r
4806 \r
4807 FRESULT f_getlabel (\r
4808 const TCHAR* path, /* Path name of the logical drive number */\r
4809 TCHAR* label, /* Pointer to a buffer to return the volume label */\r
4810 DWORD* vsn /* Pointer to a variable to return the volume serial number */\r
4811 )\r
4812 {\r
4813 FRESULT res;\r
4814 DIR dj;\r
4815 FATFS *fs;\r
4816 UINT si, di;\r
4817 #if _LFN_UNICODE || _FS_EXFAT\r
4818 WCHAR w;\r
4819 #endif\r
4820 \r
4821 /* Get logical drive number */\r
4822 res = find_volume(&path, &fs, 0);\r
4823 \r
4824 /* Get volume label */\r
4825 if (res == FR_OK && label) {\r
4826 dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */\r
4827 res = dir_sdi(&dj, 0);\r
4828 if (res == FR_OK) {\r
4829 res = dir_read(&dj, 1); /* Find a volume label entry */\r
4830 if (res == FR_OK) {\r
4831 #if _FS_EXFAT\r
4832 if (fs->fs_type == FS_EXFAT) {\r
4833 for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */\r
4834 w = ld_word(dj.dir + XDIR_Label + si * 2);\r
4835 #if _LFN_UNICODE\r
4836 label[di++] = w;\r
4837 #else\r
4838 w = ff_convert(w, 0); /* Unicode -> OEM */\r
4839 if (w == 0) w = '?'; /* Replace wrong character */\r
4840 if (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8);\r
4841 label[di++] = (char)w;\r
4842 #endif\r
4843 }\r
4844 label[di] = 0;\r
4845 } else\r
4846 #endif\r
4847 {\r
4848 si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */\r
4849 do {\r
4850 #if _LFN_UNICODE\r
4851 w = (si < 11) ? dj.dir[si++] : ' ';\r
4852 if (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) {\r
4853 w = w << 8 | dj.dir[si++];\r
4854 }\r
4855 label[di++] = ff_convert(w, 1); /* OEM -> Unicode */\r
4856 #else\r
4857 label[di++] = dj.dir[si++];\r
4858 #endif\r
4859 } while (di < 11);\r
4860 do { /* Truncate trailing spaces */\r
4861 label[di] = 0;\r
4862 if (di == 0) break;\r
4863 } while (label[--di] == ' ');\r
4864 }\r
4865 }\r
4866 }\r
4867 if (res == FR_NO_FILE) { /* No label entry and return nul string */\r
4868 label[0] = 0;\r
4869 res = FR_OK;\r
4870 }\r
4871 }\r
4872 \r
4873 /* Get volume serial number */\r
4874 if (res == FR_OK && vsn) {\r
4875 res = move_window(fs, fs->volbase);\r
4876 if (res == FR_OK) {\r
4877 switch (fs->fs_type) {\r
4878 case FS_EXFAT: di = BPB_VolIDEx; break;\r
4879 case FS_FAT32: di = BS_VolID32; break;\r
4880 default: di = BS_VolID;\r
4881 }\r
4882 *vsn = ld_dword(&fs->win[di]);\r
4883 }\r
4884 }\r
4885 \r
4886 LEAVE_FF(fs, res);\r
4887 }\r
4888 \r
4889 \r
4890 \r
4891 #if !_FS_READONLY\r
4892 /*-----------------------------------------------------------------------*/\r
4893 /* Set Volume Label */\r
4894 /*-----------------------------------------------------------------------*/\r
4895 \r
4896 FRESULT f_setlabel (\r
4897 const TCHAR* label /* Pointer to the volume label to set */\r
4898 )\r
4899 {\r
4900 FRESULT res;\r
4901 DIR dj;\r
4902 FATFS *fs;\r
4903 BYTE dirvn[22];\r
4904 UINT i, j, slen;\r
4905 WCHAR w;\r
4906 static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F";\r
4907 \r
4908 \r
4909 /* Get logical drive number */\r
4910 res = find_volume(&label, &fs, FA_WRITE);\r
4911 if (res != FR_OK) LEAVE_FF(fs, res);\r
4912 dj.obj.fs = fs;\r
4913 \r
4914 /* Get length of given volume label */\r
4915 for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */\r
4916 \r
4917 #if _FS_EXFAT\r
4918 if (fs->fs_type == FS_EXFAT) { /* At the exFAT */\r
4919 for (i = j = 0; i < slen; ) { /* Create volume label in directory form */\r
4920 w = label[i++];\r
4921 #if !_LFN_UNICODE\r
4922 if (IsDBCS1(w)) {\r
4923 w = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;\r
4924 }\r
4925 w = ff_convert(w, 1);\r
4926 #endif\r
4927 if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */\r
4928 LEAVE_FF(fs, FR_INVALID_NAME);\r
4929 }\r
4930 st_word(dirvn + j, w); j += 2;\r
4931 }\r
4932 slen = j;\r
4933 } else\r
4934 #endif\r
4935 { /* At the FAT12/16/32 */\r
4936 for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */\r
4937 if (slen) { /* Is there a volume label to be set? */\r
4938 dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */\r
4939 do {\r
4940 #if _LFN_UNICODE\r
4941 w = ff_convert(ff_wtoupper(label[i++]), 0);\r
4942 #else\r
4943 w = (BYTE)label[i++];\r
4944 if (IsDBCS1(w)) {\r
4945 w = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;\r
4946 }\r
4947 #if _USE_LFN != 0\r
4948 w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0);\r
4949 #else\r
4950 if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */\r
4951 #ifdef _EXCVT\r
4952 if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */\r
4953 #else\r
4954 if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */\r
4955 #endif\r
4956 #endif\r
4957 #endif\r
4958 if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */\r
4959 LEAVE_FF(fs, FR_INVALID_NAME);\r
4960 }\r
4961 if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8);\r
4962 dirvn[j++] = (BYTE)w;\r
4963 } while (i < slen);\r
4964 while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */\r
4965 if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */\r
4966 }\r
4967 }\r
4968 \r
4969 /* Set volume label */\r
4970 dj.obj.sclust = 0; /* Open root directory */\r
4971 res = dir_sdi(&dj, 0);\r
4972 if (res == FR_OK) {\r
4973 res = dir_read(&dj, 1); /* Get volume label entry */\r
4974 if (res == FR_OK) {\r
4975 if (_FS_EXFAT && fs->fs_type == FS_EXFAT) {\r
4976 dj.dir[XDIR_NumLabel] = slen / 2; /* Change the volume label */\r
4977 mem_cpy(dj.dir + XDIR_Label, dirvn, slen);\r
4978 } else {\r
4979 if (slen) {\r
4980 mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */\r
4981 } else {\r
4982 dj.dir[DIR_Name] = DDEM; /* Remove the volume label */\r
4983 }\r
4984 }\r
4985 fs->wflag = 1;\r
4986 res = sync_fs(fs);\r
4987 } else { /* No volume label entry is found or error */\r
4988 if (res == FR_NO_FILE) {\r
4989 res = FR_OK;\r
4990 if (slen) { /* Create a volume label entry */\r
4991 res = dir_alloc(&dj, 1); /* Allocate an entry */\r
4992 if (res == FR_OK) {\r
4993 mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */\r
4994 if (_FS_EXFAT && fs->fs_type == FS_EXFAT) {\r
4995 dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */\r
4996 dj.dir[XDIR_NumLabel] = slen / 2;\r
4997 mem_cpy(dj.dir + XDIR_Label, dirvn, slen);\r
4998 } else {\r
4999 dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */\r
5000 mem_cpy(dj.dir, dirvn, 11);\r
5001 }\r
5002 fs->wflag = 1;\r
5003 res = sync_fs(fs);\r
5004 }\r
5005 }\r
5006 }\r
5007 }\r
5008 }\r
5009 \r
5010 LEAVE_FF(fs, res);\r
5011 }\r
5012 \r
5013 #endif /* !_FS_READONLY */\r
5014 #endif /* _USE_LABEL */\r
5015 \r
5016 \r
5017 \r
5018 #if _USE_EXPAND && !_FS_READONLY\r
5019 /*-----------------------------------------------------------------------*/\r
5020 /* Allocate a Contiguous Blocks to the File */\r
5021 /*-----------------------------------------------------------------------*/\r
5022 \r
5023 FRESULT f_expand (\r
5024 FIL* fp, /* Pointer to the file object */\r
5025 FSIZE_t fsz, /* File size to be expanded to */\r
5026 BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */\r
5027 )\r
5028 {\r
5029 FRESULT res;\r
5030 FATFS *fs;\r
5031 DWORD val, clst, csz, stcl, scl, ncl, tcl;\r
5032 \r
5033 \r
5034 res = validate(fp, &fs); /* Check validity of the object */\r
5035 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */\r
5036 if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);\r
5037 #if _FS_EXFAT\r
5038 if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */\r
5039 #endif\r
5040 csz = (DWORD)fs->csize * SS(fs); /* Cluster size */\r
5041 tcl = (DWORD)(fsz / csz) + ((fsz & (csz - 1)) ? 1 : 0); /* Number of clusters required */\r
5042 stcl = fs->last_clst;\r
5043 if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2;\r
5044 \r
5045 #if _FS_EXFAT\r
5046 if (fs->fs_type == FS_EXFAT) {\r
5047 scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */\r
5048 if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */\r
5049 if (scl == 1) res = FR_INT_ERR;\r
5050 if (scl == 0xFFFFFFFF) res = FR_DISK_ERR;\r
5051 if (res == FR_OK) {\r
5052 if (opt) {\r
5053 res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */\r
5054 fs->last_clst = scl + tcl - 1;\r
5055 } else {\r
5056 fs->last_clst = scl - 1; /* Set suggested cluster to start next */\r
5057 }\r
5058 }\r
5059 } else\r
5060 #endif\r
5061 {\r
5062 scl = clst = stcl; ncl = 0;\r
5063 for (;;) { /* Find a contiguous cluster block */\r
5064 val = get_fat(&fp->obj, clst);\r
5065 if (++clst >= fs->n_fatent) clst = 2;\r
5066 if (val == 1) { res = FR_INT_ERR; break; }\r
5067 if (val == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }\r
5068 if (val == 0) { /* Is it a free cluster? */\r
5069 if (++ncl == tcl) break; /* Break if a contiguous cluster block was found */\r
5070 } else {\r
5071 scl = clst; ncl = 0; /* Not a free cluster */\r
5072 }\r
5073 if (clst == stcl) { res = FR_DENIED; break; } /* All cluster scanned? */\r
5074 }\r
5075 if (res == FR_OK) {\r
5076 if (opt) {\r
5077 for (clst = scl; tcl; clst++, tcl--) { /* Create a cluster chain on the FAT */\r
5078 val = (tcl == 1) ? 0xFFFFFFFF : clst + 1;\r
5079 res = put_fat(fs, clst, val);\r
5080 if (res != FR_OK) break;\r
5081 fs->last_clst = clst;\r
5082 }\r
5083 } else {\r
5084 fs->last_clst = scl - 1; /* Set suggested cluster to start next */\r
5085 }\r
5086 }\r
5087 }\r
5088 \r
5089 if (opt && res == FR_OK) {\r
5090 fp->obj.sclust = scl; /* Update allocation information */\r
5091 fp->obj.objsize = fsz;\r
5092 if (_FS_EXFAT) fp->obj.stat = 2;\r
5093 fp->flag |= _FA_MODIFIED;\r
5094 }\r
5095 \r
5096 LEAVE_FF(fs, res);\r
5097 }\r
5098 \r
5099 #endif /* _USE_EXPAND && !_FS_READONLY */\r
5100 \r
5101 \r
5102 \r
5103 /*-----------------------------------------------------------------------*/\r
5104 /* Forward data to the stream directly (available on only tiny cfg) */\r
5105 /*-----------------------------------------------------------------------*/\r
5106 #if _USE_FORWARD && _FS_TINY\r
5107 \r
5108 FRESULT f_forward (\r
5109 FIL* fp, /* Pointer to the file object */\r
5110 UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */\r
5111 UINT btf, /* Number of bytes to forward */\r
5112 UINT* bf /* Pointer to number of bytes forwarded */\r
5113 )\r
5114 {\r
5115 FRESULT res;\r
5116 FATFS *fs;\r
5117 DWORD clst, sect;\r
5118 FSIZE_t remain;\r
5119 UINT rcnt, csect;\r
5120 \r
5121 \r
5122 *bf = 0; /* Clear transfer byte counter */\r
5123 res = validate(fp, &fs); /* Check validity of the object */\r
5124 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */\r
5125 if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */\r
5126 \r
5127 remain = fp->fsize - fp->fptr;\r
5128 if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */\r
5129 \r
5130 for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */\r
5131 fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {\r
5132 csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */\r
5133 if ((fp->fptr % SS(fs)) == 0) { /* On the sector boundary? */\r
5134 if (csect == 0) { /* On the cluster boundary? */\r
5135 clst = (fp->fptr == 0) ? /* On the top of the file? */\r
5136 fp->sclust : get_fat(fs, fp->clust);\r
5137 if (clst <= 1) ABORT(fs, FR_INT_ERR);\r
5138 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);\r
5139 fp->clust = clst; /* Update current cluster */\r
5140 }\r
5141 }\r
5142 sect = clust2sect(fs, fp->clust); /* Get current data sector */\r
5143 if (!sect) ABORT(fs, FR_INT_ERR);\r
5144 sect += csect;\r
5145 if (move_window(fs, sect) != FR_OK) { /* Move sector window */\r
5146 ABORT(fs, FR_DISK_ERR);\r
5147 }\r
5148 fp->sect = sect;\r
5149 rcnt = SS(fs) - (WORD)(fp->fptr % SS(fs)); /* Forward data from sector window */\r
5150 if (rcnt > btf) rcnt = btf;\r
5151 rcnt = (*func)(&fs->win[(WORD)fp->fptr % SS(fs)], rcnt);\r
5152 if (!rcnt) ABORT(fs, FR_INT_ERR);\r
5153 }\r
5154 \r
5155 LEAVE_FF(fs, FR_OK);\r
5156 }\r
5157 #endif /* _USE_FORWARD */\r
5158 \r
5159 \r
5160 \r
5161 #if _USE_MKFS && !_FS_READONLY\r
5162 /*-----------------------------------------------------------------------*/\r
5163 /* Create file system on the logical drive */\r
5164 /*-----------------------------------------------------------------------*/\r
5165 #define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */\r
5166 #define N_FATS 1 /* Number of FATs (1 or 2) */\r
5167 \r
5168 \r
5169 FRESULT f_mkfs (\r
5170 const TCHAR* path, /* Logical drive number */\r
5171 BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */\r
5172 UINT au /* Size of allocation unit in unit of byte or sector */\r
5173 )\r
5174 {\r
5175 static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0};\r
5176 static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};\r
5177 int vol;\r
5178 BYTE fmt, md, sys, *tbl, pdrv, part;\r
5179 DWORD n_clst, vs, n, wsect;\r
5180 UINT i;\r
5181 DWORD b_vol, b_fat, b_dir, b_data; /* LBA */\r
5182 DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */\r
5183 FATFS *fs;\r
5184 DSTATUS stat;\r
5185 #if _USE_TRIM\r
5186 DWORD eb[2];\r
5187 #endif\r
5188 \r
5189 \r
5190 /* Check mounted drive and clear work area */\r
5191 if (sfd > 1) return FR_INVALID_PARAMETER;\r
5192 vol = get_ldnumber(&path); /* Get target volume */\r
5193 if (vol < 0) return FR_INVALID_DRIVE;\r
5194 fs = FatFs[vol]; /* Check if the volume has work area */\r
5195 if (!fs) return FR_NOT_ENABLED;\r
5196 fs->fs_type = 0;\r
5197 pdrv = LD2PD(vol); /* Physical drive */\r
5198 part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/\r
5199 \r
5200 /* Get disk statics */\r
5201 stat = disk_initialize(pdrv);\r
5202 if (stat & STA_NOINIT) return FR_NOT_READY;\r
5203 if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;\r
5204 #if _MAX_SS != _MIN_SS /* Get disk sector size */\r
5205 if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS) {\r
5206 return FR_DISK_ERR;\r
5207 }\r
5208 #endif\r
5209 if (_MULTI_PARTITION && part) {\r
5210 /* Get partition information from partition table in the MBR */\r
5211 if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;\r
5212 if (ld_word(fs->win + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;\r
5213 tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];\r
5214 if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */\r
5215 b_vol = ld_dword(tbl + 8); /* Volume start sector */\r
5216 n_vol = ld_dword(tbl + 12); /* Volume size */\r
5217 } else {\r
5218 /* Create a single-partition in this function */\r
5219 if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) {\r
5220 return FR_DISK_ERR;\r
5221 }\r
5222 b_vol = (sfd) ? 0 : 63; /* Volume start sector */\r
5223 n_vol -= b_vol; /* Volume size */\r
5224 }\r
5225 \r
5226 if (au & (au - 1)) au = 0;\r
5227 if (!au) { /* AU auto selection */\r
5228 vs = n_vol / (2000 / (SS(fs) / 512));\r
5229 for (i = 0; vs < vst[i]; i++) ;\r
5230 au = cst[i];\r
5231 }\r
5232 if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */\r
5233 if (!au) au = 1;\r
5234 if (au > 128) au = 128;\r
5235 \r
5236 /* Pre-compute number of clusters and FAT sub-type */\r
5237 n_clst = n_vol / au;\r
5238 fmt = FS_FAT12;\r
5239 if (n_clst >= MIN_FAT16) fmt = FS_FAT16;\r
5240 if (n_clst >= MIN_FAT32) fmt = FS_FAT32;\r
5241 \r
5242 /* Determine offset and size of FAT structure */\r
5243 if (fmt == FS_FAT32) {\r
5244 n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);\r
5245 n_rsv = 32;\r
5246 n_dir = 0;\r
5247 } else {\r
5248 n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;\r
5249 n_fat = (n_fat + SS(fs) - 1) / SS(fs);\r
5250 n_rsv = 1;\r
5251 n_dir = (DWORD)N_ROOTDIR * SZDIRE / SS(fs);\r
5252 }\r
5253 b_fat = b_vol + n_rsv; /* FAT area start sector */\r
5254 b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */\r
5255 b_data = b_dir + n_dir; /* Data area start sector */\r
5256 if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */\r
5257 \r
5258 /* Align data start sector to erase block boundary (for flash memory media) */\r
5259 if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;\r
5260 n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */\r
5261 n = (n - b_data) / N_FATS;\r
5262 if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */\r
5263 n_rsv += n;\r
5264 b_fat += n;\r
5265 } else { /* FAT12/16: Expand FAT size */\r
5266 n_fat += n;\r
5267 }\r
5268 \r
5269 /* Determine number of clusters and final check of validity of the FAT sub-type */\r
5270 n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;\r
5271 if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16)\r
5272 || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) {\r
5273 return FR_MKFS_ABORTED;\r
5274 }\r
5275 \r
5276 /* Determine system ID in the partition table */\r
5277 if (fmt == FS_FAT32) {\r
5278 sys = 0x0C; /* FAT32X */\r
5279 } else {\r
5280 if (fmt == FS_FAT12 && n_vol < 0x10000) {\r
5281 sys = 0x01; /* FAT12(<65536) */\r
5282 } else {\r
5283 sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */\r
5284 }\r
5285 }\r
5286 \r
5287 if (_MULTI_PARTITION && part) {\r
5288 /* Update system ID in the partition table */\r
5289 tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];\r
5290 tbl[4] = sys;\r
5291 if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) { /* Write it to teh MBR */\r
5292 return FR_DISK_ERR;\r
5293 }\r
5294 md = 0xF8;\r
5295 } else {\r
5296 if (sfd) { /* No partition table (SFD) */\r
5297 md = 0xF0;\r
5298 } else { /* Create partition table (FDISK) */\r
5299 mem_set(fs->win, 0, SS(fs));\r
5300 tbl = fs->win + MBR_Table; /* Create partition table for single partition in the drive */\r
5301 tbl[1] = 1; /* Partition start head */\r
5302 tbl[2] = 1; /* Partition start sector */\r
5303 tbl[3] = 0; /* Partition start cylinder */\r
5304 tbl[4] = sys; /* System type */\r
5305 tbl[5] = 254; /* Partition end head */\r
5306 n = (b_vol + n_vol) / 63 / 255;\r
5307 tbl[6] = (BYTE)(n >> 2 | 63); /* Partition end sector */\r
5308 tbl[7] = (BYTE)n; /* End cylinder */\r
5309 st_dword(tbl + 8, 63); /* Partition start in LBA */\r
5310 st_dword(tbl + 12, n_vol); /* Partition size in LBA */\r
5311 st_word(fs->win + BS_55AA, 0xAA55); /* MBR signature */\r
5312 if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) { /* Write it to the MBR */\r
5313 return FR_DISK_ERR;\r
5314 }\r
5315 md = 0xF8;\r
5316 }\r
5317 }\r
5318 \r
5319 /* Create BPB in the VBR */\r
5320 tbl = fs->win; /* Clear sector */\r
5321 mem_set(tbl, 0, SS(fs));\r
5322 mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */\r
5323 i = SS(fs); /* Sector size */\r
5324 st_word(tbl + BPB_BytsPerSec, (WORD)i);\r
5325 tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */\r
5326 st_word(tbl + BPB_RsvdSecCnt, (WORD)n_rsv); /* Reserved sectors */\r
5327 tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */\r
5328 i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of root directory entries */\r
5329 st_word(tbl + BPB_RootEntCnt, (WORD)i);\r
5330 if (n_vol < 0x10000) { /* Number of total sectors */\r
5331 st_word(tbl + BPB_TotSec16, (WORD)n_vol);\r
5332 } else {\r
5333 st_dword(tbl + BPB_TotSec32, (WORD)n_vol);\r
5334 }\r
5335 tbl[BPB_Media] = md; /* Media descriptor */\r
5336 st_word(tbl + BPB_SecPerTrk, 63); /* Number of sectors per track */\r
5337 st_word(tbl + BPB_NumHeads, 255); /* Number of heads */\r
5338 st_dword(tbl + BPB_HiddSec, b_vol); /* Volume offset */\r
5339 n = GET_FATTIME(); /* Use current time as VSN */\r
5340 if (fmt == FS_FAT32) {\r
5341 st_dword(tbl + BS_VolID32, n); /* VSN */\r
5342 st_dword(tbl + BPB_FATSz32, n_fat); /* Number of sectors per FAT */\r
5343 st_dword(tbl + BPB_RootClus32, 2); /* Root directory start cluster (2) */\r
5344 st_word(tbl + BPB_FSInfo32, 1); /* FSINFO record offset (VBR + 1) */\r
5345 st_word(tbl + BPB_BkBootSec32, 6); /* Backup boot record offset (VBR + 6) */\r
5346 tbl[BS_DrvNum32] = 0x80; /* Drive number */\r
5347 tbl[BS_BootSig32] = 0x29; /* Extended boot signature */\r
5348 mem_cpy(tbl + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */\r
5349 } else {\r
5350 st_dword(tbl + BS_VolID, n); /* VSN */\r
5351 st_word(tbl + BPB_FATSz16, (WORD)n_fat); /* Number of sectors per FAT */\r
5352 tbl[BS_DrvNum] = 0x80; /* Drive number */\r
5353 tbl[BS_BootSig] = 0x29; /* Extended boot signature */\r
5354 mem_cpy(tbl + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */\r
5355 }\r
5356 st_word(tbl + BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */\r
5357 if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) { /* Write it to the VBR sector */\r
5358 return FR_DISK_ERR;\r
5359 }\r
5360 if (fmt == FS_FAT32) { /* Write it to the backup VBR if needed (VBR + 6) */\r
5361 disk_write(pdrv, tbl, b_vol + 6, 1);\r
5362 }\r
5363 \r
5364 /* Initialize FAT area */\r
5365 wsect = b_fat;\r
5366 for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */\r
5367 mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */\r
5368 n = md; /* Media descriptor byte */\r
5369 if (fmt != FS_FAT32) {\r
5370 n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;\r
5371 st_dword(tbl + 0, n); /* Reserve cluster #0-1 (FAT12/16) */\r
5372 } else {\r
5373 n |= 0xFFFFFF00;\r
5374 st_dword(tbl + 0, n); /* Reserve cluster #0-1 (FAT32) */\r
5375 st_dword(tbl + 4, 0xFFFFFFFF);\r
5376 st_dword(tbl + 8, 0x0FFFFFFF); /* Reserve cluster #2 for root directory */\r
5377 }\r
5378 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) {\r
5379 return FR_DISK_ERR;\r
5380 }\r
5381 mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */\r
5382 for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */\r
5383 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) {\r
5384 return FR_DISK_ERR;\r
5385 }\r
5386 }\r
5387 }\r
5388 \r
5389 /* Initialize root directory */\r
5390 i = (fmt == FS_FAT32) ? au : (UINT)n_dir;\r
5391 do {\r
5392 if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) {\r
5393 return FR_DISK_ERR;\r
5394 }\r
5395 } while (--i);\r
5396 \r
5397 #if _USE_TRIM /* Erase data area if needed */\r
5398 {\r
5399 eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;\r
5400 disk_ioctl(pdrv, CTRL_TRIM, eb);\r
5401 }\r
5402 #endif\r
5403 \r
5404 /* Create FSINFO if needed */\r
5405 if (fmt == FS_FAT32) {\r
5406 st_dword(tbl + FSI_LeadSig, 0x41615252);\r
5407 st_dword(tbl + FSI_StrucSig, 0x61417272);\r
5408 st_dword(tbl + FSI_Free_Count, n_clst - 1); /* Number of free clusters */\r
5409 st_dword(tbl + FSI_Nxt_Free, 2); /* Last allocated cluster# */\r
5410 st_word(tbl + BS_55AA, 0xAA55);\r
5411 disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR + 1) */\r
5412 disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR + 7) */\r
5413 }\r
5414 \r
5415 return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;\r
5416 }\r
5417 \r
5418 \r
5419 \r
5420 #if _MULTI_PARTITION\r
5421 /*-----------------------------------------------------------------------*/\r
5422 /* Create partition table on the physical drive */\r
5423 /*-----------------------------------------------------------------------*/\r
5424 \r
5425 FRESULT f_fdisk (\r
5426 BYTE pdrv, /* Physical drive number */\r
5427 const DWORD szt[], /* Pointer to the size table for each partitions */\r
5428 void* work /* Pointer to the working buffer */\r
5429 )\r
5430 {\r
5431 UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;\r
5432 BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;\r
5433 DSTATUS stat;\r
5434 DWORD sz_disk, sz_part, s_part;\r
5435 \r
5436 \r
5437 stat = disk_initialize(pdrv);\r
5438 if (stat & STA_NOINIT) return FR_NOT_READY;\r
5439 if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;\r
5440 if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;\r
5441 \r
5442 /* Determine CHS in the table regardless of the drive geometry */\r
5443 for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;\r
5444 if (n == 256) n--;\r
5445 e_hd = n - 1;\r
5446 sz_cyl = 63 * n;\r
5447 tot_cyl = sz_disk / sz_cyl;\r
5448 \r
5449 /* Create partition table */\r
5450 mem_set(buf, 0, _MAX_SS);\r
5451 p = buf + MBR_Table; b_cyl = 0;\r
5452 for (i = 0; i < 4; i++, p += SZ_PTE) {\r
5453 p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;\r
5454 if (!p_cyl) continue;\r
5455 s_part = (DWORD)sz_cyl * b_cyl;\r
5456 sz_part = (DWORD)sz_cyl * p_cyl;\r
5457 if (i == 0) { /* Exclude first track of cylinder 0 */\r
5458 s_hd = 1;\r
5459 s_part += 63; sz_part -= 63;\r
5460 } else {\r
5461 s_hd = 0;\r
5462 }\r
5463 e_cyl = b_cyl + p_cyl - 1;\r
5464 if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;\r
5465 \r
5466 /* Set partition table */\r
5467 p[1] = s_hd; /* Start head */\r
5468 p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */\r
5469 p[3] = (BYTE)b_cyl; /* Start cylinder */\r
5470 p[4] = 0x06; /* System type (temporary setting) */\r
5471 p[5] = e_hd; /* End head */\r
5472 p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */\r
5473 p[7] = (BYTE)e_cyl; /* End cylinder */\r
5474 st_dword(p + 8, s_part); /* Start sector in LBA */\r
5475 st_dword(p + 12, sz_part); /* Partition size */\r
5476 \r
5477 /* Next partition */\r
5478 b_cyl += p_cyl;\r
5479 }\r
5480 st_word(p, 0xAA55);\r
5481 \r
5482 /* Write it to the MBR */\r
5483 return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK;\r
5484 }\r
5485 \r
5486 \r
5487 #endif /* _MULTI_PARTITION */\r
5488 #endif /* _USE_MKFS && !_FS_READONLY */\r
5489 \r
5490 \r
5491 \r
5492 \r
5493 #if _USE_STRFUNC\r
5494 /*-----------------------------------------------------------------------*/\r
5495 /* Get a string from the file */\r
5496 /*-----------------------------------------------------------------------*/\r
5497 \r
5498 TCHAR* f_gets (\r
5499 TCHAR* buff, /* Pointer to the string buffer to read */\r
5500 int len, /* Size of string buffer (characters) */\r
5501 FIL* fp /* Pointer to the file object */\r
5502 )\r
5503 {\r
5504 int n = 0;\r
5505 TCHAR c, *p = buff;\r
5506 BYTE s[2];\r
5507 UINT rc;\r
5508 \r
5509 \r
5510 while (n < len - 1) { /* Read characters until buffer gets filled */\r
5511 #if _LFN_UNICODE\r
5512 #if _STRF_ENCODE == 3 /* Read a character in UTF-8 */\r
5513 f_read(fp, s, 1, &rc);\r
5514 if (rc != 1) break;\r
5515 c = s[0];\r
5516 if (c >= 0x80) {\r
5517 if (c < 0xC0) continue; /* Skip stray trailer */\r
5518 if (c < 0xE0) { /* Two-byte sequence */\r
5519 f_read(fp, s, 1, &rc);\r
5520 if (rc != 1) break;\r
5521 c = (c & 0x1F) << 6 | (s[0] & 0x3F);\r
5522 if (c < 0x80) c = '?';\r
5523 } else {\r
5524 if (c < 0xF0) { /* Three-byte sequence */\r
5525 f_read(fp, s, 2, &rc);\r
5526 if (rc != 2) break;\r
5527 c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F);\r
5528 if (c < 0x800) c = '?';\r
5529 } else { /* Reject four-byte sequence */\r
5530 c = '?';\r
5531 }\r
5532 }\r
5533 }\r
5534 #elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */\r
5535 f_read(fp, s, 2, &rc);\r
5536 if (rc != 2) break;\r
5537 c = s[1] + (s[0] << 8);\r
5538 #elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */\r
5539 f_read(fp, s, 2, &rc);\r
5540 if (rc != 2) break;\r
5541 c = s[0] + (s[1] << 8);\r
5542 #else /* Read a character in ANSI/OEM */\r
5543 f_read(fp, s, 1, &rc);\r
5544 if (rc != 1) break;\r
5545 c = s[0];\r
5546 if (IsDBCS1(c)) {\r
5547 f_read(fp, s, 1, &rc);\r
5548 if (rc != 1) break;\r
5549 c = (c << 8) + s[0];\r
5550 }\r
5551 c = ff_convert(c, 1); /* OEM -> Unicode */\r
5552 if (!c) c = '?';\r
5553 #endif\r
5554 #else /* Read a character without conversion */\r
5555 f_read(fp, s, 1, &rc);\r
5556 if (rc != 1) break;\r
5557 c = s[0];\r
5558 #endif\r
5559 if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */\r
5560 *p++ = c;\r
5561 n++;\r
5562 if (c == '\n') break; /* Break on EOL */\r
5563 }\r
5564 *p = 0;\r
5565 return n ? buff : 0; /* When no data read (eof or error), return with error. */\r
5566 }\r
5567 \r
5568 \r
5569 \r
5570 \r
5571 #if !_FS_READONLY\r
5572 #include <stdarg.h>\r
5573 /*-----------------------------------------------------------------------*/\r
5574 /* Put a character to the file */\r
5575 /*-----------------------------------------------------------------------*/\r
5576 \r
5577 typedef struct {\r
5578 FIL* fp;\r
5579 int idx, nchr;\r
5580 BYTE buf[64];\r
5581 } putbuff;\r
5582 \r
5583 \r
5584 static\r
5585 void putc_bfd (\r
5586 putbuff* pb,\r
5587 TCHAR c\r
5588 )\r
5589 {\r
5590 UINT bw;\r
5591 int i;\r
5592 \r
5593 \r
5594 if (_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */\r
5595 putc_bfd(pb, '\r');\r
5596 }\r
5597 \r
5598 i = pb->idx; /* Buffer write index (-1:error) */\r
5599 if (i < 0) return;\r
5600 \r
5601 #if _LFN_UNICODE\r
5602 #if _STRF_ENCODE == 3 /* Write a character in UTF-8 */\r
5603 if (c < 0x80) { /* 7-bit */\r
5604 pb->buf[i++] = (BYTE)c;\r
5605 } else {\r
5606 if (c < 0x800) { /* 11-bit */\r
5607 pb->buf[i++] = (BYTE)(0xC0 | c >> 6);\r
5608 } else { /* 16-bit */\r
5609 pb->buf[i++] = (BYTE)(0xE0 | c >> 12);\r
5610 pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F));\r
5611 }\r
5612 pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F));\r
5613 }\r
5614 #elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */\r
5615 pb->buf[i++] = (BYTE)(c >> 8);\r
5616 pb->buf[i++] = (BYTE)c;\r
5617 #elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */\r
5618 pb->buf[i++] = (BYTE)c;\r
5619 pb->buf[i++] = (BYTE)(c >> 8);\r
5620 #else /* Write a character in ANSI/OEM */\r
5621 c = ff_convert(c, 0); /* Unicode -> OEM */\r
5622 if (!c) c = '?';\r
5623 if (c >= 0x100)\r
5624 pb->buf[i++] = (BYTE)(c >> 8);\r
5625 pb->buf[i++] = (BYTE)c;\r
5626 #endif\r
5627 #else /* Write a character without conversion */\r
5628 pb->buf[i++] = (BYTE)c;\r
5629 #endif\r
5630 \r
5631 if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */\r
5632 f_write(pb->fp, pb->buf, (UINT)i, &bw);\r
5633 i = (bw == (UINT)i) ? 0 : -1;\r
5634 }\r
5635 pb->idx = i;\r
5636 pb->nchr++;\r
5637 }\r
5638 \r
5639 \r
5640 \r
5641 int f_putc (\r
5642 TCHAR c, /* A character to be output */\r
5643 FIL* fp /* Pointer to the file object */\r
5644 )\r
5645 {\r
5646 putbuff pb;\r
5647 UINT nw;\r
5648 \r
5649 \r
5650 pb.fp = fp; /* Initialize output buffer */\r
5651 pb.nchr = pb.idx = 0;\r
5652 \r
5653 putc_bfd(&pb, c); /* Put a character */\r
5654 \r
5655 if ( pb.idx >= 0 /* Flush buffered characters to the file */\r
5656 && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK\r
5657 && (UINT)pb.idx == nw) return pb.nchr;\r
5658 return EOF;\r
5659 }\r
5660 \r
5661 \r
5662 \r
5663 \r
5664 /*-----------------------------------------------------------------------*/\r
5665 /* Put a string to the file */\r
5666 /*-----------------------------------------------------------------------*/\r
5667 \r
5668 int f_puts (\r
5669 const TCHAR* str, /* Pointer to the string to be output */\r
5670 FIL* fp /* Pointer to the file object */\r
5671 )\r
5672 {\r
5673 putbuff pb;\r
5674 UINT nw;\r
5675 \r
5676 \r
5677 pb.fp = fp; /* Initialize output buffer */\r
5678 pb.nchr = pb.idx = 0;\r
5679 \r
5680 while (*str) /* Put the string */\r
5681 putc_bfd(&pb, *str++);\r
5682 \r
5683 if ( pb.idx >= 0 /* Flush buffered characters to the file */\r
5684 && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK\r
5685 && (UINT)pb.idx == nw) return pb.nchr;\r
5686 return EOF;\r
5687 }\r
5688 \r
5689 \r
5690 \r
5691 \r
5692 /*-----------------------------------------------------------------------*/\r
5693 /* Put a formatted string to the file */\r
5694 /*-----------------------------------------------------------------------*/\r
5695 \r
5696 int f_printf (\r
5697 FIL* fp, /* Pointer to the file object */\r
5698 const TCHAR* fmt, /* Pointer to the format string */\r
5699 ... /* Optional arguments... */\r
5700 )\r
5701 {\r
5702 va_list arp;\r
5703 BYTE f, r;\r
5704 UINT nw, i, j, w;\r
5705 DWORD v;\r
5706 TCHAR c, d, str[32], *p;\r
5707 putbuff pb;\r
5708 \r
5709 \r
5710 pb.fp = fp; /* Initialize output buffer */\r
5711 pb.nchr = pb.idx = 0;\r
5712 \r
5713 va_start(arp, fmt);\r
5714 \r
5715 for (;;) {\r
5716 c = *fmt++;\r
5717 if (c == 0) break; /* End of string */\r
5718 if (c != '%') { /* Non escape character */\r
5719 putc_bfd(&pb, c);\r
5720 continue;\r
5721 }\r
5722 w = f = 0;\r
5723 c = *fmt++;\r
5724 if (c == '0') { /* Flag: '0' padding */\r
5725 f = 1; c = *fmt++;\r
5726 } else {\r
5727 if (c == '-') { /* Flag: left justified */\r
5728 f = 2; c = *fmt++;\r
5729 }\r
5730 }\r
5731 while (IsDigit(c)) { /* Precision */\r
5732 w = w * 10 + c - '0';\r
5733 c = *fmt++;\r
5734 }\r
5735 if (c == 'l' || c == 'L') { /* Prefix: Size is long int */\r
5736 f |= 4; c = *fmt++;\r
5737 }\r
5738 if (!c) break;\r
5739 d = c;\r
5740 if (IsLower(d)) d -= 0x20;\r
5741 switch (d) { /* Type is... */\r
5742 case 'S' : /* String */\r
5743 p = va_arg(arp, TCHAR*);\r
5744 for (j = 0; p[j]; j++) ;\r
5745 if (!(f & 2)) {\r
5746 while (j++ < w) putc_bfd(&pb, ' ');\r
5747 }\r
5748 while (*p) putc_bfd(&pb, *p++);\r
5749 while (j++ < w) putc_bfd(&pb, ' ');\r
5750 continue;\r
5751 case 'C' : /* Character */\r
5752 putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;\r
5753 case 'B' : /* Binary */\r
5754 r = 2; break;\r
5755 case 'O' : /* Octal */\r
5756 r = 8; break;\r
5757 case 'D' : /* Signed decimal */\r
5758 case 'U' : /* Unsigned decimal */\r
5759 r = 10; break;\r
5760 case 'X' : /* Hexdecimal */\r
5761 r = 16; break;\r
5762 default: /* Unknown type (pass-through) */\r
5763 putc_bfd(&pb, c); continue;\r
5764 }\r
5765 \r
5766 /* Get an argument and put it in numeral */\r
5767 v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));\r
5768 if (d == 'D' && (v & 0x80000000)) {\r
5769 v = 0 - v;\r
5770 f |= 8;\r
5771 }\r
5772 i = 0;\r
5773 do {\r
5774 d = (TCHAR)(v % r); v /= r;\r
5775 if (d > 9) d += (c == 'x') ? 0x27 : 0x07;\r
5776 str[i++] = d + '0';\r
5777 } while (v && i < sizeof str / sizeof str[0]);\r
5778 if (f & 8) str[i++] = '-';\r
5779 j = i; d = (f & 1) ? '0' : ' ';\r
5780 while (!(f & 2) && j++ < w) putc_bfd(&pb, d);\r
5781 do putc_bfd(&pb, str[--i]); while (i);\r
5782 while (j++ < w) putc_bfd(&pb, d);\r
5783 }\r
5784 \r
5785 va_end(arp);\r
5786 \r
5787 if ( pb.idx >= 0 /* Flush buffered characters to the file */\r
5788 && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK\r
5789 && (UINT)pb.idx == nw) return pb.nchr;\r
5790 return EOF;\r
5791 }\r
5792 \r
5793 #endif /* !_FS_READONLY */\r
5794 #endif /* _USE_STRFUNC */\r