source: EcnlProtoTool/trunk/tools/makefsdata/ff.c@ 445

Last change on this file since 445 was 445, checked in by coas-nagasima, 4 years ago

ROMファイルシステムのWebコンテンツをwwwフォルダ下に移動。
etcフォルダ追加。

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