]>
cloudbase.mooo.com Git - z180-stamp.git/blob - fatfs/source/ff.c
1 /*----------------------------------------------------------------------------/
2 / FatFs - Generic FAT Filesystem Module R0.13b /
3 /-----------------------------------------------------------------------------/
5 / Copyright (C) 2018, ChaN, all right reserved.
7 / FatFs module is an open source software. Redistribution and use of FatFs in
8 / source and binary forms, with or without modification, are permitted provided
9 / that the following condition is met:
11 / 1. Redistributions of source code must retain the above copyright notice,
12 / this condition and the following disclaimer.
14 / This software is provided by the copyright holder and contributors "AS IS"
15 / and any warranties related to this software are DISCLAIMED.
16 / The copyright owner or contributors be NOT LIABLE for any damages caused
17 / by use of this software.
19 /----------------------------------------------------------------------------*/
22 #include "ff.h" /* Declarations of FatFs API */
23 #include "diskio.h" /* Declarations of device I/O functions */
26 /*--------------------------------------------------------------------------
28 Module Private Definitions
30 ---------------------------------------------------------------------------*/
32 #if FF_DEFINED != 63463 /* Revision ID */
33 #error Wrong include file (ff.h).
37 /* Character code support macros */
38 #define IsUpper(c) ((c) >= 'A' && (c) <= 'Z' )
39 #define IsLower(c) ((c) >= 'a' && (c) <= 'z' )
40 #define IsDigit(c) ((c) >= '0' && (c) <= '9' )
41 #define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
42 #define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
43 #define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
46 /* Additional file attribute bits for internal use */
47 #define AM_VOL 0x08 /* Volume label */
48 #define AM_LFN 0x0F /* LFN entry */
49 #define AM_MASK 0x3F /* Mask of defined bits */
52 /* Additional file access control and file status flags for internal use */
53 #define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
54 #define FA_MODIFIED 0x40 /* File has been modified */
55 #define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
58 /* Name status flags in fn[11] */
59 #define NSFLAG 11 /* Index of the name status byte */
60 #define NS_LOSS 0x01 /* Out of 8.3 format */
61 #define NS_LFN 0x02 /* Force to create LFN entry */
62 #define NS_LAST 0x04 /* Last segment */
63 #define NS_BODY 0x08 /* Lower case flag (body) */
64 #define NS_EXT 0x10 /* Lower case flag (ext) */
65 #define NS_DOT 0x20 /* Dot entry */
66 #define NS_NOLFN 0x40 /* Do not find LFN */
67 #define NS_NONAME 0x80 /* Not followed */
70 /* Limits and boundaries */
71 #define MAX_DIR 0x200000 /* Max size of FAT directory */
72 #define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
73 #define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
74 #define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
75 #define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
76 #define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
79 /* FatFs refers the FAT structure as simple byte array instead of structure member
80 / because the C structure is not binary compatible between different platforms */
82 #define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
83 #define BS_OEMName 3 /* OEM name (8-byte) */
84 #define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
85 #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
86 #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
87 #define BPB_NumFATs 16 /* Number of FATs (BYTE) */
88 #define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
89 #define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
90 #define BPB_Media 21 /* Media descriptor byte (BYTE) */
91 #define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
92 #define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
93 #define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
94 #define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
95 #define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
96 #define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
97 #define BS_NTres 37 /* WindowsNT error flag (BYTE) */
98 #define BS_BootSig 38 /* Extended boot signature (BYTE) */
99 #define BS_VolID 39 /* Volume serial number (DWORD) */
100 #define BS_VolLab 43 /* Volume label string (8-byte) */
101 #define BS_FilSysType 54 /* Filesystem type string (8-byte) */
102 #define BS_BootCode 62 /* Boot code (448-byte) */
103 #define BS_55AA 510 /* Signature word (WORD) */
105 #define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
106 #define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
107 #define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
108 #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
109 #define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
110 #define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
111 #define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
112 #define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
113 #define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
114 #define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
115 #define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
116 #define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
117 #define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
119 #define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
120 #define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
121 #define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
122 #define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
123 #define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
124 #define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
125 #define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
126 #define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
127 #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
128 #define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
129 #define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */
130 #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
131 #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
132 #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
133 #define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
134 #define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
135 #define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
136 #define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
138 #define DIR_Name 0 /* Short file name (11-byte) */
139 #define DIR_Attr 11 /* Attribute (BYTE) */
140 #define DIR_NTres 12 /* Lower case flag (BYTE) */
141 #define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
142 #define DIR_CrtTime 14 /* Created time (DWORD) */
143 #define DIR_LstAccDate 18 /* Last accessed date (WORD) */
144 #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
145 #define DIR_ModTime 22 /* Modified time (DWORD) */
146 #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
147 #define DIR_FileSize 28 /* File size (DWORD) */
148 #define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
149 #define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
150 #define LDIR_Type 12 /* LFN: Entry type (BYTE) */
151 #define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
152 #define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
153 #define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
154 #define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
155 #define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
156 #define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
157 #define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
158 #define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
159 #define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
160 #define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
161 #define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
162 #define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
163 #define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
164 #define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
165 #define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
166 #define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
167 #define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
168 #define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
169 #define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
170 #define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
171 #define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
172 #define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
173 #define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
175 #define SZDIRE 32 /* Size of a directory entry */
176 #define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
177 #define RDDEM 0x05 /* Replacement of the character collides with DDEM */
178 #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
180 #define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
181 #define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
182 #define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
183 #define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
185 #define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
186 #define SZ_PTE 16 /* MBR: Size of a partition table entry */
187 #define PTE_Boot 0 /* MBR PTE: Boot indicator */
188 #define PTE_StHead 1 /* MBR PTE: Start head */
189 #define PTE_StSec 2 /* MBR PTE: Start sector */
190 #define PTE_StCyl 3 /* MBR PTE: Start cylinder */
191 #define PTE_System 4 /* MBR PTE: System ID */
192 #define PTE_EdHead 5 /* MBR PTE: End head */
193 #define PTE_EdSec 6 /* MBR PTE: End sector */
194 #define PTE_EdCyl 7 /* MBR PTE: End cylinder */
195 #define PTE_StLba 8 /* MBR PTE: Start in LBA */
196 #define PTE_SizLba 12 /* MBR PTE: Size in LBA */
199 /* Post process on fatal error in the file operations */
200 #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
203 /* Re-entrancy related */
206 #error Static LFN work area cannot be used at thread-safe configuration
208 #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
210 #define LEAVE_FF(fs, res) return res
214 /* Definitions of volume - physical location conversion */
215 #if FF_MULTI_PARTITION
216 #define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
217 #define LD2PT(vol) VolToPart[vol].pt /* Get partition index */
219 #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
220 #define LD2PT(vol) 0 /* Find first valid partition or in SFD */
224 /* Definitions of sector size */
225 #if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
226 #error Wrong sector size configuration
228 #if FF_MAX_SS == FF_MIN_SS
229 #define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
231 #define SS(fs) ((fs)->ssize) /* Variable sector size */
237 #if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
238 #error Invalid FF_FS_NORTC settings
240 #define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
242 #define GET_FATTIME() get_fattime()
246 /* File lock controls */
249 #error FF_FS_LOCK must be 0 at read-only configuration
252 FATFS
* fs
; /* Object ID 1, volume (NULL:blank entry) */
253 DWORD clu
; /* Object ID 2, containing directory (0:root) */
254 DWORD ofs
; /* Object ID 3, offset in the directory */
255 WORD ctr
; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
260 /* SBCS up-case tables (\x80-\xFF) */
261 #define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
262 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
263 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
264 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
265 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
266 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
267 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
268 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
269 #define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
270 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
271 0xA0,0xA1,0xA2,0xA3,0xA4,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 #define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
278 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
279 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
280 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
281 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
282 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
283 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
284 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
285 #define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
286 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
287 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
288 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
289 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
290 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
291 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
292 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
293 #define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
294 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
295 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
296 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
297 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
298 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
299 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
300 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
301 #define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
302 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
303 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
304 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
305 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
306 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
307 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
308 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
309 #define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
310 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
311 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
312 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
313 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
314 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
315 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
316 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
317 #define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
318 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
319 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
320 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
321 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
322 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
323 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
324 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
325 #define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
326 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
327 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
328 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
329 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
330 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
331 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
332 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
333 #define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
334 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
335 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
336 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
337 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
338 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
339 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
340 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
341 #define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
342 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
343 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
344 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
345 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
346 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
347 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
348 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
349 #define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
350 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
351 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
352 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
353 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
354 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
355 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
356 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
357 #define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
358 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
359 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
360 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
361 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
362 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
363 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
364 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
365 #define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
366 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
367 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
368 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
369 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
370 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
371 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
372 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
373 #define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
374 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
375 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
376 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
377 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
378 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
379 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
380 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
381 #define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
382 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
383 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
384 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
385 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
386 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
387 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
388 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
389 #define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
390 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
391 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
392 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
393 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
394 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
395 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
396 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
399 /* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
400 #define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
401 #define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
402 #define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
403 #define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
406 /* Macros for table definitions */
407 #define MERGE_2STR(a, b) a ## b
408 #define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
413 /*--------------------------------------------------------------------------
415 Module Private Work Area
417 ---------------------------------------------------------------------------*/
418 /* Remark: Variables defined here without initial value shall be guaranteed
419 / zero/null at start-up. If not, the linker option or start-up routine is
420 / not compliance with C standard. */
422 /*--------------------------------*/
423 /* File/Volume controls */
424 /*--------------------------------*/
426 #if FF_VOLUMES < 1 || FF_VOLUMES > 10
427 #error Wrong FF_VOLUMES setting
429 static FATFS
* FatFs
[ FF_VOLUMES
]; /* Pointer to the filesystem objects (logical drives) */
430 static WORD Fsid
; /* Filesystem mount ID */
433 static BYTE CurrVol
; /* Current drive */
437 static FILESEM Files
[ FF_FS_LOCK
]; /* Open object lock semaphores */
441 #ifdef FF_VOLUME_STRS
442 static const char * const VolumeStr
[ FF_VOLUMES
] = { FF_VOLUME_STRS
}; /* Pre-defined volume ID */
447 /*--------------------------------*/
448 /* LFN/Directory working buffer */
449 /*--------------------------------*/
451 #if FF_USE_LFN == 0 /* Non-LFN configuration */
453 #error LFN must be enabled when enable exFAT
456 #define INIT_NAMBUF(fs)
457 #define FREE_NAMBUF()
458 #define LEAVE_MKFS(res) return res
460 #else /* LFN configurations */
461 #if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
462 #error Wrong setting of FF_MAX_LFN
464 #if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
465 #error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
467 #if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
468 #error Wrong setting of FF_LFN_UNICODE
470 static const BYTE LfnOfs
[] = { 1 , 3 , 5 , 7 , 9 , 14 , 16 , 18 , 20 , 22 , 24 , 28 , 30 }; /* FAT: Offset of LFN characters in the directory entry */
471 #define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
473 #if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
475 static BYTE DirBuf
[ MAXDIRB ( FF_MAX_LFN
)]; /* Directory entry block scratchpad buffer */
477 static WCHAR LfnBuf
[ FF_MAX_LFN
+ 1 ]; /* LFN working buffer */
479 #define INIT_NAMBUF(fs)
480 #define FREE_NAMBUF()
481 #define LEAVE_MKFS(res) return res
483 #elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
485 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
486 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
487 #define FREE_NAMBUF()
489 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
490 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; }
491 #define FREE_NAMBUF()
493 #define LEAVE_MKFS(res) return res
495 #elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
497 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
498 #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); }
499 #define FREE_NAMBUF() ff_memfree(lfn)
501 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */
502 #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
503 #define FREE_NAMBUF() ff_memfree(lfn)
505 #define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
506 #define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
509 #error Wrong setting of FF_USE_LFN
511 #endif /* FF_USE_LFN == 1 */
512 #endif /* FF_USE_LFN == 0 */
516 /*--------------------------------*/
517 /* Code conversion tables */
518 /*--------------------------------*/
520 #if FF_CODE_PAGE == 0 /* Run-time code page configuration */
521 #define CODEPAGE CodePage
522 static WORD CodePage
; /* Current code page */
523 static const BYTE
* ExCvt
, * DbcTbl
; /* Pointer to current SBCS up-case table and DBCS code range table below */
524 static const BYTE Ct437
[] = TBL_CT437
;
525 static const BYTE Ct720
[] = TBL_CT720
;
526 static const BYTE Ct737
[] = TBL_CT737
;
527 static const BYTE Ct771
[] = TBL_CT771
;
528 static const BYTE Ct775
[] = TBL_CT775
;
529 static const BYTE Ct850
[] = TBL_CT850
;
530 static const BYTE Ct852
[] = TBL_CT852
;
531 static const BYTE Ct855
[] = TBL_CT855
;
532 static const BYTE Ct857
[] = TBL_CT857
;
533 static const BYTE Ct860
[] = TBL_CT860
;
534 static const BYTE Ct861
[] = TBL_CT861
;
535 static const BYTE Ct862
[] = TBL_CT862
;
536 static const BYTE Ct863
[] = TBL_CT863
;
537 static const BYTE Ct864
[] = TBL_CT864
;
538 static const BYTE Ct865
[] = TBL_CT865
;
539 static const BYTE Ct866
[] = TBL_CT866
;
540 static const BYTE Ct869
[] = TBL_CT869
;
541 static const BYTE Dc932
[] = TBL_DC932
;
542 static const BYTE Dc936
[] = TBL_DC936
;
543 static const BYTE Dc949
[] = TBL_DC949
;
544 static const BYTE Dc950
[] = TBL_DC950
;
546 #elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
547 #define CODEPAGE FF_CODE_PAGE
548 static const BYTE ExCvt
[] = MKCVTBL ( TBL_CT
, FF_CODE_PAGE
);
550 #else /* Static code page configuration (DBCS) */
551 #define CODEPAGE FF_CODE_PAGE
552 static const BYTE DbcTbl
[] = MKCVTBL ( TBL_DC
, FF_CODE_PAGE
);
559 /*--------------------------------------------------------------------------
561 Module Private Functions
563 ---------------------------------------------------------------------------*/
566 /*-----------------------------------------------------------------------*/
567 /* Load/Store multi-byte word in the FAT structure */
568 /*-----------------------------------------------------------------------*/
570 static WORD
ld_word ( const BYTE
* ptr
) /* Load a 2-byte little-endian word */
575 rv
= rv
<< 8 | ptr
[ 0 ];
579 static DWORD
ld_dword ( const BYTE
* ptr
) /* Load a 4-byte little-endian word */
584 rv
= rv
<< 8 | ptr
[ 2 ];
585 rv
= rv
<< 8 | ptr
[ 1 ];
586 rv
= rv
<< 8 | ptr
[ 0 ];
591 static QWORD
ld_qword ( const BYTE
* ptr
) /* Load an 8-byte little-endian word */
596 rv
= rv
<< 8 | ptr
[ 6 ];
597 rv
= rv
<< 8 | ptr
[ 5 ];
598 rv
= rv
<< 8 | ptr
[ 4 ];
599 rv
= rv
<< 8 | ptr
[ 3 ];
600 rv
= rv
<< 8 | ptr
[ 2 ];
601 rv
= rv
<< 8 | ptr
[ 1 ];
602 rv
= rv
<< 8 | ptr
[ 0 ];
608 static void st_word ( BYTE
* ptr
, WORD val
) /* Store a 2-byte word in little-endian */
610 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
614 static void st_dword ( BYTE
* ptr
, DWORD val
) /* Store a 4-byte word in little-endian */
616 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
617 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
618 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
623 static void st_qword ( BYTE
* ptr
, QWORD val
) /* Store an 8-byte word in little-endian */
625 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
626 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
627 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
628 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
629 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
630 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
631 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
635 #endif /* !FF_FS_READONLY */
639 /*-----------------------------------------------------------------------*/
640 /* String functions */
641 /*-----------------------------------------------------------------------*/
643 /* Copy memory to memory */
644 static void mem_cpy ( void * dst
, const void * src
, UINT cnt
)
646 BYTE
* d
= ( BYTE
*) dst
;
647 const BYTE
* s
= ( const BYTE
*) src
;
657 /* Fill memory block */
658 static void mem_set ( void * dst
, int val
, UINT cnt
)
660 BYTE
* d
= ( BYTE
*) dst
;
668 /* Compare memory block */
669 static int mem_cmp ( const void * dst
, const void * src
, UINT cnt
) /* ZR:same, NZ:different */
671 const BYTE
* d
= ( const BYTE
*) dst
, * s
= ( const BYTE
*) src
;
676 } while (-- cnt
&& r
== 0 );
682 /* Check if chr is contained in the string */
683 static int chk_chr ( const char * str
, int chr
) /* NZ:contained, ZR:not contained */
685 while (* str
&& * str
!= chr
) str
++;
690 /* Test if the character is DBC 1st byte */
691 static int dbc_1st ( BYTE c
)
693 #if FF_CODE_PAGE == 0 /* Variable code page */
694 if ( DbcTbl
&& c
>= DbcTbl
[ 0 ]) {
695 if ( c
<= DbcTbl
[ 1 ]) return 1 ; /* 1st byte range 1 */
696 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ; /* 1st byte range 2 */
698 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
699 if ( c
>= DbcTbl
[ 0 ]) {
700 if ( c
<= DbcTbl
[ 1 ]) return 1 ;
701 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ;
703 #else /* SBCS fixed code page */
704 if ( c
!= 0 ) return 0 ; /* Always false */
710 /* Test if the character is DBC 2nd byte */
711 static int dbc_2nd ( BYTE c
)
713 #if FF_CODE_PAGE == 0 /* Variable code page */
714 if ( DbcTbl
&& c
>= DbcTbl
[ 4 ]) {
715 if ( c
<= DbcTbl
[ 5 ]) return 1 ; /* 2nd byte range 1 */
716 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ; /* 2nd byte range 2 */
717 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ; /* 2nd byte range 3 */
719 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
720 if ( c
>= DbcTbl
[ 4 ]) {
721 if ( c
<= DbcTbl
[ 5 ]) return 1 ;
722 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ;
723 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ;
725 #else /* SBCS fixed code page */
726 if ( c
!= 0 ) return 0 ; /* Always false */
734 /* Get a character from TCHAR string in defined API encodeing */
735 static DWORD
tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */
736 const TCHAR
** str
/* Pointer to pointer to TCHAR string in configured encoding */
740 const TCHAR
* p
= * str
;
742 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
745 uc
= * p
++; /* Get a unit */
746 if ( IsSurrogate ( uc
)) { /* Surrogate? */
747 wc
= * p
++; /* Get low surrogate */
748 if (! IsSurrogateH ( uc
) || ! IsSurrogateL ( wc
)) return 0xFFFFFFFF ; /* Wrong surrogate? */
752 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
756 uc
= ( BYTE
)* p
++; /* Get a unit */
757 if ( uc
& 0x80 ) { /* Multiple byte code? */
758 if (( uc
& 0xE0 ) == 0xC0 ) { /* 2-byte sequence? */
761 if (( uc
& 0xF0 ) == 0xE0 ) { /* 3-byte sequence? */
764 if (( uc
& 0xF8 ) == 0xF0 ) { /* 4-byte sequence? */
766 } else { /* Wrong sequence */
771 do { /* Get trailing bytes */
773 if (( b
& 0xC0 ) != 0x80 ) return 0xFFFFFFFF ; /* Wrong sequence? */
774 uc
= uc
<< 6 | ( b
& 0x3F );
776 if ( uc
< 0x80 || IsSurrogate ( uc
) || uc
>= 0x110000 ) return 0xFFFFFFFF ; /* Wrong code? */
777 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
780 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
781 uc
= ( TCHAR
)* p
++; /* Get a unit */
782 if ( uc
>= 0x110000 ) return 0xFFFFFFFF ; /* Wrong code? */
783 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
785 #else /* ANSI/OEM input */
789 wc
= ( BYTE
)* p
++; /* Get a byte */
790 if ( dbc_1st (( BYTE
) wc
)) { /* Is it a DBC 1st byte? */
791 b
= ( BYTE
)* p
++; /* Get 2nd byte */
792 if (! dbc_2nd ( b
)) return 0xFFFFFFFF ; /* Invalid code? */
793 wc
= ( wc
<< 8 ) + b
; /* Make a DBC */
796 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM ==> Unicode */
797 if ( wc
== 0 ) return 0xFFFFFFFF ; /* Invalid code? */
802 * str
= p
; /* Next read pointer */
807 /* Output a TCHAR string in defined API encoding */
808 static BYTE
put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
809 DWORD chr
, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */
810 TCHAR
* buf
, /* Output buffer */
811 UINT szb
/* Size of the buffer */
814 #if FF_LFN_UNICODE == 1 /* UTF-16 output */
817 hs
= ( WCHAR
)( chr
>> 16 );
819 if ( hs
== 0 ) { /* Single encoding unit? */
820 if ( szb
< 1 || IsSurrogate ( wc
)) return 0 ; /* Buffer overflow or wrong code? */
824 if ( szb
< 2 || ! IsSurrogateH ( hs
) || ! IsSurrogateL ( wc
)) return 0 ; /* Buffer overflow or wrong surrogate? */
829 #elif FF_LFN_UNICODE == 2 /* UTF-8 output */
832 if ( chr
< 0x80 ) { /* Single byte code? */
833 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
837 if ( chr
< 0x800 ) { /* 2-byte sequence? */
838 if ( szb
< 2 ) return 0 ; /* Buffer overflow? */
839 * buf
++ = ( TCHAR
)( 0xC0 | ( chr
>> 6 & 0x1F ));
840 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
843 if ( chr
< 0x10000 ) { /* 3-byte sequence? */
844 if ( szb
< 3 || IsSurrogate ( chr
)) return 0 ; /* Buffer overflow or wrong code? */
845 * buf
++ = ( TCHAR
)( 0xE0 | ( chr
>> 12 & 0x0F ));
846 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
847 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
850 /* 4-byte sequence */
851 if ( szb
< 4 ) return 0 ; /* Buffer overflow? */
852 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
853 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
854 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
855 chr
= ( hc
| chr
) + 0x10000 ;
856 * buf
++ = ( TCHAR
)( 0xF0 | ( chr
>> 18 & 0x07 ));
857 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 12 & 0x3F ));
858 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
859 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
862 #elif FF_LFN_UNICODE == 3 /* UTF-32 output */
865 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
866 if ( chr
>= 0x10000 ) { /* Out of BMP? */
867 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
868 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
869 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
870 chr
= ( hc
| chr
) + 0x10000 ;
875 #else /* ANSI/OEM output */
878 wc
= ff_uni2oem ( chr
, CODEPAGE
);
879 if ( wc
>= 0x100 ) { /* Is this a DBC? */
880 if ( szb
< 2 ) return 0 ;
881 * buf
++ = ( char )( wc
>> 8 ); /* Store DBC 1st byte */
882 * buf
++ = ( TCHAR
) wc
; /* Store DBC 2nd byte */
885 if ( wc
== 0 || szb
< 1 ) return 0 ; /* Invalid char or buffer overflow? */
886 * buf
++ = ( TCHAR
) wc
; /* Store the character */
890 #endif /* FF_USE_LFN */
894 /*-----------------------------------------------------------------------*/
895 /* Request/Release grant to access the volume */
896 /*-----------------------------------------------------------------------*/
897 static int lock_fs ( /* 1:Ok, 0:timeout */
898 FATFS
* fs
/* Filesystem object */
901 return ff_req_grant ( fs
-> sobj
);
905 static void unlock_fs (
906 FATFS
* fs
, /* Filesystem object */
907 FRESULT res
/* Result code to be returned */
910 if ( fs
&& res
!= FR_NOT_ENABLED
&& res
!= FR_INVALID_DRIVE
&& res
!= FR_TIMEOUT
) {
911 ff_rel_grant ( fs
-> sobj
);
920 /*-----------------------------------------------------------------------*/
921 /* File lock control functions */
922 /*-----------------------------------------------------------------------*/
924 static FRESULT
chk_lock ( /* Check if the file can be accessed */
925 DIR * dp
, /* Directory object pointing the file to be checked */
926 int acc
/* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
931 /* Search open object table for the object */
933 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
934 if ( Files
[ i
]. fs
) { /* Existing entry */
935 if ( Files
[ i
]. fs
== dp
-> obj
. fs
&& /* Check if the object matches with an open object */
936 Files
[ i
]. clu
== dp
-> obj
. sclust
&&
937 Files
[ i
]. ofs
== dp
-> dptr
) break ;
938 } else { /* Blank entry */
942 if ( i
== FF_FS_LOCK
) { /* The object has not been opened */
943 return (! be
&& acc
!= 2 ) ? FR_TOO_MANY_OPEN_FILES
: FR_OK
; /* Is there a blank entry for new object? */
946 /* The object was opened. Reject any open against writing file and all write mode open */
947 return ( acc
!= 0 || Files
[ i
]. ctr
== 0x100 ) ? FR_LOCKED
: FR_OK
;
951 static int enq_lock ( void ) /* Check if an entry is available for a new object */
955 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ;
956 return ( i
== FF_FS_LOCK
) ? 0 : 1 ;
960 static UINT
inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
961 DIR * dp
, /* Directory object pointing the file to register or increment */
962 int acc
/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
968 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) { /* Find the object */
969 if ( Files
[ i
]. fs
== dp
-> obj
. fs
&&
970 Files
[ i
]. clu
== dp
-> obj
. sclust
&&
971 Files
[ i
]. ofs
== dp
-> dptr
) break ;
974 if ( i
== FF_FS_LOCK
) { /* Not opened. Register it as new. */
975 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ;
976 if ( i
== FF_FS_LOCK
) return 0 ; /* No free entry to register (int err) */
977 Files
[ i
]. fs
= dp
-> obj
. fs
;
978 Files
[ i
]. clu
= dp
-> obj
. sclust
;
979 Files
[ i
]. ofs
= dp
-> dptr
;
983 if ( acc
>= 1 && Files
[ i
]. ctr
) return 0 ; /* Access violation (int err) */
985 Files
[ i
]. ctr
= acc
? 0x100 : Files
[ i
]. ctr
+ 1 ; /* Set semaphore value */
987 return i
+ 1 ; /* Index number origin from 1 */
991 static FRESULT
dec_lock ( /* Decrement object open counter */
992 UINT i
/* Semaphore index (1..) */
999 if (-- i
< FF_FS_LOCK
) { /* Index number origin from 0 */
1001 if ( n
== 0x100 ) n
= 0 ; /* If write mode open, delete the entry */
1002 if ( n
> 0 ) n
--; /* Decrement read mode open count */
1004 if ( n
== 0 ) Files
[ i
]. fs
= 0 ; /* Delete the entry if open count gets zero */
1007 res
= FR_INT_ERR
; /* Invalid index nunber */
1013 static void clear_lock ( /* Clear lock entries of the volume */
1019 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
1020 if ( Files
[ i
]. fs
== fs
) Files
[ i
]. fs
= 0 ;
1024 #endif /* FF_FS_LOCK != 0 */
1028 /*-----------------------------------------------------------------------*/
1029 /* Move/Flush disk access window in the filesystem object */
1030 /*-----------------------------------------------------------------------*/
1032 static FRESULT
sync_window ( /* Returns FR_OK or FR_DISK_ERR */
1033 FATFS
* fs
/* Filesystem object */
1036 FRESULT res
= FR_OK
;
1039 if ( fs
-> wflag
) { /* Is the disk access window dirty */
1040 if ( disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 ) == RES_OK
) { /* Write back the window */
1041 fs
-> wflag
= 0 ; /* Clear window dirty flag */
1042 if ( fs
-> winsect
- fs
-> fatbase
< fs
-> fsize
) { /* Is it in the 1st FAT? */
1043 if ( fs
-> n_fats
== 2 ) disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
+ fs
-> fsize
, 1 ); /* Reflect it to 2nd FAT if needed */
1054 static FRESULT
move_window ( /* Returns FR_OK or FR_DISK_ERR */
1055 FATFS
* fs
, /* Filesystem object */
1056 DWORD sector
/* Sector number to make appearance in the fs->win[] */
1059 FRESULT res
= FR_OK
;
1062 if ( sector
!= fs
-> winsect
) { /* Window offset changed? */
1064 res
= sync_window ( fs
); /* Write-back changes */
1066 if ( res
== FR_OK
) { /* Fill sector window with new data */
1067 if ( disk_read ( fs
-> pdrv
, fs
-> win
, sector
, 1 ) != RES_OK
) {
1068 sector
= 0xFFFFFFFF ; /* Invalidate window if read data is not valid */
1071 fs
-> winsect
= sector
;
1081 /*-----------------------------------------------------------------------*/
1082 /* Synchronize filesystem and data on the storage */
1083 /*-----------------------------------------------------------------------*/
1085 static FRESULT
sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
1086 FATFS
* fs
/* Filesystem object */
1092 res
= sync_window ( fs
);
1094 if ( fs
-> fs_type
== FS_FAT32
&& fs
-> fsi_flag
== 1 ) { /* FAT32: Update FSInfo sector if needed */
1095 /* Create FSInfo structure */
1096 mem_set ( fs
-> win
, 0 , SS ( fs
));
1097 st_word ( fs
-> win
+ BS_55AA
, 0xAA55 );
1098 st_dword ( fs
-> win
+ FSI_LeadSig
, 0x41615252 );
1099 st_dword ( fs
-> win
+ FSI_StrucSig
, 0x61417272 );
1100 st_dword ( fs
-> win
+ FSI_Free_Count
, fs
-> free_clst
);
1101 st_dword ( fs
-> win
+ FSI_Nxt_Free
, fs
-> last_clst
);
1102 /* Write it into the FSInfo sector */
1103 fs
-> winsect
= fs
-> volbase
+ 1 ;
1104 disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 );
1107 /* Make sure that no pending write process in the lower layer */
1108 if ( disk_ioctl ( fs
-> pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) res
= FR_DISK_ERR
;
1118 /*-----------------------------------------------------------------------*/
1119 /* Get physical sector number from cluster number */
1120 /*-----------------------------------------------------------------------*/
1122 static DWORD
clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
1123 FATFS
* fs
, /* Filesystem object */
1124 DWORD clst
/* Cluster# to be converted */
1127 clst
-= 2 ; /* Cluster number is origin from 2 */
1128 if ( clst
>= fs
-> n_fatent
- 2 ) return 0 ; /* Is it invalid cluster number? */
1129 return fs
-> database
+ fs
-> csize
* clst
; /* Start sector number of the cluster */
1135 /*-----------------------------------------------------------------------*/
1136 /* FAT access - Read value of a FAT entry */
1137 /*-----------------------------------------------------------------------*/
1139 static DWORD
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
1140 FFOBJID
* obj
, /* Corresponding object */
1141 DWORD clst
/* Cluster number to get the value */
1146 FATFS
* fs
= obj
-> fs
;
1149 if ( clst
< 2 || clst
>= fs
-> n_fatent
) { /* Check if in valid range */
1150 val
= 1 ; /* Internal error */
1153 val
= 0xFFFFFFFF ; /* Default value falls on disk error */
1155 switch ( fs
-> fs_type
) {
1157 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ;
1158 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1159 wc
= fs
-> win
[ bc
++ % SS ( fs
)]; /* Get 1st byte of the entry */
1160 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1161 wc
|= fs
-> win
[ bc
% SS ( fs
)] << 8 ; /* Merge 2nd byte of the entry */
1162 val
= ( clst
& 1 ) ? ( wc
>> 4 ) : ( wc
& 0xFFF ); /* Adjust bit position */
1166 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 ))) != FR_OK
) break ;
1167 val
= ld_word ( fs
-> win
+ clst
* 2 % SS ( fs
)); /* Simple WORD array */
1171 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1172 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x0FFFFFFF ; /* Simple DWORD array but mask out upper 4 bits */
1176 if (( obj
-> objsize
!= 0 && obj
-> sclust
!= 0 ) || obj
-> stat
== 0 ) { /* Object except root dir must have valid data length */
1177 DWORD cofs
= clst
- obj
-> sclust
; /* Offset from start cluster */
1178 DWORD clen
= ( DWORD
)(( obj
-> objsize
- 1 ) / SS ( fs
)) / fs
-> csize
; /* Number of clusters - 1 */
1180 if ( obj
-> stat
== 2 && cofs
<= clen
) { /* Is it a contiguous chain? */
1181 val
= ( cofs
== clen
) ? 0x7FFFFFFF : clst
+ 1 ; /* No data on the FAT, generate the value */
1184 if ( obj
-> stat
== 3 && cofs
< obj
-> n_cont
) { /* Is it in the 1st fragment? */
1185 val
= clst
+ 1 ; /* Generate the value */
1188 if ( obj
-> stat
!= 2 ) { /* Get value from FAT if FAT chain is valid */
1189 if ( obj
-> n_frag
!= 0 ) { /* Is it on the growing edge? */
1190 val
= 0x7FFFFFFF ; /* Generate EOC */
1192 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1193 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x7FFFFFFF ;
1201 val
= 1 ; /* Internal error */
1212 /*-----------------------------------------------------------------------*/
1213 /* FAT access - Change value of a FAT entry */
1214 /*-----------------------------------------------------------------------*/
1216 static FRESULT
put_fat ( /* FR_OK(0):succeeded, !=0:error */
1217 FATFS
* fs
, /* Corresponding filesystem object */
1218 DWORD clst
, /* FAT index number (cluster number) to be changed */
1219 DWORD val
/* New value to be set to the entry */
1224 FRESULT res
= FR_INT_ERR
;
1227 if ( clst
>= 2 && clst
< fs
-> n_fatent
) { /* Check if in valid range */
1228 switch ( fs
-> fs_type
) {
1230 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ; /* bc: byte offset of the entry */
1231 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1232 if ( res
!= FR_OK
) break ;
1233 p
= fs
-> win
+ bc
++ % SS ( fs
);
1234 * p
= ( clst
& 1 ) ? ((* p
& 0x0F ) | (( BYTE
) val
<< 4 )) : ( BYTE
) val
; /* Put 1st byte */
1236 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1237 if ( res
!= FR_OK
) break ;
1238 p
= fs
-> win
+ bc
% SS ( fs
);
1239 * p
= ( clst
& 1 ) ? ( BYTE
)( val
>> 4 ) : ((* p
& 0xF0 ) | (( BYTE
)( val
>> 8 ) & 0x0F )); /* Put 2nd byte */
1244 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 )));
1245 if ( res
!= FR_OK
) break ;
1246 st_word ( fs
-> win
+ clst
* 2 % SS ( fs
), ( WORD
) val
); /* Simple WORD array */
1254 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 )));
1255 if ( res
!= FR_OK
) break ;
1256 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1257 val
= ( val
& 0x0FFFFFFF ) | ( ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0xF0000000 );
1259 st_dword ( fs
-> win
+ clst
* 4 % SS ( fs
), val
);
1267 #endif /* !FF_FS_READONLY */
1272 #if FF_FS_EXFAT && !FF_FS_READONLY
1273 /*-----------------------------------------------------------------------*/
1274 /* exFAT: Accessing FAT and Allocation Bitmap */
1275 /*-----------------------------------------------------------------------*/
1277 /*--------------------------------------*/
1278 /* Find a contiguous free cluster block */
1279 /*--------------------------------------*/
1281 static DWORD
find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
1282 FATFS
* fs
, /* Filesystem object */
1283 DWORD clst
, /* Cluster number to scan from */
1284 DWORD ncl
/* Number of contiguous clusters to find (1..) */
1289 DWORD val
, scl
, ctr
;
1292 clst
-= 2 ; /* The first bit in the bitmap corresponds to cluster #2 */
1293 if ( clst
>= fs
-> n_fatent
- 2 ) clst
= 0 ;
1294 scl
= val
= clst
; ctr
= 0 ;
1296 if ( move_window ( fs
, fs
-> database
+ val
/ 8 / SS ( fs
)) != FR_OK
) return 0xFFFFFFFF ; /* (assuming bitmap is located top of the cluster heap) */
1297 i
= val
/ 8 % SS ( fs
); bm
= 1 << ( val
% 8 );
1300 bv
= fs
-> win
[ i
] & bm
; bm
<<= 1 ; /* Get bit value */
1301 if (++ val
>= fs
-> n_fatent
- 2 ) { /* Next cluster (with wrap-around) */
1302 val
= 0 ; bm
= 0 ; i
= SS ( fs
);
1304 if ( bv
== 0 ) { /* Is it a free cluster? */
1305 if (++ ctr
== ncl
) return scl
+ 2 ; /* Check if run length is sufficient for required */
1307 scl
= val
; ctr
= 0 ; /* Encountered a cluster in-use, restart to scan */
1309 if ( val
== clst
) return 0 ; /* All cluster scanned? */
1312 } while (++ i
< SS ( fs
));
1317 /*----------------------------------------*/
1318 /* Set/Clear a block of allocation bitmap */
1319 /*----------------------------------------*/
1321 static FRESULT
change_bitmap (
1322 FATFS
* fs
, /* Filesystem object */
1323 DWORD clst
, /* Cluster number to change from */
1324 DWORD ncl
, /* Number of clusters to be changed */
1325 int bv
/* bit value to be set (0 or 1) */
1333 clst
-= 2 ; /* The first bit corresponds to cluster #2 */
1334 sect
= fs
-> database
+ clst
/ 8 / SS ( fs
); /* Sector address (assuming bitmap is located top of the cluster heap) */
1335 i
= clst
/ 8 % SS ( fs
); /* Byte offset in the sector */
1336 bm
= 1 << ( clst
% 8 ); /* Bit mask in the byte */
1338 if ( move_window ( fs
, sect
++) != FR_OK
) return FR_DISK_ERR
;
1341 if ( bv
== ( int )(( fs
-> win
[ i
] & bm
) != 0 )) return FR_INT_ERR
; /* Is the bit expected value? */
1342 fs
-> win
[ i
] ^= bm
; /* Flip the bit */
1344 if (-- ncl
== 0 ) return FR_OK
; /* All bits processed? */
1345 } while ( bm
<<= 1 ); /* Next bit */
1347 } while (++ i
< SS ( fs
)); /* Next byte */
1353 /*---------------------------------------------*/
1354 /* Fill the first fragment of the FAT chain */
1355 /*---------------------------------------------*/
1357 static FRESULT
fill_first_frag (
1358 FFOBJID
* obj
/* Pointer to the corresponding object */
1365 if ( obj
-> stat
== 3 ) { /* Has the object been changed 'fragmented' in this session? */
1366 for ( cl
= obj
-> sclust
, n
= obj
-> n_cont
; n
; cl
++, n
--) { /* Create cluster chain on the FAT */
1367 res
= put_fat ( obj
-> fs
, cl
, cl
+ 1 );
1368 if ( res
!= FR_OK
) return res
;
1370 obj
-> stat
= 0 ; /* Change status 'FAT chain is valid' */
1376 /*---------------------------------------------*/
1377 /* Fill the last fragment of the FAT chain */
1378 /*---------------------------------------------*/
1380 static FRESULT
fill_last_frag (
1381 FFOBJID
* obj
, /* Pointer to the corresponding object */
1382 DWORD lcl
, /* Last cluster of the fragment */
1383 DWORD term
/* Value to set the last FAT entry */
1389 while ( obj
-> n_frag
> 0 ) { /* Create the chain of last fragment */
1390 res
= put_fat ( obj
-> fs
, lcl
- obj
-> n_frag
+ 1 , ( obj
-> n_frag
> 1 ) ? lcl
- obj
-> n_frag
+ 2 : term
);
1391 if ( res
!= FR_OK
) return res
;
1397 #endif /* FF_FS_EXFAT && !FF_FS_READONLY */
1402 /*-----------------------------------------------------------------------*/
1403 /* FAT handling - Remove a cluster chain */
1404 /*-----------------------------------------------------------------------*/
1406 static FRESULT
remove_chain ( /* FR_OK(0):succeeded, !=0:error */
1407 FFOBJID
* obj
, /* Corresponding object */
1408 DWORD clst
, /* Cluster to remove a chain from */
1409 DWORD pclst
/* Previous cluster of clst (0:entire chain) */
1412 FRESULT res
= FR_OK
;
1414 FATFS
* fs
= obj
-> fs
;
1415 #if FF_FS_EXFAT || FF_USE_TRIM
1416 DWORD scl
= clst
, ecl
= clst
;
1422 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Check if in valid range */
1424 /* Mark the previous cluster 'EOC' on the FAT if it exists */
1425 if ( pclst
!= 0 && (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
|| obj
-> stat
!= 2 )) {
1426 res
= put_fat ( fs
, pclst
, 0xFFFFFFFF );
1427 if ( res
!= FR_OK
) return res
;
1430 /* Remove the chain */
1432 nxt
= get_fat ( obj
, clst
); /* Get cluster status */
1433 if ( nxt
== 0 ) break ; /* Empty cluster? */
1434 if ( nxt
== 1 ) return FR_INT_ERR
; /* Internal error? */
1435 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error? */
1436 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1437 res
= put_fat ( fs
, clst
, 0 ); /* Mark the cluster 'free' on the FAT */
1438 if ( res
!= FR_OK
) return res
;
1440 if ( fs
-> free_clst
< fs
-> n_fatent
- 2 ) { /* Update FSINFO */
1444 #if FF_FS_EXFAT || FF_USE_TRIM
1445 if ( ecl
+ 1 == nxt
) { /* Is next cluster contiguous? */
1447 } else { /* End of contiguous cluster block */
1449 if ( fs
-> fs_type
== FS_EXFAT
) {
1450 res
= change_bitmap ( fs
, scl
, ecl
- scl
+ 1 , 0 ); /* Mark the cluster block 'free' on the bitmap */
1451 if ( res
!= FR_OK
) return res
;
1455 rt
[ 0 ] = clst2sect ( fs
, scl
); /* Start of data area freed */
1456 rt
[ 1 ] = clst2sect ( fs
, ecl
) + fs
-> csize
- 1 ; /* End of data area freed */
1457 disk_ioctl ( fs
-> pdrv
, CTRL_TRIM
, rt
); /* Inform device the data in the block is no longer needed */
1462 clst
= nxt
; /* Next cluster */
1463 } while ( clst
< fs
-> n_fatent
); /* Repeat while not the last link */
1466 /* Some post processes for chain status */
1467 if ( fs
-> fs_type
== FS_EXFAT
) {
1468 if ( pclst
== 0 ) { /* Has the entire chain been removed? */
1469 obj
-> stat
= 0 ; /* Change the chain status 'initial' */
1471 if ( obj
-> stat
== 0 ) { /* Is it a fragmented chain from the beginning of this session? */
1472 clst
= obj
-> sclust
; /* Follow the chain to check if it gets contiguous */
1473 while ( clst
!= pclst
) {
1474 nxt
= get_fat ( obj
, clst
);
1475 if ( nxt
< 2 ) return FR_INT_ERR
;
1476 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
;
1477 if ( nxt
!= clst
+ 1 ) break ; /* Not contiguous? */
1480 if ( clst
== pclst
) { /* Has the chain got contiguous again? */
1481 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1484 if ( obj
-> stat
== 3 && pclst
>= obj
-> sclust
&& pclst
<= obj
-> sclust
+ obj
-> n_cont
) { /* Was the chain fragmented in this session and got contiguous again? */
1485 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1497 /*-----------------------------------------------------------------------*/
1498 /* FAT handling - Stretch a chain or Create a new chain */
1499 /*-----------------------------------------------------------------------*/
1501 static DWORD
create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
1502 FFOBJID
* obj
, /* Corresponding object */
1503 DWORD clst
/* Cluster# to stretch, 0:Create a new chain */
1508 FATFS
* fs
= obj
-> fs
;
1511 if ( clst
== 0 ) { /* Create a new chain */
1512 scl
= fs
-> last_clst
; /* Suggested cluster to start to find */
1513 if ( scl
== 0 || scl
>= fs
-> n_fatent
) scl
= 1 ;
1515 else { /* Stretch a chain */
1516 cs
= get_fat ( obj
, clst
); /* Check the cluster status */
1517 if ( cs
< 2 ) return 1 ; /* Test for insanity */
1518 if ( cs
== 0xFFFFFFFF ) return cs
; /* Test for disk error */
1519 if ( cs
< fs
-> n_fatent
) return cs
; /* It is already followed by next cluster */
1520 scl
= clst
; /* Cluster to start to find */
1522 if ( fs
-> free_clst
== 0 ) return 0 ; /* No free cluster */
1525 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
1526 ncl
= find_bitmap ( fs
, scl
, 1 ); /* Find a free cluster */
1527 if ( ncl
== 0 || ncl
== 0xFFFFFFFF ) return ncl
; /* No free cluster or hard error? */
1528 res
= change_bitmap ( fs
, ncl
, 1 , 1 ); /* Mark the cluster 'in use' */
1529 if ( res
== FR_INT_ERR
) return 1 ;
1530 if ( res
== FR_DISK_ERR
) return 0xFFFFFFFF ;
1531 if ( clst
== 0 ) { /* Is it a new chain? */
1532 obj
-> stat
= 2 ; /* Set status 'contiguous' */
1533 } else { /* It is a stretched chain */
1534 if ( obj
-> stat
== 2 && ncl
!= scl
+ 1 ) { /* Is the chain got fragmented? */
1535 obj
-> n_cont
= scl
- obj
-> sclust
; /* Set size of the contiguous part */
1536 obj
-> stat
= 3 ; /* Change status 'just fragmented' */
1539 if ( obj
-> stat
!= 2 ) { /* Is the file non-contiguous? */
1540 if ( ncl
== clst
+ 1 ) { /* Is the cluster next to previous one? */
1541 obj
-> n_frag
= obj
-> n_frag
? obj
-> n_frag
+ 1 : 2 ; /* Increment size of last framgent */
1542 } else { /* New fragment */
1543 if ( obj
-> n_frag
== 0 ) obj
-> n_frag
= 1 ;
1544 res
= fill_last_frag ( obj
, clst
, ncl
); /* Fill last fragment on the FAT and link it to new one */
1545 if ( res
== FR_OK
) obj
-> n_frag
= 1 ;
1550 { /* On the FAT/FAT32 volume */
1552 if ( scl
== clst
) { /* Stretching an existing chain? */
1553 ncl
= scl
+ 1 ; /* Test if next cluster is free */
1554 if ( ncl
>= fs
-> n_fatent
) ncl
= 2 ;
1555 cs
= get_fat ( obj
, ncl
); /* Get next cluster status */
1556 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1557 if ( cs
!= 0 ) { /* Not free? */
1558 cs
= fs
-> last_clst
; /* Start at suggested cluster if it is valid */
1559 if ( cs
>= 2 && cs
< fs
-> n_fatent
) scl
= cs
;
1563 if ( ncl
== 0 ) { /* The new cluster cannot be contiguous and find another fragment */
1564 ncl
= scl
; /* Start cluster */
1566 ncl
++; /* Next cluster */
1567 if ( ncl
>= fs
-> n_fatent
) { /* Check wrap-around */
1569 if ( ncl
> scl
) return 0 ; /* No free cluster found? */
1571 cs
= get_fat ( obj
, ncl
); /* Get the cluster status */
1572 if ( cs
== 0 ) break ; /* Found a free cluster? */
1573 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1574 if ( ncl
== scl
) return 0 ; /* No free cluster found? */
1577 res
= put_fat ( fs
, ncl
, 0xFFFFFFFF ); /* Mark the new cluster 'EOC' */
1578 if ( res
== FR_OK
&& clst
!= 0 ) {
1579 res
= put_fat ( fs
, clst
, ncl
); /* Link it from the previous one if needed */
1583 if ( res
== FR_OK
) { /* Update FSINFO if function succeeded. */
1584 fs
-> last_clst
= ncl
;
1585 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) fs
-> free_clst
--;
1588 ncl
= ( res
== FR_DISK_ERR
) ? 0xFFFFFFFF : 1 ; /* Failed. Generate error status */
1591 return ncl
; /* Return new cluster number or error status */
1594 #endif /* !FF_FS_READONLY */
1600 /*-----------------------------------------------------------------------*/
1601 /* FAT handling - Convert offset into cluster with link map table */
1602 /*-----------------------------------------------------------------------*/
1604 static DWORD
clmt_clust ( /* <2:Error, >=2:Cluster number */
1605 FIL
* fp
, /* Pointer to the file object */
1606 FSIZE_t ofs
/* File offset to be converted to cluster# */
1609 DWORD cl
, ncl
, * tbl
;
1610 FATFS
* fs
= fp
-> obj
. fs
;
1613 tbl
= fp
-> cltbl
+ 1 ; /* Top of CLMT */
1614 cl
= ( DWORD
)( ofs
/ SS ( fs
) / fs
-> csize
); /* Cluster order from top of the file */
1616 ncl
= * tbl
++; /* Number of cluters in the fragment */
1617 if ( ncl
== 0 ) return 0 ; /* End of table? (error) */
1618 if ( cl
< ncl
) break ; /* In this fragment? */
1619 cl
-= ncl
; tbl
++; /* Next fragment */
1621 return cl
+ * tbl
; /* Return the cluster number */
1624 #endif /* FF_USE_FASTSEEK */
1629 /*-----------------------------------------------------------------------*/
1630 /* Directory handling - Fill a cluster with zeros */
1631 /*-----------------------------------------------------------------------*/
1634 static FRESULT
dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
1635 FATFS
* fs
, /* Filesystem object */
1636 DWORD clst
/* Directory table to clear */
1644 if ( sync_window ( fs
) != FR_OK
) return FR_DISK_ERR
; /* Flush disk access window */
1645 sect
= clst2sect ( fs
, clst
); /* Top of the cluster */
1646 fs
-> winsect
= sect
; /* Set window to top of the cluster */
1647 mem_set ( fs
-> win
, 0 , SS ( fs
)); /* Clear window buffer */
1648 #if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
1649 /* Allocate a temporary buffer */
1650 for ( szb
= (( DWORD
) fs
-> csize
* SS ( fs
) >= MAX_MALLOC
) ? MAX_MALLOC
: fs
-> csize
* SS ( fs
), ibuf
= 0 ; szb
> SS ( fs
) && ( ibuf
= ff_memalloc ( szb
)) == 0 ; szb
/= 2 ) ;
1651 if ( szb
> SS ( fs
)) { /* Buffer allocated? */
1652 mem_set ( ibuf
, 0 , szb
);
1653 szb
/= SS ( fs
); /* Bytes -> Sectors */
1654 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1659 ibuf
= fs
-> win
; szb
= 1 ; /* Use window buffer (many single-sector writes may take a time) */
1660 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1662 return ( n
== fs
-> csize
) ? FR_OK
: FR_DISK_ERR
;
1664 #endif /* !FF_FS_READONLY */
1669 /*-----------------------------------------------------------------------*/
1670 /* Directory handling - Set directory index */
1671 /*-----------------------------------------------------------------------*/
1673 static FRESULT
dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
1674 DIR * dp
, /* Pointer to directory object */
1675 DWORD ofs
/* Offset of directory table */
1679 FATFS
* fs
= dp
-> obj
. fs
;
1682 if ( ofs
>= ( DWORD
)(( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) ? MAX_DIR_EX
: MAX_DIR
) || ofs
% SZDIRE
) { /* Check range of offset and alignment */
1685 dp
-> dptr
= ofs
; /* Set current offset */
1686 clst
= dp
-> obj
. sclust
; /* Table start cluster (0:root) */
1687 if ( clst
== 0 && fs
-> fs_type
>= FS_FAT32
) { /* Replace cluster# 0 with root cluster# */
1689 if ( FF_FS_EXFAT
) dp
-> obj
. stat
= 0 ; /* exFAT: Root dir has an FAT chain */
1692 if ( clst
== 0 ) { /* Static table (root-directory on the FAT volume) */
1693 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) return FR_INT_ERR
; /* Is index out of range? */
1694 dp
-> sect
= fs
-> dirbase
;
1696 } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
1697 csz
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Bytes per cluster */
1698 while ( ofs
>= csz
) { /* Follow cluster chain */
1699 clst
= get_fat (& dp
-> obj
, clst
); /* Get next cluster */
1700 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1701 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Reached to end of table or internal error */
1704 dp
-> sect
= clst2sect ( fs
, clst
);
1706 dp
-> clust
= clst
; /* Current cluster# */
1707 if ( dp
-> sect
== 0 ) return FR_INT_ERR
;
1708 dp
-> sect
+= ofs
/ SS ( fs
); /* Sector# of the directory entry */
1709 dp
-> dir
= fs
-> win
+ ( ofs
% SS ( fs
)); /* Pointer to the entry in the win[] */
1717 /*-----------------------------------------------------------------------*/
1718 /* Directory handling - Move directory table index next */
1719 /*-----------------------------------------------------------------------*/
1721 static FRESULT
dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
1722 DIR * dp
, /* Pointer to the directory object */
1723 int stretch
/* 0: Do not stretch table, 1: Stretch table if needed */
1727 FATFS
* fs
= dp
-> obj
. fs
;
1730 ofs
= dp
-> dptr
+ SZDIRE
; /* Next entry */
1731 if ( ofs
>= ( DWORD
)(( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) ? MAX_DIR_EX
: MAX_DIR
)) dp
-> sect
= 0 ; /* Disable it if the offset reached the max value */
1732 if ( dp
-> sect
== 0 ) return FR_NO_FILE
; /* Report EOT if it has been disabled */
1734 if ( ofs
% SS ( fs
) == 0 ) { /* Sector changed? */
1735 dp
-> sect
++; /* Next sector */
1737 if ( dp
-> clust
== 0 ) { /* Static table */
1738 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) { /* Report EOT if it reached end of static table */
1739 dp
-> sect
= 0 ; return FR_NO_FILE
;
1742 else { /* Dynamic table */
1743 if (( ofs
/ SS ( fs
) & ( fs
-> csize
- 1 )) == 0 ) { /* Cluster changed? */
1744 clst
= get_fat (& dp
-> obj
, dp
-> clust
); /* Get next cluster */
1745 if ( clst
<= 1 ) return FR_INT_ERR
; /* Internal error */
1746 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1747 if ( clst
>= fs
-> n_fatent
) { /* It reached end of dynamic table */
1749 if (! stretch
) { /* If no stretch, report EOT */
1750 dp
-> sect
= 0 ; return FR_NO_FILE
;
1752 clst
= create_chain (& dp
-> obj
, dp
-> clust
); /* Allocate a cluster */
1753 if ( clst
== 0 ) return FR_DENIED
; /* No free cluster */
1754 if ( clst
== 1 ) return FR_INT_ERR
; /* Internal error */
1755 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1756 if ( dir_clear ( fs
, clst
) != FR_OK
) return FR_DISK_ERR
; /* Clean up the stretched table */
1757 if ( FF_FS_EXFAT
) dp
-> obj
. stat
|= 4 ; /* exFAT: The directory has been stretched */
1759 if (! stretch
) dp
-> sect
= 0 ; /* (this line is to suppress compiler warning) */
1760 dp
-> sect
= 0 ; return FR_NO_FILE
; /* Report EOT */
1763 dp
-> clust
= clst
; /* Initialize data for new cluster */
1764 dp
-> sect
= clst2sect ( fs
, clst
);
1768 dp
-> dptr
= ofs
; /* Current entry */
1769 dp
-> dir
= fs
-> win
+ ofs
% SS ( fs
); /* Pointer to the entry in the win[] */
1778 /*-----------------------------------------------------------------------*/
1779 /* Directory handling - Reserve a block of directory entries */
1780 /*-----------------------------------------------------------------------*/
1782 static FRESULT
dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
1783 DIR * dp
, /* Pointer to the directory object */
1784 UINT nent
/* Number of contiguous entries to allocate */
1789 FATFS
* fs
= dp
-> obj
. fs
;
1792 res
= dir_sdi ( dp
, 0 );
1796 res
= move_window ( fs
, dp
-> sect
);
1797 if ( res
!= FR_OK
) break ;
1799 if (( fs
-> fs_type
== FS_EXFAT
) ? ( int )(( dp
-> dir
[ XDIR_Type
] & 0x80 ) == 0 ) : ( int )( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 )) {
1801 if ( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 ) {
1803 if (++ n
== nent
) break ; /* A block of contiguous free entries is found */
1805 n
= 0 ; /* Not a blank entry. Restart to search */
1807 res
= dir_next ( dp
, 1 );
1808 } while ( res
== FR_OK
); /* Next entry with table stretch enabled */
1811 if ( res
== FR_NO_FILE
) res
= FR_DENIED
; /* No directory entry to allocate */
1815 #endif /* !FF_FS_READONLY */
1820 /*-----------------------------------------------------------------------*/
1821 /* FAT: Directory handling - Load/Store start cluster number */
1822 /*-----------------------------------------------------------------------*/
1824 static DWORD
ld_clust ( /* Returns the top cluster value of the SFN entry */
1825 FATFS
* fs
, /* Pointer to the fs object */
1826 const BYTE
* dir
/* Pointer to the key entry */
1831 cl
= ld_word ( dir
+ DIR_FstClusLO
);
1832 if ( fs
-> fs_type
== FS_FAT32
) {
1833 cl
|= ( DWORD
) ld_word ( dir
+ DIR_FstClusHI
) << 16 ;
1841 static void st_clust (
1842 FATFS
* fs
, /* Pointer to the fs object */
1843 BYTE
* dir
, /* Pointer to the key entry */
1844 DWORD cl
/* Value to be set */
1847 st_word ( dir
+ DIR_FstClusLO
, ( WORD
) cl
);
1848 if ( fs
-> fs_type
== FS_FAT32
) {
1849 st_word ( dir
+ DIR_FstClusHI
, ( WORD
)( cl
>> 16 ));
1857 /*--------------------------------------------------------*/
1858 /* FAT-LFN: Compare a part of file name with an LFN entry */
1859 /*--------------------------------------------------------*/
1861 static int cmp_lfn ( /* 1:matched, 0:not matched */
1862 const WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer to be compared */
1863 BYTE
* dir
/* Pointer to the directory entry containing the part of LFN */
1870 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO */
1872 i
= (( dir
[ LDIR_Ord
] & 0x3F ) - 1 ) * 13 ; /* Offset in the LFN buffer */
1874 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1875 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1877 if ( i
>= FF_MAX_LFN
|| ff_wtoupper ( uc
) != ff_wtoupper ( lfnbuf
[ i
++])) { /* Compare it */
1878 return 0 ; /* Not matched */
1882 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1886 if (( dir
[ LDIR_Ord
] & LLEF
) && wc
&& lfnbuf
[ i
]) return 0 ; /* Last segment matched but different length */
1888 return 1 ; /* The part of LFN matched */
1892 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
1893 /*-----------------------------------------------------*/
1894 /* FAT-LFN: Pick a part of file name from an LFN entry */
1895 /*-----------------------------------------------------*/
1897 static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
1898 WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer */
1899 BYTE
* dir
/* Pointer to the LFN entry */
1906 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO is 0 */
1908 i
= (( dir
[ LDIR_Ord
] & ~ LLEF
) - 1 ) * 13 ; /* Offset in the LFN buffer */
1910 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1911 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1913 if ( i
>= FF_MAX_LFN
) return 0 ; /* Buffer overflow? */
1914 lfnbuf
[ i
++] = wc
= uc
; /* Store it */
1916 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1920 if ( dir
[ LDIR_Ord
] & LLEF
) { /* Put terminator if it is the last LFN part */
1921 if ( i
>= FF_MAX_LFN
) return 0 ; /* Buffer overflow? */
1925 return 1 ; /* The part of LFN is valid */
1931 /*-----------------------------------------*/
1932 /* FAT-LFN: Create an entry of LFN entries */
1933 /*-----------------------------------------*/
1935 static void put_lfn (
1936 const WCHAR
* lfn
, /* Pointer to the LFN */
1937 BYTE
* dir
, /* Pointer to the LFN entry to be created */
1938 BYTE ord
, /* LFN order (1-20) */
1939 BYTE sum
/* Checksum of the corresponding SFN */
1946 dir
[ LDIR_Chksum
] = sum
; /* Set checksum */
1947 dir
[ LDIR_Attr
] = AM_LFN
; /* Set attribute. LFN entry */
1949 st_word ( dir
+ LDIR_FstClusLO
, 0 );
1951 i
= ( ord
- 1 ) * 13 ; /* Get offset in the LFN working buffer */
1954 if ( wc
!= 0xFFFF ) wc
= lfn
[ i
++]; /* Get an effective character */
1955 st_word ( dir
+ LfnOfs
[ s
], wc
); /* Put it */
1956 if ( wc
== 0 ) wc
= 0xFFFF ; /* Padding characters for left locations */
1958 if ( wc
== 0xFFFF || ! lfn
[ i
]) ord
|= LLEF
; /* Last LFN part is the start of LFN sequence */
1959 dir
[ LDIR_Ord
] = ord
; /* Set the LFN order */
1962 #endif /* !FF_FS_READONLY */
1963 #endif /* FF_USE_LFN */
1967 #if FF_USE_LFN && !FF_FS_READONLY
1968 /*-----------------------------------------------------------------------*/
1969 /* FAT-LFN: Create a Numbered SFN */
1970 /*-----------------------------------------------------------------------*/
1972 static void gen_numname (
1973 BYTE
* dst
, /* Pointer to the buffer to store numbered SFN */
1974 const BYTE
* src
, /* Pointer to SFN */
1975 const WCHAR
* lfn
, /* Pointer to LFN */
1976 UINT seq
/* Sequence number */
1985 mem_cpy ( dst
, src
, 11 );
1987 if ( seq
> 5 ) { /* In case of many collisions, generate a hash number instead of sequential number */
1989 while (* lfn
) { /* Create a CRC as hash value */
1991 for ( i
= 0 ; i
< 16 ; i
++) {
1992 sr
= ( sr
<< 1 ) + ( wc
& 1 );
1994 if ( sr
& 0x10000 ) sr
^= 0x11021 ;
2000 /* itoa (hexdecimal) */
2003 c
= ( BYTE
)(( seq
% 16 ) + '0' );
2004 if ( c
> '9' ) c
+= 7 ;
2010 /* Append the number to the SFN body */
2011 for ( j
= 0 ; j
< i
&& dst
[ j
] != ' ' ; j
++) {
2012 if ( dbc_1st ( dst
[ j
])) {
2013 if ( j
== i
- 1 ) break ;
2018 dst
[ j
++] = ( i
< 8 ) ? ns
[ i
++] : ' ' ;
2021 #endif /* FF_USE_LFN && !FF_FS_READONLY */
2026 /*-----------------------------------------------------------------------*/
2027 /* FAT-LFN: Calculate checksum of an SFN entry */
2028 /*-----------------------------------------------------------------------*/
2030 static BYTE
sum_sfn (
2031 const BYTE
* dir
/* Pointer to the SFN entry */
2038 sum
= ( sum
>> 1 ) + ( sum
<< 7 ) + * dir
++;
2043 #endif /* FF_USE_LFN */
2048 /*-----------------------------------------------------------------------*/
2049 /* exFAT: Checksum */
2050 /*-----------------------------------------------------------------------*/
2052 static WORD
xdir_sum ( /* Get checksum of the directoly entry block */
2053 const BYTE
* dir
/* Directory entry block to be calculated */
2060 szblk
= ( dir
[ XDIR_NumSec
] + 1 ) * SZDIRE
; /* Number of bytes of the entry block */
2061 for ( i
= sum
= 0 ; i
< szblk
; i
++) {
2062 if ( i
== XDIR_SetSum
) { /* Skip 2-byte sum field */
2065 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + dir
[ i
];
2073 static WORD
xname_sum ( /* Get check sum (to be used as hash) of the file name */
2074 const WCHAR
* name
/* File name to be calculated */
2081 while (( chr
= * name
++) != 0 ) {
2082 chr
= ( WCHAR
) ff_wtoupper ( chr
); /* File name needs to be up-case converted */
2083 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
& 0xFF );
2084 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
>> 8 );
2090 #if !FF_FS_READONLY && FF_USE_MKFS
2091 static DWORD
xsum32 ( /* Returns 32-bit checksum */
2092 BYTE dat
, /* Byte to be calculated (byte-by-byte processing) */
2093 DWORD sum
/* Previous sum value */
2096 sum
= (( sum
& 1 ) ? 0x80000000 : 0 ) + ( sum
>> 1 ) + dat
;
2102 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2103 /*------------------------------------------------------*/
2104 /* exFAT: Get object information from a directory block */
2105 /*------------------------------------------------------*/
2107 static void get_xfileinfo (
2108 BYTE
* dirb
, /* Pointer to the direcotry entry block 85+C0+C1s */
2109 FILINFO
* fno
/* Buffer to store the extracted file information */
2115 /* Get file name from the entry block */
2116 si
= SZDIRE
* 2 ; /* 1st C1 entry */
2117 nc
= 0 ; hs
= 0 ; di
= 0 ;
2118 while ( nc
< dirb
[ XDIR_NumName
]) {
2119 if ( si
>= MAXDIRB ( FF_MAX_LFN
)) { di
= 0 ; break ; } /* Truncated directory block? */
2120 if (( si
% SZDIRE
) == 0 ) si
+= 2 ; /* Skip entry type field */
2121 wc
= ld_word ( dirb
+ si
); si
+= 2 ; nc
++; /* Get a character */
2122 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2123 hs
= wc
; continue ; /* Get low surrogate */
2125 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in API encoding */
2126 if ( wc
== 0 ) { di
= 0 ; break ; } /* Buffer overflow or wrong encoding? */
2130 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2131 if ( di
== 0 ) fno
-> fname
[ di
++] = '?' ; /* Inaccessible object name? */
2132 fno
-> fname
[ di
] = 0 ; /* Terminate the name */
2133 fno
-> altname
[ 0 ] = 0 ; /* exFAT does not support SFN */
2135 fno
-> fattrib
= dirb
[ XDIR_Attr
]; /* Attribute */
2136 fno
-> fsize
= ( fno
-> fattrib
& AM_DIR
) ? 0 : ld_qword ( dirb
+ XDIR_FileSize
); /* Size */
2137 fno
-> ftime
= ld_word ( dirb
+ XDIR_ModTime
+ 0 ); /* Time */
2138 fno
-> fdate
= ld_word ( dirb
+ XDIR_ModTime
+ 2 ); /* Date */
2141 #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2144 /*-----------------------------------*/
2145 /* exFAT: Get a directry entry block */
2146 /*-----------------------------------*/
2148 static FRESULT
load_xdir ( /* FR_INT_ERR: invalid entry block */
2149 DIR * dp
/* Reading direcotry object pointing top of the entry block to load */
2154 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */
2158 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2159 if ( res
!= FR_OK
) return res
;
2160 if ( dp
-> dir
[ XDIR_Type
] != 0x85 ) return FR_INT_ERR
; /* Invalid order */
2161 mem_cpy ( dirb
+ 0 * SZDIRE
, dp
-> dir
, SZDIRE
);
2162 sz_ent
= ( dirb
[ XDIR_NumSec
] + 1 ) * SZDIRE
;
2163 if ( sz_ent
< 3 * SZDIRE
|| sz_ent
> 19 * SZDIRE
) return FR_INT_ERR
;
2166 res
= dir_next ( dp
, 0 );
2167 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2168 if ( res
!= FR_OK
) return res
;
2169 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2170 if ( res
!= FR_OK
) return res
;
2171 if ( dp
-> dir
[ XDIR_Type
] != 0xC0 ) return FR_INT_ERR
; /* Invalid order */
2172 mem_cpy ( dirb
+ 1 * SZDIRE
, dp
-> dir
, SZDIRE
);
2173 if ( MAXDIRB ( dirb
[ XDIR_NumName
]) > sz_ent
) return FR_INT_ERR
;
2175 /* Load C1 entries */
2176 i
= 2 * SZDIRE
; /* C1 offset to load */
2178 res
= dir_next ( dp
, 0 );
2179 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2180 if ( res
!= FR_OK
) return res
;
2181 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2182 if ( res
!= FR_OK
) return res
;
2183 if ( dp
-> dir
[ XDIR_Type
] != 0xC1 ) return FR_INT_ERR
; /* Invalid order */
2184 if ( i
< MAXDIRB ( FF_MAX_LFN
)) mem_cpy ( dirb
+ i
, dp
-> dir
, SZDIRE
);
2185 } while (( i
+= SZDIRE
) < sz_ent
);
2187 /* Sanity check (do it for only accessible object) */
2188 if ( i
<= MAXDIRB ( FF_MAX_LFN
)) {
2189 if ( xdir_sum ( dirb
) != ld_word ( dirb
+ XDIR_SetSum
)) return FR_INT_ERR
;
2195 /*------------------------------------------------------------------*/
2196 /* exFAT: Initialize object allocation info with loaded entry block */
2197 /*------------------------------------------------------------------*/
2199 static void init_alloc_info (
2200 FATFS
* fs
, /* Filesystem object */
2201 FFOBJID
* obj
/* Object allocation information to be initialized */
2204 obj
-> sclust
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Start cluster */
2205 obj
-> objsize
= ld_qword ( fs
-> dirbuf
+ XDIR_FileSize
); /* Size */
2206 obj
-> stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ; /* Allocation status */
2207 obj
-> n_frag
= 0 ; /* No last fragment info */
2212 #if !FF_FS_READONLY || FF_FS_RPATH != 0
2213 /*------------------------------------------------*/
2214 /* exFAT: Load the object's directory entry block */
2215 /*------------------------------------------------*/
2217 static FRESULT
load_obj_xdir (
2218 DIR * dp
, /* Blank directory object to be used to access containing direcotry */
2219 const FFOBJID
* obj
/* Object with its containing directory information */
2224 /* Open object containing directory */
2225 dp
-> obj
. fs
= obj
-> fs
;
2226 dp
-> obj
. sclust
= obj
-> c_scl
;
2227 dp
-> obj
. stat
= ( BYTE
) obj
-> c_size
;
2228 dp
-> obj
. objsize
= obj
-> c_size
& 0xFFFFFF00 ;
2230 dp
-> blk_ofs
= obj
-> c_ofs
;
2232 res
= dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto object's entry block */
2234 res
= load_xdir ( dp
); /* Load the object's entry block */
2242 /*----------------------------------------*/
2243 /* exFAT: Store the directory entry block */
2244 /*----------------------------------------*/
2246 static FRESULT
store_xdir (
2247 DIR * dp
/* Pointer to the direcotry object */
2252 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the direcotry entry block 85+C0+C1s */
2254 /* Create set sum */
2255 st_word ( dirb
+ XDIR_SetSum
, xdir_sum ( dirb
));
2256 nent
= dirb
[ XDIR_NumSec
] + 1 ;
2258 /* Store the direcotry entry block to the directory */
2259 res
= dir_sdi ( dp
, dp
-> blk_ofs
);
2260 while ( res
== FR_OK
) {
2261 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2262 if ( res
!= FR_OK
) break ;
2263 mem_cpy ( dp
-> dir
, dirb
, SZDIRE
);
2264 dp
-> obj
. fs
-> wflag
= 1 ;
2265 if (-- nent
== 0 ) break ;
2267 res
= dir_next ( dp
, 0 );
2269 return ( res
== FR_OK
|| res
== FR_DISK_ERR
) ? res
: FR_INT_ERR
;
2274 /*-------------------------------------------*/
2275 /* exFAT: Create a new directory enrty block */
2276 /*-------------------------------------------*/
2278 static void create_xdir (
2279 BYTE
* dirb
, /* Pointer to the direcotry entry block buffer */
2280 const WCHAR
* lfn
/* Pointer to the object name */
2288 /* Create 85,C0 entry */
2289 mem_set ( dirb
, 0 , 2 * SZDIRE
);
2290 dirb
[ 0 * SZDIRE
+ XDIR_Type
] = 0x85 ; /* 85 entry */
2291 dirb
[ 1 * SZDIRE
+ XDIR_Type
] = 0xC0 ; /* C0 entry */
2293 /* Create C1 entries */
2294 i
= SZDIRE
* 2 ; /* Top of C1 entries */
2295 nlen
= nc1
= 0 ; wc
= 1 ;
2297 dirb
[ i
++] = 0xC1 ; dirb
[ i
++] = 0 ; /* Entry type C1 */
2298 do { /* Fill name field */
2299 if ( wc
!= 0 && ( wc
= lfn
[ nlen
]) != 0 ) nlen
++; /* Get a character if exist */
2300 st_word ( dirb
+ i
, wc
); /* Store it */
2302 } while ( i
% SZDIRE
!= 0 );
2304 } while ( lfn
[ nlen
]); /* Fill next entry if any char follows */
2306 dirb
[ XDIR_NumName
] = nlen
; /* Set name length */
2307 dirb
[ XDIR_NumSec
] = 1 + nc1
; /* Set secondary count (C0 + C1s) */
2308 st_word ( dirb
+ XDIR_NameHash
, xname_sum ( lfn
)); /* Set name hash */
2311 #endif /* !FF_FS_READONLY */
2312 #endif /* FF_FS_EXFAT */
2316 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
2317 /*-----------------------------------------------------------------------*/
2318 /* Read an object from the directory */
2319 /*-----------------------------------------------------------------------*/
2321 #define dir_read_file(dp) dir_read(dp, 0)
2322 #define dir_read_label(dp) dir_read(dp, 1)
2324 static FRESULT
dir_read (
2325 DIR * dp
, /* Pointer to the directory object */
2326 int vol
/* Filtered by 0:file/directory or 1:volume label */
2329 FRESULT res
= FR_NO_FILE
;
2330 FATFS
* fs
= dp
-> obj
. fs
;
2333 BYTE ord
= 0xFF , sum
= 0xFF ;
2337 res
= move_window ( fs
, dp
-> sect
);
2338 if ( res
!= FR_OK
) break ;
2339 c
= dp
-> dir
[ DIR_Name
]; /* Test for the entry type */
2341 res
= FR_NO_FILE
; break ; /* Reached to end of the directory */
2344 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2345 if ( FF_USE_LABEL
&& vol
) {
2346 if ( c
== 0x83 ) break ; /* Volume label entry? */
2348 if ( c
== 0x85 ) { /* Start of the file entry block? */
2349 dp
-> blk_ofs
= dp
-> dptr
; /* Get location of the block */
2350 res
= load_xdir ( dp
); /* Load the entry block */
2352 dp
-> obj
. attr
= fs
-> dirbuf
[ XDIR_Attr
] & AM_MASK
; /* Get attribute */
2359 { /* On the FAT/FAT32 volume */
2360 dp
-> obj
. attr
= a
= dp
-> dir
[ DIR_Attr
] & AM_MASK
; /* Get attribute */
2361 #if FF_USE_LFN /* LFN configuration */
2362 if ( c
== DDEM
|| c
== '.' || ( int )(( a
& ~ AM_ARC
) == AM_VOL
) != vol
) { /* An entry without valid data */
2365 if ( a
== AM_LFN
) { /* An LFN entry is found */
2366 if ( c
& LLEF
) { /* Is it start of an LFN sequence? */
2367 sum
= dp
-> dir
[ LDIR_Chksum
];
2368 c
&= ( BYTE
)~ LLEF
; ord
= c
;
2369 dp
-> blk_ofs
= dp
-> dptr
;
2371 /* Check LFN validity and capture it */
2372 ord
= ( c
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && pick_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2373 } else { /* An SFN entry is found */
2374 if ( ord
!= 0 || sum
!= sum_sfn ( dp
-> dir
)) { /* Is there a valid LFN? */
2375 dp
-> blk_ofs
= 0xFFFFFFFF ; /* It has no LFN. */
2380 #else /* Non LFN configuration */
2381 if ( c
!= DDEM
&& c
!= '.' && a
!= AM_LFN
&& ( int )(( a
& ~ AM_ARC
) == AM_VOL
) == vol
) { /* Is it a valid entry? */
2386 res
= dir_next ( dp
, 0 ); /* Next entry */
2387 if ( res
!= FR_OK
) break ;
2390 if ( res
!= FR_OK
) dp
-> sect
= 0 ; /* Terminate the read operation on error or EOT */
2394 #endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
2398 /*-----------------------------------------------------------------------*/
2399 /* Directory handling - Find an object in the directory */
2400 /*-----------------------------------------------------------------------*/
2402 static FRESULT
dir_find ( /* FR_OK(0):succeeded, !=0:error */
2403 DIR * dp
/* Pointer to the directory object with the file name */
2407 FATFS
* fs
= dp
-> obj
. fs
;
2413 res
= dir_sdi ( dp
, 0 ); /* Rewind directory object */
2414 if ( res
!= FR_OK
) return res
;
2416 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2419 WORD hash
= xname_sum ( fs
-> lfnbuf
); /* Hash value of the name to find */
2421 while (( res
= dir_read_file ( dp
)) == FR_OK
) { /* Read an item */
2422 #if FF_MAX_LFN < 255
2423 if ( fs
-> dirbuf
[ XDIR_NumName
] > FF_MAX_LFN
) continue ; /* Skip comparison if inaccessible object name */
2425 if ( ld_word ( fs
-> dirbuf
+ XDIR_NameHash
) != hash
) continue ; /* Skip comparison if hash mismatched */
2426 for ( nc
= fs
-> dirbuf
[ XDIR_NumName
], di
= SZDIRE
* 2 , ni
= 0 ; nc
; nc
--, di
+= 2 , ni
++) { /* Compare the name */
2427 if (( di
% SZDIRE
) == 0 ) di
+= 2 ;
2428 if ( ff_wtoupper ( ld_word ( fs
-> dirbuf
+ di
)) != ff_wtoupper ( fs
-> lfnbuf
[ ni
])) break ;
2430 if ( nc
== 0 && ! fs
-> lfnbuf
[ ni
]) break ; /* Name matched? */
2435 /* On the FAT/FAT32 volume */
2437 ord
= sum
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2440 res
= move_window ( fs
, dp
-> sect
);
2441 if ( res
!= FR_OK
) break ;
2442 c
= dp
-> dir
[ DIR_Name
];
2443 if ( c
== 0 ) { res
= FR_NO_FILE
; break ; } /* Reached to end of table */
2444 #if FF_USE_LFN /* LFN configuration */
2445 dp
-> obj
. attr
= a
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2446 if ( c
== DDEM
|| (( a
& AM_VOL
) && a
!= AM_LFN
)) { /* An entry without valid data */
2447 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2449 if ( a
== AM_LFN
) { /* An LFN entry is found */
2450 if (!( dp
-> fn
[ NSFLAG
] & NS_NOLFN
)) {
2451 if ( c
& LLEF
) { /* Is it start of LFN sequence? */
2452 sum
= dp
-> dir
[ LDIR_Chksum
];
2453 c
&= ( BYTE
)~ LLEF
; ord
= c
; /* LFN start order */
2454 dp
-> blk_ofs
= dp
-> dptr
; /* Start offset of LFN */
2456 /* Check validity of the LFN entry and compare it with given name */
2457 ord
= ( c
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && cmp_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2459 } else { /* An SFN entry is found */
2460 if ( ord
== 0 && sum
== sum_sfn ( dp
-> dir
)) break ; /* LFN matched? */
2461 if (!( dp
-> fn
[ NSFLAG
] & NS_LOSS
) && ! mem_cmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* SFN matched? */
2462 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2465 #else /* Non LFN configuration */
2466 dp
-> obj
. attr
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2467 if (!( dp
-> dir
[ DIR_Attr
] & AM_VOL
) && ! mem_cmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* Is it a valid entry? */
2469 res
= dir_next ( dp
, 0 ); /* Next entry */
2470 } while ( res
== FR_OK
);
2479 /*-----------------------------------------------------------------------*/
2480 /* Register an object to the directory */
2481 /*-----------------------------------------------------------------------*/
2483 static FRESULT
dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
2484 DIR * dp
/* Target directory with object name to be created */
2488 FATFS
* fs
= dp
-> obj
. fs
;
2489 #if FF_USE_LFN /* LFN configuration */
2494 if ( dp
-> fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
)) return FR_INVALID_NAME
; /* Check name validity */
2495 for ( nlen
= 0 ; fs
-> lfnbuf
[ nlen
]; nlen
++) ; /* Get lfn length */
2498 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2499 nent
= ( nlen
+ 14 ) / 15 + 2 ; /* Number of entries to allocate (85+C0+C1s) */
2500 res
= dir_alloc ( dp
, nent
); /* Allocate entries */
2501 if ( res
!= FR_OK
) return res
;
2502 dp
-> blk_ofs
= dp
-> dptr
- SZDIRE
* ( nent
- 1 ); /* Set the allocated entry block offset */
2504 if ( dp
-> obj
. stat
& 4 ) { /* Has the directory been stretched? */
2506 res
= fill_first_frag (& dp
-> obj
); /* Fill the first fragment on the FAT if needed */
2507 if ( res
!= FR_OK
) return res
;
2508 res
= fill_last_frag (& dp
-> obj
, dp
-> clust
, 0xFFFFFFFF ); /* Fill the last fragment on the FAT if needed */
2509 if ( res
!= FR_OK
) return res
;
2510 if ( dp
-> obj
. sclust
!= 0 ) { /* Is it a sub directory? */
2513 res
= load_obj_xdir (& dj
, & dp
-> obj
); /* Load the object status */
2514 if ( res
!= FR_OK
) return res
;
2515 dp
-> obj
. objsize
+= ( DWORD
) fs
-> csize
* SS ( fs
); /* Increase the directory size by cluster size */
2516 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, dp
-> obj
. objsize
); /* Update the allocation status */
2517 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, dp
-> obj
. objsize
);
2518 fs
-> dirbuf
[ XDIR_GenFlags
] = dp
-> obj
. stat
| 1 ;
2519 res
= store_xdir (& dj
); /* Store the object status */
2520 if ( res
!= FR_OK
) return res
;
2524 create_xdir ( fs
-> dirbuf
, fs
-> lfnbuf
); /* Create on-memory directory block to be written later */
2528 /* On the FAT/FAT32 volume */
2529 mem_cpy ( sn
, dp
-> fn
, 12 );
2530 if ( sn
[ NSFLAG
] & NS_LOSS
) { /* When LFN is out of 8.3 format, generate a numbered name */
2531 dp
-> fn
[ NSFLAG
] = NS_NOLFN
; /* Find only SFN */
2532 for ( n
= 1 ; n
< 100 ; n
++) {
2533 gen_numname ( dp
-> fn
, sn
, fs
-> lfnbuf
, n
); /* Generate a numbered name */
2534 res
= dir_find ( dp
); /* Check if the name collides with existing SFN */
2535 if ( res
!= FR_OK
) break ;
2537 if ( n
== 100 ) return FR_DENIED
; /* Abort if too many collisions */
2538 if ( res
!= FR_NO_FILE
) return res
; /* Abort if the result is other than 'not collided' */
2539 dp
-> fn
[ NSFLAG
] = sn
[ NSFLAG
];
2542 /* Create an SFN with/without LFNs. */
2543 nent
= ( sn
[ NSFLAG
] & NS_LFN
) ? ( nlen
+ 12 ) / 13 + 1 : 1 ; /* Number of entries to allocate */
2544 res
= dir_alloc ( dp
, nent
); /* Allocate entries */
2545 if ( res
== FR_OK
&& -- nent
) { /* Set LFN entry if needed */
2546 res
= dir_sdi ( dp
, dp
-> dptr
- nent
* SZDIRE
);
2548 sum
= sum_sfn ( dp
-> fn
); /* Checksum value of the SFN tied to the LFN */
2549 do { /* Store LFN entries in bottom first */
2550 res
= move_window ( fs
, dp
-> sect
);
2551 if ( res
!= FR_OK
) break ;
2552 put_lfn ( fs
-> lfnbuf
, dp
-> dir
, ( BYTE
) nent
, sum
);
2554 res
= dir_next ( dp
, 0 ); /* Next entry */
2555 } while ( res
== FR_OK
&& -- nent
);
2559 #else /* Non LFN configuration */
2560 res
= dir_alloc ( dp
, 1 ); /* Allocate an entry for SFN */
2566 res
= move_window ( fs
, dp
-> sect
);
2568 mem_set ( dp
-> dir
, 0 , SZDIRE
); /* Clean the entry */
2569 mem_cpy ( dp
-> dir
+ DIR_Name
, dp
-> fn
, 11 ); /* Put SFN */
2571 dp
-> dir
[ DIR_NTres
] = dp
-> fn
[ NSFLAG
] & ( NS_BODY
| NS_EXT
); /* Put NT flag */
2580 #endif /* !FF_FS_READONLY */
2584 #if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
2585 /*-----------------------------------------------------------------------*/
2586 /* Remove an object from the directory */
2587 /*-----------------------------------------------------------------------*/
2589 static FRESULT
dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
2590 DIR * dp
/* Directory object pointing the entry to be removed */
2594 FATFS
* fs
= dp
-> obj
. fs
;
2595 #if FF_USE_LFN /* LFN configuration */
2596 DWORD last
= dp
-> dptr
;
2598 res
= ( dp
-> blk_ofs
== 0xFFFFFFFF ) ? FR_OK
: dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto top of the entry block if LFN is exist */
2601 res
= move_window ( fs
, dp
-> sect
);
2602 if ( res
!= FR_OK
) break ;
2603 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2604 dp
-> dir
[ XDIR_Type
] &= 0x7F ; /* Clear the entry InUse flag. */
2605 } else { /* On the FAT/FAT32 volume */
2606 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'. */
2609 if ( dp
-> dptr
>= last
) break ; /* If reached last entry then all entries of the object has been deleted. */
2610 res
= dir_next ( dp
, 0 ); /* Next entry */
2611 } while ( res
== FR_OK
);
2612 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
;
2614 #else /* Non LFN configuration */
2616 res
= move_window ( fs
, dp
-> sect
);
2618 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'.*/
2626 #endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
2630 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2631 /*-----------------------------------------------------------------------*/
2632 /* Get file information from directory entry */
2633 /*-----------------------------------------------------------------------*/
2635 static void get_fileinfo (
2636 DIR * dp
, /* Pointer to the directory object */
2637 FILINFO
* fno
/* Pointer to the file information to be filled */
2643 FATFS
* fs
= dp
-> obj
. fs
;
2649 fno
-> fname
[ 0 ] = 0 ; /* Invaidate file info */
2650 if ( dp
-> sect
== 0 ) return ; /* Exit if read pointer has reached end of directory */
2652 #if FF_USE_LFN /* LFN configuration */
2654 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2655 get_xfileinfo ( fs
-> dirbuf
, fno
);
2659 { /* On the FAT/FAT32 volume */
2660 if ( dp
-> blk_ofs
!= 0xFFFFFFFF ) { /* Get LFN if available */
2662 while ( fs
-> lfnbuf
[ si
] != 0 ) {
2663 wc
= fs
-> lfnbuf
[ si
++]; /* Get an LFN character (UTF-16) */
2664 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2665 hs
= wc
; continue ; /* Get low surrogate */
2667 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in UTF-16 or UTF-8 encoding */
2668 if ( wc
== 0 ) { di
= 0 ; break ; } /* Invalid char or buffer overflow? */
2672 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2673 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN (null string means LFN is invalid) */
2678 while ( si
< 11 ) { /* Get SFN from SFN entry */
2679 wc
= dp
-> dir
[ si
++]; /* Get a char */
2680 if ( wc
== ' ' ) continue ; /* Skip padding spaces */
2681 if ( wc
== RDDEM
) wc
= DDEM
; /* Restore replaced DDEM character */
2682 if ( si
== 9 && di
< FF_SFN_BUF
) fno
-> altname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2683 #if FF_LFN_UNICODE >= 1 /* Unicode output */
2684 if ( dbc_1st (( BYTE
) wc
) && si
!= 8 && si
!= 11 && dbc_2nd ( dp
-> dir
[ si
])) { /* Make a DBC if needed */
2685 wc
= wc
<< 8 | dp
-> dir
[ si
++];
2687 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM -> Unicode */
2688 if ( wc
== 0 ) { di
= 0 ; break ; } /* Wrong char in the current code page? */
2689 wc
= put_utf ( wc
, & fno
-> altname
[ di
], FF_SFN_BUF
- di
); /* Store it in Unicode */
2690 if ( wc
== 0 ) { di
= 0 ; break ; } /* Buffer overflow? */
2692 #else /* ANSI/OEM output */
2693 fno
-> altname
[ di
++] = ( TCHAR
) wc
; /* Store it without any conversion */
2696 fno
-> altname
[ di
] = 0 ; /* Terminate the SFN (null string means SFN is invalid) */
2698 if ( fno
-> fname
[ 0 ] == 0 ) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
2699 if ( di
== 0 ) { /* If LFN and SFN both are invalid, this object is inaccesible */
2700 fno
-> fname
[ di
++] = '?' ;
2702 for ( si
= di
= 0 ; fno
-> altname
[ si
]; si
++, di
++) { /* Copy altname[] to fname[] with case information */
2703 wc
= ( WCHAR
) fno
-> altname
[ si
];
2704 if ( IsUpper ( wc
) && ( dp
-> dir
[ DIR_NTres
] & (( si
>= 9 ) ? NS_EXT
: NS_BODY
))) wc
+= 0x20 ;
2705 fno
-> fname
[ di
] = ( TCHAR
) wc
;
2708 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN */
2709 if (! dp
-> dir
[ DIR_NTres
]) fno
-> altname
[ 0 ] = 0 ; /* Altname is not needed if neither LFN nor case info is exist. */
2712 #else /* Non-LFN configuration */
2714 while ( si
< 11 ) { /* Copy name body and extension */
2715 c
= ( TCHAR
) dp
-> dir
[ si
++];
2716 if ( c
== ' ' ) continue ; /* Skip padding spaces */
2717 if ( c
== RDDEM
) c
= DDEM
; /* Restore replaced DDEM character */
2718 if ( si
== 9 ) fno
-> fname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2719 fno
-> fname
[ di
++] = c
;
2724 fno
-> fattrib
= dp
-> dir
[ DIR_Attr
]; /* Attribute */
2725 fno
-> fsize
= ld_dword ( dp
-> dir
+ DIR_FileSize
); /* Size */
2726 fno
-> ftime
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 0 ); /* Time */
2727 fno
-> fdate
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 2 ); /* Date */
2730 #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2734 #if FF_USE_FIND && FF_FS_MINIMIZE <= 1
2735 /*-----------------------------------------------------------------------*/
2736 /* Pattern matching */
2737 /*-----------------------------------------------------------------------*/
2739 static DWORD
get_achar ( /* Get a character and advances ptr */
2740 const TCHAR
** ptr
/* Pointer to pointer to the ANSI/OEM or Unicode string */
2746 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
2747 chr
= tchar2uni ( ptr
);
2748 if ( chr
== 0xFFFFFFFF ) chr
= 0 ; /* Wrong UTF encoding is recognized as end of the string */
2749 chr
= ff_wtoupper ( chr
);
2751 #else /* ANSI/OEM input */
2752 chr
= ( BYTE
)*(* ptr
)++; /* Get a byte */
2753 if ( IsLower ( chr
)) chr
-= 0x20 ; /* To upper ASCII char */
2754 #if FF_CODE_PAGE == 0
2755 if ( ExCvt
&& chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2756 #elif FF_CODE_PAGE < 900
2757 if ( chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2759 #if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
2760 if ( dbc_1st (( BYTE
) chr
)) { /* Get DBC 2nd byte if needed */
2761 chr
= dbc_2nd (( BYTE
)** ptr
) ? chr
<< 8 | ( BYTE
)*(* ptr
)++ : 0 ;
2770 static int pattern_matching ( /* 0:not matched, 1:matched */
2771 const TCHAR
* pat
, /* Matching pattern */
2772 const TCHAR
* nam
, /* String to be tested */
2773 int skip
, /* Number of pre-skip chars (number of ?s) */
2774 int inf
/* Infinite search (* specified) */
2777 const TCHAR
* pp
, * np
;
2782 while ( skip
--) { /* Pre-skip name chars */
2783 if (! get_achar (& nam
)) return 0 ; /* Branch mismatched if less name chars */
2785 if (* pat
== 0 && inf
) return 1 ; /* (short circuit) */
2788 pp
= pat
; np
= nam
; /* Top of pattern and name to match */
2790 if (* pp
== '?' || * pp
== '*' ) { /* Wildcard? */
2792 do { /* Analyze the wildcard block */
2793 if (* pp
++ == '?' ) nm
++; else nx
= 1 ;
2794 } while (* pp
== '?' || * pp
== '*' );
2795 if ( pattern_matching ( pp
, np
, nm
, nx
)) return 1 ; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
2796 nc
= * np
; break ; /* Branch mismatched */
2798 pc
= get_achar (& pp
); /* Get a pattern char */
2799 nc
= get_achar (& np
); /* Get a name char */
2800 if ( pc
!= nc
) break ; /* Branch mismatched? */
2801 if ( pc
== 0 ) return 1 ; /* Branch matched? (matched at end of both strings) */
2803 get_achar (& nam
); /* nam++ */
2804 } while ( inf
&& nc
); /* Retry until end of name if infinite search is specified */
2809 #endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
2813 /*-----------------------------------------------------------------------*/
2814 /* Pick a top segment and create the object name in directory form */
2815 /*-----------------------------------------------------------------------*/
2817 static FRESULT
create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
2818 DIR * dp
, /* Pointer to the directory object */
2819 const TCHAR
** path
/* Pointer to pointer to the segment in the path string */
2822 #if FF_USE_LFN /* LFN configuration */
2830 /* Create LFN into LFN working buffer */
2831 p
= * path
; lfn
= dp
-> obj
. fs
-> lfnbuf
; di
= 0 ;
2833 uc
= tchar2uni (& p
); /* Get a character */
2834 if ( uc
== 0xFFFFFFFF ) return FR_INVALID_NAME
; /* Invalid code or UTF decode error */
2835 if ( uc
>= 0x10000 ) lfn
[ di
++] = ( WCHAR
)( uc
>> 16 ); /* Store high surrogate if needed */
2837 if ( wc
< ' ' || wc
== '/' || wc
== ' \\ ' ) break ; /* Break if end of the path or a separator is found */
2838 if ( wc
< 0x80 && chk_chr ( " \" *:<> \? | \x7F " , wc
)) return FR_INVALID_NAME
; /* Reject illegal characters for LFN */
2839 if ( di
>= FF_MAX_LFN
) return FR_INVALID_NAME
; /* Reject too long name */
2840 lfn
[ di
++] = wc
; /* Store the Unicode character */
2842 while (* p
== '/' || * p
== ' \\ ' ) p
++; /* Skip duplicated separators if exist */
2843 * path
= p
; /* Return pointer to the next segment */
2844 cf
= ( wc
< ' ' ) ? NS_LAST
: 0 ; /* Set last segment flag if end of the path */
2846 #if FF_FS_RPATH != 0
2847 if (( di
== 1 && lfn
[ di
- 1 ] == '.' ) ||
2848 ( di
== 2 && lfn
[ di
- 1 ] == '.' && lfn
[ di
- 2 ] == '.' )) { /* Is this segment a dot name? */
2850 for ( i
= 0 ; i
< 11 ; i
++) { /* Create dot name for SFN entry */
2851 dp
-> fn
[ i
] = ( i
< di
) ? '.' : ' ' ;
2853 dp
-> fn
[ i
] = cf
| NS_DOT
; /* This is a dot entry */
2857 while ( di
) { /* Snip off trailing spaces and dots if exist */
2859 if ( wc
!= ' ' && wc
!= '.' ) break ;
2862 lfn
[ di
] = 0 ; /* LFN is created into the working buffer */
2863 if ( di
== 0 ) return FR_INVALID_NAME
; /* Reject null name */
2865 /* Create SFN in directory form */
2866 for ( si
= 0 ; lfn
[ si
] == ' ' ; si
++) ; /* Remove leading spaces */
2867 if ( si
> 0 || lfn
[ si
] == '.' ) cf
|= NS_LOSS
| NS_LFN
; /* Is there any leading space or dot? */
2868 while ( di
> 0 && lfn
[ di
- 1 ] != '.' ) di
--; /* Find last dot (di<=si: no extension) */
2870 mem_set ( dp
-> fn
, ' ' , 11 );
2873 wc
= lfn
[ si
++]; /* Get an LFN character */
2874 if ( wc
== 0 ) break ; /* Break on end of the LFN */
2875 if ( wc
== ' ' || ( wc
== '.' && si
!= di
)) { /* Remove embedded spaces and dots */
2876 cf
|= NS_LOSS
| NS_LFN
;
2880 if ( i
>= ni
|| si
== di
) { /* End of field? */
2881 if ( ni
== 11 ) { /* Name extension overflow? */
2882 cf
|= NS_LOSS
| NS_LFN
;
2885 if ( si
!= di
) cf
|= NS_LOSS
| NS_LFN
; /* Name body overflow? */
2886 if ( si
> di
) break ; /* No name extension? */
2887 si
= di
; i
= 8 ; ni
= 11 ; b
<<= 2 ; /* Enter name extension */
2891 if ( wc
>= 0x80 ) { /* Is this a non-ASCII character? */
2892 cf
|= NS_LFN
; /* LFN entry needs to be created */
2893 #if FF_CODE_PAGE == 0
2894 if ( ExCvt
) { /* At SBCS */
2895 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2896 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2897 } else { /* At DBCS */
2898 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Upper convert ==> ANSI/OEM code */
2900 #elif FF_CODE_PAGE < 900 /* SBCS cfg */
2901 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2902 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2903 #else /* DBCS cfg */
2904 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Upper convert ==> ANSI/OEM code */
2908 if ( wc
>= 0x100 ) { /* Is this a DBC? */
2909 if ( i
>= ni
- 1 ) { /* Field overflow? */
2910 cf
|= NS_LOSS
| NS_LFN
;
2911 i
= ni
; continue ; /* Next field */
2913 dp
-> fn
[ i
++] = ( BYTE
)( wc
>> 8 ); /* Put 1st byte */
2915 if ( wc
== 0 || chk_chr ( "+,;=[]" , wc
)) { /* Replace illegal characters for SFN if needed */
2916 wc
= '_' ; cf
|= NS_LOSS
| NS_LFN
; /* Lossy conversion */
2918 if ( IsUpper ( wc
)) { /* ASCII upper case? */
2921 if ( IsLower ( wc
)) { /* ASCII lower case? */
2926 dp
-> fn
[ i
++] = ( BYTE
) wc
;
2929 if ( dp
-> fn
[ 0 ] == DDEM
) dp
-> fn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
2931 if ( ni
== 8 ) b
<<= 2 ; /* Shift capital flags if no extension */
2932 if (( b
& 0x0C ) == 0x0C || ( b
& 0x03 ) == 0x03 ) cf
|= NS_LFN
; /* LFN entry needs to be created if composite capitals */
2933 if (!( cf
& NS_LFN
)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
2934 if ( b
& 0x01 ) cf
|= NS_EXT
; /* NT flag (Extension has small capital letters only) */
2935 if ( b
& 0x04 ) cf
|= NS_BODY
; /* NT flag (Body has small capital letters only) */
2938 dp
-> fn
[ NSFLAG
] = cf
; /* SFN is created into dp->fn[] */
2943 #else /* FF_USE_LFN : Non-LFN configuration */
2948 /* Create file name in directory form */
2949 p
= * path
; sfn
= dp
-> fn
;
2950 mem_set ( sfn
, ' ' , 11 );
2952 #if FF_FS_RPATH != 0
2953 if ( p
[ si
] == '.' ) { /* Is this a dot entry? */
2956 if ( c
!= '.' || si
>= 3 ) break ;
2959 if ( c
!= '/' && c
!= ' \\ ' && c
> ' ' ) return FR_INVALID_NAME
;
2960 * path
= p
+ si
; /* Return pointer to the next segment */
2961 sfn
[ NSFLAG
] = ( c
<= ' ' ) ? NS_LAST
| NS_DOT
: NS_DOT
; /* Set last segment flag if end of the path */
2966 c
= ( BYTE
) p
[ si
++]; /* Get a byte */
2967 if ( c
<= ' ' ) break ; /* Break if end of the path name */
2968 if ( c
== '/' || c
== ' \\ ' ) { /* Break if a separator is found */
2969 while ( p
[ si
] == '/' || p
[ si
] == ' \\ ' ) si
++; /* Skip duplicated separator if exist */
2972 if ( c
== '.' || i
>= ni
) { /* End of body or field overflow? */
2973 if ( ni
== 11 || c
!= '.' ) return FR_INVALID_NAME
; /* Field overflow or invalid dot? */
2974 i
= 8 ; ni
= 11 ; /* Enter file extension field */
2977 #if FF_CODE_PAGE == 0
2978 if ( ExCvt
&& c
>= 0x80 ) { /* Is SBC extended character? */
2979 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
2981 #elif FF_CODE_PAGE < 900
2982 if ( c
>= 0x80 ) { /* Is SBC extended character? */
2983 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
2986 if ( dbc_1st ( c
)) { /* Check if it is a DBC 1st byte */
2987 d
= ( BYTE
) p
[ si
++]; /* Get 2nd byte */
2988 if (! dbc_2nd ( d
) || i
>= ni
- 1 ) return FR_INVALID_NAME
; /* Reject invalid DBC */
2992 if ( chk_chr ( " \" *+,:;<=> \? []| \x7F " , c
)) return FR_INVALID_NAME
; /* Reject illegal chrs for SFN */
2993 if ( IsLower ( c
)) c
-= 0x20 ; /* To upper */
2997 * path
= p
+ si
; /* Return pointer to the next segment */
2998 if ( i
== 0 ) return FR_INVALID_NAME
; /* Reject nul string */
3000 if ( sfn
[ 0 ] == DDEM
) sfn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
3001 sfn
[ NSFLAG
] = ( c
<= ' ' ) ? NS_LAST
: 0 ; /* Set last segment flag if end of the path */
3004 #endif /* FF_USE_LFN */
3010 /*-----------------------------------------------------------------------*/
3011 /* Follow a file path */
3012 /*-----------------------------------------------------------------------*/
3014 static FRESULT
follow_path ( /* FR_OK(0): successful, !=0: error code */
3015 DIR * dp
, /* Directory object to return last directory and found object */
3016 const TCHAR
* path
/* Full-path string to find a file or directory */
3021 FATFS
* fs
= dp
-> obj
. fs
;
3024 #if FF_FS_RPATH != 0
3025 if (* path
!= '/' && * path
!= ' \\ ' ) { /* Without heading separator */
3026 dp
-> obj
. sclust
= fs
-> cdir
; /* Start from current directory */
3029 { /* With heading separator */
3030 while (* path
== '/' || * path
== ' \\ ' ) path
++; /* Strip heading separator */
3031 dp
-> obj
. sclust
= 0 ; /* Start from root directory */
3034 dp
-> obj
. n_frag
= 0 ; /* Invalidate last fragment counter of the object */
3035 #if FF_FS_RPATH != 0
3036 if ( fs
-> fs_type
== FS_EXFAT
&& dp
-> obj
. sclust
) { /* exFAT: Retrieve the sub-directory's status */
3039 dp
-> obj
. c_scl
= fs
-> cdc_scl
;
3040 dp
-> obj
. c_size
= fs
-> cdc_size
;
3041 dp
-> obj
. c_ofs
= fs
-> cdc_ofs
;
3042 res
= load_obj_xdir (& dj
, & dp
-> obj
);
3043 if ( res
!= FR_OK
) return res
;
3044 dp
-> obj
. objsize
= ld_dword ( fs
-> dirbuf
+ XDIR_FileSize
);
3045 dp
-> obj
. stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ;
3050 if (( UINT
)* path
< ' ' ) { /* Null path name is the origin directory itself */
3051 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3052 res
= dir_sdi ( dp
, 0 );
3054 } else { /* Follow path */
3056 res
= create_name ( dp
, & path
); /* Get a segment name of the path */
3057 if ( res
!= FR_OK
) break ;
3058 res
= dir_find ( dp
); /* Find an object with the segment name */
3059 ns
= dp
-> fn
[ NSFLAG
];
3060 if ( res
!= FR_OK
) { /* Failed to find the object */
3061 if ( res
== FR_NO_FILE
) { /* Object is not found */
3062 if ( FF_FS_RPATH
&& ( ns
& NS_DOT
)) { /* If dot entry is not exist, stay there */
3063 if (!( ns
& NS_LAST
)) continue ; /* Continue to follow if not last segment */
3064 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3066 } else { /* Could not find the object */
3067 if (!( ns
& NS_LAST
)) res
= FR_NO_PATH
; /* Adjust error code if not last segment */
3072 if ( ns
& NS_LAST
) break ; /* Last segment matched. Function completed. */
3073 /* Get into the sub-directory */
3074 if (!( dp
-> obj
. attr
& AM_DIR
)) { /* It is not a sub-directory and cannot follow */
3075 res
= FR_NO_PATH
; break ;
3078 if ( fs
-> fs_type
== FS_EXFAT
) { /* Save containing directory information for next dir */
3079 dp
-> obj
. c_scl
= dp
-> obj
. sclust
;
3080 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
3081 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
3082 init_alloc_info ( fs
, & dp
-> obj
); /* Open next directory */
3086 dp
-> obj
. sclust
= ld_clust ( fs
, fs
-> win
+ dp
-> dptr
% SS ( fs
)); /* Open next directory */
3097 /*-----------------------------------------------------------------------*/
3098 /* Get logical drive number from path name */
3099 /*-----------------------------------------------------------------------*/
3101 static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
3102 const TCHAR
** path
/* Pointer to pointer to the path name */
3105 const TCHAR
* tp
, * tt
;
3108 #if FF_STR_VOLUME_ID /* Find string volume ID */
3114 if (! tp
) return vol
; /* Invalid path name? */
3115 do tc
= * tt
++; while (( UINT
) tc
>= ( FF_USE_LFN
? ' ' : '!' ) && tc
!= ':' ); /* Find a colon in the path */
3117 if ( tc
== ':' ) { /* DOS/Windows style volume ID? */
3119 if ( IsDigit (* tp
) && tp
+ 2 == tt
) { /* Is there a numeric volume ID + colon? */
3120 i
= ( int )* tp
- '0' ; /* Get the LD number */
3122 #if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
3126 sp
= VolumeStr
[ i
]; tp
= * path
; /* This string volume ID and path name */
3127 do { /* Compare the volume ID with path name */
3128 c
= * sp
++; tc
= * tp
++;
3129 if ( IsLower ( c
)) c
-= 0x20 ;
3130 if ( IsLower ( tc
)) tc
-= 0x20 ;
3131 } while ( c
&& ( TCHAR
) c
== tc
);
3132 } while (( c
|| tp
!= tt
) && ++ i
< FF_VOLUMES
); /* Repeat for each id until pattern match */
3135 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3136 vol
= i
; /* Drive number */
3137 * path
= tt
; /* Snip the drive prefix off */
3141 #if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
3145 sp
= VolumeStr
[ i
]; tp
= * path
; /* This string volume ID and path name */
3146 do { /* Compare the volume ID with path name */
3147 c
= * sp
++; tc
= *(++ tp
);
3148 if ( IsLower ( c
)) c
-= 0x20 ;
3149 if ( IsLower ( tc
)) tc
-= 0x20 ;
3150 } while ( c
&& ( TCHAR
) c
== tc
);
3151 } while (( c
|| ( tc
!= '/' && ( UINT
) tc
>= ( FF_USE_LFN
? ' ' : '!' ))) && ++ i
< FF_VOLUMES
); /* Repeat for each ID until pattern match */
3152 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3153 vol
= i
; /* Drive number */
3154 * path
= tp
; /* Snip the drive prefix off */
3159 /* No drive prefix is found */
3160 #if FF_FS_RPATH != 0
3161 vol
= CurrVol
; /* Default drive is current drive */
3163 vol
= 0 ; /* Default drive is 0 */
3165 return vol
; /* Return the default drive */
3171 /*-----------------------------------------------------------------------*/
3172 /* Load a sector and check if it is an FAT VBR */
3173 /*-----------------------------------------------------------------------*/
3175 static BYTE
check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */
3176 FATFS
* fs
, /* Filesystem object */
3177 DWORD sect
/* Sector# (lba) to load and check if it is an FAT-VBR or not */
3180 fs
-> wflag
= 0 ; fs
-> winsect
= 0xFFFFFFFF ; /* Invaidate window */
3181 if ( move_window ( fs
, sect
) != FR_OK
) return 4 ; /* Load boot record */
3183 if ( ld_word ( fs
-> win
+ BS_55AA
) != 0xAA55 ) return 3 ; /* Check boot record signature (always here regardless of the sector size) */
3186 if (! mem_cmp ( fs
-> win
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 )) return 1 ; /* Check if exFAT VBR */
3188 if ( fs
-> win
[ BS_JmpBoot
] == 0xE9 || fs
-> win
[ BS_JmpBoot
] == 0xEB || fs
-> win
[ BS_JmpBoot
] == 0xE8 ) { /* Valid JumpBoot code? */
3189 if (! mem_cmp ( fs
-> win
+ BS_FilSysType
, "FAT" , 3 )) return 0 ; /* Is it an FAT VBR? */
3190 if (! mem_cmp ( fs
-> win
+ BS_FilSysType32
, "FAT32" , 5 )) return 0 ; /* Is it an FAT32 VBR? */
3192 return 2 ; /* Valid BS but not FAT */
3198 /*-----------------------------------------------------------------------*/
3199 /* Determine logical drive number and mount the volume if needed */
3200 /*-----------------------------------------------------------------------*/
3202 static FRESULT
find_volume ( /* FR_OK(0): successful, !=0: an error occurred */
3203 const TCHAR
** path
, /* Pointer to pointer to the path name (drive number) */
3204 FATFS
** rfs
, /* Pointer to pointer to the found filesystem object */
3205 BYTE mode
/* !=0: Check write protection for write access */
3211 DWORD bsect
, fasize
, tsect
, sysect
, nclst
, szbfat
, br
[ 4 ];
3217 /* Get logical drive number */
3219 vol
= get_ldnumber ( path
);
3220 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3222 /* Check if the filesystem object is valid or not */
3223 fs
= FatFs
[ vol
]; /* Get pointer to the filesystem object */
3224 if (! fs
) return FR_NOT_ENABLED
; /* Is the filesystem object available? */
3226 if (! lock_fs ( fs
)) return FR_TIMEOUT
; /* Lock the volume */
3228 * rfs
= fs
; /* Return pointer to the filesystem object */
3230 mode
&= ( BYTE
)~ FA_READ
; /* Desired access mode, write access or not */
3231 if ( fs
-> fs_type
!= 0 ) { /* If the volume has been mounted */
3232 stat
= disk_status ( fs
-> pdrv
);
3233 if (!( stat
& STA_NOINIT
)) { /* and the physical drive is kept initialized */
3234 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check write protection if needed */
3235 return FR_WRITE_PROTECTED
;
3237 return FR_OK
; /* The filesystem object is valid */
3241 /* The filesystem object is not valid. */
3242 /* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */
3244 fs
-> fs_type
= 0 ; /* Clear the filesystem object */
3245 fs
-> pdrv
= LD2PD ( vol
); /* Bind the logical drive and a physical drive */
3246 stat
= disk_initialize ( fs
-> pdrv
); /* Initialize the physical drive */
3247 if ( stat
& STA_NOINIT
) { /* Check if the initialization succeeded */
3248 return FR_NOT_READY
; /* Failed to initialize due to no medium or hard error */
3250 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check disk write protection if needed */
3251 return FR_WRITE_PROTECTED
;
3253 #if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
3254 if ( disk_ioctl ( fs
-> pdrv
, GET_SECTOR_SIZE
, & SS ( fs
)) != RES_OK
) return FR_DISK_ERR
;
3255 if ( SS ( fs
) > FF_MAX_SS
|| SS ( fs
) < FF_MIN_SS
|| ( SS ( fs
) & ( SS ( fs
) - 1 ))) return FR_DISK_ERR
;
3258 /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */
3260 fmt
= check_fs ( fs
, bsect
); /* Load sector 0 and check if it is an FAT-VBR as SFD */
3261 if ( fmt
== 2 || ( fmt
< 2 && LD2PT ( vol
) != 0 )) { /* Not an FAT-VBR or forced partition number */
3262 for ( i
= 0 ; i
< 4 ; i
++) { /* Get partition offset */
3263 pt
= fs
-> win
+ ( MBR_Table
+ i
* SZ_PTE
);
3264 br
[ i
] = pt
[ PTE_System
] ? ld_dword ( pt
+ PTE_StLba
) : 0 ;
3266 i
= LD2PT ( vol
); /* Partition number: 0:auto, 1-4:forced */
3268 do { /* Find an FAT volume */
3270 fmt
= bsect
? check_fs ( fs
, bsect
) : 3 ; /* Check the partition */
3271 } while ( LD2PT ( vol
) == 0 && fmt
>= 2 && ++ i
< 4 );
3273 if ( fmt
== 4 ) return FR_DISK_ERR
; /* An error occured in the disk I/O layer */
3274 if ( fmt
>= 2 ) return FR_NO_FILESYSTEM
; /* No FAT volume is found */
3276 /* An FAT volume is found (bsect). Following code initializes the filesystem object */
3282 for ( i
= BPB_ZeroedEx
; i
< BPB_ZeroedEx
+ 53 && fs
-> win
[ i
] == 0 ; i
++) ; /* Check zero filler */
3283 if ( i
< BPB_ZeroedEx
+ 53 ) return FR_NO_FILESYSTEM
;
3285 if ( ld_word ( fs
-> win
+ BPB_FSVerEx
) != 0x100 ) return FR_NO_FILESYSTEM
; /* Check exFAT version (must be version 1.0) */
3287 if ( 1 << fs
-> win
[ BPB_BytsPerSecEx
] != SS ( fs
)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
3288 return FR_NO_FILESYSTEM
;
3291 maxlba
= ld_qword ( fs
-> win
+ BPB_TotSecEx
) + bsect
; /* Last LBA + 1 of the volume */
3292 if ( maxlba
>= 0x100000000 ) return FR_NO_FILESYSTEM
; /* (It cannot be handled in 32-bit LBA) */
3294 fs
-> fsize
= ld_dword ( fs
-> win
+ BPB_FatSzEx
); /* Number of sectors per FAT */
3296 fs
-> n_fats
= fs
-> win
[ BPB_NumFATsEx
]; /* Number of FATs */
3297 if ( fs
-> n_fats
!= 1 ) return FR_NO_FILESYSTEM
; /* (Supports only 1 FAT) */
3299 fs
-> csize
= 1 << fs
-> win
[ BPB_SecPerClusEx
]; /* Cluster size */
3300 if ( fs
-> csize
== 0 ) return FR_NO_FILESYSTEM
; /* (Must be 1..32768) */
3302 nclst
= ld_dword ( fs
-> win
+ BPB_NumClusEx
); /* Number of clusters */
3303 if ( nclst
> MAX_EXFAT
) return FR_NO_FILESYSTEM
; /* (Too many clusters) */
3304 fs
-> n_fatent
= nclst
+ 2 ;
3306 /* Boundaries and Limits */
3307 fs
-> volbase
= bsect
;
3308 fs
-> database
= bsect
+ ld_dword ( fs
-> win
+ BPB_DataOfsEx
);
3309 fs
-> fatbase
= bsect
+ ld_dword ( fs
-> win
+ BPB_FatOfsEx
);
3310 if ( maxlba
< ( QWORD
) fs
-> database
+ nclst
* fs
-> csize
) return FR_NO_FILESYSTEM
; /* (Volume size must not be smaller than the size requiered) */
3311 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClusEx
);
3313 /* Check if bitmap location is in assumption (at the first cluster) */
3314 if ( move_window ( fs
, clst2sect ( fs
, fs
-> dirbase
)) != FR_OK
) return FR_DISK_ERR
;
3315 for ( i
= 0 ; i
< SS ( fs
); i
+= SZDIRE
) {
3316 if ( fs
-> win
[ i
] == 0x81 && ld_dword ( fs
-> win
+ i
+ 20 ) == 2 ) break ; /* 81 entry with cluster #2? */
3318 if ( i
== SS ( fs
)) return FR_NO_FILESYSTEM
;
3320 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3322 fmt
= FS_EXFAT
; /* FAT sub-type */
3324 #endif /* FF_FS_EXFAT */
3326 if ( ld_word ( fs
-> win
+ BPB_BytsPerSec
) != SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_BytsPerSec must be equal to the physical sector size) */
3328 fasize
= ld_word ( fs
-> win
+ BPB_FATSz16
); /* Number of sectors per FAT */
3329 if ( fasize
== 0 ) fasize
= ld_dword ( fs
-> win
+ BPB_FATSz32
);
3332 fs
-> n_fats
= fs
-> win
[ BPB_NumFATs
]; /* Number of FATs */
3333 if ( fs
-> n_fats
!= 1 && fs
-> n_fats
!= 2 ) return FR_NO_FILESYSTEM
; /* (Must be 1 or 2) */
3334 fasize
*= fs
-> n_fats
; /* Number of sectors for FAT area */
3336 fs
-> csize
= fs
-> win
[ BPB_SecPerClus
]; /* Cluster size */
3337 if ( fs
-> csize
== 0 || ( fs
-> csize
& ( fs
-> csize
- 1 ))) return FR_NO_FILESYSTEM
; /* (Must be power of 2) */
3339 fs
-> n_rootdir
= ld_word ( fs
-> win
+ BPB_RootEntCnt
); /* Number of root directory entries */
3340 if ( fs
-> n_rootdir
% ( SS ( fs
) / SZDIRE
)) return FR_NO_FILESYSTEM
; /* (Must be sector aligned) */
3342 tsect
= ld_word ( fs
-> win
+ BPB_TotSec16
); /* Number of sectors on the volume */
3343 if ( tsect
== 0 ) tsect
= ld_dword ( fs
-> win
+ BPB_TotSec32
);
3345 nrsv
= ld_word ( fs
-> win
+ BPB_RsvdSecCnt
); /* Number of reserved sectors */
3346 if ( nrsv
== 0 ) return FR_NO_FILESYSTEM
; /* (Must not be 0) */
3348 /* Determine the FAT sub type */
3349 sysect
= nrsv
+ fasize
+ fs
-> n_rootdir
/ ( SS ( fs
) / SZDIRE
); /* RSV + FAT + DIR */
3350 if ( tsect
< sysect
) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3351 nclst
= ( tsect
- sysect
) / fs
-> csize
; /* Number of clusters */
3352 if ( nclst
== 0 ) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3354 if ( nclst
<= MAX_FAT32
) fmt
= FS_FAT32
;
3355 if ( nclst
<= MAX_FAT16
) fmt
= FS_FAT16
;
3356 if ( nclst
<= MAX_FAT12
) fmt
= FS_FAT12
;
3357 if ( fmt
== 0 ) return FR_NO_FILESYSTEM
;
3359 /* Boundaries and Limits */
3360 fs
-> n_fatent
= nclst
+ 2 ; /* Number of FAT entries */
3361 fs
-> volbase
= bsect
; /* Volume start sector */
3362 fs
-> fatbase
= bsect
+ nrsv
; /* FAT start sector */
3363 fs
-> database
= bsect
+ sysect
; /* Data start sector */
3364 if ( fmt
== FS_FAT32
) {
3365 if ( ld_word ( fs
-> win
+ BPB_FSVer32
) != 0 ) return FR_NO_FILESYSTEM
; /* (Must be FAT32 revision 0.0) */
3366 if ( fs
-> n_rootdir
!= 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must be 0) */
3367 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClus32
); /* Root directory start cluster */
3368 szbfat
= fs
-> n_fatent
* 4 ; /* (Needed FAT size) */
3370 if ( fs
-> n_rootdir
== 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must not be 0) */
3371 fs
-> dirbase
= fs
-> fatbase
+ fasize
; /* Root directory start sector */
3372 szbfat
= ( fmt
== FS_FAT16
) ? /* (Needed FAT size) */
3373 fs
-> n_fatent
* 2 : fs
-> n_fatent
* 3 / 2 + ( fs
-> n_fatent
& 1 );
3375 if ( fs
-> fsize
< ( szbfat
+ ( SS ( fs
) - 1 )) / SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_FATSz must not be less than the size needed) */
3378 /* Get FSInfo if available */
3379 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3380 fs
-> fsi_flag
= 0x80 ;
3381 #if (FF_FS_NOFSINFO & 3) != 3
3382 if ( fmt
== FS_FAT32
/* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
3383 && ld_word ( fs
-> win
+ BPB_FSInfo32
) == 1
3384 && move_window ( fs
, bsect
+ 1 ) == FR_OK
)
3387 if ( ld_word ( fs
-> win
+ BS_55AA
) == 0xAA55 /* Load FSInfo data if available */
3388 && ld_dword ( fs
-> win
+ FSI_LeadSig
) == 0x41615252
3389 && ld_dword ( fs
-> win
+ FSI_StrucSig
) == 0x61417272 )
3391 #if (FF_FS_NOFSINFO & 1) == 0
3392 fs
-> free_clst
= ld_dword ( fs
-> win
+ FSI_Free_Count
);
3394 #if (FF_FS_NOFSINFO & 2) == 0
3395 fs
-> last_clst
= ld_dword ( fs
-> win
+ FSI_Nxt_Free
);
3399 #endif /* (FF_FS_NOFSINFO & 3) != 3 */
3400 #endif /* !FF_FS_READONLY */
3403 fs
-> fs_type
= fmt
; /* FAT sub-type */
3404 fs
-> id
= ++ Fsid
; /* Volume mount ID */
3406 fs
-> lfnbuf
= LfnBuf
; /* Static LFN working buffer */
3408 fs
-> dirbuf
= DirBuf
; /* Static directory block scratchpad buuffer */
3411 #if FF_FS_RPATH != 0
3412 fs
-> cdir
= 0 ; /* Initialize current directory */
3414 #if FF_FS_LOCK != 0 /* Clear file lock semaphores */
3423 /*-----------------------------------------------------------------------*/
3424 /* Check if the file/directory object is valid or not */
3425 /*-----------------------------------------------------------------------*/
3427 static FRESULT
validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
3428 FFOBJID
* obj
, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
3429 FATFS
** rfs
/* Pointer to pointer to the owner filesystem object to return */
3432 FRESULT res
= FR_INVALID_OBJECT
;
3435 if ( obj
&& obj
-> fs
&& obj
-> fs
-> fs_type
&& obj
-> id
== obj
-> fs
-> id
) { /* Test if the object is valid */
3437 if ( lock_fs ( obj
-> fs
)) { /* Obtain the filesystem object */
3438 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the phsical drive is kept initialized */
3441 unlock_fs ( obj
-> fs
, FR_OK
);
3447 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the phsical drive is kept initialized */
3452 * rfs
= ( res
== FR_OK
) ? obj
-> fs
: 0 ; /* Corresponding filesystem object */
3459 /*---------------------------------------------------------------------------
3461 Public Functions (FatFs API)
3463 ----------------------------------------------------------------------------*/
3467 /*-----------------------------------------------------------------------*/
3468 /* Mount/Unmount a Logical Drive */
3469 /*-----------------------------------------------------------------------*/
3472 FATFS
* fs
, /* Pointer to the filesystem object (NULL:unmount)*/
3473 const TCHAR
* path
, /* Logical drive number to be mounted/unmounted */
3474 BYTE opt
/* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
3480 const TCHAR
* rp
= path
;
3483 /* Get logical drive number */
3484 vol
= get_ldnumber (& rp
);
3485 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3486 cfs
= FatFs
[ vol
]; /* Pointer to fs object */
3492 #if FF_FS_REENTRANT /* Discard sync object of the current volume */
3493 if (! ff_del_syncobj ( cfs
-> sobj
)) return FR_INT_ERR
;
3495 cfs
-> fs_type
= 0 ; /* Clear old fs object */
3499 fs
-> fs_type
= 0 ; /* Clear new fs object */
3500 #if FF_FS_REENTRANT /* Create sync object for the new volume */
3501 if (! ff_cre_syncobj (( BYTE
) vol
, & fs
-> sobj
)) return FR_INT_ERR
;
3504 FatFs
[ vol
] = fs
; /* Register new fs object */
3506 if ( opt
== 0 ) return FR_OK
; /* Do not mount now, it will be mounted later */
3508 res
= find_volume (& path
, & fs
, 0 ); /* Force mounted the volume */
3515 /*-----------------------------------------------------------------------*/
3516 /* Open or Create a File */
3517 /*-----------------------------------------------------------------------*/
3520 FIL
* fp
, /* Pointer to the blank file object */
3521 const TCHAR
* path
, /* Pointer to the file name */
3522 BYTE mode
/* Access mode and file open mode flags */
3529 DWORD dw
, cl
, bcs
, clst
, sc
;
3535 if (! fp
) return FR_INVALID_OBJECT
;
3537 /* Get logical drive number */
3538 mode
&= FF_FS_READONLY
? FA_READ
: FA_READ
| FA_WRITE
| FA_CREATE_ALWAYS
| FA_CREATE_NEW
| FA_OPEN_ALWAYS
| FA_OPEN_APPEND
;
3539 res
= find_volume (& path
, & fs
, mode
);
3543 res
= follow_path (& dj
, path
); /* Follow the file path */
3544 #if !FF_FS_READONLY /* Read/Write configuration */
3546 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Origin directory itself? */
3547 res
= FR_INVALID_NAME
;
3551 res
= chk_lock (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Check if the file can be used */
3555 /* Create or Open a file */
3556 if ( mode
& ( FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
)) {
3557 if ( res
!= FR_OK
) { /* No file, create new */
3558 if ( res
== FR_NO_FILE
) { /* There is no file to open, create a new entry */
3560 res
= enq_lock () ? dir_register (& dj
) : FR_TOO_MANY_OPEN_FILES
;
3562 res
= dir_register (& dj
);
3565 mode
|= FA_CREATE_ALWAYS
; /* File is created */
3567 else { /* Any object with the same name is already existing */
3568 if ( dj
. obj
. attr
& ( AM_RDO
| AM_DIR
)) { /* Cannot overwrite it (R/O or DIR) */
3571 if ( mode
& FA_CREATE_NEW
) res
= FR_EXIST
; /* Cannot create as new file */
3574 if ( res
== FR_OK
&& ( mode
& FA_CREATE_ALWAYS
)) { /* Truncate the file if overwrite mode */
3576 if ( fs
-> fs_type
== FS_EXFAT
) {
3577 /* Get current allocation info */
3579 init_alloc_info ( fs
, & fp
-> obj
);
3580 /* Set directory entry block initial state */
3581 mem_set ( fs
-> dirbuf
+ 2 , 0 , 30 ); /* Clear 85 entry except for NumSec */
3582 mem_set ( fs
-> dirbuf
+ 38 , 0 , 26 ); /* Clear C0 entry except for NumName and NameHash */
3583 fs
-> dirbuf
[ XDIR_Attr
] = AM_ARC
;
3584 st_dword ( fs
-> dirbuf
+ XDIR_CrtTime
, GET_FATTIME ());
3585 fs
-> dirbuf
[ XDIR_GenFlags
] = 1 ;
3586 res
= store_xdir (& dj
);
3587 if ( res
== FR_OK
&& fp
-> obj
. sclust
!= 0 ) { /* Remove the cluster chain if exist */
3588 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
3589 fs
-> last_clst
= fp
-> obj
. sclust
- 1 ; /* Reuse the cluster hole */
3594 /* Set directory entry initial state */
3595 cl
= ld_clust ( fs
, dj
. dir
); /* Get current cluster chain */
3596 st_dword ( dj
. dir
+ DIR_CrtTime
, GET_FATTIME ()); /* Set created time */
3597 dj
. dir
[ DIR_Attr
] = AM_ARC
; /* Reset attribute */
3598 st_clust ( fs
, dj
. dir
, 0 ); /* Reset file allocation info */
3599 st_dword ( dj
. dir
+ DIR_FileSize
, 0 );
3601 if ( cl
!= 0 ) { /* Remove the cluster chain if exist */
3603 res
= remove_chain (& dj
. obj
, cl
, 0 );
3605 res
= move_window ( fs
, dw
);
3606 fs
-> last_clst
= cl
- 1 ; /* Reuse the cluster hole */
3612 else { /* Open an existing file */
3613 if ( res
== FR_OK
) { /* Is the object exsiting? */
3614 if ( dj
. obj
. attr
& AM_DIR
) { /* File open against a directory */
3617 if (( mode
& FA_WRITE
) && ( dj
. obj
. attr
& AM_RDO
)) { /* Write mode open against R/O file */
3624 if ( mode
& FA_CREATE_ALWAYS
) mode
|= FA_MODIFIED
; /* Set file change flag if created or overwritten */
3625 fp
-> dir_sect
= fs
-> winsect
; /* Pointer to the directory entry */
3626 fp
-> dir_ptr
= dj
. dir
;
3628 fp
-> obj
. lockid
= inc_lock (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Lock the file for this session */
3629 if ( fp
-> obj
. lockid
== 0 ) res
= FR_INT_ERR
;
3632 #else /* R/O configuration */
3634 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it origin directory itself? */
3635 res
= FR_INVALID_NAME
;
3637 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a directory? */
3646 if ( fs
-> fs_type
== FS_EXFAT
) {
3647 fp
-> obj
. c_scl
= dj
. obj
. sclust
; /* Get containing directory info */
3648 fp
-> obj
. c_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
3649 fp
-> obj
. c_ofs
= dj
. blk_ofs
;
3650 init_alloc_info ( fs
, & fp
-> obj
);
3654 fp
-> obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Get object allocation info */
3655 fp
-> obj
. objsize
= ld_dword ( dj
. dir
+ DIR_FileSize
);
3658 fp
-> cltbl
= 0 ; /* Disable fast seek mode */
3660 fp
-> obj
. fs
= fs
; /* Validate the file object */
3661 fp
-> obj
. id
= fs
-> id
;
3662 fp
-> flag
= mode
; /* Set file access mode */
3663 fp
-> err
= 0 ; /* Clear error flag */
3664 fp
-> sect
= 0 ; /* Invalidate current data sector */
3665 fp
-> fptr
= 0 ; /* Set file pointer top of the file */
3668 mem_set ( fp
-> buf
, 0 , FF_MAX_SS
); /* Clear sector buffer */
3670 if (( mode
& FA_SEEKEND
) && fp
-> obj
. objsize
> 0 ) { /* Seek to end of file if FA_OPEN_APPEND is specified */
3671 fp
-> fptr
= fp
-> obj
. objsize
; /* Offset to seek */
3672 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size in byte */
3673 clst
= fp
-> obj
. sclust
; /* Follow the cluster chain */
3674 for ( ofs
= fp
-> obj
. objsize
; res
== FR_OK
&& ofs
> bcs
; ofs
-= bcs
) {
3675 clst
= get_fat (& fp
-> obj
, clst
);
3676 if ( clst
<= 1 ) res
= FR_INT_ERR
;
3677 if ( clst
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
3680 if ( res
== FR_OK
&& ofs
% SS ( fs
)) { /* Fill sector buffer if not on the sector boundary */
3681 if (( sc
= clst2sect ( fs
, clst
)) == 0 ) {
3684 fp
-> sect
= sc
+ ( DWORD
)( ofs
/ SS ( fs
));
3686 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) res
= FR_DISK_ERR
;
3697 if ( res
!= FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object on error */
3705 /*-----------------------------------------------------------------------*/
3707 /*-----------------------------------------------------------------------*/
3710 FIL
* fp
, /* Pointer to the file object */
3711 void * buff
, /* Pointer to data buffer */
3712 UINT btr
, /* Number of bytes to read */
3713 UINT
* br
/* Pointer to number of bytes read */
3720 UINT rcnt
, cc
, csect
;
3721 BYTE
* rbuff
= ( BYTE
*) buff
;
3724 * br
= 0 ; /* Clear read byte counter */
3725 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3726 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
3727 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
3728 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
3729 if ( btr
> remain
) btr
= ( UINT
) remain
; /* Truncate btr by remaining bytes */
3731 for ( ; btr
; /* Repeat until btr bytes read */
3732 btr
-= rcnt
, * br
+= rcnt
, rbuff
+= rcnt
, fp
-> fptr
+= rcnt
) {
3733 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
3734 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
3735 if ( csect
== 0 ) { /* On the cluster boundary? */
3736 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
3737 clst
= fp
-> obj
. sclust
; /* Follow cluster chain from the origin */
3738 } else { /* Middle or end of the file */
3741 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
3745 clst
= get_fat (& fp
-> obj
, fp
-> clust
); /* Follow cluster chain on the FAT */
3748 if ( clst
< 2 ) ABORT ( fs
, FR_INT_ERR
);
3749 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
3750 fp
-> clust
= clst
; /* Update current cluster */
3752 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
3753 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
3755 cc
= btr
/ SS ( fs
); /* When remaining bytes >= sector size, */
3756 if ( cc
> 0 ) { /* Read maximum contiguous sectors directly */
3757 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
3758 cc
= fs
-> csize
- csect
;
3760 if ( disk_read ( fs
-> pdrv
, rbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3761 #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
3763 if ( fs
-> wflag
&& fs
-> winsect
- sect
< cc
) {
3764 mem_cpy ( rbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), fs
-> win
, SS ( fs
));
3767 if (( fp
-> flag
& FA_DIRTY
) && fp
-> sect
- sect
< cc
) {
3768 mem_cpy ( rbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), fp
-> buf
, SS ( fs
));
3772 rcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
3776 if ( fp
-> sect
!= sect
) { /* Load data sector if not in cache */
3778 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
3779 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3780 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3783 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
3788 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes left in the sector */
3789 if ( rcnt
> btr
) rcnt
= btr
; /* Clip it by btr if needed */
3791 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
3792 mem_cpy ( rbuff
, fs
-> win
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
3794 mem_cpy ( rbuff
, fp
-> buf
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
3798 LEAVE_FF ( fs
, FR_OK
);
3805 /*-----------------------------------------------------------------------*/
3807 /*-----------------------------------------------------------------------*/
3810 FIL
* fp
, /* Pointer to the file object */
3811 const void * buff
, /* Pointer to the data to be written */
3812 UINT btw
, /* Number of bytes to write */
3813 UINT
* bw
/* Pointer to number of bytes written */
3819 UINT wcnt
, cc
, csect
;
3820 const BYTE
* wbuff
= ( const BYTE
*) buff
;
3823 * bw
= 0 ; /* Clear write byte counter */
3824 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3825 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
3826 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
3828 /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
3829 if ((! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) && ( DWORD
)( fp
-> fptr
+ btw
) < ( DWORD
) fp
-> fptr
) {
3830 btw
= ( UINT
)( 0xFFFFFFFF - ( DWORD
) fp
-> fptr
);
3833 for ( ; btw
; /* Repeat until all data written */
3834 btw
-= wcnt
, * bw
+= wcnt
, wbuff
+= wcnt
, fp
-> fptr
+= wcnt
, fp
-> obj
. objsize
= ( fp
-> fptr
> fp
-> obj
. objsize
) ? fp
-> fptr
: fp
-> obj
. objsize
) {
3835 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
3836 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
)) & ( fs
-> csize
- 1 ); /* Sector offset in the cluster */
3837 if ( csect
== 0 ) { /* On the cluster boundary? */
3838 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
3839 clst
= fp
-> obj
. sclust
; /* Follow from the origin */
3840 if ( clst
== 0 ) { /* If no cluster is allocated, */
3841 clst
= create_chain (& fp
-> obj
, 0 ); /* create a new cluster chain */
3843 } else { /* On the middle or end of the file */
3846 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
3850 clst
= create_chain (& fp
-> obj
, fp
-> clust
); /* Follow or stretch cluster chain on the FAT */
3853 if ( clst
== 0 ) break ; /* Could not allocate a new cluster (disk full) */
3854 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
3855 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
3856 fp
-> clust
= clst
; /* Update current cluster */
3857 if ( fp
-> obj
. sclust
== 0 ) fp
-> obj
. sclust
= clst
; /* Set start cluster if the first write */
3860 if ( fs
-> winsect
== fp
-> sect
&& sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Write-back sector cache */
3862 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back sector cache */
3863 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3864 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3867 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
3868 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
3870 cc
= btw
/ SS ( fs
); /* When remaining bytes >= sector size, */
3871 if ( cc
> 0 ) { /* Write maximum contiguous sectors directly */
3872 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
3873 cc
= fs
-> csize
- csect
;
3875 if ( disk_write ( fs
-> pdrv
, wbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3876 #if FF_FS_MINIMIZE <= 2
3878 if ( fs
-> winsect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
3879 mem_cpy ( fs
-> win
, wbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), SS ( fs
));
3883 if ( fp
-> sect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
3884 mem_cpy ( fp
-> buf
, wbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), SS ( fs
));
3885 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3889 wcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
3893 if ( fp
-> fptr
>= fp
-> obj
. objsize
) { /* Avoid silly cache filling on the growing edge */
3894 if ( sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
);
3898 if ( fp
-> sect
!= sect
&& /* Fill sector cache with file data */
3899 fp
-> fptr
< fp
-> obj
. objsize
&&
3900 disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) {
3901 ABORT ( fs
, FR_DISK_ERR
);
3906 wcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes left in the sector */
3907 if ( wcnt
> btw
) wcnt
= btw
; /* Clip it by btw if needed */
3909 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
3910 mem_cpy ( fs
-> win
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
3913 mem_cpy ( fp
-> buf
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
3914 fp
-> flag
|= FA_DIRTY
;
3918 fp
-> flag
|= FA_MODIFIED
; /* Set file change flag */
3920 LEAVE_FF ( fs
, FR_OK
);
3926 /*-----------------------------------------------------------------------*/
3927 /* Synchronize the File */
3928 /*-----------------------------------------------------------------------*/
3931 FIL
* fp
/* Pointer to the file object */
3940 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3942 if ( fp
-> flag
& FA_MODIFIED
) { /* Is there any change to the file? */
3944 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back cached data if needed */
3945 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) LEAVE_FF ( fs
, FR_DISK_ERR
);
3946 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3949 /* Update the directory entry */
3950 tm
= GET_FATTIME (); /* Modified time */
3952 if ( fs
-> fs_type
== FS_EXFAT
) {
3953 res
= fill_first_frag (& fp
-> obj
); /* Fill first fragment on the FAT if needed */
3955 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
3962 res
= load_obj_xdir (& dj
, & fp
-> obj
); /* Load directory entry block */
3964 fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
3965 fs
-> dirbuf
[ XDIR_GenFlags
] = fp
-> obj
. stat
| 1 ; /* Update file allocation information */
3966 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, fp
-> obj
. sclust
);
3967 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, fp
-> obj
. objsize
);
3968 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, fp
-> obj
. objsize
);
3969 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Update modified time */
3970 fs
-> dirbuf
[ XDIR_ModTime10
] = 0 ;
3971 st_dword ( fs
-> dirbuf
+ XDIR_AccTime
, 0 );
3972 res
= store_xdir (& dj
); /* Restore it to the directory */
3975 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
3983 res
= move_window ( fs
, fp
-> dir_sect
);
3986 dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
3987 st_clust ( fp
-> obj
. fs
, dir
, fp
-> obj
. sclust
); /* Update file allocation information */
3988 st_dword ( dir
+ DIR_FileSize
, ( DWORD
) fp
-> obj
. objsize
); /* Update file size */
3989 st_dword ( dir
+ DIR_ModTime
, tm
); /* Update modified time */
3990 st_word ( dir
+ DIR_LstAccDate
, 0 );
3992 res
= sync_fs ( fs
); /* Restore it to the directory */
3993 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
4002 #endif /* !FF_FS_READONLY */
4007 /*-----------------------------------------------------------------------*/
4009 /*-----------------------------------------------------------------------*/
4012 FIL
* fp
/* Pointer to the file object to be closed */
4019 res
= f_sync ( fp
); /* Flush cached data */
4023 res
= validate (& fp
-> obj
, & fs
); /* Lock volume */
4026 res
= dec_lock ( fp
-> obj
. lockid
); /* Decrement file open counter */
4027 if ( res
== FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4029 fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4032 unlock_fs ( fs
, FR_OK
); /* Unlock volume */
4042 #if FF_FS_RPATH >= 1
4043 /*-----------------------------------------------------------------------*/
4044 /* Change Current Directory or Current Drive, Get Current Directory */
4045 /*-----------------------------------------------------------------------*/
4048 const TCHAR
* path
/* Drive number to set */
4054 /* Get logical drive number */
4055 vol
= get_ldnumber (& path
);
4056 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
4057 CurrVol
= ( BYTE
) vol
; /* Set it as current volume */
4065 const TCHAR
* path
/* Pointer to the directory path */
4068 #if FF_STR_VOLUME_ID == 2
4077 /* Get logical drive */
4078 res
= find_volume (& path
, & fs
, 0 );
4082 res
= follow_path (& dj
, path
); /* Follow the path */
4083 if ( res
== FR_OK
) { /* Follow completed */
4084 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it the start directory itself? */
4085 fs
-> cdir
= dj
. obj
. sclust
;
4087 if ( fs
-> fs_type
== FS_EXFAT
) {
4088 fs
-> cdc_scl
= dj
. obj
. c_scl
;
4089 fs
-> cdc_size
= dj
. obj
. c_size
;
4090 fs
-> cdc_ofs
= dj
. obj
. c_ofs
;
4094 if ( dj
. obj
. attr
& AM_DIR
) { /* It is a sub-directory */
4096 if ( fs
-> fs_type
== FS_EXFAT
) {
4097 fs
-> cdir
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Sub-directory cluster */
4098 fs
-> cdc_scl
= dj
. obj
. sclust
; /* Save containing directory information */
4099 fs
-> cdc_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
4100 fs
-> cdc_ofs
= dj
. blk_ofs
;
4104 fs
-> cdir
= ld_clust ( fs
, dj
. dir
); /* Sub-directory cluster */
4107 res
= FR_NO_PATH
; /* Reached but a file */
4112 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4113 #if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */
4115 for ( i
= FF_VOLUMES
- 1 ; i
&& fs
!= FatFs
[ i
]; i
--) ; /* Set current drive */
4125 #if FF_FS_RPATH >= 2
4127 TCHAR
* buff
, /* Pointer to the directory path */
4128 UINT len
/* Size of buff in unit of TCHAR */
4140 #if FF_STR_VOLUME_ID
4147 /* Get logical drive */
4148 res
= find_volume (( const TCHAR
**)& buff
, & fs
, 0 ); /* Get current volume */
4153 /* Follow parent directories and create the path */
4154 i
= len
; /* Bottom of buffer (directory stack base) */
4155 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) { /* (Cannot do getcwd on exFAT and returns root path) */
4156 dj
. obj
. sclust
= fs
-> cdir
; /* Start to follow upper directory from current directory */
4157 while (( ccl
= dj
. obj
. sclust
) != 0 ) { /* Repeat while current directory is a sub-directory */
4158 res
= dir_sdi (& dj
, 1 * SZDIRE
); /* Get parent directory */
4159 if ( res
!= FR_OK
) break ;
4160 res
= move_window ( fs
, dj
. sect
);
4161 if ( res
!= FR_OK
) break ;
4162 dj
. obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Goto parent directory */
4163 res
= dir_sdi (& dj
, 0 );
4164 if ( res
!= FR_OK
) break ;
4165 do { /* Find the entry links to the child directory */
4166 res
= dir_read_file (& dj
);
4167 if ( res
!= FR_OK
) break ;
4168 if ( ccl
== ld_clust ( fs
, dj
. dir
)) break ; /* Found the entry */
4169 res
= dir_next (& dj
, 0 );
4170 } while ( res
== FR_OK
);
4171 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be 'not found'. */
4172 if ( res
!= FR_OK
) break ;
4173 get_fileinfo (& dj
, & fno
); /* Get the directory name and push it to the buffer */
4174 for ( n
= 0 ; fno
. fname
[ n
]; n
++) ; /* Name length */
4175 if ( i
< n
+ 1 ) { /* Insufficient space to store the path name? */
4176 res
= FR_NOT_ENOUGH_CORE
; break ;
4178 while ( n
) buff
[-- i
] = fno
. fname
[-- n
]; /* Stack the name */
4183 if ( i
== len
) buff
[-- i
] = '/' ; /* Is it the root-directory? */
4184 #if FF_VOLUMES >= 2 /* Put drive prefix */
4186 #if FF_STR_VOLUME_ID >= 1 /* String volume ID */
4187 for ( n
= 0 , vp
= ( const char *) VolumeStr
[ CurrVol
]; vp
[ n
]; n
++) ;
4189 if ( FF_STR_VOLUME_ID
== 2 ) * tp
++ = ( TCHAR
) '/' ;
4190 for ( vl
= 0 ; vl
< n
; * tp
++ = ( TCHAR
) vp
[ vl
], vl
++) ;
4191 if ( FF_STR_VOLUME_ID
== 1 ) * tp
++ = ( TCHAR
) ':' ;
4194 #else /* Numeric volume ID */
4196 * tp
++ = ( TCHAR
) '0' + CurrVol
;
4201 if ( vl
== 0 ) res
= FR_NOT_ENOUGH_CORE
;
4203 /* Add current directory path */
4205 do * tp
++ = buff
[ i
++]; while ( i
< len
); /* Copy stacked path string */
4215 #endif /* FF_FS_RPATH >= 2 */
4216 #endif /* FF_FS_RPATH >= 1 */
4220 #if FF_FS_MINIMIZE <= 2
4221 /*-----------------------------------------------------------------------*/
4222 /* Seek File Read/Write Pointer */
4223 /*-----------------------------------------------------------------------*/
4226 FIL
* fp
, /* Pointer to the file object */
4227 FSIZE_t ofs
/* File pointer from top of file */
4232 DWORD clst
, bcs
, nsect
;
4235 DWORD cl
, pcl
, ncl
, tcl
, dsc
, tlen
, ulen
, * tbl
;
4238 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4239 if ( res
== FR_OK
) res
= ( FRESULT
) fp
-> err
;
4240 #if FF_FS_EXFAT && !FF_FS_READONLY
4241 if ( res
== FR_OK
&& fs
-> fs_type
== FS_EXFAT
) {
4242 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
4245 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
4248 if ( fp
-> cltbl
) { /* Fast seek */
4249 if ( ofs
== CREATE_LINKMAP
) { /* Create CLMT */
4251 tlen
= * tbl
++; ulen
= 2 ; /* Given table size and required table size */
4252 cl
= fp
-> obj
. sclust
; /* Origin of the chain */
4255 /* Get a fragment */
4256 tcl
= cl
; ncl
= 0 ; ulen
+= 2 ; /* Top, length and used items */
4259 cl
= get_fat (& fp
-> obj
, cl
);
4260 if ( cl
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
4261 if ( cl
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4262 } while ( cl
== pcl
+ 1 );
4263 if ( ulen
<= tlen
) { /* Store the length and top of the fragment */
4264 * tbl
++ = ncl
; * tbl
++ = tcl
;
4266 } while ( cl
< fs
-> n_fatent
); /* Repeat until end of chain */
4268 * fp
-> cltbl
= ulen
; /* Number of items used */
4270 * tbl
= 0 ; /* Terminate table */
4272 res
= FR_NOT_ENOUGH_CORE
; /* Given table size is smaller than required */
4274 } else { /* Fast seek */
4275 if ( ofs
> fp
-> obj
. objsize
) ofs
= fp
-> obj
. objsize
; /* Clip offset at the file size */
4276 fp
-> fptr
= ofs
; /* Set file pointer */
4278 fp
-> clust
= clmt_clust ( fp
, ofs
- 1 );
4279 dsc
= clst2sect ( fs
, fp
-> clust
);
4280 if ( dsc
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4281 dsc
+= ( DWORD
)(( ofs
- 1 ) / SS ( fs
)) & ( fs
-> csize
- 1 );
4282 if ( fp
-> fptr
% SS ( fs
) && dsc
!= fp
-> sect
) { /* Refill sector cache if needed */
4285 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4286 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4287 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4290 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, dsc
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Load current sector */
4302 if ( fs
-> fs_type
!= FS_EXFAT
&& ofs
>= 0x100000000 ) ofs
= 0xFFFFFFFF ; /* Clip at 4 GiB - 1 if at FATxx */
4304 if ( ofs
> fp
-> obj
. objsize
&& ( FF_FS_READONLY
|| !( fp
-> flag
& FA_WRITE
))) { /* In read-only mode, clip offset with the file size */
4305 ofs
= fp
-> obj
. objsize
;
4308 fp
-> fptr
= nsect
= 0 ;
4310 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size (byte) */
4312 ( ofs
- 1 ) / bcs
>= ( ifptr
- 1 ) / bcs
) { /* When seek to same or following cluster, */
4313 fp
-> fptr
= ( ifptr
- 1 ) & ~( FSIZE_t
)( bcs
- 1 ); /* start from the current cluster */
4316 } else { /* When seek to back cluster, */
4317 clst
= fp
-> obj
. sclust
; /* start from the first cluster */
4319 if ( clst
== 0 ) { /* If no cluster chain, create a new chain */
4320 clst
= create_chain (& fp
-> obj
, 0 );
4321 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
4322 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4323 fp
-> obj
. sclust
= clst
;
4329 while ( ofs
> bcs
) { /* Cluster following loop */
4330 ofs
-= bcs
; fp
-> fptr
+= bcs
;
4332 if ( fp
-> flag
& FA_WRITE
) { /* Check if in write mode or not */
4333 if ( FF_FS_EXFAT
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* No FAT chain object needs correct objsize to generate FAT value */
4334 fp
-> obj
. objsize
= fp
-> fptr
;
4335 fp
-> flag
|= FA_MODIFIED
;
4337 clst
= create_chain (& fp
-> obj
, clst
); /* Follow chain with forceed stretch */
4338 if ( clst
== 0 ) { /* Clip file size in case of disk full */
4344 clst
= get_fat (& fp
-> obj
, clst
); /* Follow cluster chain if not in write mode */
4346 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4347 if ( clst
<= 1 || clst
>= fs
-> n_fatent
) ABORT ( fs
, FR_INT_ERR
);
4352 nsect
= clst2sect ( fs
, clst
); /* Current sector */
4353 if ( nsect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4354 nsect
+= ( DWORD
)( ofs
/ SS ( fs
));
4358 if (! FF_FS_READONLY
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* Set file change flag if the file size is extended */
4359 fp
-> obj
. objsize
= fp
-> fptr
;
4360 fp
-> flag
|= FA_MODIFIED
;
4362 if ( fp
-> fptr
% SS ( fs
) && nsect
!= fp
-> sect
) { /* Fill sector cache if needed */
4365 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4366 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4367 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4370 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, nsect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
4381 #if FF_FS_MINIMIZE <= 1
4382 /*-----------------------------------------------------------------------*/
4383 /* Create a Directory Object */
4384 /*-----------------------------------------------------------------------*/
4387 DIR * dp
, /* Pointer to directory object to create */
4388 const TCHAR
* path
/* Pointer to the directory path */
4396 if (! dp
) return FR_INVALID_OBJECT
;
4398 /* Get logical drive */
4399 res
= find_volume (& path
, & fs
, 0 );
4403 res
= follow_path ( dp
, path
); /* Follow the path to the directory */
4404 if ( res
== FR_OK
) { /* Follow completed */
4405 if (!( dp
-> fn
[ NSFLAG
] & NS_NONAME
)) { /* It is not the origin directory itself */
4406 if ( dp
-> obj
. attr
& AM_DIR
) { /* This object is a sub-directory */
4408 if ( fs
-> fs_type
== FS_EXFAT
) {
4409 dp
-> obj
. c_scl
= dp
-> obj
. sclust
; /* Get containing directory inforamation */
4410 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
4411 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
4412 init_alloc_info ( fs
, & dp
-> obj
); /* Get object allocation info */
4416 dp
-> obj
. sclust
= ld_clust ( fs
, dp
-> dir
); /* Get object allocation info */
4418 } else { /* This object is a file */
4423 dp
-> obj
. id
= fs
-> id
;
4424 res
= dir_sdi ( dp
, 0 ); /* Rewind directory */
4427 if ( dp
-> obj
. sclust
!= 0 ) {
4428 dp
-> obj
. lockid
= inc_lock ( dp
, 0 ); /* Lock the sub directory */
4429 if (! dp
-> obj
. lockid
) res
= FR_TOO_MANY_OPEN_FILES
;
4431 dp
-> obj
. lockid
= 0 ; /* Root directory need not to be locked */
4438 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4440 if ( res
!= FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate the directory object if function faild */
4448 /*-----------------------------------------------------------------------*/
4449 /* Close Directory */
4450 /*-----------------------------------------------------------------------*/
4452 FRESULT
f_closedir (
4453 DIR * dp
/* Pointer to the directory object to be closed */
4460 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the file object */
4463 if ( dp
-> obj
. lockid
) res
= dec_lock ( dp
-> obj
. lockid
); /* Decrement sub-directory open counter */
4464 if ( res
== FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4466 dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4469 unlock_fs ( fs
, FR_OK
); /* Unlock volume */
4478 /*-----------------------------------------------------------------------*/
4479 /* Read Directory Entries in Sequence */
4480 /*-----------------------------------------------------------------------*/
4483 DIR * dp
, /* Pointer to the open directory object */
4484 FILINFO
* fno
/* Pointer to file information to return */
4492 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the directory object */
4495 res
= dir_sdi ( dp
, 0 ); /* Rewind the directory object */
4498 res
= dir_read_file ( dp
); /* Read an item */
4499 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory */
4500 if ( res
== FR_OK
) { /* A valid entry is found */
4501 get_fileinfo ( dp
, fno
); /* Get the object information */
4502 res
= dir_next ( dp
, 0 ); /* Increment index for next */
4503 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory now */
4514 /*-----------------------------------------------------------------------*/
4515 /* Find Next File */
4516 /*-----------------------------------------------------------------------*/
4518 FRESULT
f_findnext (
4519 DIR * dp
, /* Pointer to the open directory object */
4520 FILINFO
* fno
/* Pointer to the file information structure */
4527 res
= f_readdir ( dp
, fno
); /* Get a directory item */
4528 if ( res
!= FR_OK
|| ! fno
|| ! fno
-> fname
[ 0 ]) break ; /* Terminate if any error or end of directory */
4529 if ( pattern_matching ( dp
-> pat
, fno
-> fname
, 0 , 0 )) break ; /* Test for the file name */
4530 #if FF_USE_LFN && FF_USE_FIND == 2
4531 if ( pattern_matching ( dp
-> pat
, fno
-> altname
, 0 , 0 )) break ; /* Test for alternative name if exist */
4539 /*-----------------------------------------------------------------------*/
4540 /* Find First File */
4541 /*-----------------------------------------------------------------------*/
4543 FRESULT
f_findfirst (
4544 DIR * dp
, /* Pointer to the blank directory object */
4545 FILINFO
* fno
, /* Pointer to the file information structure */
4546 const TCHAR
* path
, /* Pointer to the directory to open */
4547 const TCHAR
* pattern
/* Pointer to the matching pattern */
4553 dp
-> pat
= pattern
; /* Save pointer to pattern string */
4554 res
= f_opendir ( dp
, path
); /* Open the target directory */
4556 res
= f_findnext ( dp
, fno
); /* Find the first item */
4561 #endif /* FF_USE_FIND */
4565 #if FF_FS_MINIMIZE == 0
4566 /*-----------------------------------------------------------------------*/
4567 /* Get File Status */
4568 /*-----------------------------------------------------------------------*/
4571 const TCHAR
* path
, /* Pointer to the file path */
4572 FILINFO
* fno
/* Pointer to file information to return */
4580 /* Get logical drive */
4581 res
= find_volume (& path
, & dj
. obj
. fs
, 0 );
4583 INIT_NAMBUF ( dj
. obj
. fs
);
4584 res
= follow_path (& dj
, path
); /* Follow the file path */
4585 if ( res
== FR_OK
) { /* Follow completed */
4586 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* It is origin directory */
4587 res
= FR_INVALID_NAME
;
4588 } else { /* Found an object */
4589 if ( fno
) get_fileinfo (& dj
, fno
);
4595 LEAVE_FF ( dj
. obj
. fs
, res
);
4601 /*-----------------------------------------------------------------------*/
4602 /* Get Number of Free Clusters */
4603 /*-----------------------------------------------------------------------*/
4606 const TCHAR
* path
, /* Logical drive number */
4607 DWORD
* nclst
, /* Pointer to a variable to return number of free clusters */
4608 FATFS
** fatfs
/* Pointer to return pointer to corresponding filesystem object */
4613 DWORD nfree
, clst
, sect
, stat
;
4618 /* Get logical drive */
4619 res
= find_volume (& path
, & fs
, 0 );
4621 * fatfs
= fs
; /* Return ptr to the fs object */
4622 /* If free_clst is valid, return it without full FAT scan */
4623 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) {
4624 * nclst
= fs
-> free_clst
;
4626 /* Scan FAT to obtain number of free clusters */
4628 if ( fs
-> fs_type
== FS_FAT12
) { /* FAT12: Scan bit field FAT entries */
4629 clst
= 2 ; obj
. fs
= fs
;
4631 stat
= get_fat (& obj
, clst
);
4632 if ( stat
== 0xFFFFFFFF ) { res
= FR_DISK_ERR
; break ; }
4633 if ( stat
== 1 ) { res
= FR_INT_ERR
; break ; }
4634 if ( stat
== 0 ) nfree
++;
4635 } while (++ clst
< fs
-> n_fatent
);
4638 if ( fs
-> fs_type
== FS_EXFAT
) { /* exFAT: Scan allocation bitmap */
4642 clst
= fs
-> n_fatent
- 2 ; /* Number of clusters */
4643 sect
= fs
-> database
; /* Assuming bitmap starts at cluster 2 */
4644 i
= 0 ; /* Offset in the sector */
4645 do { /* Counts numbuer of bits with zero in the bitmap */
4647 res
= move_window ( fs
, sect
++);
4648 if ( res
!= FR_OK
) break ;
4650 for ( b
= 8 , bm
= fs
-> win
[ i
]; b
&& clst
; b
--, clst
--) {
4651 if (!( bm
& 1 )) nfree
++;
4654 i
= ( i
+ 1 ) % SS ( fs
);
4658 { /* FAT16/32: Scan WORD/DWORD FAT entries */
4659 clst
= fs
-> n_fatent
; /* Number of entries */
4660 sect
= fs
-> fatbase
; /* Top of the FAT */
4661 i
= 0 ; /* Offset in the sector */
4662 do { /* Counts numbuer of entries with zero in the FAT */
4664 res
= move_window ( fs
, sect
++);
4665 if ( res
!= FR_OK
) break ;
4667 if ( fs
-> fs_type
== FS_FAT16
) {
4668 if ( ld_word ( fs
-> win
+ i
) == 0 ) nfree
++;
4671 if (( ld_dword ( fs
-> win
+ i
) & 0x0FFFFFFF ) == 0 ) nfree
++;
4678 * nclst
= nfree
; /* Return the free clusters */
4679 fs
-> free_clst
= nfree
; /* Now free_clst is valid */
4680 fs
-> fsi_flag
|= 1 ; /* FAT32: FSInfo is to be updated */
4690 /*-----------------------------------------------------------------------*/
4692 /*-----------------------------------------------------------------------*/
4694 FRESULT
f_truncate (
4695 FIL
* fp
/* Pointer to the file object */
4703 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4704 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
4705 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
4707 if ( fp
-> fptr
< fp
-> obj
. objsize
) { /* Process when fptr is not on the eof */
4708 if ( fp
-> fptr
== 0 ) { /* When set file size to zero, remove entire cluster chain */
4709 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
4711 } else { /* When truncate a part of the file, remove remaining clusters */
4712 ncl
= get_fat (& fp
-> obj
, fp
-> clust
);
4714 if ( ncl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
4715 if ( ncl
== 1 ) res
= FR_INT_ERR
;
4716 if ( res
== FR_OK
&& ncl
< fs
-> n_fatent
) {
4717 res
= remove_chain (& fp
-> obj
, ncl
, fp
-> clust
);
4720 fp
-> obj
. objsize
= fp
-> fptr
; /* Set file size to current read/write point */
4721 fp
-> flag
|= FA_MODIFIED
;
4723 if ( res
== FR_OK
&& ( fp
-> flag
& FA_DIRTY
)) {
4724 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) {
4727 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4731 if ( res
!= FR_OK
) ABORT ( fs
, res
);
4740 /*-----------------------------------------------------------------------*/
4741 /* Delete a File/Directory */
4742 /*-----------------------------------------------------------------------*/
4745 const TCHAR
* path
/* Pointer to the file or directory path */
4758 /* Get logical drive */
4759 res
= find_volume (& path
, & fs
, FA_WRITE
);
4763 res
= follow_path (& dj
, path
); /* Follow the file path */
4764 if ( FF_FS_RPATH
&& res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) {
4765 res
= FR_INVALID_NAME
; /* Cannot remove dot entry */
4768 if ( res
== FR_OK
) res
= chk_lock (& dj
, 2 ); /* Check if it is an open object */
4770 if ( res
== FR_OK
) { /* The object is accessible */
4771 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) {
4772 res
= FR_INVALID_NAME
; /* Cannot remove the origin directory */
4774 if ( dj
. obj
. attr
& AM_RDO
) {
4775 res
= FR_DENIED
; /* Cannot remove R/O object */
4781 if ( fs
-> fs_type
== FS_EXFAT
) {
4782 init_alloc_info ( fs
, & obj
);
4787 dclst
= ld_clust ( fs
, dj
. dir
);
4789 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a sub-directory? */
4790 #if FF_FS_RPATH != 0
4791 if ( dclst
== fs
-> cdir
) { /* Is it the current directory? */
4796 sdj
. obj
. fs
= fs
; /* Open the sub-directory */
4797 sdj
. obj
. sclust
= dclst
;
4799 if ( fs
-> fs_type
== FS_EXFAT
) {
4800 sdj
. obj
. objsize
= obj
. objsize
;
4801 sdj
. obj
. stat
= obj
. stat
;
4804 res
= dir_sdi (& sdj
, 0 );
4806 res
= dir_read_file (& sdj
); /* Test if the directory is empty */
4807 if ( res
== FR_OK
) res
= FR_DENIED
; /* Not empty? */
4808 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Empty? */
4814 res
= dir_remove (& dj
); /* Remove the directory entry */
4815 if ( res
== FR_OK
&& dclst
!= 0 ) { /* Remove the cluster chain if exist */
4817 res
= remove_chain (& obj
, dclst
, 0 );
4819 res
= remove_chain (& dj
. obj
, dclst
, 0 );
4822 if ( res
== FR_OK
) res
= sync_fs ( fs
);
4834 /*-----------------------------------------------------------------------*/
4835 /* Create a Directory */
4836 /*-----------------------------------------------------------------------*/
4839 const TCHAR
* path
/* Pointer to the directory path */
4850 /* Get logical drive */
4851 res
= find_volume (& path
, & fs
, FA_WRITE
);
4855 res
= follow_path (& dj
, path
); /* Follow the file path */
4856 if ( res
== FR_OK
) res
= FR_EXIST
; /* Any object with same name is already existing */
4857 if ( FF_FS_RPATH
&& res
== FR_NO_FILE
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) {
4858 res
= FR_INVALID_NAME
;
4860 if ( res
== FR_NO_FILE
) { /* Can create a new directory */
4861 dcl
= create_chain (& dj
. obj
, 0 ); /* Allocate a cluster for the new directory table */
4862 dj
. obj
. objsize
= ( DWORD
) fs
-> csize
* SS ( fs
);
4864 if ( dcl
== 0 ) res
= FR_DENIED
; /* No space to allocate a new cluster */
4865 if ( dcl
== 1 ) res
= FR_INT_ERR
;
4866 if ( dcl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
4867 if ( res
== FR_OK
) res
= sync_window ( fs
); /* Flush FAT */
4869 if ( res
== FR_OK
) { /* Initialize the new directory table */
4870 res
= dir_clear ( fs
, dcl
); /* Clean up the new table */
4871 if ( res
== FR_OK
&& (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
)) { /* Create dot entries (FAT only) */
4873 mem_set ( dir
+ DIR_Name
, ' ' , 11 ); /* Create "." entry */
4874 dir
[ DIR_Name
] = '.' ;
4875 dir
[ DIR_Attr
] = AM_DIR
;
4876 st_dword ( dir
+ DIR_ModTime
, tm
);
4877 st_clust ( fs
, dir
, dcl
);
4878 mem_cpy ( dir
+ SZDIRE
, dir
, SZDIRE
); /* Create ".." entry */
4879 dir
[ SZDIRE
+ 1 ] = '.' ; pcl
= dj
. obj
. sclust
;
4880 st_clust ( fs
, dir
+ SZDIRE
, pcl
);
4885 res
= dir_register (& dj
); /* Register the object to the directoy */
4889 if ( fs
-> fs_type
== FS_EXFAT
) { /* Initialize directory entry block */
4890 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Created time */
4891 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, dcl
); /* Table start cluster */
4892 st_dword ( fs
-> dirbuf
+ XDIR_FileSize
, ( DWORD
) dj
. obj
. objsize
); /* File size needs to be valid */
4893 st_dword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, ( DWORD
) dj
. obj
. objsize
);
4894 fs
-> dirbuf
[ XDIR_GenFlags
] = 3 ; /* Initialize the object flag */
4895 fs
-> dirbuf
[ XDIR_Attr
] = AM_DIR
; /* Attribute */
4896 res
= store_xdir (& dj
);
4901 st_dword ( dir
+ DIR_ModTime
, tm
); /* Created time */
4902 st_clust ( fs
, dir
, dcl
); /* Table start cluster */
4903 dir
[ DIR_Attr
] = AM_DIR
; /* Attribute */
4910 remove_chain (& dj
. obj
, dcl
, 0 ); /* Could not register, remove cluster chain */
4922 /*-----------------------------------------------------------------------*/
4923 /* Rename a File/Directory */
4924 /*-----------------------------------------------------------------------*/
4927 const TCHAR
* path_old
, /* Pointer to the object name to be renamed */
4928 const TCHAR
* path_new
/* Pointer to the new name */
4934 BYTE buf
[ FF_FS_EXFAT
? SZDIRE
* 2 : SZDIRE
], * dir
;
4939 get_ldnumber (& path_new
); /* Snip the drive number of new name off */
4940 res
= find_volume (& path_old
, & fs
, FA_WRITE
); /* Get logical drive of the old object */
4944 res
= follow_path (& djo
, path_old
); /* Check old object */
4945 if ( res
== FR_OK
&& ( djo
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check validity of name */
4948 res
= chk_lock (& djo
, 2 );
4951 if ( res
== FR_OK
) { /* Object to be renamed is found */
4953 if ( fs
-> fs_type
== FS_EXFAT
) { /* At exFAT volume */
4957 mem_cpy ( buf
, fs
-> dirbuf
, SZDIRE
* 2 ); /* Save 85+C0 entry of old object */
4958 mem_cpy (& djn
, & djo
, sizeof djo
);
4959 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
4960 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
4961 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
4963 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
4964 res
= dir_register (& djn
); /* Register the new entry */
4966 nf
= fs
-> dirbuf
[ XDIR_NumSec
]; nn
= fs
-> dirbuf
[ XDIR_NumName
];
4967 nh
= ld_word ( fs
-> dirbuf
+ XDIR_NameHash
);
4968 mem_cpy ( fs
-> dirbuf
, buf
, SZDIRE
* 2 ); /* Restore 85+C0 entry */
4969 fs
-> dirbuf
[ XDIR_NumSec
] = nf
; fs
-> dirbuf
[ XDIR_NumName
] = nn
;
4970 st_word ( fs
-> dirbuf
+ XDIR_NameHash
, nh
);
4971 if (!( fs
-> dirbuf
[ XDIR_Attr
] & AM_DIR
)) fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
4972 /* Start of critical section where an interruption can cause a cross-link */
4973 res
= store_xdir (& djn
);
4978 { /* At FAT/FAT32 volume */
4979 mem_cpy ( buf
, djo
. dir
, SZDIRE
); /* Save directory entry of the object */
4980 mem_cpy (& djn
, & djo
, sizeof ( DIR )); /* Duplicate the directory object */
4981 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
4982 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
4983 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
4985 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
4986 res
= dir_register (& djn
); /* Register the new entry */
4988 dir
= djn
. dir
; /* Copy directory entry of the object except name */
4989 mem_cpy ( dir
+ 13 , buf
+ 13 , SZDIRE
- 13 );
4990 dir
[ DIR_Attr
] = buf
[ DIR_Attr
];
4991 if (!( dir
[ DIR_Attr
] & AM_DIR
)) dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
4993 if (( dir
[ DIR_Attr
] & AM_DIR
) && djo
. obj
. sclust
!= djn
. obj
. sclust
) { /* Update .. entry in the sub-directory if needed */
4994 dw
= clst2sect ( fs
, ld_clust ( fs
, dir
));
4998 /* Start of critical section where an interruption can cause a cross-link */
4999 res
= move_window ( fs
, dw
);
5000 dir
= fs
-> win
+ SZDIRE
* 1 ; /* Ptr to .. entry */
5001 if ( res
== FR_OK
&& dir
[ 1 ] == '.' ) {
5002 st_clust ( fs
, dir
, djn
. obj
. sclust
);
5011 res
= dir_remove (& djo
); /* Remove old entry */
5016 /* End of the critical section */
5024 #endif /* !FF_FS_READONLY */
5025 #endif /* FF_FS_MINIMIZE == 0 */
5026 #endif /* FF_FS_MINIMIZE <= 1 */
5027 #endif /* FF_FS_MINIMIZE <= 2 */
5031 #if FF_USE_CHMOD && !FF_FS_READONLY
5032 /*-----------------------------------------------------------------------*/
5033 /* Change Attribute */
5034 /*-----------------------------------------------------------------------*/
5037 const TCHAR
* path
, /* Pointer to the file path */
5038 BYTE attr
, /* Attribute bits */
5039 BYTE mask
/* Attribute mask to change */
5048 res
= find_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5052 res
= follow_path (& dj
, path
); /* Follow the file path */
5053 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5055 mask
&= AM_RDO
| AM_HID
| AM_SYS
| AM_ARC
; /* Valid attribute mask */
5057 if ( fs
-> fs_type
== FS_EXFAT
) {
5058 fs
-> dirbuf
[ XDIR_Attr
] = ( attr
& mask
) | ( fs
-> dirbuf
[ XDIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5059 res
= store_xdir (& dj
);
5063 dj
. dir
[ DIR_Attr
] = ( attr
& mask
) | ( dj
. dir
[ DIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5079 /*-----------------------------------------------------------------------*/
5080 /* Change Timestamp */
5081 /*-----------------------------------------------------------------------*/
5084 const TCHAR
* path
, /* Pointer to the file/directory name */
5085 const FILINFO
* fno
/* Pointer to the timestamp to be set */
5094 res
= find_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5098 res
= follow_path (& dj
, path
); /* Follow the file path */
5099 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5102 if ( fs
-> fs_type
== FS_EXFAT
) {
5103 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5104 res
= store_xdir (& dj
);
5108 st_dword ( dj
. dir
+ DIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5121 #endif /* FF_USE_CHMOD && !FF_FS_READONLY */
5126 /*-----------------------------------------------------------------------*/
5127 /* Get Volume Label */
5128 /*-----------------------------------------------------------------------*/
5130 FRESULT
f_getlabel (
5131 const TCHAR
* path
, /* Logical drive number */
5132 TCHAR
* label
, /* Buffer to store the volume label */
5133 DWORD
* vsn
/* Variable to store the volume serial number */
5142 /* Get logical drive */
5143 res
= find_volume (& path
, & fs
, 0 );
5145 /* Get volume label */
5146 if ( res
== FR_OK
&& label
) {
5147 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5148 res
= dir_sdi (& dj
, 0 );
5150 res
= dir_read_label (& dj
); /* Find a volume label entry */
5153 if ( fs
-> fs_type
== FS_EXFAT
) {
5156 for ( si
= di
= hs
= 0 ; si
< dj
. dir
[ XDIR_NumLabel
]; si
++) { /* Extract volume label from 83 entry */
5157 wc
= ld_word ( dj
. dir
+ XDIR_Label
+ si
* 2 );
5158 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is the code a surrogate? */
5161 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & label
[ di
], 4 );
5162 if ( wc
== 0 ) { di
= 0 ; break ; }
5166 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
5171 si
= di
= 0 ; /* Extract volume label from AM_VOL entry */
5174 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
5175 if ( dbc_1st (( BYTE
) wc
) && si
< 11 ) wc
= wc
<< 8 | dj
. dir
[ si
++]; /* Is it a DBC? */
5176 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* Convert it into Unicode */
5177 if ( wc
!= 0 ) wc
= put_utf ( wc
, & label
[ di
], 4 ); /* Put it in Unicode */
5178 if ( wc
== 0 ) { di
= 0 ; break ; }
5180 #else /* ANSI/OEM output */
5181 label
[ di
++] = ( TCHAR
) wc
;
5184 do { /* Truncate trailing spaces */
5187 } while ( label
[-- di
] == ' ' );
5191 if ( res
== FR_NO_FILE
) { /* No label entry and return nul string */
5197 /* Get volume serial number */
5198 if ( res
== FR_OK
&& vsn
) {
5199 res
= move_window ( fs
, fs
-> volbase
);
5201 switch ( fs
-> fs_type
) {
5203 di
= BPB_VolIDEx
; break ;
5206 di
= BS_VolID32
; break ;
5211 * vsn
= ld_dword ( fs
-> win
+ di
);
5221 /*-----------------------------------------------------------------------*/
5222 /* Set Volume Label */
5223 /*-----------------------------------------------------------------------*/
5225 FRESULT
f_setlabel (
5226 const TCHAR
* label
/* Volume label to set with heading logical drive number */
5235 static const char badchr
[] = "+.,;=[]/ \\\" *:<> \? | \x7F " ; /* [0..] for FAT, [7..] for exFAT */
5240 /* Get logical drive */
5241 res
= find_volume (& label
, & fs
, FA_WRITE
);
5242 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
5245 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
5246 mem_set ( dirvn
, 0 , 22 );
5248 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5249 dc
= tchar2uni (& label
); /* Get a Unicode character */
5250 if ( dc
>= 0x10000 ) {
5251 if ( dc
== 0xFFFFFFFF || di
>= 10 ) { /* Wrong surrogate or buffer overflow */
5254 st_word ( dirvn
+ di
* 2 , ( WCHAR
)( dc
>> 16 )); di
++;
5257 if ( dc
== 0 || chk_chr ( badchr
+ 7 , ( int ) dc
) || di
>= 11 ) { /* Check validity of the volume label */
5258 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5260 st_word ( dirvn
+ di
* 2 , ( WCHAR
) dc
); di
++;
5264 { /* On the FAT/FAT32 volume */
5265 mem_set ( dirvn
, ' ' , 11 );
5267 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5269 dc
= tchar2uni (& label
);
5270 wc
= ( dc
< 0x10000 ) ? ff_uni2oem ( ff_wtoupper ( dc
), CODEPAGE
) : 0 ;
5271 #else /* ANSI/OEM input */
5272 wc
= ( BYTE
)* label
++;
5273 if ( dbc_1st (( BYTE
) wc
)) wc
= dbc_2nd (( BYTE
)* label
) ? wc
<< 8 | ( BYTE
)* label
++ : 0 ;
5274 if ( IsLower ( wc
)) wc
-= 0x20 ; /* To upper ASCII characters */
5275 #if FF_CODE_PAGE == 0
5276 if ( ExCvt
&& wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5277 #elif FF_CODE_PAGE < 900
5278 if ( wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5281 if ( wc
== 0 || chk_chr ( badchr
+ 0 , ( int ) wc
) || di
>= ( UINT
)(( wc
>= 0x100 ) ? 10 : 11 )) { /* Reject invalid characters for volume label */
5282 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5284 if ( wc
>= 0x100 ) dirvn
[ di
++] = ( BYTE
)( wc
>> 8 );
5285 dirvn
[ di
++] = ( BYTE
) wc
;
5287 if ( dirvn
[ 0 ] == DDEM
) LEAVE_FF ( fs
, FR_INVALID_NAME
); /* Reject illegal name (heading DDEM) */
5288 while ( di
&& dirvn
[ di
- 1 ] == ' ' ) di
--; /* Snip trailing spaces */
5291 /* Set volume label */
5292 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5293 res
= dir_sdi (& dj
, 0 );
5295 res
= dir_read_label (& dj
); /* Get volume label entry */
5297 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5298 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
; /* Change the volume label */
5299 mem_cpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5302 mem_cpy ( dj
. dir
, dirvn
, 11 ); /* Change the volume label */
5304 dj
. dir
[ DIR_Name
] = DDEM
; /* Remove the volume label */
5309 } else { /* No volume label entry or an error */
5310 if ( res
== FR_NO_FILE
) {
5312 if ( di
!= 0 ) { /* Create a volume label entry */
5313 res
= dir_alloc (& dj
, 1 ); /* Allocate an entry */
5315 mem_set ( dj
. dir
, 0 , SZDIRE
); /* Clean the entry */
5316 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5317 dj
. dir
[ XDIR_Type
] = 0x83 ; /* Create 83 entry */
5318 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
;
5319 mem_cpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5321 dj
. dir
[ DIR_Attr
] = AM_VOL
; /* Create volume label entry */
5322 mem_cpy ( dj
. dir
, dirvn
, 11 );
5335 #endif /* !FF_FS_READONLY */
5336 #endif /* FF_USE_LABEL */
5340 #if FF_USE_EXPAND && !FF_FS_READONLY
5341 /*-----------------------------------------------------------------------*/
5342 /* Allocate a Contiguous Blocks to the File */
5343 /*-----------------------------------------------------------------------*/
5346 FIL
* fp
, /* Pointer to the file object */
5347 FSIZE_t fsz
, /* File size to be expanded to */
5348 BYTE opt
/* Operation mode 0:Find and prepare or 1:Find and allocate */
5353 DWORD n
, clst
, stcl
, scl
, ncl
, tcl
, lclst
;
5356 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5357 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5358 if ( fsz
== 0 || fp
-> obj
. objsize
!= 0 || !( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
);
5360 if ( fs
-> fs_type
!= FS_EXFAT
&& fsz
>= 0x100000000 ) LEAVE_FF ( fs
, FR_DENIED
); /* Check if in size limit */
5362 n
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size */
5363 tcl
= ( DWORD
)( fsz
/ n
) + (( fsz
& ( n
- 1 )) ? 1 : 0 ); /* Number of clusters required */
5364 stcl
= fs
-> last_clst
; lclst
= 0 ;
5365 if ( stcl
< 2 || stcl
>= fs
-> n_fatent
) stcl
= 2 ;
5368 if ( fs
-> fs_type
== FS_EXFAT
) {
5369 scl
= find_bitmap ( fs
, stcl
, tcl
); /* Find a contiguous cluster block */
5370 if ( scl
== 0 ) res
= FR_DENIED
; /* No contiguous cluster block was found */
5371 if ( scl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
5372 if ( res
== FR_OK
) { /* A contiguous free area is found */
5373 if ( opt
) { /* Allocate it now */
5374 res
= change_bitmap ( fs
, scl
, tcl
, 1 ); /* Mark the cluster block 'in use' */
5375 lclst
= scl
+ tcl
- 1 ;
5376 } else { /* Set it as suggested point for next allocation */
5383 scl
= clst
= stcl
; ncl
= 0 ;
5384 for (;;) { /* Find a contiguous cluster block */
5385 n
= get_fat (& fp
-> obj
, clst
);
5386 if (++ clst
>= fs
-> n_fatent
) clst
= 2 ;
5387 if ( n
== 1 ) { res
= FR_INT_ERR
; break ; }
5388 if ( n
== 0xFFFFFFFF ) { res
= FR_DISK_ERR
; break ; }
5389 if ( n
== 0 ) { /* Is it a free cluster? */
5390 if (++ ncl
== tcl
) break ; /* Break if a contiguous cluster block is found */
5392 scl
= clst
; ncl
= 0 ; /* Not a free cluster */
5394 if ( clst
== stcl
) { res
= FR_DENIED
; break ; } /* No contiguous cluster? */
5396 if ( res
== FR_OK
) { /* A contiguous free area is found */
5397 if ( opt
) { /* Allocate it now */
5398 for ( clst
= scl
, n
= tcl
; n
; clst
++, n
--) { /* Create a cluster chain on the FAT */
5399 res
= put_fat ( fs
, clst
, ( n
== 1 ) ? 0xFFFFFFFF : clst
+ 1 );
5400 if ( res
!= FR_OK
) break ;
5403 } else { /* Set it as suggested point for next allocation */
5410 fs
-> last_clst
= lclst
; /* Set suggested start cluster to start next */
5411 if ( opt
) { /* Is it allocated now? */
5412 fp
-> obj
. sclust
= scl
; /* Update object allocation information */
5413 fp
-> obj
. objsize
= fsz
;
5414 if ( FF_FS_EXFAT
) fp
-> obj
. stat
= 2 ; /* Set status 'contiguous chain' */
5415 fp
-> flag
|= FA_MODIFIED
;
5416 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) { /* Update FSINFO */
5417 fs
-> free_clst
-= tcl
;
5426 #endif /* FF_USE_EXPAND && !FF_FS_READONLY */
5431 /*-----------------------------------------------------------------------*/
5432 /* Forward Data to the Stream Directly */
5433 /*-----------------------------------------------------------------------*/
5436 FIL
* fp
, /* Pointer to the file object */
5437 UINT (* func
)( const BYTE
*, UINT
), /* Pointer to the streaming function */
5438 UINT btf
, /* Number of bytes to forward */
5439 UINT
* bf
/* Pointer to number of bytes forwarded */
5450 * bf
= 0 ; /* Clear transfer byte counter */
5451 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5452 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5453 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
5455 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
5456 if ( btf
> remain
) btf
= ( UINT
) remain
; /* Truncate btf by remaining bytes */
5458 for ( ; btf
&& (* func
)( 0 , 0 ); /* Repeat until all data transferred or stream goes busy */
5459 fp
-> fptr
+= rcnt
, * bf
+= rcnt
, btf
-= rcnt
) {
5460 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
5461 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
5462 if ( csect
== 0 ) { /* On the cluster boundary? */
5463 clst
= ( fp
-> fptr
== 0 ) ? /* On the top of the file? */
5464 fp
-> obj
. sclust
: get_fat (& fp
-> obj
, fp
-> clust
);
5465 if ( clst
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
5466 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
5467 fp
-> clust
= clst
; /* Update current cluster */
5470 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current data sector */
5471 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5474 if ( move_window ( fs
, sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window to the file data */
5477 if ( fp
-> sect
!= sect
) { /* Fill sector cache with file data */
5479 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
5480 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5481 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
5484 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5489 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes left in the sector */
5490 if ( rcnt
> btf
) rcnt
= btf
; /* Clip it by btr if needed */
5491 rcnt
= (* func
)( dbuf
+ (( UINT
) fp
-> fptr
% SS ( fs
)), rcnt
); /* Forward the file data */
5492 if ( rcnt
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5495 LEAVE_FF ( fs
, FR_OK
);
5497 #endif /* FF_USE_FORWARD */
5501 #if FF_USE_MKFS && !FF_FS_READONLY
5502 /*-----------------------------------------------------------------------*/
5503 /* Create an FAT/exFAT volume */
5504 /*-----------------------------------------------------------------------*/
5507 const TCHAR
* path
, /* Logical drive number */
5508 BYTE opt
, /* Format option */
5509 DWORD au
, /* Size of allocation unit (cluster) [byte] */
5510 void * work
, /* Pointer to working buffer (null: use heap memory) */
5511 UINT len
/* Size of working buffer [byte] */
5514 const UINT n_fats
= 1 ; /* Number of FATs for FAT/FAT32 volume (1 or 2) */
5515 const UINT n_rootdir
= 512 ; /* Number of root directory entries for FAT volume */
5516 static const WORD cst
[] = { 1 , 4 , 16 , 64 , 256 , 512 , 0 }; /* Cluster size boundary for FAT volume (4Ks unit) */
5517 static const WORD cst32
[] = { 1 , 2 , 4 , 8 , 16 , 32 , 0 }; /* Cluster size boundary for FAT32 volume (128Ks unit) */
5518 BYTE fmt
, sys
, * buf
, * pte
, pdrv
, part
;
5519 WORD ss
; /* Sector size */
5520 DWORD szb_buf
, sz_buf
, sz_blk
, n_clst
, pau
, sect
, nsect
, n
;
5521 DWORD b_vol
, b_fat
, b_data
; /* Base LBA for volume, fat, data */
5522 DWORD sz_vol
, sz_rsv
, sz_fat
, sz_dir
; /* Size for volume, fat, dir, data */
5526 #if FF_USE_TRIM || FF_FS_EXFAT
5531 /* Check mounted drive and clear work area */
5532 vol
= get_ldnumber (& path
); /* Get target logical drive */
5533 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
5534 if ( FatFs
[ vol
]) FatFs
[ vol
]-> fs_type
= 0 ; /* Clear the volume if mounted */
5535 pdrv
= LD2PD ( vol
); /* Physical drive */
5536 part
= LD2PT ( vol
); /* Partition (0:create as new, 1-4:get from partition table) */
5538 /* Check physical drive status */
5539 stat
= disk_initialize ( pdrv
);
5540 if ( stat
& STA_NOINIT
) return FR_NOT_READY
;
5541 if ( stat
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
5542 if ( disk_ioctl ( pdrv
, GET_BLOCK_SIZE
, & sz_blk
) != RES_OK
|| ! sz_blk
|| sz_blk
> 32768 || ( sz_blk
& ( sz_blk
- 1 ))) sz_blk
= 1 ; /* Erase block to align data area */
5543 #if FF_MAX_SS != FF_MIN_SS /* Get sector size of the medium if variable sector size cfg. */
5544 if ( disk_ioctl ( pdrv
, GET_SECTOR_SIZE
, & ss
) != RES_OK
) return FR_DISK_ERR
;
5545 if ( ss
> FF_MAX_SS
|| ss
< FF_MIN_SS
|| ( ss
& ( ss
- 1 ))) return FR_DISK_ERR
;
5549 if (( au
!= 0 && au
< ss
) || au
> 0x1000000 || ( au
& ( au
- 1 ))) return FR_INVALID_PARAMETER
; /* Check if au is valid */
5550 au
/= ss
; /* Cluster size in unit of sector */
5552 /* Get working buffer */
5554 if (! work
) { /* Use heap memory for working buffer */
5555 for ( szb_buf
= MAX_MALLOC
, buf
= 0 ; szb_buf
>= ss
&& ( buf
= ff_memalloc ( szb_buf
)) == 0 ; szb_buf
/= 2 ) ;
5556 sz_buf
= szb_buf
/ ss
; /* Size of working buffer (sector) */
5560 buf
= ( BYTE
*) work
; /* Working buffer */
5561 sz_buf
= len
/ ss
; /* Size of working buffer (sector) */
5562 szb_buf
= sz_buf
* ss
; /* Size of working buffer (byte) */
5564 if (! buf
|| sz_buf
== 0 ) return FR_NOT_ENOUGH_CORE
;
5566 /* Determine where the volume to be located (b_vol, sz_vol) */
5567 if ( FF_MULTI_PARTITION
&& part
!= 0 ) {
5568 /* Get partition information from partition table in the MBR */
5569 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Load MBR */
5570 if ( ld_word ( buf
+ BS_55AA
) != 0xAA55 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if MBR is valid */
5571 pte
= buf
+ ( MBR_Table
+ ( part
- 1 ) * SZ_PTE
);
5572 if ( pte
[ PTE_System
] == 0 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* No partition? */
5573 b_vol
= ld_dword ( pte
+ PTE_StLba
); /* Get volume start sector */
5574 sz_vol
= ld_dword ( pte
+ PTE_SizLba
); /* Get volume size */
5576 /* Create a single-partition in this function */
5577 if ( disk_ioctl ( pdrv
, GET_SECTOR_COUNT
, & sz_vol
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5578 b_vol
= ( opt
& FM_SFD
) ? 0 : 63 ; /* Volume start sector */
5579 if ( sz_vol
< b_vol
) LEAVE_MKFS ( FR_MKFS_ABORTED
);
5580 sz_vol
-= b_vol
; /* Volume size */
5582 if ( sz_vol
< 128 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if volume size is >=128s */
5584 /* Pre-determine the FAT type */
5586 if ( FF_FS_EXFAT
&& ( opt
& FM_EXFAT
)) { /* exFAT possible? */
5587 if (( opt
& FM_ANY
) == FM_EXFAT
|| sz_vol
>= 0x4000000 || au
> 128 ) { /* exFAT only, vol >= 64Ms or au > 128s ? */
5588 fmt
= FS_EXFAT
; break ;
5591 if ( au
> 128 ) LEAVE_MKFS ( FR_INVALID_PARAMETER
); /* Too large au for FAT/FAT32 */
5592 if ( opt
& FM_FAT32
) { /* FAT32 possible? */
5593 if (( opt
& FM_ANY
) == FM_FAT32
|| !( opt
& FM_FAT
)) { /* FAT32 only or no-FAT? */
5594 fmt
= FS_FAT32
; break ;
5597 if (!( opt
& FM_FAT
)) LEAVE_MKFS ( FR_INVALID_PARAMETER
); /* no-FAT? */
5602 if ( fmt
== FS_EXFAT
) { /* Create an exFAT volume */
5603 DWORD szb_bit
, szb_case
, sum
, nb
, cl
;
5608 if ( sz_vol
< 0x1000 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
5610 tbl
[ 0 ] = b_vol
; tbl
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform the device the volume area may be erased */
5611 disk_ioctl ( pdrv
, CTRL_TRIM
, tbl
);
5613 /* Determine FAT location, data location and number of clusters */
5614 if ( au
== 0 ) { /* au auto-selection */
5616 if ( sz_vol
>= 0x80000 ) au
= 64 ; /* >= 512Ks */
5617 if ( sz_vol
>= 0x4000000 ) au
= 256 ; /* >= 64Ms */
5619 b_fat
= b_vol
+ 32 ; /* FAT start at offset 32 */
5620 sz_fat
= (( sz_vol
/ au
+ 2 ) * 4 + ss
- 1 ) / ss
; /* Number of FAT sectors */
5621 b_data
= ( b_fat
+ sz_fat
+ sz_blk
- 1 ) & ~( sz_blk
- 1 ); /* Align data area to the erase block boundary */
5622 if ( b_data
>= sz_vol
/ 2 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
5623 n_clst
= ( sz_vol
- ( b_data
- b_vol
)) / au
; /* Number of clusters */
5624 if ( n_clst
< 16 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too few clusters? */
5625 if ( n_clst
> MAX_EXFAT
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters? */
5627 szb_bit
= ( n_clst
+ 7 ) / 8 ; /* Size of allocation bitmap */
5628 tbl
[ 0 ] = ( szb_bit
+ au
* ss
- 1 ) / ( au
* ss
); /* Number of allocation bitmap clusters */
5630 /* Create a compressed up-case table */
5631 sect
= b_data
+ au
* tbl
[ 0 ]; /* Table start sector */
5632 sum
= 0 ; /* Table checksum to be stored in the 82 entry */
5633 st
= 0 ; si
= 0 ; i
= 0 ; j
= 0 ; szb_case
= 0 ;
5637 ch
= ( WCHAR
) ff_wtoupper ( si
); /* Get an up-case char */
5639 si
++; break ; /* Store the up-case char if exist */
5641 for ( j
= 1 ; ( WCHAR
)( si
+ j
) && ( WCHAR
)( si
+ j
) == ff_wtoupper (( WCHAR
)( si
+ j
)); j
++) ; /* Get run length of no-case block */
5643 ch
= 0xFFFF ; st
= 2 ; break ; /* Compress the no-case block if run is >= 128 */
5645 st
= 1 ; /* Do not compress short run */
5646 /* go to next case */
5648 ch
= si
++; /* Fill the short run */
5649 if (-- j
== 0 ) st
= 0 ;
5653 ch
= ( WCHAR
) j
; si
+= ( WCHAR
) j
; /* Number of chars to skip */
5656 sum
= xsum32 ( buf
[ i
+ 0 ] = ( BYTE
) ch
, sum
); /* Put it into the write buffer */
5657 sum
= xsum32 ( buf
[ i
+ 1 ] = ( BYTE
)( ch
>> 8 ), sum
);
5658 i
+= 2 ; szb_case
+= 2 ;
5659 if ( si
== 0 || i
== szb_buf
) { /* Write buffered data when buffer full or end of process */
5660 n
= ( i
+ ss
- 1 ) / ss
;
5661 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5665 tbl
[ 1 ] = ( szb_case
+ au
* ss
- 1 ) / ( au
* ss
); /* Number of up-case table clusters */
5666 tbl
[ 2 ] = 1 ; /* Number of root dir clusters */
5668 /* Initialize the allocation bitmap */
5669 sect
= b_data
; nsect
= ( szb_bit
+ ss
- 1 ) / ss
; /* Start of bitmap and number of sectors */
5670 nb
= tbl
[ 0 ] + tbl
[ 1 ] + tbl
[ 2 ]; /* Number of clusters in-use by system */
5672 mem_set ( buf
, 0 , szb_buf
);
5673 for ( i
= 0 ; nb
>= 8 && i
< szb_buf
; buf
[ i
++] = 0xFF , nb
-= 8 ) ;
5674 for ( b
= 1 ; nb
!= 0 && i
< szb_buf
; buf
[ i
] |= b
, b
<<= 1 , nb
--) ;
5675 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
5676 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5677 sect
+= n
; nsect
-= n
;
5680 /* Initialize the FAT */
5681 sect
= b_fat
; nsect
= sz_fat
; /* Start of FAT and number of FAT sectors */
5684 mem_set ( buf
, 0 , szb_buf
); i
= 0 ; /* Clear work area and reset write index */
5685 if ( cl
== 0 ) { /* Set entry 0 and 1 */
5686 st_dword ( buf
+ i
, 0xFFFFFFF8 ); i
+= 4 ; cl
++;
5687 st_dword ( buf
+ i
, 0xFFFFFFFF ); i
+= 4 ; cl
++;
5689 do { /* Create chains of bitmap, up-case and root dir */
5690 while ( nb
!= 0 && i
< szb_buf
) { /* Create a chain */
5691 st_dword ( buf
+ i
, ( nb
> 1 ) ? cl
+ 1 : 0xFFFFFFFF );
5694 if ( nb
== 0 && j
< 3 ) nb
= tbl
[ j
++]; /* Next chain */
5695 } while ( nb
!= 0 && i
< szb_buf
);
5696 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
5697 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5698 sect
+= n
; nsect
-= n
;
5701 /* Initialize the root directory */
5702 mem_set ( buf
, 0 , szb_buf
);
5703 buf
[ SZDIRE
* 0 + 0 ] = 0x83 ; /* 83 entry (volume label) */
5704 buf
[ SZDIRE
* 1 + 0 ] = 0x81 ; /* 81 entry (allocation bitmap) */
5705 st_dword ( buf
+ SZDIRE
* 1 + 20 , 2 ); /* cluster */
5706 st_dword ( buf
+ SZDIRE
* 1 + 24 , szb_bit
); /* size */
5707 buf
[ SZDIRE
* 2 + 0 ] = 0x82 ; /* 82 entry (up-case table) */
5708 st_dword ( buf
+ SZDIRE
* 2 + 4 , sum
); /* sum */
5709 st_dword ( buf
+ SZDIRE
* 2 + 20 , 2 + tbl
[ 0 ]); /* cluster */
5710 st_dword ( buf
+ SZDIRE
* 2 + 24 , szb_case
); /* size */
5711 sect
= b_data
+ au
* ( tbl
[ 0 ] + tbl
[ 1 ]); nsect
= au
; /* Start of the root directory and number of sectors */
5712 do { /* Fill root directory sectors */
5713 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
5714 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5715 mem_set ( buf
, 0 , ss
);
5716 sect
+= n
; nsect
-= n
;
5719 /* Create two set of the exFAT VBR blocks */
5721 for ( n
= 0 ; n
< 2 ; n
++) {
5722 /* Main record (+0) */
5723 mem_set ( buf
, 0 , ss
);
5724 mem_cpy ( buf
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 ); /* Boot jump code (x86), OEM name */
5725 st_dword ( buf
+ BPB_VolOfsEx
, b_vol
); /* Volume offset in the physical drive [sector] */
5726 st_dword ( buf
+ BPB_TotSecEx
, sz_vol
); /* Volume size [sector] */
5727 st_dword ( buf
+ BPB_FatOfsEx
, b_fat
- b_vol
); /* FAT offset [sector] */
5728 st_dword ( buf
+ BPB_FatSzEx
, sz_fat
); /* FAT size [sector] */
5729 st_dword ( buf
+ BPB_DataOfsEx
, b_data
- b_vol
); /* Data offset [sector] */
5730 st_dword ( buf
+ BPB_NumClusEx
, n_clst
); /* Number of clusters */
5731 st_dword ( buf
+ BPB_RootClusEx
, 2 + tbl
[ 0 ] + tbl
[ 1 ]); /* Root dir cluster # */
5732 st_dword ( buf
+ BPB_VolIDEx
, GET_FATTIME ()); /* VSN */
5733 st_word ( buf
+ BPB_FSVerEx
, 0x100 ); /* Filesystem version (1.00) */
5734 for ( buf
[ BPB_BytsPerSecEx
] = 0 , i
= ss
; i
>>= 1 ; buf
[ BPB_BytsPerSecEx
]++) ; /* Log2 of sector size [byte] */
5735 for ( buf
[ BPB_SecPerClusEx
] = 0 , i
= au
; i
>>= 1 ; buf
[ BPB_SecPerClusEx
]++) ; /* Log2 of cluster size [sector] */
5736 buf
[ BPB_NumFATsEx
] = 1 ; /* Number of FATs */
5737 buf
[ BPB_DrvNumEx
] = 0x80 ; /* Drive number (for int13) */
5738 st_word ( buf
+ BS_BootCodeEx
, 0xFEEB ); /* Boot code (x86) */
5739 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (placed here regardless of sector size) */
5740 for ( i
= sum
= 0 ; i
< ss
; i
++) { /* VBR checksum */
5741 if ( i
!= BPB_VolFlagEx
&& i
!= BPB_VolFlagEx
+ 1 && i
!= BPB_PercInUseEx
) sum
= xsum32 ( buf
[ i
], sum
);
5743 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5744 /* Extended bootstrap record (+1..+8) */
5745 mem_set ( buf
, 0 , ss
);
5746 st_word ( buf
+ ss
- 2 , 0xAA55 ); /* Signature (placed at end of sector) */
5747 for ( j
= 1 ; j
< 9 ; j
++) {
5748 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
5749 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5751 /* OEM/Reserved record (+9..+10) */
5752 mem_set ( buf
, 0 , ss
);
5753 for ( ; j
< 11 ; j
++) {
5754 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
5755 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5757 /* Sum record (+11) */
5758 for ( i
= 0 ; i
< ss
; i
+= 4 ) st_dword ( buf
+ i
, sum
); /* Fill with checksum value */
5759 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5763 #endif /* FF_FS_EXFAT */
5764 { /* Create an FAT/FAT32 volume */
5767 /* Pre-determine number of clusters and FAT sub-type */
5768 if ( fmt
== FS_FAT32
) { /* FAT32 volume */
5769 if ( pau
== 0 ) { /* au auto-selection */
5770 n
= sz_vol
/ 0x20000 ; /* Volume size in unit of 128KS */
5771 for ( i
= 0 , pau
= 1 ; cst32
[ i
] && cst32
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
5773 n_clst
= sz_vol
/ pau
; /* Number of clusters */
5774 sz_fat
= ( n_clst
* 4 + 8 + ss
- 1 ) / ss
; /* FAT size [sector] */
5775 sz_rsv
= 32 ; /* Number of reserved sectors */
5776 sz_dir
= 0 ; /* No static directory */
5777 if ( n_clst
<= MAX_FAT16
|| n_clst
> MAX_FAT32
) LEAVE_MKFS ( FR_MKFS_ABORTED
);
5778 } else { /* FAT volume */
5779 if ( pau
== 0 ) { /* au auto-selection */
5780 n
= sz_vol
/ 0x1000 ; /* Volume size in unit of 4KS */
5781 for ( i
= 0 , pau
= 1 ; cst
[ i
] && cst
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
5783 n_clst
= sz_vol
/ pau
;
5784 if ( n_clst
> MAX_FAT12
) {
5785 n
= n_clst
* 2 + 4 ; /* FAT size [byte] */
5788 n
= ( n_clst
* 3 + 1 ) / 2 + 3 ; /* FAT size [byte] */
5790 sz_fat
= ( n
+ ss
- 1 ) / ss
; /* FAT size [sector] */
5791 sz_rsv
= 1 ; /* Number of reserved sectors */
5792 sz_dir
= ( DWORD
) n_rootdir
* SZDIRE
/ ss
; /* Rootdir size [sector] */
5794 b_fat
= b_vol
+ sz_rsv
; /* FAT base */
5795 b_data
= b_fat
+ sz_fat
* n_fats
+ sz_dir
; /* Data base */
5797 /* Align data base to erase block boundary (for flash memory media) */
5798 n
= (( b_data
+ sz_blk
- 1 ) & ~( sz_blk
- 1 )) - b_data
; /* Next nearest erase block from current data base */
5799 if ( fmt
== FS_FAT32
) { /* FAT32: Move FAT base */
5800 sz_rsv
+= n
; b_fat
+= n
;
5801 } else { /* FAT: Expand FAT size */
5802 sz_fat
+= n
/ n_fats
;
5805 /* Determine number of clusters and final check of validity of the FAT sub-type */
5806 if ( sz_vol
< b_data
+ pau
* 16 - b_vol
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume */
5807 n_clst
= ( sz_vol
- sz_rsv
- sz_fat
* n_fats
- sz_dir
) / pau
;
5808 if ( fmt
== FS_FAT32
) {
5809 if ( n_clst
<= MAX_FAT16
) { /* Too few clusters for FAT32 */
5810 if ( au
== 0 && ( au
= pau
/ 2 ) != 0 ) continue ; /* Adjust cluster size and retry */
5811 LEAVE_MKFS ( FR_MKFS_ABORTED
);
5814 if ( fmt
== FS_FAT16
) {
5815 if ( n_clst
> MAX_FAT16
) { /* Too many clusters for FAT16 */
5816 if ( au
== 0 && ( pau
* 2 ) <= 64 ) {
5817 au
= pau
* 2 ; continue ; /* Adjust cluster size and retry */
5819 if (( opt
& FM_FAT32
)) {
5820 fmt
= FS_FAT32
; continue ; /* Switch type to FAT32 and retry */
5822 if ( au
== 0 && ( au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
5823 LEAVE_MKFS ( FR_MKFS_ABORTED
);
5825 if ( n_clst
<= MAX_FAT12
) { /* Too few clusters for FAT16 */
5826 if ( au
== 0 && ( au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
5827 LEAVE_MKFS ( FR_MKFS_ABORTED
);
5830 if ( fmt
== FS_FAT12
&& n_clst
> MAX_FAT12
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters for FAT12 */
5832 /* Ok, it is the valid cluster configuration */
5837 tbl
[ 0 ] = b_vol
; tbl
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform the device the volume area can be erased */
5838 disk_ioctl ( pdrv
, CTRL_TRIM
, tbl
);
5840 /* Create FAT VBR */
5841 mem_set ( buf
, 0 , ss
);
5842 mem_cpy ( buf
+ BS_JmpBoot
, " \xEB\xFE\x90 " "MSDOS5.0" , 11 ); /* Boot jump code (x86), OEM name */
5843 st_word ( buf
+ BPB_BytsPerSec
, ss
); /* Sector size [byte] */
5844 buf
[ BPB_SecPerClus
] = ( BYTE
) pau
; /* Cluster size [sector] */
5845 st_word ( buf
+ BPB_RsvdSecCnt
, ( WORD
) sz_rsv
); /* Size of reserved area */
5846 buf
[ BPB_NumFATs
] = ( BYTE
) n_fats
; /* Number of FATs */
5847 st_word ( buf
+ BPB_RootEntCnt
, ( WORD
)(( fmt
== FS_FAT32
) ? 0 : n_rootdir
)); /* Number of root directory entries */
5848 if ( sz_vol
< 0x10000 ) {
5849 st_word ( buf
+ BPB_TotSec16
, ( WORD
) sz_vol
); /* Volume size in 16-bit LBA */
5851 st_dword ( buf
+ BPB_TotSec32
, sz_vol
); /* Volume size in 32-bit LBA */
5853 buf
[ BPB_Media
] = 0xF8 ; /* Media descriptor byte */
5854 st_word ( buf
+ BPB_SecPerTrk
, 63 ); /* Number of sectors per track (for int13) */
5855 st_word ( buf
+ BPB_NumHeads
, 255 ); /* Number of heads (for int13) */
5856 st_dword ( buf
+ BPB_HiddSec
, b_vol
); /* Volume offset in the physical drive [sector] */
5857 if ( fmt
== FS_FAT32
) {
5858 st_dword ( buf
+ BS_VolID32
, GET_FATTIME ()); /* VSN */
5859 st_dword ( buf
+ BPB_FATSz32
, sz_fat
); /* FAT size [sector] */
5860 st_dword ( buf
+ BPB_RootClus32
, 2 ); /* Root directory cluster # (2) */
5861 st_word ( buf
+ BPB_FSInfo32
, 1 ); /* Offset of FSINFO sector (VBR + 1) */
5862 st_word ( buf
+ BPB_BkBootSec32
, 6 ); /* Offset of backup VBR (VBR + 6) */
5863 buf
[ BS_DrvNum32
] = 0x80 ; /* Drive number (for int13) */
5864 buf
[ BS_BootSig32
] = 0x29 ; /* Extended boot signature */
5865 mem_cpy ( buf
+ BS_VolLab32
, "NO NAME " "FAT32 " , 19 ); /* Volume label, FAT signature */
5867 st_dword ( buf
+ BS_VolID
, GET_FATTIME ()); /* VSN */
5868 st_word ( buf
+ BPB_FATSz16
, ( WORD
) sz_fat
); /* FAT size [sector] */
5869 buf
[ BS_DrvNum
] = 0x80 ; /* Drive number (for int13) */
5870 buf
[ BS_BootSig
] = 0x29 ; /* Extended boot signature */
5871 mem_cpy ( buf
+ BS_VolLab
, "NO NAME " "FAT " , 19 ); /* Volume label, FAT signature */
5873 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (offset is fixed here regardless of sector size) */
5874 if ( disk_write ( pdrv
, buf
, b_vol
, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it to the VBR sector */
5876 /* Create FSINFO record if needed */
5877 if ( fmt
== FS_FAT32
) {
5878 disk_write ( pdrv
, buf
, b_vol
+ 6 , 1 ); /* Write backup VBR (VBR + 6) */
5879 mem_set ( buf
, 0 , ss
);
5880 st_dword ( buf
+ FSI_LeadSig
, 0x41615252 );
5881 st_dword ( buf
+ FSI_StrucSig
, 0x61417272 );
5882 st_dword ( buf
+ FSI_Free_Count
, n_clst
- 1 ); /* Number of free clusters */
5883 st_dword ( buf
+ FSI_Nxt_Free
, 2 ); /* Last allocated cluster# */
5884 st_word ( buf
+ BS_55AA
, 0xAA55 );
5885 disk_write ( pdrv
, buf
, b_vol
+ 7 , 1 ); /* Write backup FSINFO (VBR + 7) */
5886 disk_write ( pdrv
, buf
, b_vol
+ 1 , 1 ); /* Write original FSINFO (VBR + 1) */
5889 /* Initialize FAT area */
5890 mem_set ( buf
, 0 , ( UINT
) szb_buf
);
5891 sect
= b_fat
; /* FAT start sector */
5892 for ( i
= 0 ; i
< n_fats
; i
++) { /* Initialize FATs each */
5893 if ( fmt
== FS_FAT32
) {
5894 st_dword ( buf
+ 0 , 0xFFFFFFF8 ); /* Entry 0 */
5895 st_dword ( buf
+ 4 , 0xFFFFFFFF ); /* Entry 1 */
5896 st_dword ( buf
+ 8 , 0x0FFFFFFF ); /* Entry 2 (root directory) */
5898 st_dword ( buf
+ 0 , ( fmt
== FS_FAT12
) ? 0xFFFFF8 : 0xFFFFFFF8 ); /* Entry 0 and 1 */
5900 nsect
= sz_fat
; /* Number of FAT sectors */
5901 do { /* Fill FAT sectors */
5902 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
5903 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5904 mem_set ( buf
, 0 , ss
);
5905 sect
+= n
; nsect
-= n
;
5909 /* Initialize root directory (fill with zero) */
5910 nsect
= ( fmt
== FS_FAT32
) ? pau
: sz_dir
; /* Number of root directory sectors */
5912 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
5913 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5914 sect
+= n
; nsect
-= n
;
5918 /* Determine system ID in the partition table */
5919 if ( FF_FS_EXFAT
&& fmt
== FS_EXFAT
) {
5920 sys
= 0x07 ; /* HPFS/NTFS/exFAT */
5922 if ( fmt
== FS_FAT32
) {
5923 sys
= 0x0C ; /* FAT32X */
5925 if ( sz_vol
>= 0x10000 ) {
5926 sys
= 0x06 ; /* FAT12/16 (large) */
5928 sys
= ( fmt
== FS_FAT16
) ? 0x04 : 0x01 ; /* FAT16 : FAT12 */
5933 /* Update partition information */
5934 if ( FF_MULTI_PARTITION
&& part
!= 0 ) { /* Created in the existing partition */
5935 /* Update system ID in the partition table */
5936 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Read the MBR */
5937 buf
[ MBR_Table
+ ( part
- 1 ) * SZ_PTE
+ PTE_System
] = sys
; /* Set system ID */
5938 if ( disk_write ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it back to the MBR */
5939 } else { /* Created as a new single partition */
5940 if (!( opt
& FM_SFD
)) { /* Create partition table if in FDISK format */
5941 mem_set ( buf
, 0 , ss
);
5942 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* MBR signature */
5943 pte
= buf
+ MBR_Table
; /* Create partition table for single partition in the drive */
5944 pte
[ PTE_Boot
] = 0 ; /* Boot indicator */
5945 pte
[ PTE_StHead
] = 1 ; /* Start head */
5946 pte
[ PTE_StSec
] = 1 ; /* Start sector */
5947 pte
[ PTE_StCyl
] = 0 ; /* Start cylinder */
5948 pte
[ PTE_System
] = sys
; /* System type */
5949 n
= ( b_vol
+ sz_vol
) / ( 63 * 255 ); /* (End CHS may be invalid) */
5950 pte
[ PTE_EdHead
] = 254 ; /* End head */
5951 pte
[ PTE_EdSec
] = ( BYTE
)((( n
>> 2 ) & 0xC0 ) | 63 ); /* End sector */
5952 pte
[ PTE_EdCyl
] = ( BYTE
) n
; /* End cylinder */
5953 st_dword ( pte
+ PTE_StLba
, b_vol
); /* Start offset in LBA */
5954 st_dword ( pte
+ PTE_SizLba
, sz_vol
); /* Size in sectors */
5955 if ( disk_write ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it to the MBR */
5959 if ( disk_ioctl ( pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5966 #if FF_MULTI_PARTITION
5967 /*-----------------------------------------------------------------------*/
5968 /* Create Partition Table on the Physical Drive */
5969 /*-----------------------------------------------------------------------*/
5972 BYTE pdrv
, /* Physical drive number */
5973 const DWORD
* szt
, /* Pointer to the size table for each partitions */
5974 void * work
/* Pointer to the working buffer (null: use heap memory) */
5977 UINT i
, n
, sz_cyl
, tot_cyl
, b_cyl
, e_cyl
, p_cyl
;
5978 BYTE s_hd
, e_hd
, * p
, * buf
= ( BYTE
*) work
;
5980 DWORD sz_disk
, sz_part
, s_part
;
5984 stat
= disk_initialize ( pdrv
);
5985 if ( stat
& STA_NOINIT
) return FR_NOT_READY
;
5986 if ( stat
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
5987 if ( disk_ioctl ( pdrv
, GET_SECTOR_COUNT
, & sz_disk
)) return FR_DISK_ERR
;
5991 if (! buf
) buf
= ff_memalloc ( FF_MAX_SS
); /* Use heap memory for working buffer */
5993 if (! buf
) return FR_NOT_ENOUGH_CORE
;
5995 /* Determine the CHS without any consideration of the drive geometry */
5996 for ( n
= 16 ; n
< 256 && sz_disk
/ n
/ 63 > 1024 ; n
*= 2 ) ;
5998 e_hd
= ( BYTE
)( n
- 1 );
6000 tot_cyl
= sz_disk
/ sz_cyl
;
6002 /* Create partition table */
6003 mem_set ( buf
, 0 , FF_MAX_SS
);
6004 p
= buf
+ MBR_Table
; b_cyl
= 0 ;
6005 for ( i
= 0 ; i
< 4 ; i
++, p
+= SZ_PTE
) {
6006 p_cyl
= ( szt
[ i
] <= 100U ) ? ( DWORD
) tot_cyl
* szt
[ i
] / 100 : szt
[ i
] / sz_cyl
; /* Number of cylinders */
6007 if ( p_cyl
== 0 ) continue ;
6008 s_part
= ( DWORD
) sz_cyl
* b_cyl
;
6009 sz_part
= ( DWORD
) sz_cyl
* p_cyl
;
6010 if ( i
== 0 ) { /* Exclude first track of cylinder 0 */
6012 s_part
+= 63 ; sz_part
-= 63 ;
6016 e_cyl
= b_cyl
+ p_cyl
- 1 ; /* End cylinder */
6017 if ( e_cyl
>= tot_cyl
) LEAVE_MKFS ( FR_INVALID_PARAMETER
);
6019 /* Set partition table */
6020 p
[ 1 ] = s_hd
; /* Start head */
6021 p
[ 2 ] = ( BYTE
)((( b_cyl
>> 2 ) & 0xC0 ) | 1 ); /* Start sector */
6022 p
[ 3 ] = ( BYTE
) b_cyl
; /* Start cylinder */
6023 p
[ 4 ] = 0x07 ; /* System type (temporary setting) */
6024 p
[ 5 ] = e_hd
; /* End head */
6025 p
[ 6 ] = ( BYTE
)((( e_cyl
>> 2 ) & 0xC0 ) | 63 ); /* End sector */
6026 p
[ 7 ] = ( BYTE
) e_cyl
; /* End cylinder */
6027 st_dword ( p
+ 8 , s_part
); /* Start sector in LBA */
6028 st_dword ( p
+ 12 , sz_part
); /* Number of sectors */
6030 /* Next partition */
6033 st_word ( p
, 0xAA55 ); /* MBR signature (always at offset 510) */
6035 /* Write it to the MBR */
6036 res
= ( disk_write ( pdrv
, buf
, 0 , 1 ) == RES_OK
&& disk_ioctl ( pdrv
, CTRL_SYNC
, 0 ) == RES_OK
) ? FR_OK
: FR_DISK_ERR
;
6040 #endif /* FF_MULTI_PARTITION */
6041 #endif /* FF_USE_MKFS && !FF_FS_READONLY */
6047 #if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
6048 #error Wrong FF_STRF_ENCODE setting
6050 /*-----------------------------------------------------------------------*/
6051 /* Get a String from the File */
6052 /*-----------------------------------------------------------------------*/
6055 TCHAR
* buff
, /* Pointer to the string buffer to read */
6056 int len
, /* Size of string buffer (items) */
6057 FIL
* fp
/* Pointer to the file object */
6065 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
6068 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
6072 #if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
6073 /* Make a room for the character and terminator */
6074 if ( FF_LFN_UNICODE
== 1 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 1 : 2 ;
6075 if ( FF_LFN_UNICODE
== 2 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 3 : 4 ;
6076 if ( FF_LFN_UNICODE
== 3 ) len
-= 1 ;
6078 #if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
6079 f_read ( fp
, s
, 1 , & rc
);
6082 if ( dbc_1st (( BYTE
) wc
)) {
6083 f_read ( fp
, s
, 1 , & rc
);
6084 if ( rc
!= 1 || ! dbc_2nd ( s
[ 0 ])) continue ;
6085 wc
= wc
<< 8 | s
[ 0 ];
6087 dc
= ff_oem2uni ( wc
, CODEPAGE
);
6088 if ( dc
== 0 ) continue ;
6089 #elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
6090 f_read ( fp
, s
, 2 , & rc
);
6092 dc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6093 if ( IsSurrogateL ( dc
)) continue ;
6094 if ( IsSurrogateH ( dc
)) {
6095 f_read ( fp
, s
, 2 , & rc
);
6097 wc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6098 if (! IsSurrogateL ( wc
)) continue ;
6099 dc
= (( dc
& 0x3FF ) + 0x40 ) << 10 | ( wc
& 0x3FF );
6101 #else /* Read a character in UTF-8 */
6102 f_read ( fp
, s
, 1 , & rc
);
6105 if ( dc
>= 0x80 ) { /* Multi-byte character? */
6107 if (( dc
& 0xE0 ) == 0xC0 ) { dc
&= 0x1F ; ct
= 1 ; } /* 2-byte? */
6108 if (( dc
& 0xF0 ) == 0xE0 ) { dc
&= 0x0F ; ct
= 2 ; } /* 3-byte? */
6109 if (( dc
& 0xF8 ) == 0xF0 ) { dc
&= 0x07 ; ct
= 3 ; } /* 4-byte? */
6110 if ( ct
== 0 ) continue ;
6111 f_read ( fp
, s
, ct
, & rc
); /* Get trailing bytes */
6112 if ( rc
!= ct
) break ;
6114 do { /* Merge trailing bytes */
6115 if (( s
[ rc
] & 0xC0 ) != 0x80 ) break ;
6116 dc
= dc
<< 6 | ( s
[ rc
] & 0x3F );
6117 } while (++ rc
< ct
);
6118 if ( rc
!= ct
|| dc
< 0x80 || IsSurrogate ( dc
) || dc
>= 0x110000 ) continue ; /* Wrong encoding? */
6121 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ; /* Strip \r off if needed */
6122 #if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
6123 if ( FF_LFN_UNICODE
== 1 && dc
>= 0x10000 ) { /* Out of BMP at UTF-16? */
6124 * p
++ = ( TCHAR
)( 0xD800 | (( dc
>> 10 ) - 0x40 )); nc
++; /* Make and output high surrogate */
6125 dc
= 0xDC00 | ( dc
& 0x3FF ); /* Make low surrogate */
6127 * p
++ = ( TCHAR
) dc
; nc
++;
6128 if ( dc
== ' \n ' ) break ; /* End of line? */
6129 #elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
6130 if ( dc
< 0x80 ) { /* 1-byte */
6133 if ( dc
== ' \n ' ) break ; /* End of line? */
6135 if ( dc
< 0x800 ) { /* 2-byte */
6136 * p
++ = ( TCHAR
)( 0xC0 | ( dc
>> 6 & 0x1F ));
6137 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6140 if ( dc
< 0x10000 ) { /* 3-byte */
6141 * p
++ = ( TCHAR
)( 0xE0 | ( dc
>> 12 & 0x0F ));
6142 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6143 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6145 } else { /* 4-byte */
6146 * p
++ = ( TCHAR
)( 0xF0 | ( dc
>> 18 & 0x07 ));
6147 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 12 & 0x3F ));
6148 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6149 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6157 #else /* Byte-by-byte without any conversion (ANSI/OEM API) */
6158 len
-= 1 ; /* Make a room for the terminator */
6160 f_read ( fp
, s
, 1 , & rc
);
6163 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ;
6164 * p
++ = ( TCHAR
) dc
; nc
++;
6165 if ( dc
== ' \n ' ) break ;
6169 * p
= 0 ; /* Terminate the string */
6170 return nc
? buff
: 0 ; /* When no data read due to EOF or error, return with error. */
6178 /*-----------------------------------------------------------------------*/
6179 /* Put a Character to the File */
6180 /*-----------------------------------------------------------------------*/
6182 typedef struct { /* Putchar output buffer and work area */
6183 FIL
* fp
; /* Ptr to the writing file */
6184 int idx
, nchr
; /* Write index of buf[] (-1:error), number of encoding units written */
6185 #if FF_USE_LFN && FF_LFN_UNICODE == 1
6187 #elif FF_USE_LFN && FF_LFN_UNICODE == 2
6191 BYTE buf
[ 64 ]; /* Write buffer */
6196 void putc_bfd ( /* Buffered write with code conversion */
6203 #if FF_USE_LFN && FF_LFN_UNICODE
6205 #if FF_LFN_UNICODE == 2
6211 if ( FF_USE_STRFUNC
== 2 && c
== ' \n ' ) { /* LF -> CRLF conversion */
6215 i
= pb
-> idx
; /* Write index of pb->buf[] */
6217 nc
= pb
-> nchr
; /* Write unit counter */
6219 #if FF_USE_LFN && FF_LFN_UNICODE
6220 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
6221 if ( IsSurrogateH ( c
)) {
6224 hs
= pb
-> hs
; pb
-> hs
= 0 ;
6226 if (! IsSurrogateL ( c
)) hs
= 0 ;
6228 if ( IsSurrogateL ( c
)) return ;
6231 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
6233 if ( pb
-> ct
== 0 ) { /* Out of multi-byte sequence? */
6234 pb
-> bs
[ pb
-> wi
= 0 ] = ( BYTE
) c
; /* Save 1st byte */
6235 if (( BYTE
) c
< 0x80 ) break ; /* 1-byte? */
6236 if ((( BYTE
) c
& 0xE0 ) == 0xC0 ) pb
-> ct
= 1 ; /* 2-byte? */
6237 if ((( BYTE
) c
& 0xF0 ) == 0xE0 ) pb
-> ct
= 2 ; /* 3-byte? */
6238 if ((( BYTE
) c
& 0xF1 ) == 0xF0 ) pb
-> ct
= 3 ; /* 4-byte? */
6240 } else { /* In the multi-byte sequence */
6241 if ((( BYTE
) c
& 0xC0 ) != 0x80 ) { /* Broken sequence? */
6242 pb
-> ct
= 0 ; continue ;
6244 pb
-> bs
[++ pb
-> wi
] = ( BYTE
) c
; /* Save the trailing byte */
6245 if (-- pb
-> ct
== 0 ) break ; /* End of multi-byte sequence? */
6249 tp
= ( TCHAR
*) pb
-> bs
;
6250 dc
= tchar2uni (& tp
); /* UTF-8 ==> UTF-16 */
6251 if ( dc
== 0xFFFFFFFF ) return ;
6253 hs
= ( WCHAR
)( dc
>> 16 );
6254 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
6255 if ( IsSurrogate ( c
) || c
>= 0x110000 ) return ;
6257 hs
= ( WCHAR
)( 0xD800 | (( c
>> 10 ) - 0x40 )); /* Make high surrogate */
6258 wc
= 0xDC00 | ( c
& 0x3FF ); /* Make low surrogate */
6265 #if FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */
6267 st_word (& pb
-> buf
[ i
], hs
);
6271 st_word (& pb
-> buf
[ i
], wc
);
6273 #elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */
6275 pb
-> buf
[ i
++] = ( BYTE
)( hs
>> 8 );
6276 pb
-> buf
[ i
++] = ( BYTE
) hs
;
6279 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 );
6280 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6281 #elif FF_STRF_ENCODE == 3 /* Write it in UTF-8 */
6282 if ( hs
!= 0 ) { /* 4-byte */
6284 hs
= ( hs
& 0x3FF ) + 0x40 ;
6285 pb
-> buf
[ i
++] = ( BYTE
)( 0xF0 | hs
>> 8 );
6286 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
>> 2 & 0x3F ));
6287 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
& 3 ) << 4 | ( wc
>> 6 & 0x0F ));
6288 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6290 if ( wc
< 0x80 ) { /* 1-byte */
6291 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6293 if ( wc
< 0x800 ) { /* 2-byte */
6295 pb
-> buf
[ i
++] = ( BYTE
)( 0xC0 | wc
>> 6 );
6296 } else { /* 3-byte */
6298 pb
-> buf
[ i
++] = ( BYTE
)( 0xE0 | wc
>> 12 );
6299 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
>> 6 & 0x3F ));
6301 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6304 #else /* Write it in ANSI/OEM */
6305 if ( hs
!= 0 ) return ;
6306 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* UTF-16 ==> ANSI/OEM */
6307 if ( wc
== 0 ) return ;;
6309 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 ); nc
++;
6311 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6314 #else /* ANSI/OEM input (without re-encode) */
6315 pb
-> buf
[ i
++] = ( BYTE
) c
;
6318 if ( i
>= ( int )( sizeof pb
-> buf
) - 4 ) { /* Write buffered characters to the file */
6319 f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) i
, & n
);
6320 i
= ( n
== ( UINT
) i
) ? 0 : - 1 ;
6328 int putc_flush ( /* Flush left characters in the buffer */
6334 if ( pb
-> idx
>= 0 /* Flush buffered characters to the file */
6335 && f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) pb
-> idx
, & nw
) == FR_OK
6336 && ( UINT
) pb
-> idx
== nw
) return pb
-> nchr
;
6342 void putc_init ( /* Initialize write buffer */
6347 mem_set ( pb
, 0 , sizeof ( putbuff
));
6354 TCHAR c
, /* A character to be output */
6355 FIL
* fp
/* Pointer to the file object */
6362 putc_bfd (& pb
, c
); /* Put the character */
6363 return putc_flush (& pb
);
6369 /*-----------------------------------------------------------------------*/
6370 /* Put a String to the File */
6371 /*-----------------------------------------------------------------------*/
6374 const TCHAR
* str
, /* Pointer to the string to be output */
6375 FIL
* fp
/* Pointer to the file object */
6382 while (* str
) putc_bfd (& pb
, * str
++); /* Put the string */
6383 return putc_flush (& pb
);
6389 /*-----------------------------------------------------------------------*/
6390 /* Put a Formatted String to the File */
6391 /*-----------------------------------------------------------------------*/
6394 FIL
* fp
, /* Pointer to the file object */
6395 const TCHAR
* fmt
, /* Pointer to the format string */
6396 ... /* Optional arguments... */
6404 TCHAR c
, d
, str
[ 32 ], * p
;
6413 if ( c
== 0 ) break ; /* End of string */
6414 if ( c
!= '%' ) { /* Non escape character */
6420 if ( c
== '0' ) { /* Flag: '0' padding */
6423 if ( c
== '-' ) { /* Flag: left justified */
6427 if ( c
== '*' ) { /* Minimum width by argument */
6428 w
= va_arg ( arp
, int );
6431 while ( IsDigit ( c
)) { /* Minimum width */
6432 w
= w
* 10 + c
- '0' ;
6436 if ( c
== 'l' || c
== 'L' ) { /* Type prefix: Size is long int */
6441 if ( IsLower ( d
)) d
-= 0x20 ;
6442 switch ( d
) { /* Atgument type is... */
6443 case 'S' : /* String */
6444 p
= va_arg ( arp
, TCHAR
*);
6445 for ( j
= 0 ; p
[ j
]; j
++) ;
6446 if (!( f
& 2 )) { /* Right padded */
6447 while ( j
++ < w
) putc_bfd (& pb
, ' ' ) ;
6449 while (* p
) putc_bfd (& pb
, * p
++) ; /* String body */
6450 while ( j
++ < w
) putc_bfd (& pb
, ' ' ) ; /* Left padded */
6453 case 'C' : /* Character */
6454 putc_bfd (& pb
, ( TCHAR
) va_arg ( arp
, int )); continue ;
6456 case 'B' : /* Unsigned binary */
6459 case 'O' : /* Unsigned octal */
6462 case 'D' : /* Signed decimal */
6463 case 'U' : /* Unsigned decimal */
6466 case 'X' : /* Unsigned hexdecimal */
6469 default : /* Unknown type (pass-through) */
6470 putc_bfd (& pb
, c
); continue ;
6473 /* Get an argument and put it in numeral */
6474 v
= ( f
& 4 ) ? ( DWORD
) va_arg ( arp
, long ) : (( d
== 'D' ) ? ( DWORD
)( long ) va_arg ( arp
, int ) : ( DWORD
) va_arg ( arp
, unsigned int ));
6475 if ( d
== 'D' && ( v
& 0x80000000 )) {
6481 d
= ( TCHAR
)( v
% r
); v
/= r
;
6482 if ( d
> 9 ) d
+= ( c
== 'x' ) ? 0x27 : 0x07 ;
6484 } while ( v
&& i
< sizeof str
/ sizeof * str
);
6485 if ( f
& 8 ) str
[ i
++] = '-' ;
6486 j
= i
; d
= ( f
& 1 ) ? '0' : ' ' ;
6488 while ( j
++ < w
) putc_bfd (& pb
, d
); /* Right pad */
6491 putc_bfd (& pb
, str
[-- i
]); /* Number body */
6493 while ( j
++ < w
) putc_bfd (& pb
, d
); /* Left pad */
6498 return putc_flush (& pb
);
6501 #endif /* !FF_FS_READONLY */
6502 #endif /* FF_USE_STRFUNC */
6506 #if FF_CODE_PAGE == 0
6507 /*-----------------------------------------------------------------------*/
6508 /* Set Active Codepage for the Path Name */
6509 /*-----------------------------------------------------------------------*/
6512 WORD cp
/* Value to be set as active code page */
6515 static const WORD validcp
[] = { 437 , 720 , 737 , 771 , 775 , 850 , 852 , 857 , 860 , 861 , 862 , 863 , 864 , 865 , 866 , 869 , 932 , 936 , 949 , 950 , 0 };
6516 static const BYTE
* const tables
[] = { Ct437
, Ct720
, Ct737
, Ct771
, Ct775
, Ct850
, Ct852
, Ct857
, Ct860
, Ct861
, Ct862
, Ct863
, Ct864
, Ct865
, Ct866
, Ct869
, Dc932
, Dc936
, Dc949
, Dc950
, 0 };
6520 for ( i
= 0 ; validcp
[ i
] != 0 && validcp
[ i
] != cp
; i
++) ; /* Find the code page */
6521 if ( validcp
[ i
] != cp
) return FR_INVALID_PARAMETER
; /* Not found? */
6524 if ( cp
>= 900 ) { /* DBCS */
6533 #endif /* FF_CODE_PAGE == 0 */