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