]>
glassweightruler.freedombox.rocks Git - Ventoy.git/blob - Ventoy2Disk/Ventoy2Disk/ff14/source/ff.c
22cbac5c0bdfc44896d6a48acb40d178b8ddfa58
1 /*----------------------------------------------------------------------------/
2 / FatFs - Generic FAT Filesystem Module R0.14 /
3 /-----------------------------------------------------------------------------/
5 / Copyright (C) 2019, 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 != 86606 /* Revision ID */
33 #error Wrong include file (ff.h).
37 /* Limits and boundaries */
38 #define MAX_DIR 0x200000 /* Max size of FAT directory */
39 #define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
40 #define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
41 #define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
42 #define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
43 #define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
46 /* Character code support macros */
47 #define IsUpper(c) ((c) >= 'A' && (c) <= 'Z' )
48 #define IsLower(c) ((c) >= 'a' && (c) <= 'z' )
49 #define IsDigit(c) ((c) >= '0' && (c) <= '9' )
50 #define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
51 #define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
52 #define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
55 /* Additional file access control and file status flags for internal use */
56 #define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
57 #define FA_MODIFIED 0x40 /* File has been modified */
58 #define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
61 /* Additional file attribute bits for internal use */
62 #define AM_VOL 0x08 /* Volume label */
63 #define AM_LFN 0x0F /* LFN entry */
64 #define AM_MASK 0x3F /* Mask of defined bits */
67 /* Name status flags in fn[11] */
68 #define NSFLAG 11 /* Index of the name status byte */
69 #define NS_LOSS 0x01 /* Out of 8.3 format */
70 #define NS_LFN 0x02 /* Force to create LFN entry */
71 #define NS_LAST 0x04 /* Last segment */
72 #define NS_BODY 0x08 /* Lower case flag (body) */
73 #define NS_EXT 0x10 /* Lower case flag (ext) */
74 #define NS_DOT 0x20 /* Dot entry */
75 #define NS_NOLFN 0x40 /* Do not find LFN */
76 #define NS_NONAME 0x80 /* Not followed */
79 /* exFAT directory entry types */
80 #define ET_BITMAP 0x81 /* Allocation bitmap */
81 #define ET_UPCASE 0x82 /* Up-case table */
82 #define ET_VLABEL 0x83 /* Volume label */
83 #define ET_FILEDIR 0x85 /* File and directory */
84 #define ET_STREAM 0xC0 /* Stream extension */
85 #define ET_FILENAME 0xC1 /* Name extension */
88 /* FatFs refers the FAT structure as simple byte array instead of structure member
89 / because the C structure is not binary compatible between different platforms */
91 #define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
92 #define BS_OEMName 3 /* OEM name (8-byte) */
93 #define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
94 #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
95 #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
96 #define BPB_NumFATs 16 /* Number of FATs (BYTE) */
97 #define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
98 #define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
99 #define BPB_Media 21 /* Media descriptor byte (BYTE) */
100 #define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
101 #define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
102 #define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
103 #define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
104 #define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
105 #define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
106 #define BS_NTres 37 /* WindowsNT error flag (BYTE) */
107 #define BS_BootSig 38 /* Extended boot signature (BYTE) */
108 #define BS_VolID 39 /* Volume serial number (DWORD) */
109 #define BS_VolLab 43 /* Volume label string (8-byte) */
110 #define BS_FilSysType 54 /* Filesystem type string (8-byte) */
111 #define BS_BootCode 62 /* Boot code (448-byte) */
112 #define BS_55AA 510 /* Signature word (WORD) */
114 #define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
115 #define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
116 #define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
117 #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
118 #define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
119 #define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
120 #define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
121 #define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
122 #define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
123 #define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
124 #define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
125 #define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
126 #define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
128 #define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
129 #define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
130 #define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
131 #define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
132 #define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
133 #define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
134 #define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
135 #define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
136 #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
137 #define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
138 #define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */
139 #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
140 #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
141 #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
142 #define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
143 #define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
144 #define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
145 #define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
147 #define DIR_Name 0 /* Short file name (11-byte) */
148 #define DIR_Attr 11 /* Attribute (BYTE) */
149 #define DIR_NTres 12 /* Lower case flag (BYTE) */
150 #define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
151 #define DIR_CrtTime 14 /* Created time (DWORD) */
152 #define DIR_LstAccDate 18 /* Last accessed date (WORD) */
153 #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
154 #define DIR_ModTime 22 /* Modified time (DWORD) */
155 #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
156 #define DIR_FileSize 28 /* File size (DWORD) */
157 #define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
158 #define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
159 #define LDIR_Type 12 /* LFN: Entry type (BYTE) */
160 #define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
161 #define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
162 #define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
163 #define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
164 #define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
165 #define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
166 #define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
167 #define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
168 #define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
169 #define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
170 #define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
171 #define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
172 #define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
173 #define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
174 #define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
175 #define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
176 #define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
177 #define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
178 #define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
179 #define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
180 #define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
181 #define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
182 #define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
184 #define SZDIRE 32 /* Size of a directory entry */
185 #define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
186 #define RDDEM 0x05 /* Replacement of the character collides with DDEM */
187 #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
189 #define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
190 #define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
191 #define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
192 #define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
194 #define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
195 #define SZ_PTE 16 /* MBR: Size of a partition table entry */
196 #define PTE_Boot 0 /* MBR PTE: Boot indicator */
197 #define PTE_StHead 1 /* MBR PTE: Start head */
198 #define PTE_StSec 2 /* MBR PTE: Start sector */
199 #define PTE_StCyl 3 /* MBR PTE: Start cylinder */
200 #define PTE_System 4 /* MBR PTE: System ID */
201 #define PTE_EdHead 5 /* MBR PTE: End head */
202 #define PTE_EdSec 6 /* MBR PTE: End sector */
203 #define PTE_EdCyl 7 /* MBR PTE: End cylinder */
204 #define PTE_StLba 8 /* MBR PTE: Start in LBA */
205 #define PTE_SizLba 12 /* MBR PTE: Size in LBA */
207 #define GPTH_Sign 0 /* GPT: Header signature (8-byte) */
208 #define GPTH_Rev 8 /* GPT: Revision (DWORD) */
209 #define GPTH_Size 12 /* GPT: Header size (DWORD) */
210 #define GPTH_Bcc 16 /* GPT: Header BCC (DWORD) */
211 #define GPTH_CurLba 24 /* GPT: Main header LBA (QWORD) */
212 #define GPTH_BakLba 32 /* GPT: Backup header LBA (QWORD) */
213 #define GPTH_FstLba 40 /* GPT: First LBA for partitions (QWORD) */
214 #define GPTH_LstLba 48 /* GPT: Last LBA for partitions (QWORD) */
215 #define GPTH_DskGuid 56 /* GPT: Disk GUID (16-byte) */
216 #define GPTH_PtOfs 72 /* GPT: Partation table LBA (QWORD) */
217 #define GPTH_PtNum 80 /* GPT: Number of table entries (DWORD) */
218 #define GPTH_PteSize 84 /* GPT: Size of table entry (DWORD) */
219 #define GPTH_PtBcc 88 /* GPT: Partation table BCC (DWORD) */
220 #define SZ_GPTE 128 /* GPT: Size of partition table entry */
221 #define GPTE_PtGuid 0 /* GPT PTE: Partition type GUID (16-byte) */
222 #define GPTE_UpGuid 16 /* GPT PTE: Partition unique GUID (16-byte) */
223 #define GPTE_FstLba 32 /* GPT PTE: First LBA (QWORD) */
224 #define GPTE_LstLba 40 /* GPT PTE: Last LBA inclusive (QWORD) */
225 #define GPTE_Flags 48 /* GPT PTE: Flags (QWORD) */
226 #define GPTE_Name 56 /* GPT PTE: Name */
229 /* Post process on fatal error in the file operations */
230 #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
233 /* Re-entrancy related */
236 #error Static LFN work area cannot be used at thread-safe configuration
238 #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
240 #define LEAVE_FF(fs, res) return res
244 /* Definitions of logical drive - physical location conversion */
245 #if FF_MULTI_PARTITION
246 #define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
247 #define LD2PT(vol) VolToPart[vol].pt /* Get partition index */
249 #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */
250 #define LD2PT(vol) 0 /* Find first valid partition or in SFD */
254 /* Definitions of sector size */
255 #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)
256 #error Wrong sector size configuration
258 #if FF_MAX_SS == FF_MIN_SS
259 #define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
261 #define SS(fs) ((fs)->ssize) /* Variable sector size */
267 #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
268 #error Invalid FF_FS_NORTC settings
270 #define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
272 #define GET_FATTIME() get_fattime()
276 /* File lock controls */
279 #error FF_FS_LOCK must be 0 at read-only configuration
282 FATFS
* fs
; /* Object ID 1, volume (NULL:blank entry) */
283 DWORD clu
; /* Object ID 2, containing directory (0:root) */
284 DWORD ofs
; /* Object ID 3, offset in the directory */
285 WORD ctr
; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
290 /* SBCS up-case tables (\x80-\xFF) */
291 #define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
292 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
293 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
294 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
295 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
296 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
297 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
298 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
299 #define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
300 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
301 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
302 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
303 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
304 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
305 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
306 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
307 #define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
308 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
309 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
310 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
311 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
312 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
313 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
314 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
315 #define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
316 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
317 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
318 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
319 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
320 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
321 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
322 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
323 #define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
324 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
325 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
326 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
327 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
328 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
329 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
330 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
331 #define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
332 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
333 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
334 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
335 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
336 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
337 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
338 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
339 #define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
340 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
341 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
342 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
343 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
344 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
345 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
346 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
347 #define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
348 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
349 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
350 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
351 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
352 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
353 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
354 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
355 #define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
356 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
357 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
358 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
359 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
360 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
361 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
362 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
363 #define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
364 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
365 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
366 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
367 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
368 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
369 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
370 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
371 #define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
372 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
373 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
374 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
375 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
376 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
377 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
378 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
379 #define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
380 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
381 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
382 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
383 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
384 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
385 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
386 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
387 #define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
388 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
389 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
390 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
391 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
392 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
393 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
394 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
395 #define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
396 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
397 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
398 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
399 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
400 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
401 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
402 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
403 #define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
404 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
405 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
406 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
407 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
408 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
409 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
410 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
411 #define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
412 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
413 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
414 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
415 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
416 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
417 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
418 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
419 #define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
420 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
421 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
422 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
423 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
424 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
425 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
426 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
429 /* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
430 /* <------> <------> <------> <------> <------> */
431 #define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
432 #define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
433 #define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
434 #define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
437 /* Macros for table definitions */
438 #define MERGE_2STR(a, b) a ## b
439 #define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
444 /*--------------------------------------------------------------------------
446 Module Private Work Area
448 ---------------------------------------------------------------------------*/
449 /* Remark: Variables defined here without initial value shall be guaranteed
450 / zero/null at start-up. If not, the linker option or start-up routine is
451 / not compliance with C standard. */
453 /*--------------------------------*/
454 /* File/Volume controls */
455 /*--------------------------------*/
457 #if FF_VOLUMES < 1 || FF_VOLUMES > 10
458 #error Wrong FF_VOLUMES setting
460 static FATFS
* FatFs
[ FF_VOLUMES
]; /* Pointer to the filesystem objects (logical drives) */
461 static WORD Fsid
; /* Filesystem mount ID */
464 static BYTE CurrVol
; /* Current drive */
468 static FILESEM Files
[ FF_FS_LOCK
]; /* Open object lock semaphores */
472 #ifdef FF_VOLUME_STRS
473 static const char * const VolumeStr
[ FF_VOLUMES
] = { FF_VOLUME_STRS
}; /* Pre-defined volume ID */
478 #if FF_MIN_GPT > 0x100000000
479 #error Wrong FF_MIN_GPT setting
481 static const BYTE GUID_MS_Basic
[ 16 ] = { 0xA2 , 0xA0 , 0xD0 , 0xEB , 0xE5 , 0xB9 , 0x33 , 0x44 , 0x87 , 0xC0 , 0x68 , 0xB6 , 0xB7 , 0x26 , 0x99 , 0xC7 };
486 /*--------------------------------*/
487 /* LFN/Directory working buffer */
488 /*--------------------------------*/
490 #if FF_USE_LFN == 0 /* Non-LFN configuration */
492 #error LFN must be enabled when enable exFAT
495 #define INIT_NAMBUF(fs)
496 #define FREE_NAMBUF()
497 #define LEAVE_MKFS(res) return res
499 #else /* LFN configurations */
500 #if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
501 #error Wrong setting of FF_MAX_LFN
503 #if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
504 #error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
506 #if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
507 #error Wrong setting of FF_LFN_UNICODE
509 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 */
510 #define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
512 #if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
514 static BYTE DirBuf
[ MAXDIRB ( FF_MAX_LFN
)]; /* Directory entry block scratchpad buffer */
516 static WCHAR LfnBuf
[ FF_MAX_LFN
+ 1 ]; /* LFN working buffer */
518 #define INIT_NAMBUF(fs)
519 #define FREE_NAMBUF()
520 #define LEAVE_MKFS(res) return res
522 #elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
524 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
525 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
526 #define FREE_NAMBUF()
528 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
529 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; }
530 #define FREE_NAMBUF()
532 #define LEAVE_MKFS(res) return res
534 #elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
536 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
537 #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); }
538 #define FREE_NAMBUF() ff_memfree(lfn)
540 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */
541 #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
542 #define FREE_NAMBUF() ff_memfree(lfn)
544 #define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
545 #define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
548 #error Wrong setting of FF_USE_LFN
550 #endif /* FF_USE_LFN == 1 */
551 #endif /* FF_USE_LFN == 0 */
555 /*--------------------------------*/
556 /* Code conversion tables */
557 /*--------------------------------*/
559 #if FF_CODE_PAGE == 0 /* Run-time code page configuration */
560 #define CODEPAGE CodePage
561 static WORD CodePage
; /* Current code page */
562 static const BYTE
* ExCvt
, * DbcTbl
; /* Pointer to current SBCS up-case table and DBCS code range table below */
564 static const BYTE Ct437
[] = TBL_CT437
;
565 static const BYTE Ct720
[] = TBL_CT720
;
566 static const BYTE Ct737
[] = TBL_CT737
;
567 static const BYTE Ct771
[] = TBL_CT771
;
568 static const BYTE Ct775
[] = TBL_CT775
;
569 static const BYTE Ct850
[] = TBL_CT850
;
570 static const BYTE Ct852
[] = TBL_CT852
;
571 static const BYTE Ct855
[] = TBL_CT855
;
572 static const BYTE Ct857
[] = TBL_CT857
;
573 static const BYTE Ct860
[] = TBL_CT860
;
574 static const BYTE Ct861
[] = TBL_CT861
;
575 static const BYTE Ct862
[] = TBL_CT862
;
576 static const BYTE Ct863
[] = TBL_CT863
;
577 static const BYTE Ct864
[] = TBL_CT864
;
578 static const BYTE Ct865
[] = TBL_CT865
;
579 static const BYTE Ct866
[] = TBL_CT866
;
580 static const BYTE Ct869
[] = TBL_CT869
;
581 static const BYTE Dc932
[] = TBL_DC932
;
582 static const BYTE Dc936
[] = TBL_DC936
;
583 static const BYTE Dc949
[] = TBL_DC949
;
584 static const BYTE Dc950
[] = TBL_DC950
;
586 #elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
587 #define CODEPAGE FF_CODE_PAGE
588 static const BYTE ExCvt
[] = MKCVTBL ( TBL_CT
, FF_CODE_PAGE
);
590 #else /* Static code page configuration (DBCS) */
591 #define CODEPAGE FF_CODE_PAGE
592 static const BYTE DbcTbl
[] = MKCVTBL ( TBL_DC
, FF_CODE_PAGE
);
599 /*--------------------------------------------------------------------------
601 Module Private Functions
603 ---------------------------------------------------------------------------*/
606 /*-----------------------------------------------------------------------*/
607 /* Load/Store multi-byte word in the FAT structure */
608 /*-----------------------------------------------------------------------*/
610 static WORD
ld_word ( const BYTE
* ptr
) /* Load a 2-byte little-endian word */
615 rv
= rv
<< 8 | ptr
[ 0 ];
619 static DWORD
ld_dword ( const BYTE
* ptr
) /* Load a 4-byte little-endian word */
624 rv
= rv
<< 8 | ptr
[ 2 ];
625 rv
= rv
<< 8 | ptr
[ 1 ];
626 rv
= rv
<< 8 | ptr
[ 0 ];
631 static QWORD
ld_qword ( const BYTE
* ptr
) /* Load an 8-byte little-endian word */
636 rv
= rv
<< 8 | ptr
[ 6 ];
637 rv
= rv
<< 8 | ptr
[ 5 ];
638 rv
= rv
<< 8 | ptr
[ 4 ];
639 rv
= rv
<< 8 | ptr
[ 3 ];
640 rv
= rv
<< 8 | ptr
[ 2 ];
641 rv
= rv
<< 8 | ptr
[ 1 ];
642 rv
= rv
<< 8 | ptr
[ 0 ];
648 static void st_word ( BYTE
* ptr
, WORD val
) /* Store a 2-byte word in little-endian */
650 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
654 static void st_dword ( BYTE
* ptr
, DWORD val
) /* Store a 4-byte word in little-endian */
656 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
657 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
658 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
663 static void st_qword ( BYTE
* ptr
, QWORD val
) /* Store an 8-byte word in little-endian */
665 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
666 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
667 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
668 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
669 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
670 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
671 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
675 #endif /* !FF_FS_READONLY */
679 /*-----------------------------------------------------------------------*/
680 /* String functions */
681 /*-----------------------------------------------------------------------*/
683 /* Copy memory to memory */
684 static void mem_cpy ( void * dst
, const void * src
, UINT cnt
)
686 BYTE
* d
= ( BYTE
*) dst
;
687 const BYTE
* s
= ( const BYTE
*) src
;
697 /* Fill memory block */
698 static void mem_set ( void * dst
, int val
, UINT cnt
)
700 BYTE
* d
= ( BYTE
*) dst
;
708 /* Compare memory block */
709 static int mem_cmp ( const void * dst
, const void * src
, UINT cnt
) /* ZR:same, NZ:different */
711 const BYTE
* d
= ( const BYTE
*) dst
, * s
= ( const BYTE
*) src
;
716 } while (-- cnt
&& r
== 0 );
722 /* Check if chr is contained in the string */
723 static int chk_chr ( const char * str
, int chr
) /* NZ:contained, ZR:not contained */
725 while (* str
&& * str
!= chr
) str
++;
730 /* Test if the byte is DBC 1st byte */
731 static int dbc_1st ( BYTE c
)
733 #if FF_CODE_PAGE == 0 /* Variable code page */
734 if ( DbcTbl
&& c
>= DbcTbl
[ 0 ]) {
735 if ( c
<= DbcTbl
[ 1 ]) return 1 ; /* 1st byte range 1 */
736 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ; /* 1st byte range 2 */
738 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
739 if ( c
>= DbcTbl
[ 0 ]) {
740 if ( c
<= DbcTbl
[ 1 ]) return 1 ;
741 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ;
743 #else /* SBCS fixed code page */
744 if ( c
!= 0 ) return 0 ; /* Always false */
750 /* Test if the byte is DBC 2nd byte */
751 static int dbc_2nd ( BYTE c
)
753 #if FF_CODE_PAGE == 0 /* Variable code page */
754 if ( DbcTbl
&& c
>= DbcTbl
[ 4 ]) {
755 if ( c
<= DbcTbl
[ 5 ]) return 1 ; /* 2nd byte range 1 */
756 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ; /* 2nd byte range 2 */
757 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ; /* 2nd byte range 3 */
759 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
760 if ( c
>= DbcTbl
[ 4 ]) {
761 if ( c
<= DbcTbl
[ 5 ]) return 1 ;
762 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ;
763 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ;
765 #else /* SBCS fixed code page */
766 if ( c
!= 0 ) return 0 ; /* Always false */
774 /* Get a Unicode code point from the TCHAR string in defined API encodeing */
775 static DWORD
tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */
776 const TCHAR
** str
/* Pointer to pointer to TCHAR string in configured encoding */
780 const TCHAR
* p
= * str
;
782 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
785 uc
= * p
++; /* Get a unit */
786 if ( IsSurrogate ( uc
)) { /* Surrogate? */
787 wc
= * p
++; /* Get low surrogate */
788 if (! IsSurrogateH ( uc
) || ! IsSurrogateL ( wc
)) return 0xFFFFFFFF ; /* Wrong surrogate? */
792 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
796 uc
= ( BYTE
)* p
++; /* Get an encoding unit */
797 if ( uc
& 0x80 ) { /* Multiple byte code? */
798 if (( uc
& 0xE0 ) == 0xC0 ) { /* 2-byte sequence? */
801 if (( uc
& 0xF0 ) == 0xE0 ) { /* 3-byte sequence? */
804 if (( uc
& 0xF8 ) == 0xF0 ) { /* 4-byte sequence? */
806 } else { /* Wrong sequence */
811 do { /* Get trailing bytes */
813 if (( b
& 0xC0 ) != 0x80 ) return 0xFFFFFFFF ; /* Wrong sequence? */
814 uc
= uc
<< 6 | ( b
& 0x3F );
816 if ( uc
< 0x80 || IsSurrogate ( uc
) || uc
>= 0x110000 ) return 0xFFFFFFFF ; /* Wrong code? */
817 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
820 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
821 uc
= ( TCHAR
)* p
++; /* Get a unit */
822 if ( uc
>= 0x110000 || IsSurrogate ( uc
)) return 0xFFFFFFFF ; /* Wrong code? */
823 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
825 #else /* ANSI/OEM input */
829 wc
= ( BYTE
)* p
++; /* Get a byte */
830 if ( dbc_1st (( BYTE
) wc
)) { /* Is it a DBC 1st byte? */
831 b
= ( BYTE
)* p
++; /* Get 2nd byte */
832 if (! dbc_2nd ( b
)) return 0xFFFFFFFF ; /* Invalid code? */
833 wc
= ( wc
<< 8 ) + b
; /* Make a DBC */
836 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM ==> Unicode */
837 if ( wc
== 0 ) return 0xFFFFFFFF ; /* Invalid code? */
842 * str
= p
; /* Next read pointer */
847 /* Output a TCHAR string in defined API encoding */
848 static BYTE
put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
849 DWORD chr
, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */
850 TCHAR
* buf
, /* Output buffer */
851 UINT szb
/* Size of the buffer */
854 #if FF_LFN_UNICODE == 1 /* UTF-16 output */
857 hs
= ( WCHAR
)( chr
>> 16 );
859 if ( hs
== 0 ) { /* Single encoding unit? */
860 if ( szb
< 1 || IsSurrogate ( wc
)) return 0 ; /* Buffer overflow or wrong code? */
864 if ( szb
< 2 || ! IsSurrogateH ( hs
) || ! IsSurrogateL ( wc
)) return 0 ; /* Buffer overflow or wrong surrogate? */
869 #elif FF_LFN_UNICODE == 2 /* UTF-8 output */
872 if ( chr
< 0x80 ) { /* Single byte code? */
873 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
877 if ( chr
< 0x800 ) { /* 2-byte sequence? */
878 if ( szb
< 2 ) return 0 ; /* Buffer overflow? */
879 * buf
++ = ( TCHAR
)( 0xC0 | ( chr
>> 6 & 0x1F ));
880 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
883 if ( chr
< 0x10000 ) { /* 3-byte sequence? */
884 if ( szb
< 3 || IsSurrogate ( chr
)) return 0 ; /* Buffer overflow or wrong code? */
885 * buf
++ = ( TCHAR
)( 0xE0 | ( chr
>> 12 & 0x0F ));
886 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
887 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
890 /* 4-byte sequence */
891 if ( szb
< 4 ) return 0 ; /* Buffer overflow? */
892 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
893 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
894 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
895 chr
= ( hc
| chr
) + 0x10000 ;
896 * buf
++ = ( TCHAR
)( 0xF0 | ( chr
>> 18 & 0x07 ));
897 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 12 & 0x3F ));
898 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
899 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
902 #elif FF_LFN_UNICODE == 3 /* UTF-32 output */
905 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
906 if ( chr
>= 0x10000 ) { /* Out of BMP? */
907 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
908 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
909 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
910 chr
= ( hc
| chr
) + 0x10000 ;
915 #else /* ANSI/OEM output */
918 wc
= ff_uni2oem ( chr
, CODEPAGE
);
919 if ( wc
>= 0x100 ) { /* Is this a DBC? */
920 if ( szb
< 2 ) return 0 ;
921 * buf
++ = ( char )( wc
>> 8 ); /* Store DBC 1st byte */
922 * buf
++ = ( TCHAR
) wc
; /* Store DBC 2nd byte */
925 if ( wc
== 0 || szb
< 1 ) return 0 ; /* Invalid char or buffer overflow? */
926 * buf
++ = ( TCHAR
) wc
; /* Store the character */
930 #endif /* FF_USE_LFN */
934 /*-----------------------------------------------------------------------*/
935 /* Request/Release grant to access the volume */
936 /*-----------------------------------------------------------------------*/
937 static int lock_fs ( /* 1:Ok, 0:timeout */
938 FATFS
* fs
/* Filesystem object */
941 return ff_req_grant ( fs
-> sobj
);
945 static void unlock_fs (
946 FATFS
* fs
, /* Filesystem object */
947 FRESULT res
/* Result code to be returned */
950 if ( fs
&& res
!= FR_NOT_ENABLED
&& res
!= FR_INVALID_DRIVE
&& res
!= FR_TIMEOUT
) {
951 ff_rel_grant ( fs
-> sobj
);
960 /*-----------------------------------------------------------------------*/
961 /* File lock control functions */
962 /*-----------------------------------------------------------------------*/
964 static FRESULT
chk_lock ( /* Check if the file can be accessed */
965 DIR * dp
, /* Directory object pointing the file to be checked */
966 int acc
/* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
971 /* Search open object table for the object */
973 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
974 if ( Files
[ i
]. fs
) { /* Existing entry */
975 if ( Files
[ i
]. fs
== dp
-> obj
. fs
&& /* Check if the object matches with an open object */
976 Files
[ i
]. clu
== dp
-> obj
. sclust
&&
977 Files
[ i
]. ofs
== dp
-> dptr
) break ;
978 } else { /* Blank entry */
982 if ( i
== FF_FS_LOCK
) { /* The object has not been opened */
983 return (! be
&& acc
!= 2 ) ? FR_TOO_MANY_OPEN_FILES
: FR_OK
; /* Is there a blank entry for new object? */
986 /* The object was opened. Reject any open against writing file and all write mode open */
987 return ( acc
!= 0 || Files
[ i
]. ctr
== 0x100 ) ? FR_LOCKED
: FR_OK
;
991 static int enq_lock ( void ) /* Check if an entry is available for a new object */
995 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ;
996 return ( i
== FF_FS_LOCK
) ? 0 : 1 ;
1000 static UINT
inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
1001 DIR * dp
, /* Directory object pointing the file to register or increment */
1002 int acc
/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
1008 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) { /* Find the object */
1009 if ( Files
[ i
]. fs
== dp
-> obj
. fs
1010 && Files
[ i
]. clu
== dp
-> obj
. sclust
1011 && Files
[ i
]. ofs
== dp
-> dptr
) break ;
1014 if ( i
== FF_FS_LOCK
) { /* Not opened. Register it as new. */
1015 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ;
1016 if ( i
== FF_FS_LOCK
) return 0 ; /* No free entry to register (int err) */
1017 Files
[ i
]. fs
= dp
-> obj
. fs
;
1018 Files
[ i
]. clu
= dp
-> obj
. sclust
;
1019 Files
[ i
]. ofs
= dp
-> dptr
;
1023 if ( acc
>= 1 && Files
[ i
]. ctr
) return 0 ; /* Access violation (int err) */
1025 Files
[ i
]. ctr
= acc
? 0x100 : Files
[ i
]. ctr
+ 1 ; /* Set semaphore value */
1027 return i
+ 1 ; /* Index number origin from 1 */
1031 static FRESULT
dec_lock ( /* Decrement object open counter */
1032 UINT i
/* Semaphore index (1..) */
1039 if (-- i
< FF_FS_LOCK
) { /* Index number origin from 0 */
1041 if ( n
== 0x100 ) n
= 0 ; /* If write mode open, delete the entry */
1042 if ( n
> 0 ) n
--; /* Decrement read mode open count */
1044 if ( n
== 0 ) Files
[ i
]. fs
= 0 ; /* Delete the entry if open count gets zero */
1047 res
= FR_INT_ERR
; /* Invalid index nunber */
1053 static void clear_lock ( /* Clear lock entries of the volume */
1059 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
1060 if ( Files
[ i
]. fs
== fs
) Files
[ i
]. fs
= 0 ;
1064 #endif /* FF_FS_LOCK != 0 */
1068 /*-----------------------------------------------------------------------*/
1069 /* Move/Flush disk access window in the filesystem object */
1070 /*-----------------------------------------------------------------------*/
1072 static FRESULT
sync_window ( /* Returns FR_OK or FR_DISK_ERR */
1073 FATFS
* fs
/* Filesystem object */
1076 FRESULT res
= FR_OK
;
1079 if ( fs
-> wflag
) { /* Is the disk access window dirty? */
1080 if ( disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 ) == RES_OK
) { /* Write it back into the volume */
1081 fs
-> wflag
= 0 ; /* Clear window dirty flag */
1082 if ( fs
-> winsect
- fs
-> fatbase
< fs
-> fsize
) { /* Is it in the 1st FAT? */
1083 if ( fs
-> n_fats
== 2 ) disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
+ fs
-> fsize
, 1 ); /* Reflect it to 2nd FAT if needed */
1094 static FRESULT
move_window ( /* Returns FR_OK or FR_DISK_ERR */
1095 FATFS
* fs
, /* Filesystem object */
1096 LBA_t sect
/* Sector LBA to make appearance in the fs->win[] */
1099 FRESULT res
= FR_OK
;
1102 if ( sect
!= fs
-> winsect
) { /* Window offset changed? */
1104 res
= sync_window ( fs
); /* Flush the window */
1106 if ( res
== FR_OK
) { /* Fill sector window with new data */
1107 if ( disk_read ( fs
-> pdrv
, fs
-> win
, sect
, 1 ) != RES_OK
) {
1108 sect
= ( LBA_t
) 0 - 1 ; /* Invalidate window if read data is not valid */
1121 /*-----------------------------------------------------------------------*/
1122 /* Synchronize filesystem and data on the storage */
1123 /*-----------------------------------------------------------------------*/
1125 static FRESULT
sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
1126 FATFS
* fs
/* Filesystem object */
1132 res
= sync_window ( fs
);
1134 if ( fs
-> fs_type
== FS_FAT32
&& fs
-> fsi_flag
== 1 ) { /* FAT32: Update FSInfo sector if needed */
1135 /* Create FSInfo structure */
1136 mem_set ( fs
-> win
, 0 , sizeof fs
-> win
);
1137 st_word ( fs
-> win
+ BS_55AA
, 0xAA55 );
1138 st_dword ( fs
-> win
+ FSI_LeadSig
, 0x41615252 );
1139 st_dword ( fs
-> win
+ FSI_StrucSig
, 0x61417272 );
1140 st_dword ( fs
-> win
+ FSI_Free_Count
, fs
-> free_clst
);
1141 st_dword ( fs
-> win
+ FSI_Nxt_Free
, fs
-> last_clst
);
1142 /* Write it into the FSInfo sector */
1143 fs
-> winsect
= fs
-> volbase
+ 1 ;
1144 disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 );
1147 /* Make sure that no pending write process in the lower layer */
1148 if ( disk_ioctl ( fs
-> pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) res
= FR_DISK_ERR
;
1158 /*-----------------------------------------------------------------------*/
1159 /* Get physical sector number from cluster number */
1160 /*-----------------------------------------------------------------------*/
1162 static LBA_t
clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
1163 FATFS
* fs
, /* Filesystem object */
1164 DWORD clst
/* Cluster# to be converted */
1167 clst
-= 2 ; /* Cluster number is origin from 2 */
1168 if ( clst
>= fs
-> n_fatent
- 2 ) return 0 ; /* Is it invalid cluster number? */
1169 return fs
-> database
+ ( LBA_t
) fs
-> csize
* clst
; /* Start sector number of the cluster */
1175 /*-----------------------------------------------------------------------*/
1176 /* FAT access - Read value of a FAT entry */
1177 /*-----------------------------------------------------------------------*/
1179 static DWORD
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
1180 FFOBJID
* obj
, /* Corresponding object */
1181 DWORD clst
/* Cluster number to get the value */
1186 FATFS
* fs
= obj
-> fs
;
1189 if ( clst
< 2 || clst
>= fs
-> n_fatent
) { /* Check if in valid range */
1190 val
= 1 ; /* Internal error */
1193 val
= 0xFFFFFFFF ; /* Default value falls on disk error */
1195 switch ( fs
-> fs_type
) {
1197 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ;
1198 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1199 wc
= fs
-> win
[ bc
++ % SS ( fs
)]; /* Get 1st byte of the entry */
1200 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1201 wc
|= fs
-> win
[ bc
% SS ( fs
)] << 8 ; /* Merge 2nd byte of the entry */
1202 val
= ( clst
& 1 ) ? ( wc
>> 4 ) : ( wc
& 0xFFF ); /* Adjust bit position */
1206 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 ))) != FR_OK
) break ;
1207 val
= ld_word ( fs
-> win
+ clst
* 2 % SS ( fs
)); /* Simple WORD array */
1211 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1212 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x0FFFFFFF ; /* Simple DWORD array but mask out upper 4 bits */
1216 if (( obj
-> objsize
!= 0 && obj
-> sclust
!= 0 ) || obj
-> stat
== 0 ) { /* Object except root dir must have valid data length */
1217 DWORD cofs
= clst
- obj
-> sclust
; /* Offset from start cluster */
1218 DWORD clen
= ( DWORD
)(( LBA_t
)(( obj
-> objsize
- 1 ) / SS ( fs
)) / fs
-> csize
); /* Number of clusters - 1 */
1220 if ( obj
-> stat
== 2 && cofs
<= clen
) { /* Is it a contiguous chain? */
1221 val
= ( cofs
== clen
) ? 0x7FFFFFFF : clst
+ 1 ; /* No data on the FAT, generate the value */
1224 if ( obj
-> stat
== 3 && cofs
< obj
-> n_cont
) { /* Is it in the 1st fragment? */
1225 val
= clst
+ 1 ; /* Generate the value */
1228 if ( obj
-> stat
!= 2 ) { /* Get value from FAT if FAT chain is valid */
1229 if ( obj
-> n_frag
!= 0 ) { /* Is it on the growing edge? */
1230 val
= 0x7FFFFFFF ; /* Generate EOC */
1232 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1233 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x7FFFFFFF ;
1241 val
= 1 ; /* Internal error */
1252 /*-----------------------------------------------------------------------*/
1253 /* FAT access - Change value of a FAT entry */
1254 /*-----------------------------------------------------------------------*/
1256 static FRESULT
put_fat ( /* FR_OK(0):succeeded, !=0:error */
1257 FATFS
* fs
, /* Corresponding filesystem object */
1258 DWORD clst
, /* FAT index number (cluster number) to be changed */
1259 DWORD val
/* New value to be set to the entry */
1264 FRESULT res
= FR_INT_ERR
;
1267 if ( clst
>= 2 && clst
< fs
-> n_fatent
) { /* Check if in valid range */
1268 switch ( fs
-> fs_type
) {
1270 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ; /* bc: byte offset of the entry */
1271 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1272 if ( res
!= FR_OK
) break ;
1273 p
= fs
-> win
+ bc
++ % SS ( fs
);
1274 * p
= ( clst
& 1 ) ? ((* p
& 0x0F ) | (( BYTE
) val
<< 4 )) : ( BYTE
) val
; /* Update 1st byte */
1276 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1277 if ( res
!= FR_OK
) break ;
1278 p
= fs
-> win
+ bc
% SS ( fs
);
1279 * p
= ( clst
& 1 ) ? ( BYTE
)( val
>> 4 ) : ((* p
& 0xF0 ) | (( BYTE
)( val
>> 8 ) & 0x0F )); /* Update 2nd byte */
1284 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 )));
1285 if ( res
!= FR_OK
) break ;
1286 st_word ( fs
-> win
+ clst
* 2 % SS ( fs
), ( WORD
) val
); /* Simple WORD array */
1294 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 )));
1295 if ( res
!= FR_OK
) break ;
1296 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1297 val
= ( val
& 0x0FFFFFFF ) | ( ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0xF0000000 );
1299 st_dword ( fs
-> win
+ clst
* 4 % SS ( fs
), val
);
1307 #endif /* !FF_FS_READONLY */
1312 #if FF_FS_EXFAT && !FF_FS_READONLY
1313 /*-----------------------------------------------------------------------*/
1314 /* exFAT: Accessing FAT and Allocation Bitmap */
1315 /*-----------------------------------------------------------------------*/
1317 /*--------------------------------------*/
1318 /* Find a contiguous free cluster block */
1319 /*--------------------------------------*/
1321 static DWORD
find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
1322 FATFS
* fs
, /* Filesystem object */
1323 DWORD clst
, /* Cluster number to scan from */
1324 DWORD ncl
/* Number of contiguous clusters to find (1..) */
1329 DWORD val
, scl
, ctr
;
1332 clst
-= 2 ; /* The first bit in the bitmap corresponds to cluster #2 */
1333 if ( clst
>= fs
-> n_fatent
- 2 ) clst
= 0 ;
1334 scl
= val
= clst
; ctr
= 0 ;
1336 if ( move_window ( fs
, fs
-> bitbase
+ val
/ 8 / SS ( fs
)) != FR_OK
) return 0xFFFFFFFF ;
1337 i
= val
/ 8 % SS ( fs
); bm
= 1 << ( val
% 8 );
1340 bv
= fs
-> win
[ i
] & bm
; bm
<<= 1 ; /* Get bit value */
1341 if (++ val
>= fs
-> n_fatent
- 2 ) { /* Next cluster (with wrap-around) */
1342 val
= 0 ; bm
= 0 ; i
= SS ( fs
);
1344 if ( bv
== 0 ) { /* Is it a free cluster? */
1345 if (++ ctr
== ncl
) return scl
+ 2 ; /* Check if run length is sufficient for required */
1347 scl
= val
; ctr
= 0 ; /* Encountered a cluster in-use, restart to scan */
1349 if ( val
== clst
) return 0 ; /* All cluster scanned? */
1352 } while (++ i
< SS ( fs
));
1357 /*----------------------------------------*/
1358 /* Set/Clear a block of allocation bitmap */
1359 /*----------------------------------------*/
1361 static FRESULT
change_bitmap (
1362 FATFS
* fs
, /* Filesystem object */
1363 DWORD clst
, /* Cluster number to change from */
1364 DWORD ncl
, /* Number of clusters to be changed */
1365 int bv
/* bit value to be set (0 or 1) */
1373 clst
-= 2 ; /* The first bit corresponds to cluster #2 */
1374 sect
= fs
-> bitbase
+ clst
/ 8 / SS ( fs
); /* Sector address */
1375 i
= clst
/ 8 % SS ( fs
); /* Byte offset in the sector */
1376 bm
= 1 << ( clst
% 8 ); /* Bit mask in the byte */
1378 if ( move_window ( fs
, sect
++) != FR_OK
) return FR_DISK_ERR
;
1381 if ( bv
== ( int )(( fs
-> win
[ i
] & bm
) != 0 )) return FR_INT_ERR
; /* Is the bit expected value? */
1382 fs
-> win
[ i
] ^= bm
; /* Flip the bit */
1384 if (-- ncl
== 0 ) return FR_OK
; /* All bits processed? */
1385 } while ( bm
<<= 1 ); /* Next bit */
1387 } while (++ i
< SS ( fs
)); /* Next byte */
1393 /*---------------------------------------------*/
1394 /* Fill the first fragment of the FAT chain */
1395 /*---------------------------------------------*/
1397 static FRESULT
fill_first_frag (
1398 FFOBJID
* obj
/* Pointer to the corresponding object */
1405 if ( obj
-> stat
== 3 ) { /* Has the object been changed 'fragmented' in this session? */
1406 for ( cl
= obj
-> sclust
, n
= obj
-> n_cont
; n
; cl
++, n
--) { /* Create cluster chain on the FAT */
1407 res
= put_fat ( obj
-> fs
, cl
, cl
+ 1 );
1408 if ( res
!= FR_OK
) return res
;
1410 obj
-> stat
= 0 ; /* Change status 'FAT chain is valid' */
1416 /*---------------------------------------------*/
1417 /* Fill the last fragment of the FAT chain */
1418 /*---------------------------------------------*/
1420 static FRESULT
fill_last_frag (
1421 FFOBJID
* obj
, /* Pointer to the corresponding object */
1422 DWORD lcl
, /* Last cluster of the fragment */
1423 DWORD term
/* Value to set the last FAT entry */
1429 while ( obj
-> n_frag
> 0 ) { /* Create the chain of last fragment */
1430 res
= put_fat ( obj
-> fs
, lcl
- obj
-> n_frag
+ 1 , ( obj
-> n_frag
> 1 ) ? lcl
- obj
-> n_frag
+ 2 : term
);
1431 if ( res
!= FR_OK
) return res
;
1437 #endif /* FF_FS_EXFAT && !FF_FS_READONLY */
1442 /*-----------------------------------------------------------------------*/
1443 /* FAT handling - Remove a cluster chain */
1444 /*-----------------------------------------------------------------------*/
1446 static FRESULT
remove_chain ( /* FR_OK(0):succeeded, !=0:error */
1447 FFOBJID
* obj
, /* Corresponding object */
1448 DWORD clst
, /* Cluster to remove a chain from */
1449 DWORD pclst
/* Previous cluster of clst (0 if entire chain) */
1452 FRESULT res
= FR_OK
;
1454 FATFS
* fs
= obj
-> fs
;
1455 #if FF_FS_EXFAT || FF_USE_TRIM
1456 DWORD scl
= clst
, ecl
= clst
;
1462 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Check if in valid range */
1464 /* Mark the previous cluster 'EOC' on the FAT if it exists */
1465 if ( pclst
!= 0 && (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
|| obj
-> stat
!= 2 )) {
1466 res
= put_fat ( fs
, pclst
, 0xFFFFFFFF );
1467 if ( res
!= FR_OK
) return res
;
1470 /* Remove the chain */
1472 nxt
= get_fat ( obj
, clst
); /* Get cluster status */
1473 if ( nxt
== 0 ) break ; /* Empty cluster? */
1474 if ( nxt
== 1 ) return FR_INT_ERR
; /* Internal error? */
1475 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error? */
1476 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1477 res
= put_fat ( fs
, clst
, 0 ); /* Mark the cluster 'free' on the FAT */
1478 if ( res
!= FR_OK
) return res
;
1480 if ( fs
-> free_clst
< fs
-> n_fatent
- 2 ) { /* Update FSINFO */
1484 #if FF_FS_EXFAT || FF_USE_TRIM
1485 if ( ecl
+ 1 == nxt
) { /* Is next cluster contiguous? */
1487 } else { /* End of contiguous cluster block */
1489 if ( fs
-> fs_type
== FS_EXFAT
) {
1490 res
= change_bitmap ( fs
, scl
, ecl
- scl
+ 1 , 0 ); /* Mark the cluster block 'free' on the bitmap */
1491 if ( res
!= FR_OK
) return res
;
1495 rt
[ 0 ] = clst2sect ( fs
, scl
); /* Start of data area to be freed */
1496 rt
[ 1 ] = clst2sect ( fs
, ecl
) + fs
-> csize
- 1 ; /* End of data area to be freed */
1497 disk_ioctl ( fs
-> pdrv
, CTRL_TRIM
, rt
); /* Inform storage device that the data in the block may be erased */
1502 clst
= nxt
; /* Next cluster */
1503 } while ( clst
< fs
-> n_fatent
); /* Repeat while not the last link */
1506 /* Some post processes for chain status */
1507 if ( fs
-> fs_type
== FS_EXFAT
) {
1508 if ( pclst
== 0 ) { /* Has the entire chain been removed? */
1509 obj
-> stat
= 0 ; /* Change the chain status 'initial' */
1511 if ( obj
-> stat
== 0 ) { /* Is it a fragmented chain from the beginning of this session? */
1512 clst
= obj
-> sclust
; /* Follow the chain to check if it gets contiguous */
1513 while ( clst
!= pclst
) {
1514 nxt
= get_fat ( obj
, clst
);
1515 if ( nxt
< 2 ) return FR_INT_ERR
;
1516 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
;
1517 if ( nxt
!= clst
+ 1 ) break ; /* Not contiguous? */
1520 if ( clst
== pclst
) { /* Has the chain got contiguous again? */
1521 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1524 if ( obj
-> stat
== 3 && pclst
>= obj
-> sclust
&& pclst
<= obj
-> sclust
+ obj
-> n_cont
) { /* Was the chain fragmented in this session and got contiguous again? */
1525 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1537 /*-----------------------------------------------------------------------*/
1538 /* FAT handling - Stretch a chain or Create a new chain */
1539 /*-----------------------------------------------------------------------*/
1541 static DWORD
create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
1542 FFOBJID
* obj
, /* Corresponding object */
1543 DWORD clst
/* Cluster# to stretch, 0:Create a new chain */
1548 FATFS
* fs
= obj
-> fs
;
1551 if ( clst
== 0 ) { /* Create a new chain */
1552 scl
= fs
-> last_clst
; /* Suggested cluster to start to find */
1553 if ( scl
== 0 || scl
>= fs
-> n_fatent
) scl
= 1 ;
1555 else { /* Stretch a chain */
1556 cs
= get_fat ( obj
, clst
); /* Check the cluster status */
1557 if ( cs
< 2 ) return 1 ; /* Test for insanity */
1558 if ( cs
== 0xFFFFFFFF ) return cs
; /* Test for disk error */
1559 if ( cs
< fs
-> n_fatent
) return cs
; /* It is already followed by next cluster */
1560 scl
= clst
; /* Cluster to start to find */
1562 if ( fs
-> free_clst
== 0 ) return 0 ; /* No free cluster */
1565 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
1566 ncl
= find_bitmap ( fs
, scl
, 1 ); /* Find a free cluster */
1567 if ( ncl
== 0 || ncl
== 0xFFFFFFFF ) return ncl
; /* No free cluster or hard error? */
1568 res
= change_bitmap ( fs
, ncl
, 1 , 1 ); /* Mark the cluster 'in use' */
1569 if ( res
== FR_INT_ERR
) return 1 ;
1570 if ( res
== FR_DISK_ERR
) return 0xFFFFFFFF ;
1571 if ( clst
== 0 ) { /* Is it a new chain? */
1572 obj
-> stat
= 2 ; /* Set status 'contiguous' */
1573 } else { /* It is a stretched chain */
1574 if ( obj
-> stat
== 2 && ncl
!= scl
+ 1 ) { /* Is the chain got fragmented? */
1575 obj
-> n_cont
= scl
- obj
-> sclust
; /* Set size of the contiguous part */
1576 obj
-> stat
= 3 ; /* Change status 'just fragmented' */
1579 if ( obj
-> stat
!= 2 ) { /* Is the file non-contiguous? */
1580 if ( ncl
== clst
+ 1 ) { /* Is the cluster next to previous one? */
1581 obj
-> n_frag
= obj
-> n_frag
? obj
-> n_frag
+ 1 : 2 ; /* Increment size of last framgent */
1582 } else { /* New fragment */
1583 if ( obj
-> n_frag
== 0 ) obj
-> n_frag
= 1 ;
1584 res
= fill_last_frag ( obj
, clst
, ncl
); /* Fill last fragment on the FAT and link it to new one */
1585 if ( res
== FR_OK
) obj
-> n_frag
= 1 ;
1590 { /* On the FAT/FAT32 volume */
1592 if ( scl
== clst
) { /* Stretching an existing chain? */
1593 ncl
= scl
+ 1 ; /* Test if next cluster is free */
1594 if ( ncl
>= fs
-> n_fatent
) ncl
= 2 ;
1595 cs
= get_fat ( obj
, ncl
); /* Get next cluster status */
1596 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1597 if ( cs
!= 0 ) { /* Not free? */
1598 cs
= fs
-> last_clst
; /* Start at suggested cluster if it is valid */
1599 if ( cs
>= 2 && cs
< fs
-> n_fatent
) scl
= cs
;
1603 if ( ncl
== 0 ) { /* The new cluster cannot be contiguous and find another fragment */
1604 ncl
= scl
; /* Start cluster */
1606 ncl
++; /* Next cluster */
1607 if ( ncl
>= fs
-> n_fatent
) { /* Check wrap-around */
1609 if ( ncl
> scl
) return 0 ; /* No free cluster found? */
1611 cs
= get_fat ( obj
, ncl
); /* Get the cluster status */
1612 if ( cs
== 0 ) break ; /* Found a free cluster? */
1613 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1614 if ( ncl
== scl
) return 0 ; /* No free cluster found? */
1617 res
= put_fat ( fs
, ncl
, 0xFFFFFFFF ); /* Mark the new cluster 'EOC' */
1618 if ( res
== FR_OK
&& clst
!= 0 ) {
1619 res
= put_fat ( fs
, clst
, ncl
); /* Link it from the previous one if needed */
1623 if ( res
== FR_OK
) { /* Update FSINFO if function succeeded. */
1624 fs
-> last_clst
= ncl
;
1625 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) fs
-> free_clst
--;
1628 ncl
= ( res
== FR_DISK_ERR
) ? 0xFFFFFFFF : 1 ; /* Failed. Generate error status */
1631 return ncl
; /* Return new cluster number or error status */
1634 #endif /* !FF_FS_READONLY */
1640 /*-----------------------------------------------------------------------*/
1641 /* FAT handling - Convert offset into cluster with link map table */
1642 /*-----------------------------------------------------------------------*/
1644 static DWORD
clmt_clust ( /* <2:Error, >=2:Cluster number */
1645 FIL
* fp
, /* Pointer to the file object */
1646 FSIZE_t ofs
/* File offset to be converted to cluster# */
1649 DWORD cl
, ncl
, * tbl
;
1650 FATFS
* fs
= fp
-> obj
. fs
;
1653 tbl
= fp
-> cltbl
+ 1 ; /* Top of CLMT */
1654 cl
= ( DWORD
)( ofs
/ SS ( fs
) / fs
-> csize
); /* Cluster order from top of the file */
1656 ncl
= * tbl
++; /* Number of cluters in the fragment */
1657 if ( ncl
== 0 ) return 0 ; /* End of table? (error) */
1658 if ( cl
< ncl
) break ; /* In this fragment? */
1659 cl
-= ncl
; tbl
++; /* Next fragment */
1661 return cl
+ * tbl
; /* Return the cluster number */
1664 #endif /* FF_USE_FASTSEEK */
1669 /*-----------------------------------------------------------------------*/
1670 /* Directory handling - Fill a cluster with zeros */
1671 /*-----------------------------------------------------------------------*/
1674 static FRESULT
dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
1675 FATFS
* fs
, /* Filesystem object */
1676 DWORD clst
/* Directory table to clear */
1684 if ( sync_window ( fs
) != FR_OK
) return FR_DISK_ERR
; /* Flush disk access window */
1685 sect
= clst2sect ( fs
, clst
); /* Top of the cluster */
1686 fs
-> winsect
= sect
; /* Set window to top of the cluster */
1687 mem_set ( fs
-> win
, 0 , sizeof fs
-> win
); /* Clear window buffer */
1688 #if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
1689 /* Allocate a temporary buffer */
1690 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 ) ;
1691 if ( szb
> SS ( fs
)) { /* Buffer allocated? */
1692 mem_set ( ibuf
, 0 , szb
);
1693 szb
/= SS ( fs
); /* Bytes -> Sectors */
1694 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1699 ibuf
= fs
-> win
; szb
= 1 ; /* Use window buffer (many single-sector writes may take a time) */
1700 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1702 return ( n
== fs
-> csize
) ? FR_OK
: FR_DISK_ERR
;
1704 #endif /* !FF_FS_READONLY */
1709 /*-----------------------------------------------------------------------*/
1710 /* Directory handling - Set directory index */
1711 /*-----------------------------------------------------------------------*/
1713 static FRESULT
dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
1714 DIR * dp
, /* Pointer to directory object */
1715 DWORD ofs
/* Offset of directory table */
1719 FATFS
* fs
= dp
-> obj
. fs
;
1722 if ( ofs
>= ( DWORD
)(( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) ? MAX_DIR_EX
: MAX_DIR
) || ofs
% SZDIRE
) { /* Check range of offset and alignment */
1725 dp
-> dptr
= ofs
; /* Set current offset */
1726 clst
= dp
-> obj
. sclust
; /* Table start cluster (0:root) */
1727 if ( clst
== 0 && fs
-> fs_type
>= FS_FAT32
) { /* Replace cluster# 0 with root cluster# */
1728 clst
= ( DWORD
) fs
-> dirbase
;
1729 if ( FF_FS_EXFAT
) dp
-> obj
. stat
= 0 ; /* exFAT: Root dir has an FAT chain */
1732 if ( clst
== 0 ) { /* Static table (root-directory on the FAT volume) */
1733 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) return FR_INT_ERR
; /* Is index out of range? */
1734 dp
-> sect
= fs
-> dirbase
;
1736 } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
1737 csz
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Bytes per cluster */
1738 while ( ofs
>= csz
) { /* Follow cluster chain */
1739 clst
= get_fat (& dp
-> obj
, clst
); /* Get next cluster */
1740 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1741 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Reached to end of table or internal error */
1744 dp
-> sect
= clst2sect ( fs
, clst
);
1746 dp
-> clust
= clst
; /* Current cluster# */
1747 if ( dp
-> sect
== 0 ) return FR_INT_ERR
;
1748 dp
-> sect
+= ofs
/ SS ( fs
); /* Sector# of the directory entry */
1749 dp
-> dir
= fs
-> win
+ ( ofs
% SS ( fs
)); /* Pointer to the entry in the win[] */
1757 /*-----------------------------------------------------------------------*/
1758 /* Directory handling - Move directory table index next */
1759 /*-----------------------------------------------------------------------*/
1761 static FRESULT
dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
1762 DIR * dp
, /* Pointer to the directory object */
1763 int stretch
/* 0: Do not stretch table, 1: Stretch table if needed */
1767 FATFS
* fs
= dp
-> obj
. fs
;
1770 ofs
= dp
-> dptr
+ SZDIRE
; /* Next entry */
1771 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 */
1772 if ( dp
-> sect
== 0 ) return FR_NO_FILE
; /* Report EOT if it has been disabled */
1774 if ( ofs
% SS ( fs
) == 0 ) { /* Sector changed? */
1775 dp
-> sect
++; /* Next sector */
1777 if ( dp
-> clust
== 0 ) { /* Static table */
1778 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) { /* Report EOT if it reached end of static table */
1779 dp
-> sect
= 0 ; return FR_NO_FILE
;
1782 else { /* Dynamic table */
1783 if (( ofs
/ SS ( fs
) & ( fs
-> csize
- 1 )) == 0 ) { /* Cluster changed? */
1784 clst
= get_fat (& dp
-> obj
, dp
-> clust
); /* Get next cluster */
1785 if ( clst
<= 1 ) return FR_INT_ERR
; /* Internal error */
1786 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1787 if ( clst
>= fs
-> n_fatent
) { /* It reached end of dynamic table */
1789 if (! stretch
) { /* If no stretch, report EOT */
1790 dp
-> sect
= 0 ; return FR_NO_FILE
;
1792 clst
= create_chain (& dp
-> obj
, dp
-> clust
); /* Allocate a cluster */
1793 if ( clst
== 0 ) return FR_DENIED
; /* No free cluster */
1794 if ( clst
== 1 ) return FR_INT_ERR
; /* Internal error */
1795 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1796 if ( dir_clear ( fs
, clst
) != FR_OK
) return FR_DISK_ERR
; /* Clean up the stretched table */
1797 if ( FF_FS_EXFAT
) dp
-> obj
. stat
|= 4 ; /* exFAT: The directory has been stretched */
1799 if (! stretch
) dp
-> sect
= 0 ; /* (this line is to suppress compiler warning) */
1800 dp
-> sect
= 0 ; return FR_NO_FILE
; /* Report EOT */
1803 dp
-> clust
= clst
; /* Initialize data for new cluster */
1804 dp
-> sect
= clst2sect ( fs
, clst
);
1808 dp
-> dptr
= ofs
; /* Current entry */
1809 dp
-> dir
= fs
-> win
+ ofs
% SS ( fs
); /* Pointer to the entry in the win[] */
1818 /*-----------------------------------------------------------------------*/
1819 /* Directory handling - Reserve a block of directory entries */
1820 /*-----------------------------------------------------------------------*/
1822 static FRESULT
dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
1823 DIR * dp
, /* Pointer to the directory object */
1824 UINT nent
/* Number of contiguous entries to allocate */
1829 FATFS
* fs
= dp
-> obj
. fs
;
1832 res
= dir_sdi ( dp
, 0 );
1836 res
= move_window ( fs
, dp
-> sect
);
1837 if ( res
!= FR_OK
) break ;
1839 if (( fs
-> fs_type
== FS_EXFAT
) ? ( int )(( dp
-> dir
[ XDIR_Type
] & 0x80 ) == 0 ) : ( int )( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 )) {
1841 if ( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 ) {
1843 if (++ n
== nent
) break ; /* A block of contiguous free entries is found */
1845 n
= 0 ; /* Not a blank entry. Restart to search */
1847 res
= dir_next ( dp
, 1 );
1848 } while ( res
== FR_OK
); /* Next entry with table stretch enabled */
1851 if ( res
== FR_NO_FILE
) res
= FR_DENIED
; /* No directory entry to allocate */
1855 #endif /* !FF_FS_READONLY */
1860 /*-----------------------------------------------------------------------*/
1861 /* FAT: Directory handling - Load/Store start cluster number */
1862 /*-----------------------------------------------------------------------*/
1864 static DWORD
ld_clust ( /* Returns the top cluster value of the SFN entry */
1865 FATFS
* fs
, /* Pointer to the fs object */
1866 const BYTE
* dir
/* Pointer to the key entry */
1871 cl
= ld_word ( dir
+ DIR_FstClusLO
);
1872 if ( fs
-> fs_type
== FS_FAT32
) {
1873 cl
|= ( DWORD
) ld_word ( dir
+ DIR_FstClusHI
) << 16 ;
1881 static void st_clust (
1882 FATFS
* fs
, /* Pointer to the fs object */
1883 BYTE
* dir
, /* Pointer to the key entry */
1884 DWORD cl
/* Value to be set */
1887 st_word ( dir
+ DIR_FstClusLO
, ( WORD
) cl
);
1888 if ( fs
-> fs_type
== FS_FAT32
) {
1889 st_word ( dir
+ DIR_FstClusHI
, ( WORD
)( cl
>> 16 ));
1897 /*--------------------------------------------------------*/
1898 /* FAT-LFN: Compare a part of file name with an LFN entry */
1899 /*--------------------------------------------------------*/
1901 static int cmp_lfn ( /* 1:matched, 0:not matched */
1902 const WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer to be compared */
1903 BYTE
* dir
/* Pointer to the directory entry containing the part of LFN */
1910 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO */
1912 i
= (( dir
[ LDIR_Ord
] & 0x3F ) - 1 ) * 13 ; /* Offset in the LFN buffer */
1914 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1915 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1917 if ( i
>= FF_MAX_LFN
+ 1 || ff_wtoupper ( uc
) != ff_wtoupper ( lfnbuf
[ i
++])) { /* Compare it */
1918 return 0 ; /* Not matched */
1922 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1926 if (( dir
[ LDIR_Ord
] & LLEF
) && wc
&& lfnbuf
[ i
]) return 0 ; /* Last segment matched but different length */
1928 return 1 ; /* The part of LFN matched */
1932 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
1933 /*-----------------------------------------------------*/
1934 /* FAT-LFN: Pick a part of file name from an LFN entry */
1935 /*-----------------------------------------------------*/
1937 static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
1938 WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer */
1939 BYTE
* dir
/* Pointer to the LFN entry */
1946 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO is 0 */
1948 i
= (( dir
[ LDIR_Ord
] & ~ LLEF
) - 1 ) * 13 ; /* Offset in the LFN buffer */
1950 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1951 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1953 if ( i
>= FF_MAX_LFN
+ 1 ) return 0 ; /* Buffer overflow? */
1954 lfnbuf
[ i
++] = wc
= uc
; /* Store it */
1956 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1960 if ( dir
[ LDIR_Ord
] & LLEF
&& wc
!= 0 ) { /* Put terminator if it is the last LFN part and not terminated */
1961 if ( i
>= FF_MAX_LFN
+ 1 ) return 0 ; /* Buffer overflow? */
1965 return 1 ; /* The part of LFN is valid */
1971 /*-----------------------------------------*/
1972 /* FAT-LFN: Create an entry of LFN entries */
1973 /*-----------------------------------------*/
1975 static void put_lfn (
1976 const WCHAR
* lfn
, /* Pointer to the LFN */
1977 BYTE
* dir
, /* Pointer to the LFN entry to be created */
1978 BYTE ord
, /* LFN order (1-20) */
1979 BYTE sum
/* Checksum of the corresponding SFN */
1986 dir
[ LDIR_Chksum
] = sum
; /* Set checksum */
1987 dir
[ LDIR_Attr
] = AM_LFN
; /* Set attribute. LFN entry */
1989 st_word ( dir
+ LDIR_FstClusLO
, 0 );
1991 i
= ( ord
- 1 ) * 13 ; /* Get offset in the LFN working buffer */
1994 if ( wc
!= 0xFFFF ) wc
= lfn
[ i
++]; /* Get an effective character */
1995 st_word ( dir
+ LfnOfs
[ s
], wc
); /* Put it */
1996 if ( wc
== 0 ) wc
= 0xFFFF ; /* Padding characters for following items */
1998 if ( wc
== 0xFFFF || ! lfn
[ i
]) ord
|= LLEF
; /* Last LFN part is the start of LFN sequence */
1999 dir
[ LDIR_Ord
] = ord
; /* Set the LFN order */
2002 #endif /* !FF_FS_READONLY */
2003 #endif /* FF_USE_LFN */
2007 #if FF_USE_LFN && !FF_FS_READONLY
2008 /*-----------------------------------------------------------------------*/
2009 /* FAT-LFN: Create a Numbered SFN */
2010 /*-----------------------------------------------------------------------*/
2012 static void gen_numname (
2013 BYTE
* dst
, /* Pointer to the buffer to store numbered SFN */
2014 const BYTE
* src
, /* Pointer to SFN */
2015 const WCHAR
* lfn
, /* Pointer to LFN */
2016 UINT seq
/* Sequence number */
2025 mem_cpy ( dst
, src
, 11 );
2027 if ( seq
> 5 ) { /* In case of many collisions, generate a hash number instead of sequential number */
2029 while (* lfn
) { /* Create a CRC as hash value */
2031 for ( i
= 0 ; i
< 16 ; i
++) {
2032 sreg
= ( sreg
<< 1 ) + ( wc
& 1 );
2034 if ( sreg
& 0x10000 ) sreg
^= 0x11021 ;
2040 /* itoa (hexdecimal) */
2043 c
= ( BYTE
)(( seq
% 16 ) + '0' );
2044 if ( c
> '9' ) c
+= 7 ;
2050 /* Append the number to the SFN body */
2051 for ( j
= 0 ; j
< i
&& dst
[ j
] != ' ' ; j
++) {
2052 if ( dbc_1st ( dst
[ j
])) {
2053 if ( j
== i
- 1 ) break ;
2058 dst
[ j
++] = ( i
< 8 ) ? ns
[ i
++] : ' ' ;
2061 #endif /* FF_USE_LFN && !FF_FS_READONLY */
2066 /*-----------------------------------------------------------------------*/
2067 /* FAT-LFN: Calculate checksum of an SFN entry */
2068 /*-----------------------------------------------------------------------*/
2070 static BYTE
sum_sfn (
2071 const BYTE
* dir
/* Pointer to the SFN entry */
2078 sum
= ( sum
>> 1 ) + ( sum
<< 7 ) + * dir
++;
2083 #endif /* FF_USE_LFN */
2088 /*-----------------------------------------------------------------------*/
2089 /* exFAT: Checksum */
2090 /*-----------------------------------------------------------------------*/
2092 static WORD
xdir_sum ( /* Get checksum of the directoly entry block */
2093 const BYTE
* dir
/* Directory entry block to be calculated */
2100 szblk
= ( dir
[ XDIR_NumSec
] + 1 ) * SZDIRE
; /* Number of bytes of the entry block */
2101 for ( i
= sum
= 0 ; i
< szblk
; i
++) {
2102 if ( i
== XDIR_SetSum
) { /* Skip 2-byte sum field */
2105 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + dir
[ i
];
2113 static WORD
xname_sum ( /* Get check sum (to be used as hash) of the file name */
2114 const WCHAR
* name
/* File name to be calculated */
2121 while (( chr
= * name
++) != 0 ) {
2122 chr
= ( WCHAR
) ff_wtoupper ( chr
); /* File name needs to be up-case converted */
2123 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
& 0xFF );
2124 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
>> 8 );
2130 #if !FF_FS_READONLY && FF_USE_MKFS
2131 static DWORD
xsum32 ( /* Returns 32-bit checksum */
2132 BYTE dat
, /* Byte to be calculated (byte-by-byte processing) */
2133 DWORD sum
/* Previous sum value */
2136 sum
= (( sum
& 1 ) ? 0x80000000 : 0 ) + ( sum
>> 1 ) + dat
;
2142 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2143 /*------------------------------------------------------*/
2144 /* exFAT: Get object information from a directory block */
2145 /*------------------------------------------------------*/
2147 static void get_xfileinfo (
2148 BYTE
* dirb
, /* Pointer to the direcotry entry block 85+C0+C1s */
2149 FILINFO
* fno
/* Buffer to store the extracted file information */
2155 /* Get file name from the entry block */
2156 si
= SZDIRE
* 2 ; /* 1st C1 entry */
2157 nc
= 0 ; hs
= 0 ; di
= 0 ;
2158 while ( nc
< dirb
[ XDIR_NumName
]) {
2159 if ( si
>= MAXDIRB ( FF_MAX_LFN
)) { di
= 0 ; break ; } /* Truncated directory block? */
2160 if (( si
% SZDIRE
) == 0 ) si
+= 2 ; /* Skip entry type field */
2161 wc
= ld_word ( dirb
+ si
); si
+= 2 ; nc
++; /* Get a character */
2162 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2163 hs
= wc
; continue ; /* Get low surrogate */
2165 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in API encoding */
2166 if ( wc
== 0 ) { di
= 0 ; break ; } /* Buffer overflow or wrong encoding? */
2170 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2171 if ( di
== 0 ) fno
-> fname
[ di
++] = '?' ; /* Inaccessible object name? */
2172 fno
-> fname
[ di
] = 0 ; /* Terminate the name */
2173 fno
-> altname
[ 0 ] = 0 ; /* exFAT does not support SFN */
2175 fno
-> fattrib
= dirb
[ XDIR_Attr
]; /* Attribute */
2176 fno
-> fsize
= ( fno
-> fattrib
& AM_DIR
) ? 0 : ld_qword ( dirb
+ XDIR_FileSize
); /* Size */
2177 fno
-> ftime
= ld_word ( dirb
+ XDIR_ModTime
+ 0 ); /* Time */
2178 fno
-> fdate
= ld_word ( dirb
+ XDIR_ModTime
+ 2 ); /* Date */
2181 #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2184 /*-----------------------------------*/
2185 /* exFAT: Get a directry entry block */
2186 /*-----------------------------------*/
2188 static FRESULT
load_xdir ( /* FR_INT_ERR: invalid entry block */
2189 DIR * dp
/* Reading direcotry object pointing top of the entry block to load */
2194 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */
2197 /* Load file-directory entry */
2198 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2199 if ( res
!= FR_OK
) return res
;
2200 if ( dp
-> dir
[ XDIR_Type
] != ET_FILEDIR
) return FR_INT_ERR
; /* Invalid order */
2201 mem_cpy ( dirb
+ 0 * SZDIRE
, dp
-> dir
, SZDIRE
);
2202 sz_ent
= ( dirb
[ XDIR_NumSec
] + 1 ) * SZDIRE
;
2203 if ( sz_ent
< 3 * SZDIRE
|| sz_ent
> 19 * SZDIRE
) return FR_INT_ERR
;
2205 /* Load stream-extension entry */
2206 res
= dir_next ( dp
, 0 );
2207 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2208 if ( res
!= FR_OK
) return res
;
2209 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2210 if ( res
!= FR_OK
) return res
;
2211 if ( dp
-> dir
[ XDIR_Type
] != ET_STREAM
) return FR_INT_ERR
; /* Invalid order */
2212 mem_cpy ( dirb
+ 1 * SZDIRE
, dp
-> dir
, SZDIRE
);
2213 if ( MAXDIRB ( dirb
[ XDIR_NumName
]) > sz_ent
) return FR_INT_ERR
;
2215 /* Load file-name entries */
2216 i
= 2 * SZDIRE
; /* Name offset to load */
2218 res
= dir_next ( dp
, 0 );
2219 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2220 if ( res
!= FR_OK
) return res
;
2221 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2222 if ( res
!= FR_OK
) return res
;
2223 if ( dp
-> dir
[ XDIR_Type
] != ET_FILENAME
) return FR_INT_ERR
; /* Invalid order */
2224 if ( i
< MAXDIRB ( FF_MAX_LFN
)) mem_cpy ( dirb
+ i
, dp
-> dir
, SZDIRE
);
2225 } while (( i
+= SZDIRE
) < sz_ent
);
2227 /* Sanity check (do it for only accessible object) */
2228 if ( i
<= MAXDIRB ( FF_MAX_LFN
)) {
2229 if ( xdir_sum ( dirb
) != ld_word ( dirb
+ XDIR_SetSum
)) return FR_INT_ERR
;
2235 /*------------------------------------------------------------------*/
2236 /* exFAT: Initialize object allocation info with loaded entry block */
2237 /*------------------------------------------------------------------*/
2239 static void init_alloc_info (
2240 FATFS
* fs
, /* Filesystem object */
2241 FFOBJID
* obj
/* Object allocation information to be initialized */
2244 obj
-> sclust
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Start cluster */
2245 obj
-> objsize
= ld_qword ( fs
-> dirbuf
+ XDIR_FileSize
); /* Size */
2246 obj
-> stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ; /* Allocation status */
2247 obj
-> n_frag
= 0 ; /* No last fragment info */
2252 #if !FF_FS_READONLY || FF_FS_RPATH != 0
2253 /*------------------------------------------------*/
2254 /* exFAT: Load the object's directory entry block */
2255 /*------------------------------------------------*/
2257 static FRESULT
load_obj_xdir (
2258 DIR * dp
, /* Blank directory object to be used to access containing direcotry */
2259 const FFOBJID
* obj
/* Object with its containing directory information */
2264 /* Open object containing directory */
2265 dp
-> obj
. fs
= obj
-> fs
;
2266 dp
-> obj
. sclust
= obj
-> c_scl
;
2267 dp
-> obj
. stat
= ( BYTE
) obj
-> c_size
;
2268 dp
-> obj
. objsize
= obj
-> c_size
& 0xFFFFFF00 ;
2270 dp
-> blk_ofs
= obj
-> c_ofs
;
2272 res
= dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto object's entry block */
2274 res
= load_xdir ( dp
); /* Load the object's entry block */
2282 /*----------------------------------------*/
2283 /* exFAT: Store the directory entry block */
2284 /*----------------------------------------*/
2286 static FRESULT
store_xdir (
2287 DIR * dp
/* Pointer to the direcotry object */
2292 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the direcotry entry block 85+C0+C1s */
2294 /* Create set sum */
2295 st_word ( dirb
+ XDIR_SetSum
, xdir_sum ( dirb
));
2296 nent
= dirb
[ XDIR_NumSec
] + 1 ;
2298 /* Store the direcotry entry block to the directory */
2299 res
= dir_sdi ( dp
, dp
-> blk_ofs
);
2300 while ( res
== FR_OK
) {
2301 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2302 if ( res
!= FR_OK
) break ;
2303 mem_cpy ( dp
-> dir
, dirb
, SZDIRE
);
2304 dp
-> obj
. fs
-> wflag
= 1 ;
2305 if (-- nent
== 0 ) break ;
2307 res
= dir_next ( dp
, 0 );
2309 return ( res
== FR_OK
|| res
== FR_DISK_ERR
) ? res
: FR_INT_ERR
;
2314 /*-------------------------------------------*/
2315 /* exFAT: Create a new directory enrty block */
2316 /*-------------------------------------------*/
2318 static void create_xdir (
2319 BYTE
* dirb
, /* Pointer to the direcotry entry block buffer */
2320 const WCHAR
* lfn
/* Pointer to the object name */
2328 /* Create file-directory and stream-extension entry */
2329 mem_set ( dirb
, 0 , 2 * SZDIRE
);
2330 dirb
[ 0 * SZDIRE
+ XDIR_Type
] = ET_FILEDIR
;
2331 dirb
[ 1 * SZDIRE
+ XDIR_Type
] = ET_STREAM
;
2333 /* Create file-name entries */
2334 i
= SZDIRE
* 2 ; /* Top of file_name entries */
2335 nlen
= nc1
= 0 ; wc
= 1 ;
2337 dirb
[ i
++] = ET_FILENAME
; dirb
[ i
++] = 0 ;
2338 do { /* Fill name field */
2339 if ( wc
!= 0 && ( wc
= lfn
[ nlen
]) != 0 ) nlen
++; /* Get a character if exist */
2340 st_word ( dirb
+ i
, wc
); /* Store it */
2342 } while ( i
% SZDIRE
!= 0 );
2344 } while ( lfn
[ nlen
]); /* Fill next entry if any char follows */
2346 dirb
[ XDIR_NumName
] = nlen
; /* Set name length */
2347 dirb
[ XDIR_NumSec
] = 1 + nc1
; /* Set secondary count (C0 + C1s) */
2348 st_word ( dirb
+ XDIR_NameHash
, xname_sum ( lfn
)); /* Set name hash */
2351 #endif /* !FF_FS_READONLY */
2352 #endif /* FF_FS_EXFAT */
2356 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
2357 /*-----------------------------------------------------------------------*/
2358 /* Read an object from the directory */
2359 /*-----------------------------------------------------------------------*/
2361 #define DIR_READ_FILE(dp) dir_read(dp, 0)
2362 #define DIR_READ_LABEL(dp) dir_read(dp, 1)
2364 static FRESULT
dir_read (
2365 DIR * dp
, /* Pointer to the directory object */
2366 int vol
/* Filtered by 0:file/directory or 1:volume label */
2369 FRESULT res
= FR_NO_FILE
;
2370 FATFS
* fs
= dp
-> obj
. fs
;
2373 BYTE ord
= 0xFF , sum
= 0xFF ;
2377 res
= move_window ( fs
, dp
-> sect
);
2378 if ( res
!= FR_OK
) break ;
2379 b
= dp
-> dir
[ DIR_Name
]; /* Test for the entry type */
2381 res
= FR_NO_FILE
; break ; /* Reached to end of the directory */
2384 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2385 if ( FF_USE_LABEL
&& vol
) {
2386 if ( b
== ET_VLABEL
) break ; /* Volume label entry? */
2388 if ( b
== ET_FILEDIR
) { /* Start of the file entry block? */
2389 dp
-> blk_ofs
= dp
-> dptr
; /* Get location of the block */
2390 res
= load_xdir ( dp
); /* Load the entry block */
2392 dp
-> obj
. attr
= fs
-> dirbuf
[ XDIR_Attr
] & AM_MASK
; /* Get attribute */
2399 { /* On the FAT/FAT32 volume */
2400 dp
-> obj
. attr
= attr
= dp
-> dir
[ DIR_Attr
] & AM_MASK
; /* Get attribute */
2401 #if FF_USE_LFN /* LFN configuration */
2402 if ( b
== DDEM
|| b
== '.' || ( int )(( attr
& ~ AM_ARC
) == AM_VOL
) != vol
) { /* An entry without valid data */
2405 if ( attr
== AM_LFN
) { /* An LFN entry is found */
2406 if ( b
& LLEF
) { /* Is it start of an LFN sequence? */
2407 sum
= dp
-> dir
[ LDIR_Chksum
];
2408 b
&= ( BYTE
)~ LLEF
; ord
= b
;
2409 dp
-> blk_ofs
= dp
-> dptr
;
2411 /* Check LFN validity and capture it */
2412 ord
= ( b
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && pick_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2413 } else { /* An SFN entry is found */
2414 if ( ord
!= 0 || sum
!= sum_sfn ( dp
-> dir
)) { /* Is there a valid LFN? */
2415 dp
-> blk_ofs
= 0xFFFFFFFF ; /* It has no LFN. */
2420 #else /* Non LFN configuration */
2421 if ( b
!= DDEM
&& b
!= '.' && attr
!= AM_LFN
&& ( int )(( attr
& ~ AM_ARC
) == AM_VOL
) == vol
) { /* Is it a valid entry? */
2426 res
= dir_next ( dp
, 0 ); /* Next entry */
2427 if ( res
!= FR_OK
) break ;
2430 if ( res
!= FR_OK
) dp
-> sect
= 0 ; /* Terminate the read operation on error or EOT */
2434 #endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
2438 /*-----------------------------------------------------------------------*/
2439 /* Directory handling - Find an object in the directory */
2440 /*-----------------------------------------------------------------------*/
2442 static FRESULT
dir_find ( /* FR_OK(0):succeeded, !=0:error */
2443 DIR * dp
/* Pointer to the directory object with the file name */
2447 FATFS
* fs
= dp
-> obj
. fs
;
2453 res
= dir_sdi ( dp
, 0 ); /* Rewind directory object */
2454 if ( res
!= FR_OK
) return res
;
2456 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2459 WORD hash
= xname_sum ( fs
-> lfnbuf
); /* Hash value of the name to find */
2461 while (( res
= DIR_READ_FILE ( dp
)) == FR_OK
) { /* Read an item */
2462 #if FF_MAX_LFN < 255
2463 if ( fs
-> dirbuf
[ XDIR_NumName
] > FF_MAX_LFN
) continue ; /* Skip comparison if inaccessible object name */
2465 if ( ld_word ( fs
-> dirbuf
+ XDIR_NameHash
) != hash
) continue ; /* Skip comparison if hash mismatched */
2466 for ( nc
= fs
-> dirbuf
[ XDIR_NumName
], di
= SZDIRE
* 2 , ni
= 0 ; nc
; nc
--, di
+= 2 , ni
++) { /* Compare the name */
2467 if (( di
% SZDIRE
) == 0 ) di
+= 2 ;
2468 if ( ff_wtoupper ( ld_word ( fs
-> dirbuf
+ di
)) != ff_wtoupper ( fs
-> lfnbuf
[ ni
])) break ;
2470 if ( nc
== 0 && ! fs
-> lfnbuf
[ ni
]) break ; /* Name matched? */
2475 /* On the FAT/FAT32 volume */
2477 ord
= sum
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2480 res
= move_window ( fs
, dp
-> sect
);
2481 if ( res
!= FR_OK
) break ;
2482 c
= dp
-> dir
[ DIR_Name
];
2483 if ( c
== 0 ) { res
= FR_NO_FILE
; break ; } /* Reached to end of table */
2484 #if FF_USE_LFN /* LFN configuration */
2485 dp
-> obj
. attr
= a
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2486 if ( c
== DDEM
|| (( a
& AM_VOL
) && a
!= AM_LFN
)) { /* An entry without valid data */
2487 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2489 if ( a
== AM_LFN
) { /* An LFN entry is found */
2490 if (!( dp
-> fn
[ NSFLAG
] & NS_NOLFN
)) {
2491 if ( c
& LLEF
) { /* Is it start of LFN sequence? */
2492 sum
= dp
-> dir
[ LDIR_Chksum
];
2493 c
&= ( BYTE
)~ LLEF
; ord
= c
; /* LFN start order */
2494 dp
-> blk_ofs
= dp
-> dptr
; /* Start offset of LFN */
2496 /* Check validity of the LFN entry and compare it with given name */
2497 ord
= ( c
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && cmp_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2499 } else { /* An SFN entry is found */
2500 if ( ord
== 0 && sum
== sum_sfn ( dp
-> dir
)) break ; /* LFN matched? */
2501 if (!( dp
-> fn
[ NSFLAG
] & NS_LOSS
) && ! mem_cmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* SFN matched? */
2502 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2505 #else /* Non LFN configuration */
2506 dp
-> obj
. attr
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2507 if (!( dp
-> dir
[ DIR_Attr
] & AM_VOL
) && ! mem_cmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* Is it a valid entry? */
2509 res
= dir_next ( dp
, 0 ); /* Next entry */
2510 } while ( res
== FR_OK
);
2519 /*-----------------------------------------------------------------------*/
2520 /* Register an object to the directory */
2521 /*-----------------------------------------------------------------------*/
2523 static FRESULT
dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
2524 DIR * dp
/* Target directory with object name to be created */
2528 FATFS
* fs
= dp
-> obj
. fs
;
2529 #if FF_USE_LFN /* LFN configuration */
2534 if ( dp
-> fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
)) return FR_INVALID_NAME
; /* Check name validity */
2535 for ( nlen
= 0 ; fs
-> lfnbuf
[ nlen
]; nlen
++) ; /* Get lfn length */
2538 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2539 nent
= ( nlen
+ 14 ) / 15 + 2 ; /* Number of entries to allocate (85+C0+C1s) */
2540 res
= dir_alloc ( dp
, nent
); /* Allocate directory entries */
2541 if ( res
!= FR_OK
) return res
;
2542 dp
-> blk_ofs
= dp
-> dptr
- SZDIRE
* ( nent
- 1 ); /* Set the allocated entry block offset */
2544 if ( dp
-> obj
. stat
& 4 ) { /* Has the directory been stretched by new allocation? */
2546 res
= fill_first_frag (& dp
-> obj
); /* Fill the first fragment on the FAT if needed */
2547 if ( res
!= FR_OK
) return res
;
2548 res
= fill_last_frag (& dp
-> obj
, dp
-> clust
, 0xFFFFFFFF ); /* Fill the last fragment on the FAT if needed */
2549 if ( res
!= FR_OK
) return res
;
2550 if ( dp
-> obj
. sclust
!= 0 ) { /* Is it a sub-directory? */
2553 res
= load_obj_xdir (& dj
, & dp
-> obj
); /* Load the object status */
2554 if ( res
!= FR_OK
) return res
;
2555 dp
-> obj
. objsize
+= ( DWORD
) fs
-> csize
* SS ( fs
); /* Increase the directory size by cluster size */
2556 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, dp
-> obj
. objsize
);
2557 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, dp
-> obj
. objsize
);
2558 fs
-> dirbuf
[ XDIR_GenFlags
] = dp
-> obj
. stat
| 1 ; /* Update the allocation status */
2559 res
= store_xdir (& dj
); /* Store the object status */
2560 if ( res
!= FR_OK
) return res
;
2564 create_xdir ( fs
-> dirbuf
, fs
-> lfnbuf
); /* Create on-memory directory block to be written later */
2568 /* On the FAT/FAT32 volume */
2569 mem_cpy ( sn
, dp
-> fn
, 12 );
2570 if ( sn
[ NSFLAG
] & NS_LOSS
) { /* When LFN is out of 8.3 format, generate a numbered name */
2571 dp
-> fn
[ NSFLAG
] = NS_NOLFN
; /* Find only SFN */
2572 for ( n
= 1 ; n
< 100 ; n
++) {
2573 gen_numname ( dp
-> fn
, sn
, fs
-> lfnbuf
, n
); /* Generate a numbered name */
2574 res
= dir_find ( dp
); /* Check if the name collides with existing SFN */
2575 if ( res
!= FR_OK
) break ;
2577 if ( n
== 100 ) return FR_DENIED
; /* Abort if too many collisions */
2578 if ( res
!= FR_NO_FILE
) return res
; /* Abort if the result is other than 'not collided' */
2579 dp
-> fn
[ NSFLAG
] = sn
[ NSFLAG
];
2582 /* Create an SFN with/without LFNs. */
2583 nent
= ( sn
[ NSFLAG
] & NS_LFN
) ? ( nlen
+ 12 ) / 13 + 1 : 1 ; /* Number of entries to allocate */
2584 res
= dir_alloc ( dp
, nent
); /* Allocate entries */
2585 if ( res
== FR_OK
&& -- nent
) { /* Set LFN entry if needed */
2586 res
= dir_sdi ( dp
, dp
-> dptr
- nent
* SZDIRE
);
2588 sum
= sum_sfn ( dp
-> fn
); /* Checksum value of the SFN tied to the LFN */
2589 do { /* Store LFN entries in bottom first */
2590 res
= move_window ( fs
, dp
-> sect
);
2591 if ( res
!= FR_OK
) break ;
2592 put_lfn ( fs
-> lfnbuf
, dp
-> dir
, ( BYTE
) nent
, sum
);
2594 res
= dir_next ( dp
, 0 ); /* Next entry */
2595 } while ( res
== FR_OK
&& -- nent
);
2599 #else /* Non LFN configuration */
2600 res
= dir_alloc ( dp
, 1 ); /* Allocate an entry for SFN */
2606 res
= move_window ( fs
, dp
-> sect
);
2608 mem_set ( dp
-> dir
, 0 , SZDIRE
); /* Clean the entry */
2609 mem_cpy ( dp
-> dir
+ DIR_Name
, dp
-> fn
, 11 ); /* Put SFN */
2611 dp
-> dir
[ DIR_NTres
] = dp
-> fn
[ NSFLAG
] & ( NS_BODY
| NS_EXT
); /* Put NT flag */
2620 #endif /* !FF_FS_READONLY */
2624 #if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
2625 /*-----------------------------------------------------------------------*/
2626 /* Remove an object from the directory */
2627 /*-----------------------------------------------------------------------*/
2629 static FRESULT
dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
2630 DIR * dp
/* Directory object pointing the entry to be removed */
2634 FATFS
* fs
= dp
-> obj
. fs
;
2635 #if FF_USE_LFN /* LFN configuration */
2636 DWORD last
= dp
-> dptr
;
2638 res
= ( dp
-> blk_ofs
== 0xFFFFFFFF ) ? FR_OK
: dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto top of the entry block if LFN is exist */
2641 res
= move_window ( fs
, dp
-> sect
);
2642 if ( res
!= FR_OK
) break ;
2643 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2644 dp
-> dir
[ XDIR_Type
] &= 0x7F ; /* Clear the entry InUse flag. */
2645 } else { /* On the FAT/FAT32 volume */
2646 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'. */
2649 if ( dp
-> dptr
>= last
) break ; /* If reached last entry then all entries of the object has been deleted. */
2650 res
= dir_next ( dp
, 0 ); /* Next entry */
2651 } while ( res
== FR_OK
);
2652 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
;
2654 #else /* Non LFN configuration */
2656 res
= move_window ( fs
, dp
-> sect
);
2658 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'.*/
2666 #endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
2670 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2671 /*-----------------------------------------------------------------------*/
2672 /* Get file information from directory entry */
2673 /*-----------------------------------------------------------------------*/
2675 static void get_fileinfo (
2676 DIR * dp
, /* Pointer to the directory object */
2677 FILINFO
* fno
/* Pointer to the file information to be filled */
2684 FATFS
* fs
= dp
-> obj
. fs
;
2690 fno
-> fname
[ 0 ] = 0 ; /* Invaidate file info */
2691 if ( dp
-> sect
== 0 ) return ; /* Exit if read pointer has reached end of directory */
2693 #if FF_USE_LFN /* LFN configuration */
2695 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2696 get_xfileinfo ( fs
-> dirbuf
, fno
);
2700 { /* On the FAT/FAT32 volume */
2701 if ( dp
-> blk_ofs
!= 0xFFFFFFFF ) { /* Get LFN if available */
2703 while ( fs
-> lfnbuf
[ si
] != 0 ) {
2704 wc
= fs
-> lfnbuf
[ si
++]; /* Get an LFN character (UTF-16) */
2705 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2706 hs
= wc
; continue ; /* Get low surrogate */
2708 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in UTF-16 or UTF-8 encoding */
2709 if ( wc
== 0 ) { di
= 0 ; break ; } /* Invalid char or buffer overflow? */
2713 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2714 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN (null string means LFN is invalid) */
2719 while ( si
< 11 ) { /* Get SFN from SFN entry */
2720 wc
= dp
-> dir
[ si
++]; /* Get a char */
2721 if ( wc
== ' ' ) continue ; /* Skip padding spaces */
2722 if ( wc
== RDDEM
) wc
= DDEM
; /* Restore replaced DDEM character */
2723 if ( si
== 9 && di
< FF_SFN_BUF
) fno
-> altname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2724 #if FF_LFN_UNICODE >= 1 /* Unicode output */
2725 if ( dbc_1st (( BYTE
) wc
) && si
!= 8 && si
!= 11 && dbc_2nd ( dp
-> dir
[ si
])) { /* Make a DBC if needed */
2726 wc
= wc
<< 8 | dp
-> dir
[ si
++];
2728 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM -> Unicode */
2729 if ( wc
== 0 ) { di
= 0 ; break ; } /* Wrong char in the current code page? */
2730 wc
= put_utf ( wc
, & fno
-> altname
[ di
], FF_SFN_BUF
- di
); /* Store it in Unicode */
2731 if ( wc
== 0 ) { di
= 0 ; break ; } /* Buffer overflow? */
2733 #else /* ANSI/OEM output */
2734 fno
-> altname
[ di
++] = ( TCHAR
) wc
; /* Store it without any conversion */
2737 fno
-> altname
[ di
] = 0 ; /* Terminate the SFN (null string means SFN is invalid) */
2739 if ( fno
-> fname
[ 0 ] == 0 ) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
2740 if ( di
== 0 ) { /* If LFN and SFN both are invalid, this object is inaccesible */
2741 fno
-> fname
[ di
++] = '?' ;
2743 for ( si
= di
= 0 , lcf
= NS_BODY
; fno
-> altname
[ si
]; si
++, di
++) { /* Copy altname[] to fname[] with case information */
2744 wc
= ( WCHAR
) fno
-> altname
[ si
];
2745 if ( wc
== '.' ) lcf
= NS_EXT
;
2746 if ( IsUpper ( wc
) && ( dp
-> dir
[ DIR_NTres
] & lcf
)) wc
+= 0x20 ;
2747 fno
-> fname
[ di
] = ( TCHAR
) wc
;
2750 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN */
2751 if (! dp
-> dir
[ DIR_NTres
]) fno
-> altname
[ 0 ] = 0 ; /* Altname is not needed if neither LFN nor case info is exist. */
2754 #else /* Non-LFN configuration */
2756 while ( si
< 11 ) { /* Copy name body and extension */
2757 c
= ( TCHAR
) dp
-> dir
[ si
++];
2758 if ( c
== ' ' ) continue ; /* Skip padding spaces */
2759 if ( c
== RDDEM
) c
= DDEM
; /* Restore replaced DDEM character */
2760 if ( si
== 9 ) fno
-> fname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2761 fno
-> fname
[ di
++] = c
;
2766 fno
-> fattrib
= dp
-> dir
[ DIR_Attr
]; /* Attribute */
2767 fno
-> fsize
= ld_dword ( dp
-> dir
+ DIR_FileSize
); /* Size */
2768 fno
-> ftime
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 0 ); /* Time */
2769 fno
-> fdate
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 2 ); /* Date */
2772 #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2776 #if FF_USE_FIND && FF_FS_MINIMIZE <= 1
2777 /*-----------------------------------------------------------------------*/
2778 /* Pattern matching */
2779 /*-----------------------------------------------------------------------*/
2781 static DWORD
get_achar ( /* Get a character and advances ptr */
2782 const TCHAR
** ptr
/* Pointer to pointer to the ANSI/OEM or Unicode string */
2788 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
2789 chr
= tchar2uni ( ptr
);
2790 if ( chr
== 0xFFFFFFFF ) chr
= 0 ; /* Wrong UTF encoding is recognized as end of the string */
2791 chr
= ff_wtoupper ( chr
);
2793 #else /* ANSI/OEM input */
2794 chr
= ( BYTE
)*(* ptr
)++; /* Get a byte */
2795 if ( IsLower ( chr
)) chr
-= 0x20 ; /* To upper ASCII char */
2796 #if FF_CODE_PAGE == 0
2797 if ( ExCvt
&& chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2798 #elif FF_CODE_PAGE < 900
2799 if ( chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2801 #if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
2802 if ( dbc_1st (( BYTE
) chr
)) { /* Get DBC 2nd byte if needed */
2803 chr
= dbc_2nd (( BYTE
)** ptr
) ? chr
<< 8 | ( BYTE
)*(* ptr
)++ : 0 ;
2812 static int pattern_matching ( /* 0:not matched, 1:matched */
2813 const TCHAR
* pat
, /* Matching pattern */
2814 const TCHAR
* nam
, /* String to be tested */
2815 int skip
, /* Number of pre-skip chars (number of ?s) */
2816 int inf
/* Infinite search (* specified) */
2819 const TCHAR
* pp
, * np
;
2824 while ( skip
--) { /* Pre-skip name chars */
2825 if (! get_achar (& nam
)) return 0 ; /* Branch mismatched if less name chars */
2827 if (* pat
== 0 && inf
) return 1 ; /* (short circuit) */
2830 pp
= pat
; np
= nam
; /* Top of pattern and name to match */
2832 if (* pp
== '?' || * pp
== '*' ) { /* Wildcard? */
2834 do { /* Analyze the wildcard block */
2835 if (* pp
++ == '?' ) nm
++; else nx
= 1 ;
2836 } while (* pp
== '?' || * pp
== '*' );
2837 if ( pattern_matching ( pp
, np
, nm
, nx
)) return 1 ; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
2838 nc
= * np
; break ; /* Branch mismatched */
2840 pc
= get_achar (& pp
); /* Get a pattern char */
2841 nc
= get_achar (& np
); /* Get a name char */
2842 if ( pc
!= nc
) break ; /* Branch mismatched? */
2843 if ( pc
== 0 ) return 1 ; /* Branch matched? (matched at end of both strings) */
2845 get_achar (& nam
); /* nam++ */
2846 } while ( inf
&& nc
); /* Retry until end of name if infinite search is specified */
2851 #endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
2855 /*-----------------------------------------------------------------------*/
2856 /* Pick a top segment and create the object name in directory form */
2857 /*-----------------------------------------------------------------------*/
2859 static FRESULT
create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
2860 DIR * dp
, /* Pointer to the directory object */
2861 const TCHAR
** path
/* Pointer to pointer to the segment in the path string */
2864 #if FF_USE_LFN /* LFN configuration */
2872 /* Create LFN into LFN working buffer */
2873 p
= * path
; lfn
= dp
-> obj
. fs
-> lfnbuf
; di
= 0 ;
2875 uc
= tchar2uni (& p
); /* Get a character */
2876 if ( uc
== 0xFFFFFFFF ) return FR_INVALID_NAME
; /* Invalid code or UTF decode error */
2877 if ( uc
>= 0x10000 ) lfn
[ di
++] = ( WCHAR
)( uc
>> 16 ); /* Store high surrogate if needed */
2879 if ( wc
< ' ' || wc
== '/' || wc
== ' \\ ' ) break ; /* Break if end of the path or a separator is found */
2880 if ( wc
< 0x80 && chk_chr ( " \" *:<> \? | \x7F " , wc
)) return FR_INVALID_NAME
; /* Reject illegal characters for LFN */
2881 if ( di
>= FF_MAX_LFN
) return FR_INVALID_NAME
; /* Reject too long name */
2882 lfn
[ di
++] = wc
; /* Store the Unicode character */
2884 if ( wc
< ' ' ) { /* End of path? */
2885 cf
= NS_LAST
; /* Set last segment flag */
2887 cf
= 0 ; /* Next segment follows */
2888 while (* p
== '/' || * p
== ' \\ ' ) p
++; /* Skip duplicated separators if exist */
2890 * path
= p
; /* Return pointer to the next segment */
2892 #if FF_FS_RPATH != 0
2893 if (( di
== 1 && lfn
[ di
- 1 ] == '.' ) ||
2894 ( di
== 2 && lfn
[ di
- 1 ] == '.' && lfn
[ di
- 2 ] == '.' )) { /* Is this segment a dot name? */
2896 for ( i
= 0 ; i
< 11 ; i
++) { /* Create dot name for SFN entry */
2897 dp
-> fn
[ i
] = ( i
< di
) ? '.' : ' ' ;
2899 dp
-> fn
[ i
] = cf
| NS_DOT
; /* This is a dot entry */
2903 while ( di
) { /* Snip off trailing spaces and dots if exist */
2905 if ( wc
!= ' ' && wc
!= '.' ) break ;
2908 lfn
[ di
] = 0 ; /* LFN is created into the working buffer */
2909 if ( di
== 0 ) return FR_INVALID_NAME
; /* Reject null name */
2911 /* Create SFN in directory form */
2912 for ( si
= 0 ; lfn
[ si
] == ' ' ; si
++) ; /* Remove leading spaces */
2913 if ( si
> 0 || lfn
[ si
] == '.' ) cf
|= NS_LOSS
| NS_LFN
; /* Is there any leading space or dot? */
2914 while ( di
> 0 && lfn
[ di
- 1 ] != '.' ) di
--; /* Find last dot (di<=si: no extension) */
2916 mem_set ( dp
-> fn
, ' ' , 11 );
2919 wc
= lfn
[ si
++]; /* Get an LFN character */
2920 if ( wc
== 0 ) break ; /* Break on end of the LFN */
2921 if ( wc
== ' ' || ( wc
== '.' && si
!= di
)) { /* Remove embedded spaces and dots */
2922 cf
|= NS_LOSS
| NS_LFN
;
2926 if ( i
>= ni
|| si
== di
) { /* End of field? */
2927 if ( ni
== 11 ) { /* Name extension overflow? */
2928 cf
|= NS_LOSS
| NS_LFN
;
2931 if ( si
!= di
) cf
|= NS_LOSS
| NS_LFN
; /* Name body overflow? */
2932 if ( si
> di
) break ; /* No name extension? */
2933 si
= di
; i
= 8 ; ni
= 11 ; b
<<= 2 ; /* Enter name extension */
2937 if ( wc
>= 0x80 ) { /* Is this a non-ASCII character? */
2938 cf
|= NS_LFN
; /* LFN entry needs to be created */
2939 #if FF_CODE_PAGE == 0
2940 if ( ExCvt
) { /* At SBCS */
2941 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2942 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2943 } else { /* At DBCS */
2944 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Upper convert ==> ANSI/OEM code */
2946 #elif FF_CODE_PAGE < 900 /* SBCS cfg */
2947 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2948 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2949 #else /* DBCS cfg */
2950 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Upper convert ==> ANSI/OEM code */
2954 if ( wc
>= 0x100 ) { /* Is this a DBC? */
2955 if ( i
>= ni
- 1 ) { /* Field overflow? */
2956 cf
|= NS_LOSS
| NS_LFN
;
2957 i
= ni
; continue ; /* Next field */
2959 dp
-> fn
[ i
++] = ( BYTE
)( wc
>> 8 ); /* Put 1st byte */
2961 if ( wc
== 0 || chk_chr ( "+,;=[]" , wc
)) { /* Replace illegal characters for SFN if needed */
2962 wc
= '_' ; cf
|= NS_LOSS
| NS_LFN
; /* Lossy conversion */
2964 if ( IsUpper ( wc
)) { /* ASCII upper case? */
2967 if ( IsLower ( wc
)) { /* ASCII lower case? */
2972 dp
-> fn
[ i
++] = ( BYTE
) wc
;
2975 if ( dp
-> fn
[ 0 ] == DDEM
) dp
-> fn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
2977 if ( ni
== 8 ) b
<<= 2 ; /* Shift capital flags if no extension */
2978 if (( b
& 0x0C ) == 0x0C || ( b
& 0x03 ) == 0x03 ) cf
|= NS_LFN
; /* LFN entry needs to be created if composite capitals */
2979 if (!( cf
& NS_LFN
)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
2980 if ( b
& 0x01 ) cf
|= NS_EXT
; /* NT flag (Extension has small capital letters only) */
2981 if ( b
& 0x04 ) cf
|= NS_BODY
; /* NT flag (Body has small capital letters only) */
2984 dp
-> fn
[ NSFLAG
] = cf
; /* SFN is created into dp->fn[] */
2989 #else /* FF_USE_LFN : Non-LFN configuration */
2994 /* Create file name in directory form */
2995 p
= * path
; sfn
= dp
-> fn
;
2996 mem_set ( sfn
, ' ' , 11 );
2998 #if FF_FS_RPATH != 0
2999 if ( p
[ si
] == '.' ) { /* Is this a dot entry? */
3002 if ( c
!= '.' || si
>= 3 ) break ;
3005 if ( c
!= '/' && c
!= ' \\ ' && c
> ' ' ) return FR_INVALID_NAME
;
3006 * path
= p
+ si
; /* Return pointer to the next segment */
3007 sfn
[ NSFLAG
] = ( c
<= ' ' ) ? NS_LAST
| NS_DOT
: NS_DOT
; /* Set last segment flag if end of the path */
3012 c
= ( BYTE
) p
[ si
++]; /* Get a byte */
3013 if ( c
<= ' ' ) break ; /* Break if end of the path name */
3014 if ( c
== '/' || c
== ' \\ ' ) { /* Break if a separator is found */
3015 while ( p
[ si
] == '/' || p
[ si
] == ' \\ ' ) si
++; /* Skip duplicated separator if exist */
3018 if ( c
== '.' || i
>= ni
) { /* End of body or field overflow? */
3019 if ( ni
== 11 || c
!= '.' ) return FR_INVALID_NAME
; /* Field overflow or invalid dot? */
3020 i
= 8 ; ni
= 11 ; /* Enter file extension field */
3023 #if FF_CODE_PAGE == 0
3024 if ( ExCvt
&& c
>= 0x80 ) { /* Is SBC extended character? */
3025 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
3027 #elif FF_CODE_PAGE < 900
3028 if ( c
>= 0x80 ) { /* Is SBC extended character? */
3029 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
3032 if ( dbc_1st ( c
)) { /* Check if it is a DBC 1st byte */
3033 d
= ( BYTE
) p
[ si
++]; /* Get 2nd byte */
3034 if (! dbc_2nd ( d
) || i
>= ni
- 1 ) return FR_INVALID_NAME
; /* Reject invalid DBC */
3038 if ( chk_chr ( " \" *+,:;<=> \? []| \x7F " , c
)) return FR_INVALID_NAME
; /* Reject illegal chrs for SFN */
3039 if ( IsLower ( c
)) c
-= 0x20 ; /* To upper */
3043 * path
= p
+ si
; /* Return pointer to the next segment */
3044 if ( i
== 0 ) return FR_INVALID_NAME
; /* Reject nul string */
3046 if ( sfn
[ 0 ] == DDEM
) sfn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
3047 sfn
[ NSFLAG
] = ( c
<= ' ' ) ? NS_LAST
: 0 ; /* Set last segment flag if end of the path */
3050 #endif /* FF_USE_LFN */
3056 /*-----------------------------------------------------------------------*/
3057 /* Follow a file path */
3058 /*-----------------------------------------------------------------------*/
3060 static FRESULT
follow_path ( /* FR_OK(0): successful, !=0: error code */
3061 DIR * dp
, /* Directory object to return last directory and found object */
3062 const TCHAR
* path
/* Full-path string to find a file or directory */
3067 FATFS
* fs
= dp
-> obj
. fs
;
3070 #if FF_FS_RPATH != 0
3071 if (* path
!= '/' && * path
!= ' \\ ' ) { /* Without heading separator */
3072 dp
-> obj
. sclust
= fs
-> cdir
; /* Start from current directory */
3075 { /* With heading separator */
3076 while (* path
== '/' || * path
== ' \\ ' ) path
++; /* Strip heading separator */
3077 dp
-> obj
. sclust
= 0 ; /* Start from root directory */
3080 dp
-> obj
. n_frag
= 0 ; /* Invalidate last fragment counter of the object */
3081 #if FF_FS_RPATH != 0
3082 if ( fs
-> fs_type
== FS_EXFAT
&& dp
-> obj
. sclust
) { /* exFAT: Retrieve the sub-directory's status */
3085 dp
-> obj
. c_scl
= fs
-> cdc_scl
;
3086 dp
-> obj
. c_size
= fs
-> cdc_size
;
3087 dp
-> obj
. c_ofs
= fs
-> cdc_ofs
;
3088 res
= load_obj_xdir (& dj
, & dp
-> obj
);
3089 if ( res
!= FR_OK
) return res
;
3090 dp
-> obj
. objsize
= ld_dword ( fs
-> dirbuf
+ XDIR_FileSize
);
3091 dp
-> obj
. stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ;
3096 if (( UINT
)* path
< ' ' ) { /* Null path name is the origin directory itself */
3097 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3098 res
= dir_sdi ( dp
, 0 );
3100 } else { /* Follow path */
3102 res
= create_name ( dp
, & path
); /* Get a segment name of the path */
3103 if ( res
!= FR_OK
) break ;
3104 res
= dir_find ( dp
); /* Find an object with the segment name */
3105 ns
= dp
-> fn
[ NSFLAG
];
3106 if ( res
!= FR_OK
) { /* Failed to find the object */
3107 if ( res
== FR_NO_FILE
) { /* Object is not found */
3108 if ( FF_FS_RPATH
&& ( ns
& NS_DOT
)) { /* If dot entry is not exist, stay there */
3109 if (!( ns
& NS_LAST
)) continue ; /* Continue to follow if not last segment */
3110 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3112 } else { /* Could not find the object */
3113 if (!( ns
& NS_LAST
)) res
= FR_NO_PATH
; /* Adjust error code if not last segment */
3118 if ( ns
& NS_LAST
) break ; /* Last segment matched. Function completed. */
3119 /* Get into the sub-directory */
3120 if (!( dp
-> obj
. attr
& AM_DIR
)) { /* It is not a sub-directory and cannot follow */
3121 res
= FR_NO_PATH
; break ;
3124 if ( fs
-> fs_type
== FS_EXFAT
) { /* Save containing directory information for next dir */
3125 dp
-> obj
. c_scl
= dp
-> obj
. sclust
;
3126 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
3127 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
3128 init_alloc_info ( fs
, & dp
-> obj
); /* Open next directory */
3132 dp
-> obj
. sclust
= ld_clust ( fs
, fs
-> win
+ dp
-> dptr
% SS ( fs
)); /* Open next directory */
3143 /*-----------------------------------------------------------------------*/
3144 /* Get logical drive number from path name */
3145 /*-----------------------------------------------------------------------*/
3147 static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
3148 const TCHAR
** path
/* Pointer to pointer to the path name */
3151 const TCHAR
* tp
, * tt
;
3154 #if FF_STR_VOLUME_ID /* Find string volume ID */
3160 if (! tp
) return vol
; /* Invalid path name? */
3161 do tc
= * tt
++; while (( UINT
) tc
>= ( FF_USE_LFN
? ' ' : '!' ) && tc
!= ':' ); /* Find a colon in the path */
3163 if ( tc
== ':' ) { /* DOS/Windows style volume ID? */
3165 if ( IsDigit (* tp
) && tp
+ 2 == tt
) { /* Is there a numeric volume ID + colon? */
3166 i
= ( int )* tp
- '0' ; /* Get the LD number */
3168 #if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
3172 sp
= VolumeStr
[ i
]; tp
= * path
; /* This string volume ID and path name */
3173 do { /* Compare the volume ID with path name */
3174 c
= * sp
++; tc
= * tp
++;
3175 if ( IsLower ( c
)) c
-= 0x20 ;
3176 if ( IsLower ( tc
)) tc
-= 0x20 ;
3177 } while ( c
&& ( TCHAR
) c
== tc
);
3178 } while (( c
|| tp
!= tt
) && ++ i
< FF_VOLUMES
); /* Repeat for each id until pattern match */
3181 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3182 vol
= i
; /* Drive number */
3183 * path
= tt
; /* Snip the drive prefix off */
3187 #if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
3191 sp
= VolumeStr
[ i
]; tp
= * path
; /* This string volume ID and path name */
3192 do { /* Compare the volume ID with path name */
3193 c
= * sp
++; tc
= *(++ tp
);
3194 if ( IsLower ( c
)) c
-= 0x20 ;
3195 if ( IsLower ( tc
)) tc
-= 0x20 ;
3196 } while ( c
&& ( TCHAR
) c
== tc
);
3197 } while (( c
|| ( tc
!= '/' && ( UINT
) tc
>= ( FF_USE_LFN
? ' ' : '!' ))) && ++ i
< FF_VOLUMES
); /* Repeat for each ID until pattern match */
3198 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3199 vol
= i
; /* Drive number */
3200 * path
= tp
; /* Snip the drive prefix off */
3205 /* No drive prefix is found */
3206 #if FF_FS_RPATH != 0
3207 vol
= CurrVol
; /* Default drive is current drive */
3209 vol
= 0 ; /* Default drive is 0 */
3211 return vol
; /* Return the default drive */
3217 /*-----------------------------------------------------------------------*/
3218 /* GPT support functions */
3219 /*-----------------------------------------------------------------------*/
3223 /* Calculate CRC32 in byte-by-byte */
3225 static DWORD
crc32 ( /* Returns next CRC value */
3226 DWORD crc
, /* Current CRC value */
3227 BYTE d
/* A byte to be processed */
3233 for ( b
= 1 ; b
; b
<<= 1 ) {
3234 crc
^= ( d
& b
) ? 1 : 0 ;
3235 crc
= ( crc
& 1 ) ? crc
>> 1 ^ 0xEDB88320 : crc
>> 1 ;
3241 /* Check validity of GPT header */
3243 static int test_gpt_header ( /* 0:Invalid, 1:Valid */
3244 const BYTE
* gpth
/* Pointer to the GPT header */
3251 if ( mem_cmp ( gpth
+ GPTH_Sign
, "EFI PART" "\0\0\1\0" " \x5C \0\0" , 16 )) return 0 ; /* Check sign, version (1.0) and length (92) */
3252 for ( i
= 0 , bcc
= 0xFFFFFFFF ; i
< 92 ; i
++) { /* Check header BCC */
3253 bcc
= crc32 ( bcc
, i
- GPTH_Bcc
< 4 ? 0 : gpth
[ i
]);
3255 if (~ bcc
!= ld_dword ( gpth
+ GPTH_Bcc
)) return 0 ;
3256 if ( ld_dword ( gpth
+ GPTH_PteSize
) != SZ_GPTE
) return 0 ; /* Table entry size (must be SZ_GPTE bytes) */
3257 if ( ld_dword ( gpth
+ GPTH_PtNum
) > 128 ) return 0 ; /* Table size (must be 128 entries or less) */
3262 #if !FF_FS_READONLY && FF_USE_MKFS
3264 /* Generate random value */
3265 static DWORD
make_rand (
3266 DWORD seed
, /* Seed value */
3267 BYTE
* buff
, /* Output buffer */
3268 UINT n
/* Data length */
3274 if ( seed
== 0 ) seed
= 1 ;
3276 for ( r
= 0 ; r
< 8 ; r
++) seed
= seed
& 1 ? seed
>> 1 ^ 0xA3000000 : seed
>> 1 ; /* Shift 8 bits the 32-bit LFSR */
3277 * buff
++ = ( BYTE
) seed
;
3287 /*-----------------------------------------------------------------------*/
3288 /* Load a sector and check if it is an FAT VBR */
3289 /*-----------------------------------------------------------------------*/
3291 /* Check what the sector is */
3293 static UINT
check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Invalid BS, 4:Disk error */
3294 FATFS
* fs
, /* Filesystem object */
3295 LBA_t sect
/* Sector to load and check if it is an FAT-VBR or not */
3298 fs
-> wflag
= 0 ; fs
-> winsect
= ( LBA_t
) 0 - 1 ; /* Invaidate window */
3299 if ( move_window ( fs
, sect
) != FR_OK
) return 4 ; /* Load the boot sector */
3301 if ( ld_word ( fs
-> win
+ BS_55AA
) != 0xAA55 ) return 3 ; /* Check boot signature (always here regardless of the sector size) */
3303 if ( FF_FS_EXFAT
&& ! mem_cmp ( fs
-> win
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 )) return 1 ; /* Check if exFAT VBR */
3305 if ( fs
-> win
[ BS_JmpBoot
] == 0xE9 || fs
-> win
[ BS_JmpBoot
] == 0xEB || fs
-> win
[ BS_JmpBoot
] == 0xE8 ) { /* Valid JumpBoot code? */
3306 if (! mem_cmp ( fs
-> win
+ BS_FilSysType
, "FAT" , 3 )) return 0 ; /* Is it an FAT VBR? */
3307 if (! mem_cmp ( fs
-> win
+ BS_FilSysType32
, "FAT32" , 5 )) return 0 ; /* Is it an FAT32 VBR? */
3309 return 2 ; /* Valid BS but not FAT */
3313 /* Find an FAT volume */
3314 /* (It supports only generic partitioning rules, MBR, GPT and SFD) */
3316 static UINT
find_volume ( /* Returns BS status found in the hosting drive */
3317 FATFS
* fs
, /* Filesystem object */
3318 UINT part
/* Partition to fined = 0:auto, 1..:forced */
3325 fmt
= check_fs ( fs
, 0 ); /* Load sector 0 and check if it is an FAT VBR as SFD */
3326 if ( fmt
!= 2 && ( fmt
>= 3 || part
== 0 )) return fmt
; /* Returns if it is a FAT VBR as auto scan, not a BS or disk error */
3328 /* Sector 0 is not an FAT VBR or forced partition number wants a partition */
3331 if ( fs
-> win
[ MBR_Table
+ PTE_System
] == 0xEE ) { /* GPT protective MBR? */
3332 DWORD n_ent
, v_ent
, ofs
;
3335 if ( move_window ( fs
, 1 ) != FR_OK
) return 4 ; /* Load GPT header sector (next to MBR) */
3336 if (! test_gpt_header ( fs
-> win
)) return 3 ; /* Check if GPT header is valid */
3337 n_ent
= ld_dword ( fs
-> win
+ GPTH_PtNum
); /* Number of entries */
3338 pt_lba
= ld_qword ( fs
-> win
+ GPTH_PtOfs
); /* Table location */
3339 for ( v_ent
= i
= 0 ; i
< n_ent
; i
++) { /* Find FAT partition */
3340 if ( move_window ( fs
, pt_lba
+ i
* SZ_GPTE
/ SS ( fs
)) != FR_OK
) return 4 ; /* PT sector */
3341 ofs
= i
* SZ_GPTE
% SS ( fs
); /* Offset in the sector */
3342 if (! mem_cmp ( fs
-> win
+ ofs
+ GPTE_PtGuid
, GUID_MS_Basic
, 16 )) { /* MS basic data partition? */
3344 fmt
= check_fs ( fs
, ld_qword ( fs
-> win
+ ofs
+ GPTE_FstLba
)); /* Load VBR and check status */
3345 if ( part
== 0 && fmt
<= 1 ) return fmt
; /* Auto search (valid FAT volume found first) */
3346 if ( part
!= 0 && v_ent
== part
) return fmt
; /* Forced partition order (regardless of it is valid or not) */
3349 return 3 ; /* Not found */
3352 if ( FF_MULTI_PARTITION
&& part
> 4 ) return 3 ; /* MBR has 4 partitions max */
3353 for ( i
= 0 ; i
< 4 ; i
++) { /* Load partition offset in the MBR */
3354 mbr_pt
[ i
] = ld_dword ( fs
-> win
+ MBR_Table
+ i
* SZ_PTE
+ PTE_StLba
);
3356 i
= part
? part
- 1 : 0 ; /* Table index to find first */
3357 do { /* Find an FAT volume */
3358 fmt
= mbr_pt
[ i
] ? check_fs ( fs
, mbr_pt
[ i
]) : 3 ; /* Check if the partition is FAT */
3359 } while ( part
== 0 && fmt
>= 2 && ++ i
< 4 );
3366 /*-----------------------------------------------------------------------*/
3367 /* Determine logical drive number and mount the volume if needed */
3368 /*-----------------------------------------------------------------------*/
3370 static FRESULT
mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
3371 const TCHAR
** path
, /* Pointer to pointer to the path name (drive number) */
3372 FATFS
** rfs
, /* Pointer to pointer to the found filesystem object */
3373 BYTE mode
/* !=0: Check write protection for write access */
3379 DWORD tsect
, sysect
, fasize
, nclst
, szbfat
;
3385 /* Get logical drive number */
3387 vol
= get_ldnumber ( path
);
3388 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3390 /* Check if the filesystem object is valid or not */
3391 fs
= FatFs
[ vol
]; /* Get pointer to the filesystem object */
3392 if (! fs
) return FR_NOT_ENABLED
; /* Is the filesystem object available? */
3394 if (! lock_fs ( fs
)) return FR_TIMEOUT
; /* Lock the volume */
3396 * rfs
= fs
; /* Return pointer to the filesystem object */
3398 mode
&= ( BYTE
)~ FA_READ
; /* Desired access mode, write access or not */
3399 if ( fs
-> fs_type
!= 0 ) { /* If the volume has been mounted */
3400 stat
= disk_status ( fs
-> pdrv
);
3401 if (!( stat
& STA_NOINIT
)) { /* and the physical drive is kept initialized */
3402 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check write protection if needed */
3403 return FR_WRITE_PROTECTED
;
3405 return FR_OK
; /* The filesystem object is already valid */
3409 /* The filesystem object is not valid. */
3410 /* Following code attempts to mount the volume. (find a FAT volume, analyze the BPB and initialize the filesystem object) */
3412 fs
-> fs_type
= 0 ; /* Clear the filesystem object */
3413 fs
-> pdrv
= LD2PD ( vol
); /* Volume hosting physical drive */
3414 stat
= disk_initialize ( fs
-> pdrv
); /* Initialize the physical drive */
3415 if ( stat
& STA_NOINIT
) { /* Check if the initialization succeeded */
3416 return FR_NOT_READY
; /* Failed to initialize due to no medium or hard error */
3418 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check disk write protection if needed */
3419 return FR_WRITE_PROTECTED
;
3421 #if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
3422 if ( disk_ioctl ( fs
-> pdrv
, GET_SECTOR_SIZE
, & SS ( fs
)) != RES_OK
) return FR_DISK_ERR
;
3423 if ( SS ( fs
) > FF_MAX_SS
|| SS ( fs
) < FF_MIN_SS
|| ( SS ( fs
) & ( SS ( fs
) - 1 ))) return FR_DISK_ERR
;
3426 /* Find an FAT volume on the drive */
3427 fmt
= find_volume ( fs
, LD2PT ( vol
));
3428 if ( fmt
== 4 ) return FR_DISK_ERR
; /* An error occured in the disk I/O layer */
3429 if ( fmt
>= 2 ) return FR_NO_FILESYSTEM
; /* No FAT volume is found */
3430 bsect
= fs
-> winsect
; /* Volume location */
3432 /* An FAT volume is found (bsect). Following code initializes the filesystem object */
3437 DWORD so
, cv
, bcl
, i
;
3439 for ( i
= BPB_ZeroedEx
; i
< BPB_ZeroedEx
+ 53 && fs
-> win
[ i
] == 0 ; i
++) ; /* Check zero filler */
3440 if ( i
< BPB_ZeroedEx
+ 53 ) return FR_NO_FILESYSTEM
;
3442 if ( ld_word ( fs
-> win
+ BPB_FSVerEx
) != 0x100 ) return FR_NO_FILESYSTEM
; /* Check exFAT version (must be version 1.0) */
3444 if ( 1 << fs
-> win
[ BPB_BytsPerSecEx
] != SS ( fs
)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
3445 return FR_NO_FILESYSTEM
;
3448 maxlba
= ld_qword ( fs
-> win
+ BPB_TotSecEx
) + bsect
; /* Last LBA + 1 of the volume */
3449 if (! FF_LBA64
&& maxlba
>= 0x100000000 ) return FR_NO_FILESYSTEM
; /* (It cannot be handled in 32-bit LBA) */
3451 fs
-> fsize
= ld_dword ( fs
-> win
+ BPB_FatSzEx
); /* Number of sectors per FAT */
3453 fs
-> n_fats
= fs
-> win
[ BPB_NumFATsEx
]; /* Number of FATs */
3454 if ( fs
-> n_fats
!= 1 ) return FR_NO_FILESYSTEM
; /* (Supports only 1 FAT) */
3456 fs
-> csize
= 1 << fs
-> win
[ BPB_SecPerClusEx
]; /* Cluster size */
3457 if ( fs
-> csize
== 0 ) return FR_NO_FILESYSTEM
; /* (Must be 1..32768) */
3459 nclst
= ld_dword ( fs
-> win
+ BPB_NumClusEx
); /* Number of clusters */
3460 if ( nclst
> MAX_EXFAT
) return FR_NO_FILESYSTEM
; /* (Too many clusters) */
3461 fs
-> n_fatent
= nclst
+ 2 ;
3463 /* Boundaries and Limits */
3464 fs
-> volbase
= bsect
;
3465 fs
-> database
= bsect
+ ld_dword ( fs
-> win
+ BPB_DataOfsEx
);
3466 fs
-> fatbase
= bsect
+ ld_dword ( fs
-> win
+ BPB_FatOfsEx
);
3467 if ( maxlba
< ( QWORD
) fs
-> database
+ nclst
* fs
-> csize
) return FR_NO_FILESYSTEM
; /* (Volume size must not be smaller than the size requiered) */
3468 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClusEx
);
3470 /* Get bitmap location and check if it is contiguous (implementation assumption) */
3472 for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */
3474 if ( so
>= fs
-> csize
) return FR_NO_FILESYSTEM
; /* Not found? */
3475 if ( move_window ( fs
, clst2sect ( fs
, ( DWORD
) fs
-> dirbase
) + so
) != FR_OK
) return FR_DISK_ERR
;
3478 if ( fs
-> win
[ i
] == ET_BITMAP
) break ; /* Is it a bitmap entry? */
3479 i
= ( i
+ SZDIRE
) % SS ( fs
); /* Next entry */
3481 bcl
= ld_dword ( fs
-> win
+ i
+ 20 ); /* Bitmap cluster */
3482 if ( bcl
< 2 || bcl
>= fs
-> n_fatent
) return FR_NO_FILESYSTEM
;
3483 fs
-> bitbase
= fs
-> database
+ fs
-> csize
* ( bcl
- 2 ); /* Bitmap sector */
3484 for (;;) { /* Check if bitmap is contiguous */
3485 if ( move_window ( fs
, fs
-> fatbase
+ bcl
/ ( SS ( fs
) / 4 )) != FR_OK
) return FR_DISK_ERR
;
3486 cv
= ld_dword ( fs
-> win
+ bcl
% ( SS ( fs
) / 4 ) * 4 );
3487 if ( cv
== 0xFFFFFFFF ) break ; /* Last link? */
3488 if ( cv
!= ++ bcl
) return FR_NO_FILESYSTEM
; /* Fragmented? */
3492 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3494 fmt
= FS_EXFAT
; /* FAT sub-type */
3496 #endif /* FF_FS_EXFAT */
3498 if ( ld_word ( fs
-> win
+ BPB_BytsPerSec
) != SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_BytsPerSec must be equal to the physical sector size) */
3500 fasize
= ld_word ( fs
-> win
+ BPB_FATSz16
); /* Number of sectors per FAT */
3501 if ( fasize
== 0 ) fasize
= ld_dword ( fs
-> win
+ BPB_FATSz32
);
3504 fs
-> n_fats
= fs
-> win
[ BPB_NumFATs
]; /* Number of FATs */
3505 if ( fs
-> n_fats
!= 1 && fs
-> n_fats
!= 2 ) return FR_NO_FILESYSTEM
; /* (Must be 1 or 2) */
3506 fasize
*= fs
-> n_fats
; /* Number of sectors for FAT area */
3508 fs
-> csize
= fs
-> win
[ BPB_SecPerClus
]; /* Cluster size */
3509 if ( fs
-> csize
== 0 || ( fs
-> csize
& ( fs
-> csize
- 1 ))) return FR_NO_FILESYSTEM
; /* (Must be power of 2) */
3511 fs
-> n_rootdir
= ld_word ( fs
-> win
+ BPB_RootEntCnt
); /* Number of root directory entries */
3512 if ( fs
-> n_rootdir
% ( SS ( fs
) / SZDIRE
)) return FR_NO_FILESYSTEM
; /* (Must be sector aligned) */
3514 tsect
= ld_word ( fs
-> win
+ BPB_TotSec16
); /* Number of sectors on the volume */
3515 if ( tsect
== 0 ) tsect
= ld_dword ( fs
-> win
+ BPB_TotSec32
);
3517 nrsv
= ld_word ( fs
-> win
+ BPB_RsvdSecCnt
); /* Number of reserved sectors */
3518 if ( nrsv
== 0 ) return FR_NO_FILESYSTEM
; /* (Must not be 0) */
3520 /* Determine the FAT sub type */
3521 sysect
= nrsv
+ fasize
+ fs
-> n_rootdir
/ ( SS ( fs
) / SZDIRE
); /* RSV + FAT + DIR */
3522 if ( tsect
< sysect
) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3523 nclst
= ( tsect
- sysect
) / fs
-> csize
; /* Number of clusters */
3524 if ( nclst
== 0 ) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3526 if ( nclst
<= MAX_FAT32
) fmt
= FS_FAT32
;
3527 if ( nclst
<= MAX_FAT16
) fmt
= FS_FAT16
;
3528 if ( nclst
<= MAX_FAT12
) fmt
= FS_FAT12
;
3529 if ( fmt
== 0 ) return FR_NO_FILESYSTEM
;
3531 /* Boundaries and Limits */
3532 fs
-> n_fatent
= nclst
+ 2 ; /* Number of FAT entries */
3533 fs
-> volbase
= bsect
; /* Volume start sector */
3534 fs
-> fatbase
= bsect
+ nrsv
; /* FAT start sector */
3535 fs
-> database
= bsect
+ sysect
; /* Data start sector */
3536 if ( fmt
== FS_FAT32
) {
3537 if ( ld_word ( fs
-> win
+ BPB_FSVer32
) != 0 ) return FR_NO_FILESYSTEM
; /* (Must be FAT32 revision 0.0) */
3538 if ( fs
-> n_rootdir
!= 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must be 0) */
3539 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClus32
); /* Root directory start cluster */
3540 szbfat
= fs
-> n_fatent
* 4 ; /* (Needed FAT size) */
3542 if ( fs
-> n_rootdir
== 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must not be 0) */
3543 fs
-> dirbase
= fs
-> fatbase
+ fasize
; /* Root directory start sector */
3544 szbfat
= ( fmt
== FS_FAT16
) ? /* (Needed FAT size) */
3545 fs
-> n_fatent
* 2 : fs
-> n_fatent
* 3 / 2 + ( fs
-> n_fatent
& 1 );
3547 if ( fs
-> fsize
< ( szbfat
+ ( SS ( fs
) - 1 )) / SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_FATSz must not be less than the size needed) */
3550 /* Get FSInfo if available */
3551 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3552 fs
-> fsi_flag
= 0x80 ;
3553 #if (FF_FS_NOFSINFO & 3) != 3
3554 if ( fmt
== FS_FAT32
/* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
3555 && ld_word ( fs
-> win
+ BPB_FSInfo32
) == 1
3556 && move_window ( fs
, bsect
+ 1 ) == FR_OK
)
3559 if ( ld_word ( fs
-> win
+ BS_55AA
) == 0xAA55 /* Load FSInfo data if available */
3560 && ld_dword ( fs
-> win
+ FSI_LeadSig
) == 0x41615252
3561 && ld_dword ( fs
-> win
+ FSI_StrucSig
) == 0x61417272 )
3563 #if (FF_FS_NOFSINFO & 1) == 0
3564 fs
-> free_clst
= ld_dword ( fs
-> win
+ FSI_Free_Count
);
3566 #if (FF_FS_NOFSINFO & 2) == 0
3567 fs
-> last_clst
= ld_dword ( fs
-> win
+ FSI_Nxt_Free
);
3571 #endif /* (FF_FS_NOFSINFO & 3) != 3 */
3572 #endif /* !FF_FS_READONLY */
3575 fs
-> fs_type
= ( BYTE
) fmt
; /* FAT sub-type */
3576 fs
-> id
= ++ Fsid
; /* Volume mount ID */
3578 fs
-> lfnbuf
= LfnBuf
; /* Static LFN working buffer */
3580 fs
-> dirbuf
= DirBuf
; /* Static directory block scratchpad buuffer */
3583 #if FF_FS_RPATH != 0
3584 fs
-> cdir
= 0 ; /* Initialize current directory */
3586 #if FF_FS_LOCK != 0 /* Clear file lock semaphores */
3595 /*-----------------------------------------------------------------------*/
3596 /* Check if the file/directory object is valid or not */
3597 /*-----------------------------------------------------------------------*/
3599 static FRESULT
validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
3600 FFOBJID
* obj
, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
3601 FATFS
** rfs
/* Pointer to pointer to the owner filesystem object to return */
3604 FRESULT res
= FR_INVALID_OBJECT
;
3607 if ( obj
&& obj
-> fs
&& obj
-> fs
-> fs_type
&& obj
-> id
== obj
-> fs
-> id
) { /* Test if the object is valid */
3609 if ( lock_fs ( obj
-> fs
)) { /* Obtain the filesystem object */
3610 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the phsical drive is kept initialized */
3613 unlock_fs ( obj
-> fs
, FR_OK
);
3619 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the phsical drive is kept initialized */
3624 * rfs
= ( res
== FR_OK
) ? obj
-> fs
: 0 ; /* Corresponding filesystem object */
3631 /*---------------------------------------------------------------------------
3633 Public Functions (FatFs API)
3635 ----------------------------------------------------------------------------*/
3639 /*-----------------------------------------------------------------------*/
3640 /* Mount/Unmount a Logical Drive */
3641 /*-----------------------------------------------------------------------*/
3644 FATFS
* fs
, /* Pointer to the filesystem object (NULL:unmount)*/
3645 const TCHAR
* path
, /* Logical drive number to be mounted/unmounted */
3646 BYTE opt
/* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
3652 const TCHAR
* rp
= path
;
3655 /* Get logical drive number */
3656 vol
= get_ldnumber (& rp
);
3657 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3658 cfs
= FatFs
[ vol
]; /* Pointer to fs object */
3664 #if FF_FS_REENTRANT /* Discard sync object of the current volume */
3665 if (! ff_del_syncobj ( cfs
-> sobj
)) return FR_INT_ERR
;
3667 cfs
-> fs_type
= 0 ; /* Clear old fs object */
3671 fs
-> fs_type
= 0 ; /* Clear new fs object */
3672 #if FF_FS_REENTRANT /* Create sync object for the new volume */
3673 if (! ff_cre_syncobj (( BYTE
) vol
, & fs
-> sobj
)) return FR_INT_ERR
;
3676 FatFs
[ vol
] = fs
; /* Register new fs object */
3678 if ( opt
== 0 ) return FR_OK
; /* Do not mount now, it will be mounted later */
3680 res
= mount_volume (& path
, & fs
, 0 ); /* Force mounted the volume */
3687 /*-----------------------------------------------------------------------*/
3688 /* Open or Create a File */
3689 /*-----------------------------------------------------------------------*/
3692 FIL
* fp
, /* Pointer to the blank file object */
3693 const TCHAR
* path
, /* Pointer to the file name */
3694 BYTE mode
/* Access mode and file open mode flags */
3701 DWORD cl
, bcs
, clst
;
3708 if (! fp
) return FR_INVALID_OBJECT
;
3710 /* Get logical drive number */
3711 mode
&= FF_FS_READONLY
? FA_READ
: FA_READ
| FA_WRITE
| FA_CREATE_ALWAYS
| FA_CREATE_NEW
| FA_OPEN_ALWAYS
| FA_OPEN_APPEND
;
3712 res
= mount_volume (& path
, & fs
, mode
);
3716 res
= follow_path (& dj
, path
); /* Follow the file path */
3717 #if !FF_FS_READONLY /* Read/Write configuration */
3719 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Origin directory itself? */
3720 res
= FR_INVALID_NAME
;
3724 res
= chk_lock (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Check if the file can be used */
3728 /* Create or Open a file */
3729 if ( mode
& ( FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
)) {
3730 if ( res
!= FR_OK
) { /* No file, create new */
3731 if ( res
== FR_NO_FILE
) { /* There is no file to open, create a new entry */
3733 res
= enq_lock () ? dir_register (& dj
) : FR_TOO_MANY_OPEN_FILES
;
3735 res
= dir_register (& dj
);
3738 mode
|= FA_CREATE_ALWAYS
; /* File is created */
3740 else { /* Any object with the same name is already existing */
3741 if ( dj
. obj
. attr
& ( AM_RDO
| AM_DIR
)) { /* Cannot overwrite it (R/O or DIR) */
3744 if ( mode
& FA_CREATE_NEW
) res
= FR_EXIST
; /* Cannot create as new file */
3747 if ( res
== FR_OK
&& ( mode
& FA_CREATE_ALWAYS
)) { /* Truncate the file if overwrite mode */
3749 if ( fs
-> fs_type
== FS_EXFAT
) {
3750 /* Get current allocation info */
3752 init_alloc_info ( fs
, & fp
-> obj
);
3753 /* Set directory entry block initial state */
3754 mem_set ( fs
-> dirbuf
+ 2 , 0 , 30 ); /* Clear 85 entry except for NumSec */
3755 mem_set ( fs
-> dirbuf
+ 38 , 0 , 26 ); /* Clear C0 entry except for NumName and NameHash */
3756 fs
-> dirbuf
[ XDIR_Attr
] = AM_ARC
;
3757 st_dword ( fs
-> dirbuf
+ XDIR_CrtTime
, GET_FATTIME ());
3758 fs
-> dirbuf
[ XDIR_GenFlags
] = 1 ;
3759 res
= store_xdir (& dj
);
3760 if ( res
== FR_OK
&& fp
-> obj
. sclust
!= 0 ) { /* Remove the cluster chain if exist */
3761 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
3762 fs
-> last_clst
= fp
-> obj
. sclust
- 1 ; /* Reuse the cluster hole */
3767 /* Set directory entry initial state */
3768 cl
= ld_clust ( fs
, dj
. dir
); /* Get current cluster chain */
3769 st_dword ( dj
. dir
+ DIR_CrtTime
, GET_FATTIME ()); /* Set created time */
3770 dj
. dir
[ DIR_Attr
] = AM_ARC
; /* Reset attribute */
3771 st_clust ( fs
, dj
. dir
, 0 ); /* Reset file allocation info */
3772 st_dword ( dj
. dir
+ DIR_FileSize
, 0 );
3774 if ( cl
!= 0 ) { /* Remove the cluster chain if exist */
3776 res
= remove_chain (& dj
. obj
, cl
, 0 );
3778 res
= move_window ( fs
, sc
);
3779 fs
-> last_clst
= cl
- 1 ; /* Reuse the cluster hole */
3785 else { /* Open an existing file */
3786 if ( res
== FR_OK
) { /* Is the object exsiting? */
3787 if ( dj
. obj
. attr
& AM_DIR
) { /* File open against a directory */
3790 if (( mode
& FA_WRITE
) && ( dj
. obj
. attr
& AM_RDO
)) { /* Write mode open against R/O file */
3797 if ( mode
& FA_CREATE_ALWAYS
) mode
|= FA_MODIFIED
; /* Set file change flag if created or overwritten */
3798 fp
-> dir_sect
= fs
-> winsect
; /* Pointer to the directory entry */
3799 fp
-> dir_ptr
= dj
. dir
;
3801 fp
-> obj
. lockid
= inc_lock (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Lock the file for this session */
3802 if ( fp
-> obj
. lockid
== 0 ) res
= FR_INT_ERR
;
3805 #else /* R/O configuration */
3807 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it origin directory itself? */
3808 res
= FR_INVALID_NAME
;
3810 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a directory? */
3819 if ( fs
-> fs_type
== FS_EXFAT
) {
3820 fp
-> obj
. c_scl
= dj
. obj
. sclust
; /* Get containing directory info */
3821 fp
-> obj
. c_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
3822 fp
-> obj
. c_ofs
= dj
. blk_ofs
;
3823 init_alloc_info ( fs
, & fp
-> obj
);
3827 fp
-> obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Get object allocation info */
3828 fp
-> obj
. objsize
= ld_dword ( dj
. dir
+ DIR_FileSize
);
3831 fp
-> cltbl
= 0 ; /* Disable fast seek mode */
3833 fp
-> obj
. fs
= fs
; /* Validate the file object */
3834 fp
-> obj
. id
= fs
-> id
;
3835 fp
-> flag
= mode
; /* Set file access mode */
3836 fp
-> err
= 0 ; /* Clear error flag */
3837 fp
-> sect
= 0 ; /* Invalidate current data sector */
3838 fp
-> fptr
= 0 ; /* Set file pointer top of the file */
3841 mem_set ( fp
-> buf
, 0 , sizeof fp
-> buf
); /* Clear sector buffer */
3843 if (( mode
& FA_SEEKEND
) && fp
-> obj
. objsize
> 0 ) { /* Seek to end of file if FA_OPEN_APPEND is specified */
3844 fp
-> fptr
= fp
-> obj
. objsize
; /* Offset to seek */
3845 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size in byte */
3846 clst
= fp
-> obj
. sclust
; /* Follow the cluster chain */
3847 for ( ofs
= fp
-> obj
. objsize
; res
== FR_OK
&& ofs
> bcs
; ofs
-= bcs
) {
3848 clst
= get_fat (& fp
-> obj
, clst
);
3849 if ( clst
<= 1 ) res
= FR_INT_ERR
;
3850 if ( clst
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
3853 if ( res
== FR_OK
&& ofs
% SS ( fs
)) { /* Fill sector buffer if not on the sector boundary */
3854 sc
= clst2sect ( fs
, clst
);
3858 fp
-> sect
= sc
+ ( DWORD
)( ofs
/ SS ( fs
));
3860 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) res
= FR_DISK_ERR
;
3871 if ( res
!= FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object on error */
3879 /*-----------------------------------------------------------------------*/
3881 /*-----------------------------------------------------------------------*/
3884 FIL
* fp
, /* Pointer to the file object */
3885 void * buff
, /* Pointer to data buffer */
3886 UINT btr
, /* Number of bytes to read */
3887 UINT
* br
/* Pointer to number of bytes read */
3895 UINT rcnt
, cc
, csect
;
3896 BYTE
* rbuff
= ( BYTE
*) buff
;
3899 * br
= 0 ; /* Clear read byte counter */
3900 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3901 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
3902 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
3903 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
3904 if ( btr
> remain
) btr
= ( UINT
) remain
; /* Truncate btr by remaining bytes */
3906 for ( ; btr
; /* Repeat until btr bytes read */
3907 btr
-= rcnt
, * br
+= rcnt
, rbuff
+= rcnt
, fp
-> fptr
+= rcnt
) {
3908 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
3909 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
3910 if ( csect
== 0 ) { /* On the cluster boundary? */
3911 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
3912 clst
= fp
-> obj
. sclust
; /* Follow cluster chain from the origin */
3913 } else { /* Middle or end of the file */
3916 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
3920 clst
= get_fat (& fp
-> obj
, fp
-> clust
); /* Follow cluster chain on the FAT */
3923 if ( clst
< 2 ) ABORT ( fs
, FR_INT_ERR
);
3924 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
3925 fp
-> clust
= clst
; /* Update current cluster */
3927 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
3928 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
3930 cc
= btr
/ SS ( fs
); /* When remaining bytes >= sector size, */
3931 if ( cc
> 0 ) { /* Read maximum contiguous sectors directly */
3932 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
3933 cc
= fs
-> csize
- csect
;
3935 if ( disk_read ( fs
-> pdrv
, rbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3936 #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
3938 if ( fs
-> wflag
&& fs
-> winsect
- sect
< cc
) {
3939 mem_cpy ( rbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), fs
-> win
, SS ( fs
));
3942 if (( fp
-> flag
& FA_DIRTY
) && fp
-> sect
- sect
< cc
) {
3943 mem_cpy ( rbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), fp
-> buf
, SS ( fs
));
3947 rcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
3951 if ( fp
-> sect
!= sect
) { /* Load data sector if not in cache */
3953 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
3954 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3955 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3958 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
3963 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes remains in the sector */
3964 if ( rcnt
> btr
) rcnt
= btr
; /* Clip it by btr if needed */
3966 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
3967 mem_cpy ( rbuff
, fs
-> win
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
3969 mem_cpy ( rbuff
, fp
-> buf
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
3973 LEAVE_FF ( fs
, FR_OK
);
3980 /*-----------------------------------------------------------------------*/
3982 /*-----------------------------------------------------------------------*/
3985 FIL
* fp
, /* Pointer to the file object */
3986 const void * buff
, /* Pointer to the data to be written */
3987 UINT btw
, /* Number of bytes to write */
3988 UINT
* bw
/* Pointer to number of bytes written */
3995 UINT wcnt
, cc
, csect
;
3996 const BYTE
* wbuff
= ( const BYTE
*) buff
;
3999 * bw
= 0 ; /* Clear write byte counter */
4000 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4001 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
4002 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
4004 /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
4005 if ((! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) && ( DWORD
)( fp
-> fptr
+ btw
) < ( DWORD
) fp
-> fptr
) {
4006 btw
= ( UINT
)( 0xFFFFFFFF - ( DWORD
) fp
-> fptr
);
4009 for ( ; btw
; /* Repeat until all data written */
4010 btw
-= wcnt
, * bw
+= wcnt
, wbuff
+= wcnt
, fp
-> fptr
+= wcnt
, fp
-> obj
. objsize
= ( fp
-> fptr
> fp
-> obj
. objsize
) ? fp
-> fptr
: fp
-> obj
. objsize
) {
4011 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
4012 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
)) & ( fs
-> csize
- 1 ); /* Sector offset in the cluster */
4013 if ( csect
== 0 ) { /* On the cluster boundary? */
4014 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
4015 clst
= fp
-> obj
. sclust
; /* Follow from the origin */
4016 if ( clst
== 0 ) { /* If no cluster is allocated, */
4017 clst
= create_chain (& fp
-> obj
, 0 ); /* create a new cluster chain */
4019 } else { /* On the middle or end of the file */
4022 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
4026 clst
= create_chain (& fp
-> obj
, fp
-> clust
); /* Follow or stretch cluster chain on the FAT */
4029 if ( clst
== 0 ) break ; /* Could not allocate a new cluster (disk full) */
4030 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
4031 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4032 fp
-> clust
= clst
; /* Update current cluster */
4033 if ( fp
-> obj
. sclust
== 0 ) fp
-> obj
. sclust
= clst
; /* Set start cluster if the first write */
4036 if ( fs
-> winsect
== fp
-> sect
&& sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Write-back sector cache */
4038 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back sector cache */
4039 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4040 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4043 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
4044 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4046 cc
= btw
/ SS ( fs
); /* When remaining bytes >= sector size, */
4047 if ( cc
> 0 ) { /* Write maximum contiguous sectors directly */
4048 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
4049 cc
= fs
-> csize
- csect
;
4051 if ( disk_write ( fs
-> pdrv
, wbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4052 #if FF_FS_MINIMIZE <= 2
4054 if ( fs
-> winsect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
4055 mem_cpy ( fs
-> win
, wbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), SS ( fs
));
4059 if ( fp
-> sect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
4060 mem_cpy ( fp
-> buf
, wbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), SS ( fs
));
4061 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4065 wcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
4069 if ( fp
-> fptr
>= fp
-> obj
. objsize
) { /* Avoid silly cache filling on the growing edge */
4070 if ( sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
);
4074 if ( fp
-> sect
!= sect
&& /* Fill sector cache with file data */
4075 fp
-> fptr
< fp
-> obj
. objsize
&&
4076 disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) {
4077 ABORT ( fs
, FR_DISK_ERR
);
4082 wcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes remains in the sector */
4083 if ( wcnt
> btw
) wcnt
= btw
; /* Clip it by btw if needed */
4085 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
4086 mem_cpy ( fs
-> win
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
4089 mem_cpy ( fp
-> buf
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
4090 fp
-> flag
|= FA_DIRTY
;
4094 fp
-> flag
|= FA_MODIFIED
; /* Set file change flag */
4096 LEAVE_FF ( fs
, FR_OK
);
4102 /*-----------------------------------------------------------------------*/
4103 /* Synchronize the File */
4104 /*-----------------------------------------------------------------------*/
4107 FIL
* fp
/* Pointer to the file object */
4116 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4118 if ( fp
-> flag
& FA_MODIFIED
) { /* Is there any change to the file? */
4120 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back cached data if needed */
4121 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) LEAVE_FF ( fs
, FR_DISK_ERR
);
4122 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4125 /* Update the directory entry */
4126 tm
= GET_FATTIME (); /* Modified time */
4128 if ( fs
-> fs_type
== FS_EXFAT
) {
4129 res
= fill_first_frag (& fp
-> obj
); /* Fill first fragment on the FAT if needed */
4131 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
4138 res
= load_obj_xdir (& dj
, & fp
-> obj
); /* Load directory entry block */
4140 fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
4141 fs
-> dirbuf
[ XDIR_GenFlags
] = fp
-> obj
. stat
| 1 ; /* Update file allocation information */
4142 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, fp
-> obj
. sclust
); /* Update start cluster */
4143 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, fp
-> obj
. objsize
); /* Update file size */
4144 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, fp
-> obj
. objsize
); /* (FatFs does not support Valid File Size feature) */
4145 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Update modified time */
4146 fs
-> dirbuf
[ XDIR_ModTime10
] = 0 ;
4147 st_dword ( fs
-> dirbuf
+ XDIR_AccTime
, 0 );
4148 res
= store_xdir (& dj
); /* Restore it to the directory */
4151 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
4159 res
= move_window ( fs
, fp
-> dir_sect
);
4162 dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
4163 st_clust ( fp
-> obj
. fs
, dir
, fp
-> obj
. sclust
); /* Update file allocation information */
4164 st_dword ( dir
+ DIR_FileSize
, ( DWORD
) fp
-> obj
. objsize
); /* Update file size */
4165 st_dword ( dir
+ DIR_ModTime
, tm
); /* Update modified time */
4166 st_word ( dir
+ DIR_LstAccDate
, 0 );
4168 res
= sync_fs ( fs
); /* Restore it to the directory */
4169 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
4178 #endif /* !FF_FS_READONLY */
4183 /*-----------------------------------------------------------------------*/
4185 /*-----------------------------------------------------------------------*/
4188 FIL
* fp
/* Pointer to the file object to be closed */
4195 res
= f_sync ( fp
); /* Flush cached data */
4199 res
= validate (& fp
-> obj
, & fs
); /* Lock volume */
4202 res
= dec_lock ( fp
-> obj
. lockid
); /* Decrement file open counter */
4203 if ( res
== FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4205 fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4208 unlock_fs ( fs
, FR_OK
); /* Unlock volume */
4218 #if FF_FS_RPATH >= 1
4219 /*-----------------------------------------------------------------------*/
4220 /* Change Current Directory or Current Drive, Get Current Directory */
4221 /*-----------------------------------------------------------------------*/
4224 const TCHAR
* path
/* Drive number to set */
4230 /* Get logical drive number */
4231 vol
= get_ldnumber (& path
);
4232 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
4233 CurrVol
= ( BYTE
) vol
; /* Set it as current volume */
4241 const TCHAR
* path
/* Pointer to the directory path */
4244 #if FF_STR_VOLUME_ID == 2
4253 /* Get logical drive */
4254 res
= mount_volume (& path
, & fs
, 0 );
4258 res
= follow_path (& dj
, path
); /* Follow the path */
4259 if ( res
== FR_OK
) { /* Follow completed */
4260 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it the start directory itself? */
4261 fs
-> cdir
= dj
. obj
. sclust
;
4263 if ( fs
-> fs_type
== FS_EXFAT
) {
4264 fs
-> cdc_scl
= dj
. obj
. c_scl
;
4265 fs
-> cdc_size
= dj
. obj
. c_size
;
4266 fs
-> cdc_ofs
= dj
. obj
. c_ofs
;
4270 if ( dj
. obj
. attr
& AM_DIR
) { /* It is a sub-directory */
4272 if ( fs
-> fs_type
== FS_EXFAT
) {
4273 fs
-> cdir
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Sub-directory cluster */
4274 fs
-> cdc_scl
= dj
. obj
. sclust
; /* Save containing directory information */
4275 fs
-> cdc_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
4276 fs
-> cdc_ofs
= dj
. blk_ofs
;
4280 fs
-> cdir
= ld_clust ( fs
, dj
. dir
); /* Sub-directory cluster */
4283 res
= FR_NO_PATH
; /* Reached but a file */
4288 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4289 #if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */
4291 for ( i
= FF_VOLUMES
- 1 ; i
&& fs
!= FatFs
[ i
]; i
--) ; /* Set current drive */
4301 #if FF_FS_RPATH >= 2
4303 TCHAR
* buff
, /* Pointer to the directory path */
4304 UINT len
/* Size of buff in unit of TCHAR */
4315 #if FF_STR_VOLUME_ID
4323 /* Get logical drive */
4324 buff
[ 0 ] = 0 ; /* Set null string to get current volume */
4325 res
= mount_volume (( const TCHAR
**)& buff
, & fs
, 0 ); /* Get current volume */
4330 /* Follow parent directories and create the path */
4331 i
= len
; /* Bottom of buffer (directory stack base) */
4332 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) { /* (Cannot do getcwd on exFAT and returns root path) */
4333 dj
. obj
. sclust
= fs
-> cdir
; /* Start to follow upper directory from current directory */
4334 while (( ccl
= dj
. obj
. sclust
) != 0 ) { /* Repeat while current directory is a sub-directory */
4335 res
= dir_sdi (& dj
, 1 * SZDIRE
); /* Get parent directory */
4336 if ( res
!= FR_OK
) break ;
4337 res
= move_window ( fs
, dj
. sect
);
4338 if ( res
!= FR_OK
) break ;
4339 dj
. obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Goto parent directory */
4340 res
= dir_sdi (& dj
, 0 );
4341 if ( res
!= FR_OK
) break ;
4342 do { /* Find the entry links to the child directory */
4343 res
= DIR_READ_FILE (& dj
);
4344 if ( res
!= FR_OK
) break ;
4345 if ( ccl
== ld_clust ( fs
, dj
. dir
)) break ; /* Found the entry */
4346 res
= dir_next (& dj
, 0 );
4347 } while ( res
== FR_OK
);
4348 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be 'not found'. */
4349 if ( res
!= FR_OK
) break ;
4350 get_fileinfo (& dj
, & fno
); /* Get the directory name and push it to the buffer */
4351 for ( n
= 0 ; fno
. fname
[ n
]; n
++) ; /* Name length */
4352 if ( i
< n
+ 1 ) { /* Insufficient space to store the path name? */
4353 res
= FR_NOT_ENOUGH_CORE
; break ;
4355 while ( n
) buff
[-- i
] = fno
. fname
[-- n
]; /* Stack the name */
4360 if ( i
== len
) buff
[-- i
] = '/' ; /* Is it the root-directory? */
4361 #if FF_VOLUMES >= 2 /* Put drive prefix */
4363 #if FF_STR_VOLUME_ID >= 1 /* String volume ID */
4364 for ( n
= 0 , vp
= ( const char *) VolumeStr
[ CurrVol
]; vp
[ n
]; n
++) ;
4366 if ( FF_STR_VOLUME_ID
== 2 ) * tp
++ = ( TCHAR
) '/' ;
4367 for ( vl
= 0 ; vl
< n
; * tp
++ = ( TCHAR
) vp
[ vl
], vl
++) ;
4368 if ( FF_STR_VOLUME_ID
== 1 ) * tp
++ = ( TCHAR
) ':' ;
4371 #else /* Numeric volume ID */
4373 * tp
++ = ( TCHAR
) '0' + CurrVol
;
4378 if ( vl
== 0 ) res
= FR_NOT_ENOUGH_CORE
;
4380 /* Add current directory path */
4382 do * tp
++ = buff
[ i
++]; while ( i
< len
); /* Copy stacked path string */
4392 #endif /* FF_FS_RPATH >= 2 */
4393 #endif /* FF_FS_RPATH >= 1 */
4397 #if FF_FS_MINIMIZE <= 2
4398 /*-----------------------------------------------------------------------*/
4399 /* Seek File Read/Write Pointer */
4400 /*-----------------------------------------------------------------------*/
4403 FIL
* fp
, /* Pointer to the file object */
4404 FSIZE_t ofs
/* File pointer from top of file */
4413 DWORD cl
, pcl
, ncl
, tcl
, tlen
, ulen
, * tbl
;
4417 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4418 if ( res
== FR_OK
) res
= ( FRESULT
) fp
-> err
;
4419 #if FF_FS_EXFAT && !FF_FS_READONLY
4420 if ( res
== FR_OK
&& fs
-> fs_type
== FS_EXFAT
) {
4421 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
4424 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
4427 if ( fp
-> cltbl
) { /* Fast seek */
4428 if ( ofs
== CREATE_LINKMAP
) { /* Create CLMT */
4430 tlen
= * tbl
++; ulen
= 2 ; /* Given table size and required table size */
4431 cl
= fp
-> obj
. sclust
; /* Origin of the chain */
4434 /* Get a fragment */
4435 tcl
= cl
; ncl
= 0 ; ulen
+= 2 ; /* Top, length and used items */
4438 cl
= get_fat (& fp
-> obj
, cl
);
4439 if ( cl
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
4440 if ( cl
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4441 } while ( cl
== pcl
+ 1 );
4442 if ( ulen
<= tlen
) { /* Store the length and top of the fragment */
4443 * tbl
++ = ncl
; * tbl
++ = tcl
;
4445 } while ( cl
< fs
-> n_fatent
); /* Repeat until end of chain */
4447 * fp
-> cltbl
= ulen
; /* Number of items used */
4449 * tbl
= 0 ; /* Terminate table */
4451 res
= FR_NOT_ENOUGH_CORE
; /* Given table size is smaller than required */
4453 } else { /* Fast seek */
4454 if ( ofs
> fp
-> obj
. objsize
) ofs
= fp
-> obj
. objsize
; /* Clip offset at the file size */
4455 fp
-> fptr
= ofs
; /* Set file pointer */
4457 fp
-> clust
= clmt_clust ( fp
, ofs
- 1 );
4458 dsc
= clst2sect ( fs
, fp
-> clust
);
4459 if ( dsc
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4460 dsc
+= ( DWORD
)(( ofs
- 1 ) / SS ( fs
)) & ( fs
-> csize
- 1 );
4461 if ( fp
-> fptr
% SS ( fs
) && dsc
!= fp
-> sect
) { /* Refill sector cache if needed */
4464 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4465 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4466 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4469 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, dsc
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Load current sector */
4481 if ( fs
-> fs_type
!= FS_EXFAT
&& ofs
>= 0x100000000 ) ofs
= 0xFFFFFFFF ; /* Clip at 4 GiB - 1 if at FATxx */
4483 if ( ofs
> fp
-> obj
. objsize
&& ( FF_FS_READONLY
|| !( fp
-> flag
& FA_WRITE
))) { /* In read-only mode, clip offset with the file size */
4484 ofs
= fp
-> obj
. objsize
;
4487 fp
-> fptr
= nsect
= 0 ;
4489 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size (byte) */
4491 ( ofs
- 1 ) / bcs
>= ( ifptr
- 1 ) / bcs
) { /* When seek to same or following cluster, */
4492 fp
-> fptr
= ( ifptr
- 1 ) & ~( FSIZE_t
)( bcs
- 1 ); /* start from the current cluster */
4495 } else { /* When seek to back cluster, */
4496 clst
= fp
-> obj
. sclust
; /* start from the first cluster */
4498 if ( clst
== 0 ) { /* If no cluster chain, create a new chain */
4499 clst
= create_chain (& fp
-> obj
, 0 );
4500 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
4501 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4502 fp
-> obj
. sclust
= clst
;
4508 while ( ofs
> bcs
) { /* Cluster following loop */
4509 ofs
-= bcs
; fp
-> fptr
+= bcs
;
4511 if ( fp
-> flag
& FA_WRITE
) { /* Check if in write mode or not */
4512 if ( FF_FS_EXFAT
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* No FAT chain object needs correct objsize to generate FAT value */
4513 fp
-> obj
. objsize
= fp
-> fptr
;
4514 fp
-> flag
|= FA_MODIFIED
;
4516 clst
= create_chain (& fp
-> obj
, clst
); /* Follow chain with forceed stretch */
4517 if ( clst
== 0 ) { /* Clip file size in case of disk full */
4523 clst
= get_fat (& fp
-> obj
, clst
); /* Follow cluster chain if not in write mode */
4525 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4526 if ( clst
<= 1 || clst
>= fs
-> n_fatent
) ABORT ( fs
, FR_INT_ERR
);
4531 nsect
= clst2sect ( fs
, clst
); /* Current sector */
4532 if ( nsect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4533 nsect
+= ( DWORD
)( ofs
/ SS ( fs
));
4537 if (! FF_FS_READONLY
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* Set file change flag if the file size is extended */
4538 fp
-> obj
. objsize
= fp
-> fptr
;
4539 fp
-> flag
|= FA_MODIFIED
;
4541 if ( fp
-> fptr
% SS ( fs
) && nsect
!= fp
-> sect
) { /* Fill sector cache if needed */
4544 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4545 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4546 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4549 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, nsect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
4560 #if FF_FS_MINIMIZE <= 1
4561 /*-----------------------------------------------------------------------*/
4562 /* Create a Directory Object */
4563 /*-----------------------------------------------------------------------*/
4566 DIR * dp
, /* Pointer to directory object to create */
4567 const TCHAR
* path
/* Pointer to the directory path */
4575 if (! dp
) return FR_INVALID_OBJECT
;
4577 /* Get logical drive */
4578 res
= mount_volume (& path
, & fs
, 0 );
4582 res
= follow_path ( dp
, path
); /* Follow the path to the directory */
4583 if ( res
== FR_OK
) { /* Follow completed */
4584 if (!( dp
-> fn
[ NSFLAG
] & NS_NONAME
)) { /* It is not the origin directory itself */
4585 if ( dp
-> obj
. attr
& AM_DIR
) { /* This object is a sub-directory */
4587 if ( fs
-> fs_type
== FS_EXFAT
) {
4588 dp
-> obj
. c_scl
= dp
-> obj
. sclust
; /* Get containing directory inforamation */
4589 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
4590 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
4591 init_alloc_info ( fs
, & dp
-> obj
); /* Get object allocation info */
4595 dp
-> obj
. sclust
= ld_clust ( fs
, dp
-> dir
); /* Get object allocation info */
4597 } else { /* This object is a file */
4602 dp
-> obj
. id
= fs
-> id
;
4603 res
= dir_sdi ( dp
, 0 ); /* Rewind directory */
4606 if ( dp
-> obj
. sclust
!= 0 ) {
4607 dp
-> obj
. lockid
= inc_lock ( dp
, 0 ); /* Lock the sub directory */
4608 if (! dp
-> obj
. lockid
) res
= FR_TOO_MANY_OPEN_FILES
;
4610 dp
-> obj
. lockid
= 0 ; /* Root directory need not to be locked */
4617 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4619 if ( res
!= FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate the directory object if function faild */
4627 /*-----------------------------------------------------------------------*/
4628 /* Close Directory */
4629 /*-----------------------------------------------------------------------*/
4631 FRESULT
f_closedir (
4632 DIR * dp
/* Pointer to the directory object to be closed */
4639 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the file object */
4642 if ( dp
-> obj
. lockid
) res
= dec_lock ( dp
-> obj
. lockid
); /* Decrement sub-directory open counter */
4643 if ( res
== FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4645 dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4648 unlock_fs ( fs
, FR_OK
); /* Unlock volume */
4657 /*-----------------------------------------------------------------------*/
4658 /* Read Directory Entries in Sequence */
4659 /*-----------------------------------------------------------------------*/
4662 DIR * dp
, /* Pointer to the open directory object */
4663 FILINFO
* fno
/* Pointer to file information to return */
4671 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the directory object */
4674 res
= dir_sdi ( dp
, 0 ); /* Rewind the directory object */
4677 res
= DIR_READ_FILE ( dp
); /* Read an item */
4678 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory */
4679 if ( res
== FR_OK
) { /* A valid entry is found */
4680 get_fileinfo ( dp
, fno
); /* Get the object information */
4681 res
= dir_next ( dp
, 0 ); /* Increment index for next */
4682 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory now */
4693 /*-----------------------------------------------------------------------*/
4694 /* Find Next File */
4695 /*-----------------------------------------------------------------------*/
4697 FRESULT
f_findnext (
4698 DIR * dp
, /* Pointer to the open directory object */
4699 FILINFO
* fno
/* Pointer to the file information structure */
4706 res
= f_readdir ( dp
, fno
); /* Get a directory item */
4707 if ( res
!= FR_OK
|| ! fno
|| ! fno
-> fname
[ 0 ]) break ; /* Terminate if any error or end of directory */
4708 if ( pattern_matching ( dp
-> pat
, fno
-> fname
, 0 , 0 )) break ; /* Test for the file name */
4709 #if FF_USE_LFN && FF_USE_FIND == 2
4710 if ( pattern_matching ( dp
-> pat
, fno
-> altname
, 0 , 0 )) break ; /* Test for alternative name if exist */
4718 /*-----------------------------------------------------------------------*/
4719 /* Find First File */
4720 /*-----------------------------------------------------------------------*/
4722 FRESULT
f_findfirst (
4723 DIR * dp
, /* Pointer to the blank directory object */
4724 FILINFO
* fno
, /* Pointer to the file information structure */
4725 const TCHAR
* path
, /* Pointer to the directory to open */
4726 const TCHAR
* pattern
/* Pointer to the matching pattern */
4732 dp
-> pat
= pattern
; /* Save pointer to pattern string */
4733 res
= f_opendir ( dp
, path
); /* Open the target directory */
4735 res
= f_findnext ( dp
, fno
); /* Find the first item */
4740 #endif /* FF_USE_FIND */
4744 #if FF_FS_MINIMIZE == 0
4745 /*-----------------------------------------------------------------------*/
4746 /* Get File Status */
4747 /*-----------------------------------------------------------------------*/
4750 const TCHAR
* path
, /* Pointer to the file path */
4751 FILINFO
* fno
/* Pointer to file information to return */
4759 /* Get logical drive */
4760 res
= mount_volume (& path
, & dj
. obj
. fs
, 0 );
4762 INIT_NAMBUF ( dj
. obj
. fs
);
4763 res
= follow_path (& dj
, path
); /* Follow the file path */
4764 if ( res
== FR_OK
) { /* Follow completed */
4765 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* It is origin directory */
4766 res
= FR_INVALID_NAME
;
4767 } else { /* Found an object */
4768 if ( fno
) get_fileinfo (& dj
, fno
);
4774 LEAVE_FF ( dj
. obj
. fs
, res
);
4780 /*-----------------------------------------------------------------------*/
4781 /* Get Number of Free Clusters */
4782 /*-----------------------------------------------------------------------*/
4785 const TCHAR
* path
, /* Logical drive number */
4786 DWORD
* nclst
, /* Pointer to a variable to return number of free clusters */
4787 FATFS
** fatfs
/* Pointer to return pointer to corresponding filesystem object */
4792 DWORD nfree
, clst
, stat
;
4798 /* Get logical drive */
4799 res
= mount_volume (& path
, & fs
, 0 );
4801 * fatfs
= fs
; /* Return ptr to the fs object */
4802 /* If free_clst is valid, return it without full FAT scan */
4803 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) {
4804 * nclst
= fs
-> free_clst
;
4806 /* Scan FAT to obtain number of free clusters */
4808 if ( fs
-> fs_type
== FS_FAT12
) { /* FAT12: Scan bit field FAT entries */
4809 clst
= 2 ; obj
. fs
= fs
;
4811 stat
= get_fat (& obj
, clst
);
4812 if ( stat
== 0xFFFFFFFF ) { res
= FR_DISK_ERR
; break ; }
4813 if ( stat
== 1 ) { res
= FR_INT_ERR
; break ; }
4814 if ( stat
== 0 ) nfree
++;
4815 } while (++ clst
< fs
-> n_fatent
);
4818 if ( fs
-> fs_type
== FS_EXFAT
) { /* exFAT: Scan allocation bitmap */
4822 clst
= fs
-> n_fatent
- 2 ; /* Number of clusters */
4823 sect
= fs
-> bitbase
; /* Bitmap sector */
4824 i
= 0 ; /* Offset in the sector */
4825 do { /* Counts numbuer of bits with zero in the bitmap */
4827 res
= move_window ( fs
, sect
++);
4828 if ( res
!= FR_OK
) break ;
4830 for ( b
= 8 , bm
= fs
-> win
[ i
]; b
&& clst
; b
--, clst
--) {
4831 if (!( bm
& 1 )) nfree
++;
4834 i
= ( i
+ 1 ) % SS ( fs
);
4838 { /* FAT16/32: Scan WORD/DWORD FAT entries */
4839 clst
= fs
-> n_fatent
; /* Number of entries */
4840 sect
= fs
-> fatbase
; /* Top of the FAT */
4841 i
= 0 ; /* Offset in the sector */
4842 do { /* Counts numbuer of entries with zero in the FAT */
4844 res
= move_window ( fs
, sect
++);
4845 if ( res
!= FR_OK
) break ;
4847 if ( fs
-> fs_type
== FS_FAT16
) {
4848 if ( ld_word ( fs
-> win
+ i
) == 0 ) nfree
++;
4851 if (( ld_dword ( fs
-> win
+ i
) & 0x0FFFFFFF ) == 0 ) nfree
++;
4858 * nclst
= nfree
; /* Return the free clusters */
4859 fs
-> free_clst
= nfree
; /* Now free_clst is valid */
4860 fs
-> fsi_flag
|= 1 ; /* FAT32: FSInfo is to be updated */
4870 /*-----------------------------------------------------------------------*/
4872 /*-----------------------------------------------------------------------*/
4874 FRESULT
f_truncate (
4875 FIL
* fp
/* Pointer to the file object */
4883 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4884 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
4885 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
4887 if ( fp
-> fptr
< fp
-> obj
. objsize
) { /* Process when fptr is not on the eof */
4888 if ( fp
-> fptr
== 0 ) { /* When set file size to zero, remove entire cluster chain */
4889 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
4891 } else { /* When truncate a part of the file, remove remaining clusters */
4892 ncl
= get_fat (& fp
-> obj
, fp
-> clust
);
4894 if ( ncl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
4895 if ( ncl
== 1 ) res
= FR_INT_ERR
;
4896 if ( res
== FR_OK
&& ncl
< fs
-> n_fatent
) {
4897 res
= remove_chain (& fp
-> obj
, ncl
, fp
-> clust
);
4900 fp
-> obj
. objsize
= fp
-> fptr
; /* Set file size to current read/write point */
4901 fp
-> flag
|= FA_MODIFIED
;
4903 if ( res
== FR_OK
&& ( fp
-> flag
& FA_DIRTY
)) {
4904 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) {
4907 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4911 if ( res
!= FR_OK
) ABORT ( fs
, res
);
4920 /*-----------------------------------------------------------------------*/
4921 /* Delete a File/Directory */
4922 /*-----------------------------------------------------------------------*/
4925 const TCHAR
* path
/* Pointer to the file or directory path */
4938 /* Get logical drive */
4939 res
= mount_volume (& path
, & fs
, FA_WRITE
);
4943 res
= follow_path (& dj
, path
); /* Follow the file path */
4944 if ( FF_FS_RPATH
&& res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) {
4945 res
= FR_INVALID_NAME
; /* Cannot remove dot entry */
4948 if ( res
== FR_OK
) res
= chk_lock (& dj
, 2 ); /* Check if it is an open object */
4950 if ( res
== FR_OK
) { /* The object is accessible */
4951 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) {
4952 res
= FR_INVALID_NAME
; /* Cannot remove the origin directory */
4954 if ( dj
. obj
. attr
& AM_RDO
) {
4955 res
= FR_DENIED
; /* Cannot remove R/O object */
4961 if ( fs
-> fs_type
== FS_EXFAT
) {
4962 init_alloc_info ( fs
, & obj
);
4967 dclst
= ld_clust ( fs
, dj
. dir
);
4969 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a sub-directory? */
4970 #if FF_FS_RPATH != 0
4971 if ( dclst
== fs
-> cdir
) { /* Is it the current directory? */
4976 sdj
. obj
. fs
= fs
; /* Open the sub-directory */
4977 sdj
. obj
. sclust
= dclst
;
4979 if ( fs
-> fs_type
== FS_EXFAT
) {
4980 sdj
. obj
. objsize
= obj
. objsize
;
4981 sdj
. obj
. stat
= obj
. stat
;
4984 res
= dir_sdi (& sdj
, 0 );
4986 res
= DIR_READ_FILE (& sdj
); /* Test if the directory is empty */
4987 if ( res
== FR_OK
) res
= FR_DENIED
; /* Not empty? */
4988 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Empty? */
4994 res
= dir_remove (& dj
); /* Remove the directory entry */
4995 if ( res
== FR_OK
&& dclst
!= 0 ) { /* Remove the cluster chain if exist */
4997 res
= remove_chain (& obj
, dclst
, 0 );
4999 res
= remove_chain (& dj
. obj
, dclst
, 0 );
5002 if ( res
== FR_OK
) res
= sync_fs ( fs
);
5014 /*-----------------------------------------------------------------------*/
5015 /* Create a Directory */
5016 /*-----------------------------------------------------------------------*/
5019 const TCHAR
* path
/* Pointer to the directory path */
5030 res
= mount_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5034 res
= follow_path (& dj
, path
); /* Follow the file path */
5035 if ( res
== FR_OK
) res
= FR_EXIST
; /* Name collision? */
5036 if ( FF_FS_RPATH
&& res
== FR_NO_FILE
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) { /* Invalid name? */
5037 res
= FR_INVALID_NAME
;
5039 if ( res
== FR_NO_FILE
) { /* It is clear to create a new directory */
5040 sobj
. fs
= fs
; /* New object id to create a new chain */
5041 dcl
= create_chain (& sobj
, 0 ); /* Allocate a cluster for the new directory */
5043 if ( dcl
== 0 ) res
= FR_DENIED
; /* No space to allocate a new cluster? */
5044 if ( dcl
== 1 ) res
= FR_INT_ERR
; /* Any insanity? */
5045 if ( dcl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
; /* Disk error? */
5048 res
= dir_clear ( fs
, dcl
); /* Clean up the new table */
5050 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) { /* Create dot entries (FAT only) */
5051 mem_set ( fs
-> win
+ DIR_Name
, ' ' , 11 ); /* Create "." entry */
5052 fs
-> win
[ DIR_Name
] = '.' ;
5053 fs
-> win
[ DIR_Attr
] = AM_DIR
;
5054 st_dword ( fs
-> win
+ DIR_ModTime
, tm
);
5055 st_clust ( fs
, fs
-> win
, dcl
);
5056 mem_cpy ( fs
-> win
+ SZDIRE
, fs
-> win
, SZDIRE
); /* Create ".." entry */
5057 fs
-> win
[ SZDIRE
+ 1 ] = '.' ; pcl
= dj
. obj
. sclust
;
5058 st_clust ( fs
, fs
-> win
+ SZDIRE
, pcl
);
5061 res
= dir_register (& dj
); /* Register the object to the parent directoy */
5066 if ( fs
-> fs_type
== FS_EXFAT
) { /* Initialize directory entry block */
5067 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Created time */
5068 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, dcl
); /* Table start cluster */
5069 st_dword ( fs
-> dirbuf
+ XDIR_FileSize
, ( DWORD
) fs
-> csize
* SS ( fs
)); /* Directory size needs to be valid */
5070 st_dword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, ( DWORD
) fs
-> csize
* SS ( fs
));
5071 fs
-> dirbuf
[ XDIR_GenFlags
] = 3 ; /* Initialize the object flag */
5072 fs
-> dirbuf
[ XDIR_Attr
] = AM_DIR
; /* Attribute */
5073 res
= store_xdir (& dj
);
5077 st_dword ( dj
. dir
+ DIR_ModTime
, tm
); /* Created time */
5078 st_clust ( fs
, dj
. dir
, dcl
); /* Table start cluster */
5079 dj
. dir
[ DIR_Attr
] = AM_DIR
; /* Attribute */
5086 remove_chain (& sobj
, dcl
, 0 ); /* Could not register, remove the allocated cluster */
5098 /*-----------------------------------------------------------------------*/
5099 /* Rename a File/Directory */
5100 /*-----------------------------------------------------------------------*/
5103 const TCHAR
* path_old
, /* Pointer to the object name to be renamed */
5104 const TCHAR
* path_new
/* Pointer to the new name */
5110 BYTE buf
[ FF_FS_EXFAT
? SZDIRE
* 2 : SZDIRE
], * dir
;
5115 get_ldnumber (& path_new
); /* Snip the drive number of new name off */
5116 res
= mount_volume (& path_old
, & fs
, FA_WRITE
); /* Get logical drive of the old object */
5120 res
= follow_path (& djo
, path_old
); /* Check old object */
5121 if ( res
== FR_OK
&& ( djo
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check validity of name */
5124 res
= chk_lock (& djo
, 2 );
5127 if ( res
== FR_OK
) { /* Object to be renamed is found */
5129 if ( fs
-> fs_type
== FS_EXFAT
) { /* At exFAT volume */
5133 mem_cpy ( buf
, fs
-> dirbuf
, SZDIRE
* 2 ); /* Save 85+C0 entry of old object */
5134 mem_cpy (& djn
, & djo
, sizeof djo
);
5135 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
5136 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
5137 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
5139 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
5140 res
= dir_register (& djn
); /* Register the new entry */
5142 nf
= fs
-> dirbuf
[ XDIR_NumSec
]; nn
= fs
-> dirbuf
[ XDIR_NumName
];
5143 nh
= ld_word ( fs
-> dirbuf
+ XDIR_NameHash
);
5144 mem_cpy ( fs
-> dirbuf
, buf
, SZDIRE
* 2 ); /* Restore 85+C0 entry */
5145 fs
-> dirbuf
[ XDIR_NumSec
] = nf
; fs
-> dirbuf
[ XDIR_NumName
] = nn
;
5146 st_word ( fs
-> dirbuf
+ XDIR_NameHash
, nh
);
5147 if (!( fs
-> dirbuf
[ XDIR_Attr
] & AM_DIR
)) fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
5148 /* Start of critical section where an interruption can cause a cross-link */
5149 res
= store_xdir (& djn
);
5154 { /* At FAT/FAT32 volume */
5155 mem_cpy ( buf
, djo
. dir
, SZDIRE
); /* Save directory entry of the object */
5156 mem_cpy (& djn
, & djo
, sizeof ( DIR )); /* Duplicate the directory object */
5157 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
5158 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
5159 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
5161 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
5162 res
= dir_register (& djn
); /* Register the new entry */
5164 dir
= djn
. dir
; /* Copy directory entry of the object except name */
5165 mem_cpy ( dir
+ 13 , buf
+ 13 , SZDIRE
- 13 );
5166 dir
[ DIR_Attr
] = buf
[ DIR_Attr
];
5167 if (!( dir
[ DIR_Attr
] & AM_DIR
)) dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
5169 if (( dir
[ DIR_Attr
] & AM_DIR
) && djo
. obj
. sclust
!= djn
. obj
. sclust
) { /* Update .. entry in the sub-directory if needed */
5170 sect
= clst2sect ( fs
, ld_clust ( fs
, dir
));
5174 /* Start of critical section where an interruption can cause a cross-link */
5175 res
= move_window ( fs
, sect
);
5176 dir
= fs
-> win
+ SZDIRE
* 1 ; /* Ptr to .. entry */
5177 if ( res
== FR_OK
&& dir
[ 1 ] == '.' ) {
5178 st_clust ( fs
, dir
, djn
. obj
. sclust
);
5187 res
= dir_remove (& djo
); /* Remove old entry */
5192 /* End of the critical section */
5200 #endif /* !FF_FS_READONLY */
5201 #endif /* FF_FS_MINIMIZE == 0 */
5202 #endif /* FF_FS_MINIMIZE <= 1 */
5203 #endif /* FF_FS_MINIMIZE <= 2 */
5207 #if FF_USE_CHMOD && !FF_FS_READONLY
5208 /*-----------------------------------------------------------------------*/
5209 /* Change Attribute */
5210 /*-----------------------------------------------------------------------*/
5213 const TCHAR
* path
, /* Pointer to the file path */
5214 BYTE attr
, /* Attribute bits */
5215 BYTE mask
/* Attribute mask to change */
5224 res
= mount_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5228 res
= follow_path (& dj
, path
); /* Follow the file path */
5229 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5231 mask
&= AM_RDO
| AM_HID
| AM_SYS
| AM_ARC
; /* Valid attribute mask */
5233 if ( fs
-> fs_type
== FS_EXFAT
) {
5234 fs
-> dirbuf
[ XDIR_Attr
] = ( attr
& mask
) | ( fs
-> dirbuf
[ XDIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5235 res
= store_xdir (& dj
);
5239 dj
. dir
[ DIR_Attr
] = ( attr
& mask
) | ( dj
. dir
[ DIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5255 /*-----------------------------------------------------------------------*/
5256 /* Change Timestamp */
5257 /*-----------------------------------------------------------------------*/
5260 const TCHAR
* path
, /* Pointer to the file/directory name */
5261 const FILINFO
* fno
/* Pointer to the timestamp to be set */
5270 res
= mount_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5274 res
= follow_path (& dj
, path
); /* Follow the file path */
5275 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5278 if ( fs
-> fs_type
== FS_EXFAT
) {
5279 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5280 res
= store_xdir (& dj
);
5284 st_dword ( dj
. dir
+ DIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5297 #endif /* FF_USE_CHMOD && !FF_FS_READONLY */
5302 /*-----------------------------------------------------------------------*/
5303 /* Get Volume Label */
5304 /*-----------------------------------------------------------------------*/
5306 FRESULT
f_getlabel (
5307 const TCHAR
* path
, /* Logical drive number */
5308 TCHAR
* label
, /* Buffer to store the volume label */
5309 DWORD
* vsn
/* Variable to store the volume serial number */
5318 /* Get logical drive */
5319 res
= mount_volume (& path
, & fs
, 0 );
5321 /* Get volume label */
5322 if ( res
== FR_OK
&& label
) {
5323 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5324 res
= dir_sdi (& dj
, 0 );
5326 res
= DIR_READ_LABEL (& dj
); /* Find a volume label entry */
5329 if ( fs
-> fs_type
== FS_EXFAT
) {
5332 for ( si
= di
= hs
= 0 ; si
< dj
. dir
[ XDIR_NumLabel
]; si
++) { /* Extract volume label from 83 entry */
5333 wc
= ld_word ( dj
. dir
+ XDIR_Label
+ si
* 2 );
5334 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is the code a surrogate? */
5337 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & label
[ di
], 4 );
5338 if ( wc
== 0 ) { di
= 0 ; break ; }
5342 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
5347 si
= di
= 0 ; /* Extract volume label from AM_VOL entry */
5350 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
5351 if ( dbc_1st (( BYTE
) wc
) && si
< 11 ) wc
= wc
<< 8 | dj
. dir
[ si
++]; /* Is it a DBC? */
5352 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* Convert it into Unicode */
5353 if ( wc
!= 0 ) wc
= put_utf ( wc
, & label
[ di
], 4 ); /* Put it in Unicode */
5354 if ( wc
== 0 ) { di
= 0 ; break ; }
5356 #else /* ANSI/OEM output */
5357 label
[ di
++] = ( TCHAR
) wc
;
5360 do { /* Truncate trailing spaces */
5363 } while ( label
[-- di
] == ' ' );
5367 if ( res
== FR_NO_FILE
) { /* No label entry and return nul string */
5373 /* Get volume serial number */
5374 if ( res
== FR_OK
&& vsn
) {
5375 res
= move_window ( fs
, fs
-> volbase
);
5377 switch ( fs
-> fs_type
) {
5379 di
= BPB_VolIDEx
; break ;
5382 di
= BS_VolID32
; break ;
5387 * vsn
= ld_dword ( fs
-> win
+ di
);
5397 /*-----------------------------------------------------------------------*/
5398 /* Set Volume Label */
5399 /*-----------------------------------------------------------------------*/
5401 FRESULT
f_setlabel (
5402 const TCHAR
* label
/* Volume label to set with heading logical drive number */
5411 static const char badchr
[] = "+.,;=[]/ \\\" *:<> \? | \x7F " ; /* [0..] for FAT, [7..] for exFAT */
5416 /* Get logical drive */
5417 res
= mount_volume (& label
, & fs
, FA_WRITE
);
5418 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
5421 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
5422 mem_set ( dirvn
, 0 , 22 );
5424 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5425 dc
= tchar2uni (& label
); /* Get a Unicode character */
5426 if ( dc
>= 0x10000 ) {
5427 if ( dc
== 0xFFFFFFFF || di
>= 10 ) { /* Wrong surrogate or buffer overflow */
5430 st_word ( dirvn
+ di
* 2 , ( WCHAR
)( dc
>> 16 )); di
++;
5433 if ( dc
== 0 || chk_chr ( badchr
+ 7 , ( int ) dc
) || di
>= 11 ) { /* Check validity of the volume label */
5434 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5436 st_word ( dirvn
+ di
* 2 , ( WCHAR
) dc
); di
++;
5440 { /* On the FAT/FAT32 volume */
5441 mem_set ( dirvn
, ' ' , 11 );
5443 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5445 dc
= tchar2uni (& label
);
5446 wc
= ( dc
< 0x10000 ) ? ff_uni2oem ( ff_wtoupper ( dc
), CODEPAGE
) : 0 ;
5447 #else /* ANSI/OEM input */
5448 wc
= ( BYTE
)* label
++;
5449 if ( dbc_1st (( BYTE
) wc
)) wc
= dbc_2nd (( BYTE
)* label
) ? wc
<< 8 | ( BYTE
)* label
++ : 0 ;
5450 if ( IsLower ( wc
)) wc
-= 0x20 ; /* To upper ASCII characters */
5451 #if FF_CODE_PAGE == 0
5452 if ( ExCvt
&& wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5453 #elif FF_CODE_PAGE < 900
5454 if ( wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5457 if ( wc
== 0 || chk_chr ( badchr
+ 0 , ( int ) wc
) || di
>= ( UINT
)(( wc
>= 0x100 ) ? 10 : 11 )) { /* Reject invalid characters for volume label */
5458 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5460 if ( wc
>= 0x100 ) dirvn
[ di
++] = ( BYTE
)( wc
>> 8 );
5461 dirvn
[ di
++] = ( BYTE
) wc
;
5463 if ( dirvn
[ 0 ] == DDEM
) LEAVE_FF ( fs
, FR_INVALID_NAME
); /* Reject illegal name (heading DDEM) */
5464 while ( di
&& dirvn
[ di
- 1 ] == ' ' ) di
--; /* Snip trailing spaces */
5467 /* Set volume label */
5468 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5469 res
= dir_sdi (& dj
, 0 );
5471 res
= DIR_READ_LABEL (& dj
); /* Get volume label entry */
5473 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5474 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
; /* Change the volume label */
5475 mem_cpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5478 mem_cpy ( dj
. dir
, dirvn
, 11 ); /* Change the volume label */
5480 dj
. dir
[ DIR_Name
] = DDEM
; /* Remove the volume label */
5485 } else { /* No volume label entry or an error */
5486 if ( res
== FR_NO_FILE
) {
5488 if ( di
!= 0 ) { /* Create a volume label entry */
5489 res
= dir_alloc (& dj
, 1 ); /* Allocate an entry */
5491 mem_set ( dj
. dir
, 0 , SZDIRE
); /* Clean the entry */
5492 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5493 dj
. dir
[ XDIR_Type
] = ET_VLABEL
; /* Create volume label entry */
5494 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
;
5495 mem_cpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5497 dj
. dir
[ DIR_Attr
] = AM_VOL
; /* Create volume label entry */
5498 mem_cpy ( dj
. dir
, dirvn
, 11 );
5511 #endif /* !FF_FS_READONLY */
5512 #endif /* FF_USE_LABEL */
5516 #if FF_USE_EXPAND && !FF_FS_READONLY
5517 /*-----------------------------------------------------------------------*/
5518 /* Allocate a Contiguous Blocks to the File */
5519 /*-----------------------------------------------------------------------*/
5522 FIL
* fp
, /* Pointer to the file object */
5523 FSIZE_t fsz
, /* File size to be expanded to */
5524 BYTE opt
/* Operation mode 0:Find and prepare or 1:Find and allocate */
5529 DWORD n
, clst
, stcl
, scl
, ncl
, tcl
, lclst
;
5532 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5533 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5534 if ( fsz
== 0 || fp
-> obj
. objsize
!= 0 || !( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
);
5536 if ( fs
-> fs_type
!= FS_EXFAT
&& fsz
>= 0x100000000 ) LEAVE_FF ( fs
, FR_DENIED
); /* Check if in size limit */
5538 n
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size */
5539 tcl
= ( DWORD
)( fsz
/ n
) + (( fsz
& ( n
- 1 )) ? 1 : 0 ); /* Number of clusters required */
5540 stcl
= fs
-> last_clst
; lclst
= 0 ;
5541 if ( stcl
< 2 || stcl
>= fs
-> n_fatent
) stcl
= 2 ;
5544 if ( fs
-> fs_type
== FS_EXFAT
) {
5545 scl
= find_bitmap ( fs
, stcl
, tcl
); /* Find a contiguous cluster block */
5546 if ( scl
== 0 ) res
= FR_DENIED
; /* No contiguous cluster block was found */
5547 if ( scl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
5548 if ( res
== FR_OK
) { /* A contiguous free area is found */
5549 if ( opt
) { /* Allocate it now */
5550 res
= change_bitmap ( fs
, scl
, tcl
, 1 ); /* Mark the cluster block 'in use' */
5551 lclst
= scl
+ tcl
- 1 ;
5552 } else { /* Set it as suggested point for next allocation */
5559 scl
= clst
= stcl
; ncl
= 0 ;
5560 for (;;) { /* Find a contiguous cluster block */
5561 n
= get_fat (& fp
-> obj
, clst
);
5562 if (++ clst
>= fs
-> n_fatent
) clst
= 2 ;
5563 if ( n
== 1 ) { res
= FR_INT_ERR
; break ; }
5564 if ( n
== 0xFFFFFFFF ) { res
= FR_DISK_ERR
; break ; }
5565 if ( n
== 0 ) { /* Is it a free cluster? */
5566 if (++ ncl
== tcl
) break ; /* Break if a contiguous cluster block is found */
5568 scl
= clst
; ncl
= 0 ; /* Not a free cluster */
5570 if ( clst
== stcl
) { res
= FR_DENIED
; break ; } /* No contiguous cluster? */
5572 if ( res
== FR_OK
) { /* A contiguous free area is found */
5573 if ( opt
) { /* Allocate it now */
5574 for ( clst
= scl
, n
= tcl
; n
; clst
++, n
--) { /* Create a cluster chain on the FAT */
5575 res
= put_fat ( fs
, clst
, ( n
== 1 ) ? 0xFFFFFFFF : clst
+ 1 );
5576 if ( res
!= FR_OK
) break ;
5579 } else { /* Set it as suggested point for next allocation */
5586 fs
-> last_clst
= lclst
; /* Set suggested start cluster to start next */
5587 if ( opt
) { /* Is it allocated now? */
5588 fp
-> obj
. sclust
= scl
; /* Update object allocation information */
5589 fp
-> obj
. objsize
= fsz
;
5590 if ( FF_FS_EXFAT
) fp
-> obj
. stat
= 2 ; /* Set status 'contiguous chain' */
5591 fp
-> flag
|= FA_MODIFIED
;
5592 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) { /* Update FSINFO */
5593 fs
-> free_clst
-= tcl
;
5602 #endif /* FF_USE_EXPAND && !FF_FS_READONLY */
5607 /*-----------------------------------------------------------------------*/
5608 /* Forward Data to the Stream Directly */
5609 /*-----------------------------------------------------------------------*/
5612 FIL
* fp
, /* Pointer to the file object */
5613 UINT (* func
)( const BYTE
*, UINT
), /* Pointer to the streaming function */
5614 UINT btf
, /* Number of bytes to forward */
5615 UINT
* bf
/* Pointer to number of bytes forwarded */
5627 * bf
= 0 ; /* Clear transfer byte counter */
5628 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5629 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5630 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
5632 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
5633 if ( btf
> remain
) btf
= ( UINT
) remain
; /* Truncate btf by remaining bytes */
5635 for ( ; btf
&& (* func
)( 0 , 0 ); /* Repeat until all data transferred or stream goes busy */
5636 fp
-> fptr
+= rcnt
, * bf
+= rcnt
, btf
-= rcnt
) {
5637 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
5638 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
5639 if ( csect
== 0 ) { /* On the cluster boundary? */
5640 clst
= ( fp
-> fptr
== 0 ) ? /* On the top of the file? */
5641 fp
-> obj
. sclust
: get_fat (& fp
-> obj
, fp
-> clust
);
5642 if ( clst
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
5643 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
5644 fp
-> clust
= clst
; /* Update current cluster */
5647 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current data sector */
5648 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5651 if ( move_window ( fs
, sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window to the file data */
5654 if ( fp
-> sect
!= sect
) { /* Fill sector cache with file data */
5656 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
5657 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5658 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
5661 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5666 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes remains in the sector */
5667 if ( rcnt
> btf
) rcnt
= btf
; /* Clip it by btr if needed */
5668 rcnt
= (* func
)( dbuf
+ (( UINT
) fp
-> fptr
% SS ( fs
)), rcnt
); /* Forward the file data */
5669 if ( rcnt
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5672 LEAVE_FF ( fs
, FR_OK
);
5674 #endif /* FF_USE_FORWARD */
5678 #if !FF_FS_READONLY && FF_USE_MKFS
5679 /*-----------------------------------------------------------------------*/
5680 /* Create an FAT/exFAT volume */
5681 /*-----------------------------------------------------------------------*/
5683 #define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
5684 #define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT [byte] (>=128KB) */
5685 #define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */
5688 /* Create partitions on the physical drive */
5690 static FRESULT
create_partition (
5691 BYTE drv
, /* Physical drive number */
5692 const LBA_t plst
[], /* Partition list */
5693 UINT sys
, /* System ID (for only MBR, temp setting) and bit8:GPT */
5694 BYTE
* buf
/* Working buffer for a sector */
5699 DWORD sz_drv32
, s_lba32
, n_lba32
;
5700 BYTE
* pte
, hd
, n_hd
, sc
, n_sc
;
5702 /* Get drive size */
5703 if ( disk_ioctl ( drv
, GET_SECTOR_COUNT
, & sz_drv
) != RES_OK
) return FR_DISK_ERR
;
5706 if ( sz_drv
>= FF_MIN_GPT
) { /* Create partitions in GPT */
5708 UINT sz_pt
, pi
, si
, ofs
;
5709 DWORD bcc
, rnd
, align
;
5710 QWORD s_lba64
, n_lba64
, sz_pool
, s_bpt
;
5711 static const BYTE gpt_mbr
[ 16 ] = { 0x00 , 0x00 , 0x02 , 0x00 , 0xEE , 0xFE , 0xFF , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0xFF , 0xFF , 0xFF , 0xFF };
5713 #if FF_MAX_SS != FF_MIN_SS
5714 if ( disk_ioctl ( drv
, GET_SECTOR_SIZE
, & ss
) != RES_OK
) return FR_DISK_ERR
; /* Get sector size */
5715 if ( ss
> FF_MAX_SS
|| ss
< FF_MIN_SS
|| ( ss
& ( ss
- 1 ))) return FR_DISK_ERR
;
5719 rnd
= GET_FATTIME (); /* Random seed */
5720 align
= GPT_ALIGN
/ ss
; /* Partition alignment [sector] */
5721 sz_pt
= GPT_ITEMS
* SZ_GPTE
/ ss
; /* Size of PT [sector] */
5722 s_bpt
= sz_drv
- sz_pt
- 1 ; /* Backup PT start sector */
5723 s_lba64
= 2 + sz_pt
; /* First allocatable sector */
5724 sz_pool
= s_bpt
- s_lba64
; /* Size of allocatable area */
5725 bcc
= 0xFFFFFFFF ; n_lba64
= 1 ;
5726 pi
= si
= 0 ; /* partition table index, size table index */
5728 if ( pi
* SZ_GPTE
% ss
== 0 ) mem_set ( buf
, 0 , ss
); /* Clean the buffer if needed */
5729 if ( n_lba64
!= 0 ) { /* Is the size table not termintated? */
5730 s_lba64
= ( s_lba64
+ align
- 1 ) & (( QWORD
) 0 - align
); /* Align partition start */
5731 n_lba64
= plst
[ si
++]; /* Get a partition size */
5732 if ( n_lba64
<= 100 ) { /* Is the size in percentage? */
5733 n_lba64
= sz_pool
* n_lba64
/ 100 ;
5734 n_lba64
= ( n_lba64
+ align
- 1 ) & (( QWORD
) 0 - align
); /* Align partition end (only if in percentage) */
5736 if ( s_lba64
+ n_lba64
> s_bpt
) { /* Clip at end of the pool */
5737 n_lba64
= ( s_lba64
< s_bpt
) ? s_bpt
- s_lba64
: 0 ;
5740 if ( n_lba64
!= 0 ) { /* Add a partition? */
5741 ofs
= pi
* SZ_GPTE
% ss
;
5742 mem_cpy ( buf
+ ofs
+ GPTE_PtGuid
, GUID_MS_Basic
, 16 ); /* Partition GUID (Microsoft Basic Data) */
5743 rnd
= make_rand ( rnd
, buf
+ ofs
+ GPTE_UpGuid
, 16 ); /* Unique partition GUID */
5744 st_qword ( buf
+ ofs
+ GPTE_FstLba
, s_lba64
); /* Partition start LBA */
5745 st_qword ( buf
+ ofs
+ GPTE_LstLba
, s_lba64
+ n_lba64
- 1 ); /* Partition end LBA */
5746 s_lba64
+= n_lba64
; /* Next partition LBA */
5748 if (( pi
+ 1 ) * SZ_GPTE
% ss
== 0 ) { /* Write the buffer if it is filled up */
5749 for ( i
= 0 ; i
< ss
; bcc
= crc32 ( bcc
, buf
[ i
++])) ; /* Calculate table check sum */
5750 if ( disk_write ( drv
, buf
, 2 + pi
* SZ_GPTE
/ ss
, 1 ) != RES_OK
) return FR_DISK_ERR
; /* Primary table */
5751 if ( disk_write ( drv
, buf
, s_bpt
+ pi
* SZ_GPTE
/ ss
, 1 ) != RES_OK
) return FR_DISK_ERR
; /* Secondary table */
5753 } while (++ pi
< GPT_ITEMS
);
5755 /* Create primary GPT header */
5756 mem_set ( buf
, 0 , ss
);
5757 mem_cpy ( buf
+ GPTH_Sign
, "EFI PART" "\0\0\1\0" " \x5C \0\0" , 16 ); /* Signature, version (1.0) and size (92) */
5758 st_dword ( buf
+ GPTH_PtBcc
, ~ bcc
); /* Table check sum */
5759 st_qword ( buf
+ GPTH_CurLba
, 1 ); /* LBA of this header */
5760 st_qword ( buf
+ GPTH_BakLba
, sz_drv
- 1 ); /* LBA of another header */
5761 st_qword ( buf
+ GPTH_FstLba
, 2 + sz_pt
); /* LBA of first allocatable sector */
5762 st_qword ( buf
+ GPTH_LstLba
, s_bpt
- 1 ); /* LBA of last allocatable sector */
5763 st_dword ( buf
+ GPTH_PteSize
, SZ_GPTE
); /* Size of a table entry */
5764 st_dword ( buf
+ GPTH_PtNum
, GPT_ITEMS
); /* Number of table entries */
5765 st_dword ( buf
+ GPTH_PtOfs
, 2 ); /* LBA of this table */
5766 rnd
= make_rand ( rnd
, buf
+ GPTH_DskGuid
, 16 ); /* Disk GUID */
5767 for ( i
= 0 , bcc
= 0xFFFFFFFF ; i
< 92 ; bcc
= crc32 ( bcc
, buf
[ i
++])) ; /* Calculate header check sum */
5768 st_dword ( buf
+ GPTH_Bcc
, ~ bcc
); /* Header check sum */
5769 if ( disk_write ( drv
, buf
, 1 , 1 ) != RES_OK
) return FR_DISK_ERR
;
5771 /* Create secondary GPT header */
5772 st_qword ( buf
+ GPTH_CurLba
, sz_drv
- 1 ); /* LBA of this header */
5773 st_qword ( buf
+ GPTH_BakLba
, 1 ); /* LBA of another header */
5774 st_qword ( buf
+ GPTH_PtOfs
, s_bpt
); /* LBA of this table */
5775 st_dword ( buf
+ GPTH_Bcc
, 0 );
5776 for ( i
= 0 , bcc
= 0xFFFFFFFF ; i
< 92 ; bcc
= crc32 ( bcc
, buf
[ i
++])) ; /* Calculate header check sum */
5777 st_dword ( buf
+ GPTH_Bcc
, ~ bcc
); /* Header check sum */
5778 if ( disk_write ( drv
, buf
, sz_drv
- 1 , 1 ) != RES_OK
) return FR_DISK_ERR
;
5780 /* Create protective MBR */
5781 mem_set ( buf
, 0 , ss
);
5782 mem_cpy ( buf
+ MBR_Table
, gpt_mbr
, 16 ); /* Create a GPT partition */
5783 st_word ( buf
+ BS_55AA
, 0xAA55 );
5784 if ( disk_write ( drv
, buf
, 0 , 1 ) != RES_OK
) return FR_DISK_ERR
;
5788 { /* Create partitions in MBR */
5789 sz_drv32
= ( DWORD
) sz_drv
;
5790 n_sc
= N_SEC_TRACK
; /* Determine drive CHS without any consideration of the drive geometry */
5791 for ( n_hd
= 8 ; n_hd
!= 0 && sz_drv32
/ n_hd
/ n_sc
> 1024 ; n_hd
*= 2 ) ;
5792 if ( n_hd
== 0 ) n_hd
= 255 ; /* Number of heads needs to be <256 */
5794 mem_set ( buf
, 0 , FF_MAX_SS
); /* Clear MBR */
5795 pte
= buf
+ MBR_Table
; /* Partition table in the MBR */
5796 for ( i
= 0 , s_lba32
= 2048 ; i
< 4 && s_lba32
!= 0 && s_lba32
< sz_drv32
; i
++, s_lba32
+= n_lba32
) {
5797 n_lba32
= ( DWORD
) plst
[ i
]; /* Get partition size */
5798 if ( n_lba32
<= 100 ) n_lba32
= ( n_lba32
== 100 ) ? sz_drv32
: sz_drv32
/ 100 * n_lba32
; /* Size in percentage? */
5799 if ( s_lba32
+ n_lba32
> sz_drv32
|| s_lba32
+ n_lba32
< s_lba32
) n_lba32
= sz_drv32
- s_lba32
; /* Clip at drive size */
5800 if ( n_lba32
== 0 ) break ; /* End of table or no sector to allocate? */
5802 st_dword ( pte
+ PTE_StLba
, s_lba32
); /* Start LBA */
5803 st_dword ( pte
+ PTE_SizLba
, n_lba32
); /* Number of sectors */
5804 pte
[ PTE_System
] = ( BYTE
) sys
; /* System type */
5806 cy
= ( UINT
)( s_lba32
/ n_sc
/ n_hd
); /* Start cylinder */
5807 hd
= ( BYTE
)( s_lba32
/ n_sc
% n_hd
); /* Start head */
5808 sc
= ( BYTE
)( s_lba32
% n_sc
+ 1 ); /* Start sector */
5809 pte
[ PTE_StHead
] = hd
;
5810 pte
[ PTE_StSec
] = ( BYTE
)(( cy
>> 2 & 0xC0 ) | sc
);
5811 pte
[ PTE_StCyl
] = ( BYTE
) cy
;
5813 cy
= ( UINT
)(( s_lba32
+ n_lba32
- 1 ) / n_sc
/ n_hd
); /* End cylinder */
5814 hd
= ( BYTE
)(( s_lba32
+ n_lba32
- 1 ) / n_sc
% n_hd
); /* End head */
5815 sc
= ( BYTE
)(( s_lba32
+ n_lba32
- 1 ) % n_sc
+ 1 ); /* End sector */
5816 pte
[ PTE_EdHead
] = hd
;
5817 pte
[ PTE_EdSec
] = ( BYTE
)(( cy
>> 2 & 0xC0 ) | sc
);
5818 pte
[ PTE_EdCyl
] = ( BYTE
) cy
;
5820 pte
+= SZ_PTE
; /* Next entry */
5823 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* MBR signature */
5824 if ( disk_write ( drv
, buf
, 0 , 1 ) != RES_OK
) return FR_DISK_ERR
; /* Write it to the MBR */
5833 const TCHAR
* path
, /* Logical drive number */
5834 const MKFS_PARM
* opt
, /* Format options */
5835 void * work
, /* Pointer to working buffer (null: use heap memory) */
5836 UINT len
/* Size of working buffer [byte] */
5839 static const WORD cst
[] = { 1 , 4 , 16 , 64 , 256 , 512 , 0 }; /* Cluster size boundary for FAT volume (4Ks unit) */
5840 static const WORD cst32
[] = { 1 , 2 , 4 , 8 , 16 , 32 , 0 }; /* Cluster size boundary for FAT32 volume (128Ks unit) */
5841 static const MKFS_PARM defopt
= { FM_ANY
, 0 , 0 , 0 , 0 }; /* Default parameter */
5842 BYTE fsopt
, fsty
, sys
, * buf
, * pte
, pdrv
, ipart
;
5843 WORD ss
; /* Sector size */
5844 DWORD sz_buf
, sz_blk
, n_clst
, pau
, nsect
, n
;
5845 LBA_t sz_vol
, b_vol
, b_fat
, b_data
; /* Size of volume, Base LBA of volume, fat, data */
5847 DWORD sz_rsv
, sz_fat
, sz_dir
, sz_au
; /* Size of reserved, fat, dir, data, cluster */
5848 UINT n_fat
, n_root
, i
; /* Index, Number of FATs and Number of roor dir entries */
5854 /* Check mounted drive and clear work area */
5855 vol
= get_ldnumber (& path
); /* Get target logical drive */
5856 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
5857 if ( FatFs
[ vol
]) FatFs
[ vol
]-> fs_type
= 0 ; /* Clear the fs object if mounted */
5858 pdrv
= LD2PD ( vol
); /* Physical drive */
5859 ipart
= LD2PT ( vol
); /* Partition (0:create as new, 1..:get from partition table) */
5860 if (! opt
) opt
= & defopt
; /* Use default parameter if it is not given */
5862 /* Get physical drive status (sz_drv, sz_blk, ss) */
5863 ds
= disk_initialize ( pdrv
);
5864 if ( ds
& STA_NOINIT
) return FR_NOT_READY
;
5865 if ( ds
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
5866 sz_blk
= opt
-> align
;
5867 if ( sz_blk
== 0 && disk_ioctl ( pdrv
, GET_BLOCK_SIZE
, & sz_blk
) != RES_OK
) sz_blk
= 1 ;
5868 if ( sz_blk
== 0 || sz_blk
> 0x8000 || ( sz_blk
& ( sz_blk
- 1 ))) sz_blk
= 1 ;
5869 #if FF_MAX_SS != FF_MIN_SS
5870 if ( disk_ioctl ( pdrv
, GET_SECTOR_SIZE
, & ss
) != RES_OK
) return FR_DISK_ERR
;
5871 if ( ss
> FF_MAX_SS
|| ss
< FF_MIN_SS
|| ( ss
& ( ss
- 1 ))) return FR_DISK_ERR
;
5875 /* Options for FAT sub-type and FAT parameters */
5876 fsopt
= opt
-> fmt
& ( FM_ANY
| FM_SFD
);
5877 n_fat
= ( opt
-> n_fat
>= 1 && opt
-> n_fat
<= 2 ) ? opt
-> n_fat
: 1 ;
5878 n_root
= ( opt
-> n_root
>= 1 && opt
-> n_root
<= 32768 && ( opt
-> n_root
% ( ss
/ SZDIRE
)) == 0 ) ? opt
-> n_root
: 512 ;
5879 sz_au
= ( opt
-> au_size
<= 0x1000000 && ( opt
-> au_size
& ( opt
-> au_size
- 1 )) == 0 ) ? opt
-> au_size
: 0 ;
5880 sz_au
/= ss
; /* Byte --> Sector */
5882 /* Get working buffer */
5883 sz_buf
= len
/ ss
; /* Size of working buffer [sector] */
5884 if ( sz_buf
== 0 ) return FR_NOT_ENOUGH_CORE
;
5885 buf
= ( BYTE
*) work
; /* Working buffer */
5887 if (! buf
) buf
= ff_memalloc ( sz_buf
* ss
); /* Use heap memory for working buffer */
5889 if (! buf
) return FR_NOT_ENOUGH_CORE
;
5891 /* Determine where the volume to be located (b_vol, sz_vol) */
5893 if ( FF_MULTI_PARTITION
&& ipart
!= 0 ) { /* Is the volume associated with any specific partition? */
5894 /* Get partition location from the existing partition table */
5895 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Load MBR */
5896 if ( ld_word ( buf
+ BS_55AA
) != 0xAA55 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if MBR is valid */
5898 if ( buf
[ MBR_Table
+ PTE_System
] == 0xEE ) { /* GPT protective MBR? */
5902 /* Get the partition location from GPT */
5903 if ( disk_read ( pdrv
, buf
, 1 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Load GPT header sector (next to MBR) */
5904 if (! test_gpt_header ( buf
)) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if GPT header is valid */
5905 n_ent
= ld_dword ( buf
+ GPTH_PtNum
); /* Number of entries */
5906 pt_lba
= ld_qword ( buf
+ GPTH_PtOfs
); /* Table start sector */
5908 while ( n_ent
) { /* Find MS Basic partition with order of ipart */
5909 if ( ofs
== 0 && disk_read ( pdrv
, buf
, pt_lba
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Get PT sector */
5910 if (! mem_cmp ( buf
+ ofs
+ GPTE_PtGuid
, GUID_MS_Basic
, 16 ) && ++ i
== ipart
) { /* MS basic data partition? */
5911 b_vol
= ld_qword ( buf
+ ofs
+ GPTE_FstLba
);
5912 sz_vol
= ld_qword ( buf
+ ofs
+ GPTE_LstLba
) - b_vol
+ 1 ;
5915 n_ent
--; ofs
= ( ofs
+ SZ_GPTE
) % ss
; /* Next entry */
5917 if ( n_ent
== 0 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Partition not found */
5918 fsopt
|= 0x80 ; /* Partitioning is in GPT */
5921 { /* Get the partition location from MBR partition table */
5922 pte
= buf
+ ( MBR_Table
+ ( ipart
- 1 ) * SZ_PTE
);
5923 if ( ipart
> 4 || pte
[ PTE_System
] == 0 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* No partition? */
5924 b_vol
= ld_dword ( pte
+ PTE_StLba
); /* Get volume start sector */
5925 sz_vol
= ld_dword ( pte
+ PTE_SizLba
); /* Get volume size */
5927 } else { /* The volume is associated with a physical drive */
5928 if ( disk_ioctl ( pdrv
, GET_SECTOR_COUNT
, & sz_vol
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5929 if (!( fsopt
& FM_SFD
)) { /* To be partitioned? */
5930 /* Create a single-partition on the drive in this function */
5932 if ( sz_vol
>= FF_MIN_GPT
) { /* Which partition type to create, MBR or GPT? */
5933 fsopt
|= 0x80 ; /* Partitioning is in GPT */
5934 b_vol
= GPT_ALIGN
/ ss
; sz_vol
-= b_vol
+ GPT_ITEMS
* SZ_GPTE
/ ss
+ 1 ; /* Estimated partition offset and size */
5937 { /* Partitioning is in MBR */
5938 if ( sz_vol
> N_SEC_TRACK
) {
5939 b_vol
= 2048 ; sz_vol
-= b_vol
; /* Estimated partition offset and size */
5944 if ( sz_vol
< 128 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if volume size is >=128s */
5946 /* Now start to create a FAT volume at b_vol and sz_vol */
5948 do { /* Pre-determine the FAT type */
5949 if ( FF_FS_EXFAT
&& ( fsopt
& FM_EXFAT
)) { /* exFAT possible? */
5950 if (( fsopt
& FM_ANY
) == FM_EXFAT
|| sz_vol
>= 0x4000000 || sz_au
> 128 ) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */
5951 fsty
= FS_EXFAT
; break ;
5955 if ( sz_vol
>= 0x100000000 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too large volume for FAT/FAT32 */
5957 if ( sz_au
> 128 ) sz_au
= 128 ; /* Invalid AU for FAT/FAT32? */
5958 if ( fsopt
& FM_FAT32
) { /* FAT32 possible? */
5959 if (!( fsopt
& FM_FAT
)) { /* no-FAT? */
5960 fsty
= FS_FAT32
; break ;
5963 if (!( fsopt
& FM_FAT
)) LEAVE_MKFS ( FR_INVALID_PARAMETER
); /* no-FAT? */
5968 if ( fsty
== FS_EXFAT
) { /* Create an exFAT volume */
5969 DWORD szb_bit
, szb_case
, sum
, nb
, cl
, tbl
[ 3 ];
5974 if ( sz_vol
< 0x1000 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume for exFAT? */
5976 lba
[ 0 ] = b_vol
; lba
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform storage device that the volume area may be erased */
5977 disk_ioctl ( pdrv
, CTRL_TRIM
, lba
);
5979 /* Determine FAT location, data location and number of clusters */
5980 if ( sz_au
== 0 ) { /* AU auto-selection */
5982 if ( sz_vol
>= 0x80000 ) sz_au
= 64 ; /* >= 512Ks */
5983 if ( sz_vol
>= 0x4000000 ) sz_au
= 256 ; /* >= 64Ms */
5985 b_fat
= b_vol
+ 32 ; /* FAT start at offset 32 */
5986 sz_fat
= ( DWORD
)(( sz_vol
/ sz_au
+ 2 ) * 4 + ss
- 1 ) / ss
; /* Number of FAT sectors */
5987 b_data
= ( b_fat
+ sz_fat
+ sz_blk
- 1 ) & ~(( LBA_t
) sz_blk
- 1 ); /* Align data area to the erase block boundary */
5988 if ( b_data
- b_vol
>= sz_vol
/ 2 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
5989 n_clst
= ( DWORD
)( sz_vol
- ( b_data
- b_vol
)) / sz_au
; /* Number of clusters */
5990 if ( n_clst
< 16 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too few clusters? */
5991 if ( n_clst
> MAX_EXFAT
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters? */
5993 szb_bit
= ( n_clst
+ 7 ) / 8 ; /* Size of allocation bitmap */
5994 tbl
[ 0 ] = ( szb_bit
+ sz_au
* ss
- 1 ) / ( sz_au
* ss
); /* Number of allocation bitmap clusters */
5996 /* Create a compressed up-case table */
5997 sect
= b_data
+ sz_au
* tbl
[ 0 ]; /* Table start sector */
5998 sum
= 0 ; /* Table checksum to be stored in the 82 entry */
5999 st
= 0 ; si
= 0 ; i
= 0 ; j
= 0 ; szb_case
= 0 ;
6003 ch
= ( WCHAR
) ff_wtoupper ( si
); /* Get an up-case char */
6005 si
++; break ; /* Store the up-case char if exist */
6007 for ( j
= 1 ; ( WCHAR
)( si
+ j
) && ( WCHAR
)( si
+ j
) == ff_wtoupper (( WCHAR
)( si
+ j
)); j
++) ; /* Get run length of no-case block */
6009 ch
= 0xFFFF ; st
= 2 ; break ; /* Compress the no-case block if run is >= 128 */
6011 st
= 1 ; /* Do not compress short run */
6012 /* go to next case */
6014 ch
= si
++; /* Fill the short run */
6015 if (-- j
== 0 ) st
= 0 ;
6019 ch
= ( WCHAR
) j
; si
+= ( WCHAR
) j
; /* Number of chars to skip */
6022 sum
= xsum32 ( buf
[ i
+ 0 ] = ( BYTE
) ch
, sum
); /* Put it into the write buffer */
6023 sum
= xsum32 ( buf
[ i
+ 1 ] = ( BYTE
)( ch
>> 8 ), sum
);
6024 i
+= 2 ; szb_case
+= 2 ;
6025 if ( si
== 0 || i
== sz_buf
* ss
) { /* Write buffered data when buffer full or end of process */
6026 n
= ( i
+ ss
- 1 ) / ss
;
6027 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6031 tbl
[ 1 ] = ( szb_case
+ sz_au
* ss
- 1 ) / ( sz_au
* ss
); /* Number of up-case table clusters */
6032 tbl
[ 2 ] = 1 ; /* Number of root dir clusters */
6034 /* Initialize the allocation bitmap */
6035 sect
= b_data
; nsect
= ( szb_bit
+ ss
- 1 ) / ss
; /* Start of bitmap and number of sectors */
6036 nb
= tbl
[ 0 ] + tbl
[ 1 ] + tbl
[ 2 ]; /* Number of clusters in-use by system */
6038 mem_set ( buf
, 0 , sz_buf
* ss
);
6039 for ( i
= 0 ; nb
>= 8 && i
< sz_buf
* ss
; buf
[ i
++] = 0xFF , nb
-= 8 ) ;
6040 for ( b
= 1 ; nb
!= 0 && i
< sz_buf
* ss
; buf
[ i
] |= b
, b
<<= 1 , nb
--) ;
6041 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
6042 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6043 sect
+= n
; nsect
-= n
;
6046 /* Initialize the FAT */
6047 sect
= b_fat
; nsect
= sz_fat
; /* Start of FAT and number of FAT sectors */
6050 mem_set ( buf
, 0 , sz_buf
* ss
); i
= 0 ; /* Clear work area and reset write index */
6051 if ( cl
== 0 ) { /* Set FAT [0] and FAT[1] */
6052 st_dword ( buf
+ i
, 0xFFFFFFF8 ); i
+= 4 ; cl
++;
6053 st_dword ( buf
+ i
, 0xFFFFFFFF ); i
+= 4 ; cl
++;
6055 do { /* Create chains of bitmap, up-case and root dir */
6056 while ( nb
!= 0 && i
< sz_buf
* ss
) { /* Create a chain */
6057 st_dword ( buf
+ i
, ( nb
> 1 ) ? cl
+ 1 : 0xFFFFFFFF );
6060 if ( nb
== 0 && j
< 3 ) nb
= tbl
[ j
++]; /* Next chain */
6061 } while ( nb
!= 0 && i
< sz_buf
* ss
);
6062 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
6063 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6064 sect
+= n
; nsect
-= n
;
6067 /* Initialize the root directory */
6068 mem_set ( buf
, 0 , sz_buf
* ss
);
6069 buf
[ SZDIRE
* 0 + 0 ] = ET_VLABEL
; /* Volume label entry (no label) */
6070 buf
[ SZDIRE
* 1 + 0 ] = ET_BITMAP
; /* Bitmap entry */
6071 st_dword ( buf
+ SZDIRE
* 1 + 20 , 2 ); /* cluster */
6072 st_dword ( buf
+ SZDIRE
* 1 + 24 , szb_bit
); /* size */
6073 buf
[ SZDIRE
* 2 + 0 ] = ET_UPCASE
; /* Up-case table entry */
6074 st_dword ( buf
+ SZDIRE
* 2 + 4 , sum
); /* sum */
6075 st_dword ( buf
+ SZDIRE
* 2 + 20 , 2 + tbl
[ 0 ]); /* cluster */
6076 st_dword ( buf
+ SZDIRE
* 2 + 24 , szb_case
); /* size */
6077 sect
= b_data
+ sz_au
* ( tbl
[ 0 ] + tbl
[ 1 ]); nsect
= sz_au
; /* Start of the root directory and number of sectors */
6078 do { /* Fill root directory sectors */
6079 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
6080 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6081 mem_set ( buf
, 0 , ss
);
6082 sect
+= n
; nsect
-= n
;
6085 /* Create two set of the exFAT VBR blocks */
6087 for ( n
= 0 ; n
< 2 ; n
++) {
6088 /* Main record (+0) */
6089 mem_set ( buf
, 0 , ss
);
6090 mem_cpy ( buf
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 ); /* Boot jump code (x86), OEM name */
6091 st_qword ( buf
+ BPB_VolOfsEx
, b_vol
); /* Volume offset in the physical drive [sector] */
6092 st_qword ( buf
+ BPB_TotSecEx
, sz_vol
); /* Volume size [sector] */
6093 st_dword ( buf
+ BPB_FatOfsEx
, ( DWORD
)( b_fat
- b_vol
)); /* FAT offset [sector] */
6094 st_dword ( buf
+ BPB_FatSzEx
, sz_fat
); /* FAT size [sector] */
6095 st_dword ( buf
+ BPB_DataOfsEx
, ( DWORD
)( b_data
- b_vol
)); /* Data offset [sector] */
6096 st_dword ( buf
+ BPB_NumClusEx
, n_clst
); /* Number of clusters */
6097 st_dword ( buf
+ BPB_RootClusEx
, 2 + tbl
[ 0 ] + tbl
[ 1 ]); /* Root dir cluster # */
6098 st_dword ( buf
+ BPB_VolIDEx
, GET_FATTIME ()); /* VSN */
6099 st_word ( buf
+ BPB_FSVerEx
, 0x100 ); /* Filesystem version (1.00) */
6100 for ( buf
[ BPB_BytsPerSecEx
] = 0 , i
= ss
; i
>>= 1 ; buf
[ BPB_BytsPerSecEx
]++) ; /* Log2 of sector size [byte] */
6101 for ( buf
[ BPB_SecPerClusEx
] = 0 , i
= sz_au
; i
>>= 1 ; buf
[ BPB_SecPerClusEx
]++) ; /* Log2 of cluster size [sector] */
6102 buf
[ BPB_NumFATsEx
] = 1 ; /* Number of FATs */
6103 buf
[ BPB_DrvNumEx
] = 0x80 ; /* Drive number (for int13) */
6104 st_word ( buf
+ BS_BootCodeEx
, 0xFEEB ); /* Boot code (x86) */
6105 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (placed here regardless of sector size) */
6106 for ( i
= sum
= 0 ; i
< ss
; i
++) { /* VBR checksum */
6107 if ( i
!= BPB_VolFlagEx
&& i
!= BPB_VolFlagEx
+ 1 && i
!= BPB_PercInUseEx
) sum
= xsum32 ( buf
[ i
], sum
);
6109 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6110 /* Extended bootstrap record (+1..+8) */
6111 mem_set ( buf
, 0 , ss
);
6112 st_word ( buf
+ ss
- 2 , 0xAA55 ); /* Signature (placed at end of sector) */
6113 for ( j
= 1 ; j
< 9 ; j
++) {
6114 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
6115 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6117 /* OEM/Reserved record (+9..+10) */
6118 mem_set ( buf
, 0 , ss
);
6119 for ( ; j
< 11 ; j
++) {
6120 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
6121 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6123 /* Sum record (+11) */
6124 for ( i
= 0 ; i
< ss
; i
+= 4 ) st_dword ( buf
+ i
, sum
); /* Fill with checksum value */
6125 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6129 #endif /* FF_FS_EXFAT */
6130 { /* Create an FAT/FAT32 volume */
6133 /* Pre-determine number of clusters and FAT sub-type */
6134 if ( fsty
== FS_FAT32
) { /* FAT32 volume */
6135 if ( pau
== 0 ) { /* AU auto-selection */
6136 n
= ( DWORD
) sz_vol
/ 0x20000 ; /* Volume size in unit of 128KS */
6137 for ( i
= 0 , pau
= 1 ; cst32
[ i
] && cst32
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
6139 n_clst
= ( DWORD
) sz_vol
/ pau
; /* Number of clusters */
6140 sz_fat
= ( n_clst
* 4 + 8 + ss
- 1 ) / ss
; /* FAT size [sector] */
6141 sz_rsv
= 32 ; /* Number of reserved sectors */
6142 sz_dir
= 0 ; /* No static directory */
6143 if ( n_clst
<= MAX_FAT16
|| n_clst
> MAX_FAT32
) LEAVE_MKFS ( FR_MKFS_ABORTED
);
6144 } else { /* FAT volume */
6145 if ( pau
== 0 ) { /* au auto-selection */
6146 n
= ( DWORD
) sz_vol
/ 0x1000 ; /* Volume size in unit of 4KS */
6147 for ( i
= 0 , pau
= 1 ; cst
[ i
] && cst
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
6149 n_clst
= ( DWORD
) sz_vol
/ pau
;
6150 if ( n_clst
> MAX_FAT12
) {
6151 n
= n_clst
* 2 + 4 ; /* FAT size [byte] */
6154 n
= ( n_clst
* 3 + 1 ) / 2 + 3 ; /* FAT size [byte] */
6156 sz_fat
= ( n
+ ss
- 1 ) / ss
; /* FAT size [sector] */
6157 sz_rsv
= 1 ; /* Number of reserved sectors */
6158 sz_dir
= ( DWORD
) n_root
* SZDIRE
/ ss
; /* Root dir size [sector] */
6160 b_fat
= b_vol
+ sz_rsv
; /* FAT base */
6161 b_data
= b_fat
+ sz_fat
* n_fat
+ sz_dir
; /* Data base */
6163 /* Align data area to erase block boundary (for flash memory media) */
6164 n
= ( DWORD
)((( b_data
+ sz_blk
- 1 ) & ~( sz_blk
- 1 )) - b_data
); /* Sectors to next nearest from current data base */
6165 if ( fsty
== FS_FAT32
) { /* FAT32: Move FAT */
6166 sz_rsv
+= n
; b_fat
+= n
;
6167 } else { /* FAT: Expand FAT */
6168 if ( n
% n_fat
) { /* Adjust fractional error if needed */
6169 n
--; sz_rsv
++; b_fat
++;
6171 sz_fat
+= n
/ n_fat
;
6174 /* Determine number of clusters and final check of validity of the FAT sub-type */
6175 if ( sz_vol
< b_data
+ pau
* 16 - b_vol
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
6176 n_clst
= (( DWORD
) sz_vol
- sz_rsv
- sz_fat
* n_fat
- sz_dir
) / pau
;
6177 if ( fsty
== FS_FAT32
) {
6178 if ( n_clst
<= MAX_FAT16
) { /* Too few clusters for FAT32? */
6179 if ( sz_au
== 0 && ( sz_au
= pau
/ 2 ) != 0 ) continue ; /* Adjust cluster size and retry */
6180 LEAVE_MKFS ( FR_MKFS_ABORTED
);
6183 if ( fsty
== FS_FAT16
) {
6184 if ( n_clst
> MAX_FAT16
) { /* Too many clusters for FAT16 */
6185 if ( sz_au
== 0 && ( pau
* 2 ) <= 64 ) {
6186 sz_au
= pau
* 2 ; continue ; /* Adjust cluster size and retry */
6188 if (( fsopt
& FM_FAT32
)) {
6189 fsty
= FS_FAT32
; continue ; /* Switch type to FAT32 and retry */
6191 if ( sz_au
== 0 && ( sz_au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
6192 LEAVE_MKFS ( FR_MKFS_ABORTED
);
6194 if ( n_clst
<= MAX_FAT12
) { /* Too few clusters for FAT16 */
6195 if ( sz_au
== 0 && ( sz_au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
6196 LEAVE_MKFS ( FR_MKFS_ABORTED
);
6199 if ( fsty
== FS_FAT12
&& n_clst
> MAX_FAT12
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters for FAT12 */
6201 /* Ok, it is the valid cluster configuration */
6206 lba
[ 0 ] = b_vol
; lba
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform storage device that the volume area may be erased */
6207 disk_ioctl ( pdrv
, CTRL_TRIM
, lba
);
6209 /* Create FAT VBR */
6210 mem_set ( buf
, 0 , ss
);
6211 mem_cpy ( buf
+ BS_JmpBoot
, " \xEB\xFE\x90 " "MSDOS5.0" , 11 ); /* Boot jump code (x86), OEM name */
6212 st_word ( buf
+ BPB_BytsPerSec
, ss
); /* Sector size [byte] */
6213 buf
[ BPB_SecPerClus
] = ( BYTE
) pau
; /* Cluster size [sector] */
6214 st_word ( buf
+ BPB_RsvdSecCnt
, ( WORD
) sz_rsv
); /* Size of reserved area */
6215 buf
[ BPB_NumFATs
] = ( BYTE
) n_fat
; /* Number of FATs */
6216 st_word ( buf
+ BPB_RootEntCnt
, ( WORD
)(( fsty
== FS_FAT32
) ? 0 : n_root
)); /* Number of root directory entries */
6217 if ( sz_vol
< 0x10000 ) {
6218 st_word ( buf
+ BPB_TotSec16
, ( WORD
) sz_vol
); /* Volume size in 16-bit LBA */
6220 st_dword ( buf
+ BPB_TotSec32
, ( DWORD
) sz_vol
); /* Volume size in 32-bit LBA */
6222 buf
[ BPB_Media
] = 0xF8 ; /* Media descriptor byte */
6223 st_word ( buf
+ BPB_SecPerTrk
, 63 ); /* Number of sectors per track (for int13) */
6224 st_word ( buf
+ BPB_NumHeads
, 255 ); /* Number of heads (for int13) */
6225 st_dword ( buf
+ BPB_HiddSec
, ( DWORD
) b_vol
); /* Volume offset in the physical drive [sector] */
6226 if ( fsty
== FS_FAT32
) {
6227 st_dword ( buf
+ BS_VolID32
, GET_FATTIME ()); /* VSN */
6228 st_dword ( buf
+ BPB_FATSz32
, sz_fat
); /* FAT size [sector] */
6229 st_dword ( buf
+ BPB_RootClus32
, 2 ); /* Root directory cluster # (2) */
6230 st_word ( buf
+ BPB_FSInfo32
, 1 ); /* Offset of FSINFO sector (VBR + 1) */
6231 st_word ( buf
+ BPB_BkBootSec32
, 6 ); /* Offset of backup VBR (VBR + 6) */
6232 buf
[ BS_DrvNum32
] = 0x80 ; /* Drive number (for int13) */
6233 buf
[ BS_BootSig32
] = 0x29 ; /* Extended boot signature */
6234 mem_cpy ( buf
+ BS_VolLab32
, "NO NAME " "FAT32 " , 19 ); /* Volume label, FAT signature */
6236 st_dword ( buf
+ BS_VolID
, GET_FATTIME ()); /* VSN */
6237 st_word ( buf
+ BPB_FATSz16
, ( WORD
) sz_fat
); /* FAT size [sector] */
6238 buf
[ BS_DrvNum
] = 0x80 ; /* Drive number (for int13) */
6239 buf
[ BS_BootSig
] = 0x29 ; /* Extended boot signature */
6240 mem_cpy ( buf
+ BS_VolLab
, "NO NAME " "FAT " , 19 ); /* Volume label, FAT signature */
6242 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (offset is fixed here regardless of sector size) */
6243 if ( disk_write ( pdrv
, buf
, b_vol
, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it to the VBR sector */
6245 /* Create FSINFO record if needed */
6246 if ( fsty
== FS_FAT32
) {
6247 disk_write ( pdrv
, buf
, b_vol
+ 6 , 1 ); /* Write backup VBR (VBR + 6) */
6248 mem_set ( buf
, 0 , ss
);
6249 st_dword ( buf
+ FSI_LeadSig
, 0x41615252 );
6250 st_dword ( buf
+ FSI_StrucSig
, 0x61417272 );
6251 st_dword ( buf
+ FSI_Free_Count
, n_clst
- 1 ); /* Number of free clusters */
6252 st_dword ( buf
+ FSI_Nxt_Free
, 2 ); /* Last allocated cluster# */
6253 st_word ( buf
+ BS_55AA
, 0xAA55 );
6254 disk_write ( pdrv
, buf
, b_vol
+ 7 , 1 ); /* Write backup FSINFO (VBR + 7) */
6255 disk_write ( pdrv
, buf
, b_vol
+ 1 , 1 ); /* Write original FSINFO (VBR + 1) */
6258 /* Initialize FAT area */
6259 mem_set ( buf
, 0 , sz_buf
* ss
);
6260 sect
= b_fat
; /* FAT start sector */
6261 for ( i
= 0 ; i
< n_fat
; i
++) { /* Initialize FATs each */
6262 if ( fsty
== FS_FAT32
) {
6263 st_dword ( buf
+ 0 , 0xFFFFFFF8 ); /* FAT[0] */
6264 st_dword ( buf
+ 4 , 0xFFFFFFFF ); /* FAT[1] */
6265 st_dword ( buf
+ 8 , 0x0FFFFFFF ); /* FAT[2] (root directory) */
6267 st_dword ( buf
+ 0 , ( fsty
== FS_FAT12
) ? 0xFFFFF8 : 0xFFFFFFF8 ); /* FAT[0] and FAT[1] */
6269 nsect
= sz_fat
; /* Number of FAT sectors */
6270 do { /* Fill FAT sectors */
6271 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
6272 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6273 mem_set ( buf
, 0 , ss
); /* Rest of FAT all are cleared */
6274 sect
+= n
; nsect
-= n
;
6278 /* Initialize root directory (fill with zero) */
6279 nsect
= ( fsty
== FS_FAT32
) ? pau
: sz_dir
; /* Number of root directory sectors */
6281 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
6282 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6283 sect
+= n
; nsect
-= n
;
6287 /* A FAT volume has been created here */
6289 /* Determine system ID in the MBR partition table */
6290 if ( FF_FS_EXFAT
&& fsty
== FS_EXFAT
) {
6291 sys
= 0x07 ; /* exFAT */
6293 if ( fsty
== FS_FAT32
) {
6294 sys
= 0x0C ; /* FAT32X */
6296 if ( sz_vol
>= 0x10000 ) {
6297 sys
= 0x06 ; /* FAT12/16 (large) */
6299 sys
= ( fsty
== FS_FAT16
) ? 0x04 : 0x01 ; /* FAT16 : FAT12 */
6304 /* Update partition information */
6305 if ( FF_MULTI_PARTITION
&& ipart
!= 0 ) { /* Volume is in the existing partition */
6306 if (! FF_LBA64
|| !( fsopt
& 0x80 )) {
6307 /* Update system ID in the partition table */
6308 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Read the MBR */
6309 buf
[ MBR_Table
+ ( ipart
- 1 ) * SZ_PTE
+ PTE_System
] = sys
; /* Set system ID */
6310 if ( disk_write ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it back to the MBR */
6312 } else { /* Volume as a new single partition */
6313 if (!( fsopt
& FM_SFD
)) { /* Create partition table if not in SFD */
6314 lba
[ 0 ] = sz_vol
, lba
[ 1 ] = 0 ;
6315 fr
= create_partition ( pdrv
, lba
, sys
, buf
);
6316 if ( fr
!= FR_OK
) LEAVE_MKFS ( fr
);
6320 if ( disk_ioctl ( pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6328 #if FF_MULTI_PARTITION
6329 /*-----------------------------------------------------------------------*/
6330 /* Create Partition Table on the Physical Drive */
6331 /*-----------------------------------------------------------------------*/
6334 BYTE pdrv
, /* Physical drive number */
6335 const LBA_t ptbl
[], /* Pointer to the size table for each partitions */
6336 void * work
/* Pointer to the working buffer (null: use heap memory) */
6339 BYTE
* buf
= ( BYTE
*) work
;
6343 stat
= disk_initialize ( pdrv
);
6344 if ( stat
& STA_NOINIT
) return FR_NOT_READY
;
6345 if ( stat
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
6347 if (! buf
) buf
= ff_memalloc ( FF_MAX_SS
); /* Use heap memory for working buffer */
6349 if (! buf
) return FR_NOT_ENOUGH_CORE
;
6351 LEAVE_MKFS ( create_partition ( pdrv
, ptbl
, 0x07 , buf
));
6354 #endif /* FF_MULTI_PARTITION */
6355 #endif /* !FF_FS_READONLY && FF_USE_MKFS */
6361 #if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
6362 #error Wrong FF_STRF_ENCODE setting
6364 /*-----------------------------------------------------------------------*/
6365 /* Get a String from the File */
6366 /*-----------------------------------------------------------------------*/
6369 TCHAR
* buff
, /* Pointer to the buffer to store read string */
6370 int len
, /* Size of string buffer (items) */
6371 FIL
* fp
/* Pointer to the file object */
6379 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
6382 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
6386 #if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
6387 /* Make a room for the character and terminator */
6388 if ( FF_LFN_UNICODE
== 1 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 1 : 2 ;
6389 if ( FF_LFN_UNICODE
== 2 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 3 : 4 ;
6390 if ( FF_LFN_UNICODE
== 3 ) len
-= 1 ;
6392 #if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
6393 f_read ( fp
, s
, 1 , & rc
); /* Get a code unit */
6394 if ( rc
!= 1 ) break ; /* EOF? */
6396 if ( dbc_1st (( BYTE
) wc
)) { /* DBC 1st byte? */
6397 f_read ( fp
, s
, 1 , & rc
); /* Get DBC 2nd byte */
6398 if ( rc
!= 1 || ! dbc_2nd ( s
[ 0 ])) continue ; /* Wrong code? */
6399 wc
= wc
<< 8 | s
[ 0 ];
6401 dc
= ff_oem2uni ( wc
, CODEPAGE
); /* OEM --> */
6402 if ( dc
== 0 ) continue ;
6403 #elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
6404 f_read ( fp
, s
, 2 , & rc
); /* Get a code unit */
6405 if ( rc
!= 2 ) break ; /* EOF? */
6406 dc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6407 if ( IsSurrogateL ( dc
)) continue ; /* Broken surrogate pair? */
6408 if ( IsSurrogateH ( dc
)) { /* High surrogate? */
6409 f_read ( fp
, s
, 2 , & rc
); /* Get low surrogate */
6410 if ( rc
!= 2 ) break ; /* EOF? */
6411 wc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6412 if (! IsSurrogateL ( wc
)) continue ; /* Broken surrogate pair? */
6413 dc
= (( dc
& 0x3FF ) + 0x40 ) << 10 | ( wc
& 0x3FF ); /* Merge surrogate pair */
6415 #else /* Read a character in UTF-8 */
6416 f_read ( fp
, s
, 1 , & rc
); /* Get a code unit */
6417 if ( rc
!= 1 ) break ; /* EOF? */
6419 if ( dc
>= 0x80 ) { /* Multi-byte sequence? */
6421 if (( dc
& 0xE0 ) == 0xC0 ) { dc
&= 0x1F ; ct
= 1 ; } /* 2-byte sequence? */
6422 if (( dc
& 0xF0 ) == 0xE0 ) { dc
&= 0x0F ; ct
= 2 ; } /* 3-byte sequence? */
6423 if (( dc
& 0xF8 ) == 0xF0 ) { dc
&= 0x07 ; ct
= 3 ; } /* 4-byte sequence? */
6424 if ( ct
== 0 ) continue ;
6425 f_read ( fp
, s
, ct
, & rc
); /* Get trailing bytes */
6426 if ( rc
!= ct
) break ;
6428 do { /* Merge the byte sequence */
6429 if (( s
[ rc
] & 0xC0 ) != 0x80 ) break ;
6430 dc
= dc
<< 6 | ( s
[ rc
] & 0x3F );
6431 } while (++ rc
< ct
);
6432 if ( rc
!= ct
|| dc
< 0x80 || IsSurrogate ( dc
) || dc
>= 0x110000 ) continue ; /* Wrong encoding? */
6435 /* A code point is avaialble in dc to be output */
6437 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ; /* Strip \r off if needed */
6438 #if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
6439 if ( FF_LFN_UNICODE
== 1 && dc
>= 0x10000 ) { /* Out of BMP at UTF-16? */
6440 * p
++ = ( TCHAR
)( 0xD800 | (( dc
>> 10 ) - 0x40 )); nc
++; /* Make and output high surrogate */
6441 dc
= 0xDC00 | ( dc
& 0x3FF ); /* Make low surrogate */
6443 * p
++ = ( TCHAR
) dc
; nc
++;
6444 if ( dc
== ' \n ' ) break ; /* End of line? */
6445 #elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
6446 if ( dc
< 0x80 ) { /* Single byte? */
6449 if ( dc
== ' \n ' ) break ; /* End of line? */
6451 if ( dc
< 0x800 ) { /* 2-byte sequence? */
6452 * p
++ = ( TCHAR
)( 0xC0 | ( dc
>> 6 & 0x1F ));
6453 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6456 if ( dc
< 0x10000 ) { /* 3-byte sequence? */
6457 * p
++ = ( TCHAR
)( 0xE0 | ( dc
>> 12 & 0x0F ));
6458 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6459 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6461 } else { /* 4-byte sequence? */
6462 * p
++ = ( TCHAR
)( 0xF0 | ( dc
>> 18 & 0x07 ));
6463 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 12 & 0x3F ));
6464 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6465 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6473 #else /* Byte-by-byte read without any conversion (ANSI/OEM API) */
6474 len
-= 1 ; /* Make a room for the terminator */
6476 f_read ( fp
, s
, 1 , & rc
); /* Get a byte */
6477 if ( rc
!= 1 ) break ; /* EOF? */
6479 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ;
6480 * p
++ = ( TCHAR
) dc
; nc
++;
6481 if ( dc
== ' \n ' ) break ;
6485 * p
= 0 ; /* Terminate the string */
6486 return nc
? buff
: 0 ; /* When no data read due to EOF or error, return with error. */
6494 /*-----------------------------------------------------------------------*/
6495 /* Put a Character to the File (sub-functions) */
6496 /*-----------------------------------------------------------------------*/
6498 /* Putchar output buffer and work area */
6501 FIL
* fp
; /* Ptr to the writing file */
6502 int idx
, nchr
; /* Write index of buf[] (-1:error), number of encoding units written */
6503 #if FF_USE_LFN && FF_LFN_UNICODE == 1
6505 #elif FF_USE_LFN && FF_LFN_UNICODE == 2
6509 BYTE buf
[ 64 ]; /* Write buffer */
6513 /* Buffered write with code conversion */
6515 static void putc_bfd ( putbuff
* pb
, TCHAR c
)
6519 #if FF_USE_LFN && FF_LFN_UNICODE
6521 #if FF_LFN_UNICODE == 2
6527 if ( FF_USE_STRFUNC
== 2 && c
== ' \n ' ) { /* LF -> CRLF conversion */
6531 i
= pb
-> idx
; /* Write index of pb->buf[] */
6533 nc
= pb
-> nchr
; /* Write unit counter */
6535 #if FF_USE_LFN && FF_LFN_UNICODE
6536 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
6537 if ( IsSurrogateH ( c
)) { /* High surrogate? */
6538 pb
-> hs
= c
; return ; /* Save it for next */
6540 hs
= pb
-> hs
; pb
-> hs
= 0 ;
6541 if ( hs
!= 0 ) { /* There is a leading high surrogate */
6542 if (! IsSurrogateL ( c
)) hs
= 0 ; /* Discard high surrogate if not a surrogate pair */
6544 if ( IsSurrogateL ( c
)) return ; /* Discard stray low surrogate */
6547 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
6549 if ( pb
-> ct
== 0 ) { /* Out of multi-byte sequence? */
6550 pb
-> bs
[ pb
-> wi
= 0 ] = ( BYTE
) c
; /* Save 1st byte */
6551 if (( BYTE
) c
< 0x80 ) break ; /* Single byte? */
6552 if ((( BYTE
) c
& 0xE0 ) == 0xC0 ) pb
-> ct
= 1 ; /* 2-byte sequence? */
6553 if ((( BYTE
) c
& 0xF0 ) == 0xE0 ) pb
-> ct
= 2 ; /* 3-byte sequence? */
6554 if ((( BYTE
) c
& 0xF1 ) == 0xF0 ) pb
-> ct
= 3 ; /* 4-byte sequence? */
6556 } else { /* In the multi-byte sequence */
6557 if ((( BYTE
) c
& 0xC0 ) != 0x80 ) { /* Broken sequence? */
6558 pb
-> ct
= 0 ; continue ;
6560 pb
-> bs
[++ pb
-> wi
] = ( BYTE
) c
; /* Save the trailing byte */
6561 if (-- pb
-> ct
== 0 ) break ; /* End of multi-byte sequence? */
6565 tp
= ( TCHAR
*) pb
-> bs
;
6566 dc
= tchar2uni (& tp
); /* UTF-8 ==> UTF-16 */
6567 if ( dc
== 0xFFFFFFFF ) return ; /* Wrong code? */
6569 hs
= ( WCHAR
)( dc
>> 16 );
6570 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
6571 if ( IsSurrogate ( c
) || c
>= 0x110000 ) return ; /* Discard invalid code */
6572 if ( c
>= 0x10000 ) { /* Out of BMP? */
6573 hs
= ( WCHAR
)( 0xD800 | (( c
>> 10 ) - 0x40 )); /* Make high surrogate */
6574 wc
= 0xDC00 | ( c
& 0x3FF ); /* Make low surrogate */
6580 /* A code point in UTF-16 is available in hs and wc */
6582 #if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */
6583 if ( hs
!= 0 ) { /* Surrogate pair? */
6584 st_word (& pb
-> buf
[ i
], hs
);
6588 st_word (& pb
-> buf
[ i
], wc
);
6590 #elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */
6591 if ( hs
!= 0 ) { /* Surrogate pair? */
6592 pb
-> buf
[ i
++] = ( BYTE
)( hs
>> 8 );
6593 pb
-> buf
[ i
++] = ( BYTE
) hs
;
6596 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 );
6597 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6598 #elif FF_STRF_ENCODE == 3 /* Write a code point in UTF-8 */
6599 if ( hs
!= 0 ) { /* 4-byte sequence? */
6601 hs
= ( hs
& 0x3FF ) + 0x40 ;
6602 pb
-> buf
[ i
++] = ( BYTE
)( 0xF0 | hs
>> 8 );
6603 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
>> 2 & 0x3F ));
6604 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
& 3 ) << 4 | ( wc
>> 6 & 0x0F ));
6605 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6607 if ( wc
< 0x80 ) { /* Single byte? */
6608 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6610 if ( wc
< 0x800 ) { /* 2-byte sequence? */
6612 pb
-> buf
[ i
++] = ( BYTE
)( 0xC0 | wc
>> 6 );
6613 } else { /* 3-byte sequence */
6615 pb
-> buf
[ i
++] = ( BYTE
)( 0xE0 | wc
>> 12 );
6616 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
>> 6 & 0x3F ));
6618 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6621 #else /* Write a code point in ANSI/OEM */
6622 if ( hs
!= 0 ) return ;
6623 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* UTF-16 ==> ANSI/OEM */
6624 if ( wc
== 0 ) return ;
6626 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 ); nc
++;
6628 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6631 #else /* ANSI/OEM input (without re-encoding) */
6632 pb
-> buf
[ i
++] = ( BYTE
) c
;
6635 if ( i
>= ( int )( sizeof pb
-> buf
) - 4 ) { /* Write buffered characters to the file */
6636 f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) i
, & n
);
6637 i
= ( n
== ( UINT
) i
) ? 0 : - 1 ;
6644 /* Flush remaining characters in the buffer */
6646 static int putc_flush ( putbuff
* pb
)
6650 if ( pb
-> idx
>= 0 /* Flush buffered characters to the file */
6651 && f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) pb
-> idx
, & nw
) == FR_OK
6652 && ( UINT
) pb
-> idx
== nw
) return pb
-> nchr
;
6657 /* Initialize write buffer */
6659 static void putc_init ( putbuff
* pb
, FIL
* fp
)
6661 mem_set ( pb
, 0 , sizeof ( putbuff
));
6668 TCHAR c
, /* A character to be output */
6669 FIL
* fp
/* Pointer to the file object */
6676 putc_bfd (& pb
, c
); /* Put the character */
6677 return putc_flush (& pb
);
6683 /*-----------------------------------------------------------------------*/
6684 /* Put a String to the File */
6685 /*-----------------------------------------------------------------------*/
6688 const TCHAR
* str
, /* Pointer to the string to be output */
6689 FIL
* fp
/* Pointer to the file object */
6696 while (* str
) putc_bfd (& pb
, * str
++); /* Put the string */
6697 return putc_flush (& pb
);
6703 /*-----------------------------------------------------------------------*/
6704 /* Put a Formatted String to the File */
6705 /*-----------------------------------------------------------------------*/
6708 FIL
* fp
, /* Pointer to the file object */
6709 const TCHAR
* fmt
, /* Pointer to the format string */
6710 ... /* Optional arguments... */
6718 TCHAR c
, d
, str
[ 32 ], * p
;
6727 if ( c
== 0 ) break ; /* End of string */
6728 if ( c
!= '%' ) { /* Non escape character */
6734 if ( c
== '0' ) { /* Flag: '0' padding */
6737 if ( c
== '-' ) { /* Flag: left justified */
6741 if ( c
== '*' ) { /* Minimum width by argument */
6742 w
= va_arg ( arp
, int );
6745 while ( IsDigit ( c
)) { /* Minimum width */
6746 w
= w
* 10 + c
- '0' ;
6750 if ( c
== 'l' || c
== 'L' ) { /* Type prefix: Size is long int */
6755 if ( IsLower ( d
)) d
-= 0x20 ;
6756 switch ( d
) { /* Atgument type is... */
6757 case 'S' : /* String */
6758 p
= va_arg ( arp
, TCHAR
*);
6759 for ( j
= 0 ; p
[ j
]; j
++) ;
6760 if (!( f
& 2 )) { /* Right padded */
6761 while ( j
++ < w
) putc_bfd (& pb
, ' ' ) ;
6763 while (* p
) putc_bfd (& pb
, * p
++) ; /* String body */
6764 while ( j
++ < w
) putc_bfd (& pb
, ' ' ) ; /* Left padded */
6767 case 'C' : /* Character */
6768 putc_bfd (& pb
, ( TCHAR
) va_arg ( arp
, int )); continue ;
6770 case 'B' : /* Unsigned binary */
6773 case 'O' : /* Unsigned octal */
6776 case 'D' : /* Signed decimal */
6777 case 'U' : /* Unsigned decimal */
6780 case 'X' : /* Unsigned hexdecimal */
6783 default : /* Unknown type (pass-through) */
6784 putc_bfd (& pb
, c
); continue ;
6787 /* Get an argument and put it in numeral */
6788 v
= ( f
& 4 ) ? ( DWORD
) va_arg ( arp
, long ) : (( d
== 'D' ) ? ( DWORD
)( long ) va_arg ( arp
, int ) : ( DWORD
) va_arg ( arp
, unsigned int ));
6789 if ( d
== 'D' && ( v
& 0x80000000 )) {
6795 d
= ( TCHAR
)( v
% r
); v
/= r
;
6796 if ( d
> 9 ) d
+= ( c
== 'x' ) ? 0x27 : 0x07 ;
6798 } while ( v
&& i
< sizeof str
/ sizeof * str
);
6799 if ( f
& 8 ) str
[ i
++] = '-' ;
6800 j
= i
; d
= ( f
& 1 ) ? '0' : ' ' ;
6802 while ( j
++ < w
) putc_bfd (& pb
, d
); /* Right pad */
6805 putc_bfd (& pb
, str
[-- i
]); /* Number body */
6807 while ( j
++ < w
) putc_bfd (& pb
, d
); /* Left pad */
6812 return putc_flush (& pb
);
6815 #endif /* !FF_FS_READONLY */
6816 #endif /* FF_USE_STRFUNC */
6820 #if FF_CODE_PAGE == 0
6821 /*-----------------------------------------------------------------------*/
6822 /* Set Active Codepage for the Path Name */
6823 /*-----------------------------------------------------------------------*/
6826 WORD cp
/* Value to be set as active code page */
6829 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 };
6830 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 };
6834 for ( i
= 0 ; validcp
[ i
] != 0 && validcp
[ i
] != cp
; i
++) ; /* Find the code page */
6835 if ( validcp
[ i
] != cp
) return FR_INVALID_PARAMETER
; /* Not found? */
6838 if ( cp
>= 900 ) { /* DBCS */
6847 #endif /* FF_CODE_PAGE == 0 */