]>
cloudbase.mooo.com Git - z180-stamp.git/blob - fatfs/source/ff.c
1 /*----------------------------------------------------------------------------/
2 / FatFs - Generic FAT Filesystem Module R0.15 w/patch1 /
3 /-----------------------------------------------------------------------------/
5 / Copyright (C) 2022, 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 /----------------------------------------------------------------------------*/
23 #include "ff.h" /* Declarations of FatFs API */
24 #include "diskio.h" /* Declarations of device I/O functions */
27 /*--------------------------------------------------------------------------
29 Module Private Definitions
31 ---------------------------------------------------------------------------*/
33 #if FF_DEFINED != 80286 /* Revision ID */
34 #error Wrong include file (ff.h).
38 /* Limits and boundaries */
39 #define MAX_DIR 0x200000 /* Max size of FAT directory */
40 #define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
41 #define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
42 #define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
43 #define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
44 #define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
47 /* Character code support macros */
48 #define IsUpper(c) ((c) >= 'A' && (c) <= 'Z' )
49 #define IsLower(c) ((c) >= 'a' && (c) <= 'z' )
50 #define IsDigit(c) ((c) >= '0' && (c) <= '9' )
51 #define IsSeparator(c) ((c) == '/' || (c) == ' \\ ' )
52 #define IsTerminator(c) ((UINT)(c) < (FF_USE_LFN ? ' ' : '!' ))
53 #define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
54 #define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
55 #define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
58 /* Additional file access control and file status flags for internal use */
59 #define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
60 #define FA_MODIFIED 0x40 /* File has been modified */
61 #define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
64 /* Additional file attribute bits for internal use */
65 #define AM_VOL 0x08 /* Volume label */
66 #define AM_LFN 0x0F /* LFN entry */
67 #define AM_MASK 0x3F /* Mask of defined bits in FAT */
68 #define AM_MASKX 0x37 /* Mask of defined bits in exFAT */
71 /* Name status flags in fn[11] */
72 #define NSFLAG 11 /* Index of the name status byte */
73 #define NS_LOSS 0x01 /* Out of 8.3 format */
74 #define NS_LFN 0x02 /* Force to create LFN entry */
75 #define NS_LAST 0x04 /* Last segment */
76 #define NS_BODY 0x08 /* Lower case flag (body) */
77 #define NS_EXT 0x10 /* Lower case flag (ext) */
78 #define NS_DOT 0x20 /* Dot entry */
79 #define NS_NOLFN 0x40 /* Do not find LFN */
80 #define NS_NONAME 0x80 /* Not followed */
83 /* exFAT directory entry types */
84 #define ET_BITMAP 0x81 /* Allocation bitmap */
85 #define ET_UPCASE 0x82 /* Up-case table */
86 #define ET_VLABEL 0x83 /* Volume label */
87 #define ET_FILEDIR 0x85 /* File and directory */
88 #define ET_STREAM 0xC0 /* Stream extension */
89 #define ET_FILENAME 0xC1 /* Name extension */
92 /* FatFs refers the FAT structure as simple byte array instead of structure member
93 / because the C structure is not binary compatible between different platforms */
95 #define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
96 #define BS_OEMName 3 /* OEM name (8-byte) */
97 #define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
98 #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
99 #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
100 #define BPB_NumFATs 16 /* Number of FATs (BYTE) */
101 #define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
102 #define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
103 #define BPB_Media 21 /* Media descriptor byte (BYTE) */
104 #define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
105 #define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
106 #define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
107 #define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
108 #define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
109 #define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
110 #define BS_NTres 37 /* WindowsNT error flag (BYTE) */
111 #define BS_BootSig 38 /* Extended boot signature (BYTE) */
112 #define BS_VolID 39 /* Volume serial number (DWORD) */
113 #define BS_VolLab 43 /* Volume label string (8-byte) */
114 #define BS_FilSysType 54 /* Filesystem type string (8-byte) */
115 #define BS_BootCode 62 /* Boot code (448-byte) */
116 #define BS_55AA 510 /* Signature word (WORD) */
118 #define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
119 #define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
120 #define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
121 #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
122 #define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
123 #define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
124 #define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
125 #define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
126 #define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
127 #define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
128 #define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
129 #define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
130 #define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
132 #define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
133 #define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
134 #define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
135 #define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
136 #define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
137 #define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
138 #define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
139 #define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
140 #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
141 #define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
142 #define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */
143 #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
144 #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
145 #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
146 #define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
147 #define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
148 #define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
149 #define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
151 #define DIR_Name 0 /* Short file name (11-byte) */
152 #define DIR_Attr 11 /* Attribute (BYTE) */
153 #define DIR_NTres 12 /* Lower case flag (BYTE) */
154 #define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
155 #define DIR_CrtTime 14 /* Created time (DWORD) */
156 #define DIR_LstAccDate 18 /* Last accessed date (WORD) */
157 #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
158 #define DIR_ModTime 22 /* Modified time (DWORD) */
159 #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
160 #define DIR_FileSize 28 /* File size (DWORD) */
161 #define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
162 #define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
163 #define LDIR_Type 12 /* LFN: Entry type (BYTE) */
164 #define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
165 #define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
166 #define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
167 #define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
168 #define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
169 #define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
170 #define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
171 #define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
172 #define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
173 #define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
174 #define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
175 #define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
176 #define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
177 #define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
178 #define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
179 #define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
180 #define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
181 #define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
182 #define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
183 #define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
184 #define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
185 #define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
186 #define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
188 #define SZDIRE 32 /* Size of a directory entry */
189 #define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
190 #define RDDEM 0x05 /* Replacement of the character collides with DDEM */
191 #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
193 #define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
194 #define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
195 #define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
196 #define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
198 #define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
199 #define SZ_PTE 16 /* MBR: Size of a partition table entry */
200 #define PTE_Boot 0 /* MBR PTE: Boot indicator */
201 #define PTE_StHead 1 /* MBR PTE: Start head */
202 #define PTE_StSec 2 /* MBR PTE: Start sector */
203 #define PTE_StCyl 3 /* MBR PTE: Start cylinder */
204 #define PTE_System 4 /* MBR PTE: System ID */
205 #define PTE_EdHead 5 /* MBR PTE: End head */
206 #define PTE_EdSec 6 /* MBR PTE: End sector */
207 #define PTE_EdCyl 7 /* MBR PTE: End cylinder */
208 #define PTE_StLba 8 /* MBR PTE: Start in LBA */
209 #define PTE_SizLba 12 /* MBR PTE: Size in LBA */
211 #define GPTH_Sign 0 /* GPT HDR: Signature (8-byte) */
212 #define GPTH_Rev 8 /* GPT HDR: Revision (DWORD) */
213 #define GPTH_Size 12 /* GPT HDR: Header size (DWORD) */
214 #define GPTH_Bcc 16 /* GPT HDR: Header BCC (DWORD) */
215 #define GPTH_CurLba 24 /* GPT HDR: This header LBA (QWORD) */
216 #define GPTH_BakLba 32 /* GPT HDR: Another header LBA (QWORD) */
217 #define GPTH_FstLba 40 /* GPT HDR: First LBA for partition data (QWORD) */
218 #define GPTH_LstLba 48 /* GPT HDR: Last LBA for partition data (QWORD) */
219 #define GPTH_DskGuid 56 /* GPT HDR: Disk GUID (16-byte) */
220 #define GPTH_PtOfs 72 /* GPT HDR: Partition table LBA (QWORD) */
221 #define GPTH_PtNum 80 /* GPT HDR: Number of table entries (DWORD) */
222 #define GPTH_PteSize 84 /* GPT HDR: Size of table entry (DWORD) */
223 #define GPTH_PtBcc 88 /* GPT HDR: Partition table BCC (DWORD) */
224 #define SZ_GPTE 128 /* GPT PTE: Size of partition table entry */
225 #define GPTE_PtGuid 0 /* GPT PTE: Partition type GUID (16-byte) */
226 #define GPTE_UpGuid 16 /* GPT PTE: Partition unique GUID (16-byte) */
227 #define GPTE_FstLba 32 /* GPT PTE: First LBA of partition (QWORD) */
228 #define GPTE_LstLba 40 /* GPT PTE: Last LBA of partition (QWORD) */
229 #define GPTE_Flags 48 /* GPT PTE: Partition flags (QWORD) */
230 #define GPTE_Name 56 /* GPT PTE: Partition name */
233 /* Post process on fatal error in the file operations */
234 #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
237 /* Re-entrancy related */
240 #error Static LFN work area cannot be used in thread-safe configuration
242 #define LEAVE_FF(fs, res) { unlock_volume(fs, res); return res; }
244 #define LEAVE_FF(fs, res) return res
248 /* Definitions of logical drive - physical location conversion */
249 #if FF_MULTI_PARTITION
250 #define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
251 #define LD2PT(vol) VolToPart[vol].pt /* Get partition number (0:auto search, 1..:forced partition number) */
253 #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */
254 #define LD2PT(vol) 0 /* Auto partition search */
258 /* Definitions of sector size */
259 #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)
260 #error Wrong sector size configuration
262 #if FF_MAX_SS == FF_MIN_SS
263 #define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
265 #define SS(fs) ((fs)->ssize) /* Variable sector size */
271 #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
272 #error Invalid FF_FS_NORTC settings
274 #define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
276 #define GET_FATTIME() get_fattime()
280 /* File lock controls */
283 #error FF_FS_LOCK must be 0 at read-only configuration
286 FATFS
* fs
; /* Object ID 1, volume (NULL:blank entry) */
287 DWORD clu
; /* Object ID 2, containing directory (0:root) */
288 DWORD ofs
; /* Object ID 3, offset in the directory */
289 UINT ctr
; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
294 /* SBCS up-case tables (\x80-\xFF) */
295 #define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
296 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
297 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
298 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
299 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
300 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
301 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
302 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
303 #define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
304 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
305 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
306 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
307 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
308 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
309 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
310 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
311 #define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
312 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
313 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
314 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
315 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
316 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
317 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
318 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
319 #define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
320 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
321 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
322 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
323 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
324 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
325 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
326 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
327 #define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
328 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
329 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
330 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
331 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
332 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
333 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
334 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
335 #define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
336 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
337 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
338 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
339 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
340 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
341 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
342 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
343 #define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
344 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
345 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
346 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
347 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
348 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
349 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
350 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
351 #define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
352 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
353 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
354 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
355 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
356 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
357 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
358 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
359 #define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
360 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
361 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
362 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
363 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
364 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
365 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
366 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
367 #define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
368 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
369 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
370 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
371 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
372 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
373 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
374 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
375 #define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
376 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
377 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
378 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
379 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
380 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
381 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
382 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
383 #define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
384 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
385 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
386 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
387 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
388 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
389 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
390 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
391 #define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
392 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
393 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
394 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
395 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
396 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
397 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
398 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
399 #define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
400 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
401 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
402 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
403 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
404 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
405 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
406 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
407 #define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
408 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
409 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
410 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
411 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
412 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
413 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
414 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
415 #define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
416 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
417 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
418 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
419 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
420 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
421 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
422 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
423 #define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
424 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
425 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
426 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
427 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
428 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
429 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
430 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
433 /* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
434 /* <------> <------> <------> <------> <------> */
435 #define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
436 #define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
437 #define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
438 #define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
441 /* Macros for table definitions */
442 #define MERGE_2STR(a, b) a ## b
443 #define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
448 /*--------------------------------------------------------------------------
450 Module Private Work Area
452 ---------------------------------------------------------------------------*/
453 /* Remark: Variables defined here without initial value shall be guaranteed
454 / zero/null at start-up. If not, the linker option or start-up routine is
455 / not compliance with C standard. */
457 /*--------------------------------*/
458 /* File/Volume controls */
459 /*--------------------------------*/
461 #if FF_VOLUMES < 1 || FF_VOLUMES > 10
462 #error Wrong FF_VOLUMES setting
464 static FATFS
* FatFs
[ FF_VOLUMES
]; /* Pointer to the filesystem objects (logical drives) */
465 static WORD Fsid
; /* Filesystem mount ID */
468 static BYTE CurrVol
; /* Current drive set by f_chdrive() */
472 static FILESEM Files
[ FF_FS_LOCK
]; /* Open object lock semaphores */
474 static BYTE SysLock
; /* System lock flag (0:no mutex, 1:unlocked, 2:locked) */
479 #ifdef FF_VOLUME_STRS
480 static const char * const VolumeStr
[ FF_VOLUMES
] = { FF_VOLUME_STRS
}; /* Pre-defined volume ID */
485 #if FF_MIN_GPT > 0x100000000
486 #error Wrong FF_MIN_GPT setting
488 static const BYTE GUID_MS_Basic
[ 16 ] = { 0xA2 , 0xA0 , 0xD0 , 0xEB , 0xE5 , 0xB9 , 0x33 , 0x44 , 0x87 , 0xC0 , 0x68 , 0xB6 , 0xB7 , 0x26 , 0x99 , 0xC7 };
493 /*--------------------------------*/
494 /* LFN/Directory working buffer */
495 /*--------------------------------*/
497 #if FF_USE_LFN == 0 /* Non-LFN configuration */
499 #error LFN must be enabled when enable exFAT
502 #define INIT_NAMBUF(fs)
503 #define FREE_NAMBUF()
504 #define LEAVE_MKFS(res) return res
506 #else /* LFN configurations */
507 #if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
508 #error Wrong setting of FF_MAX_LFN
510 #if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
511 #error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
513 #if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
514 #error Wrong setting of FF_LFN_UNICODE
516 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 */
517 #define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
519 #if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
521 static BYTE DirBuf
[ MAXDIRB ( FF_MAX_LFN
)]; /* Directory entry block scratchpad buffer */
523 static WCHAR LfnBuf
[ FF_MAX_LFN
+ 1 ]; /* LFN working buffer */
525 #define INIT_NAMBUF(fs)
526 #define FREE_NAMBUF()
527 #define LEAVE_MKFS(res) return res
529 #elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
531 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
532 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
533 #define FREE_NAMBUF()
535 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
536 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; }
537 #define FREE_NAMBUF()
539 #define LEAVE_MKFS(res) return res
541 #elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
543 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
544 #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); }
545 #define FREE_NAMBUF() ff_memfree(lfn)
547 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */
548 #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
549 #define FREE_NAMBUF() ff_memfree(lfn)
551 #define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
552 #define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
555 #error Wrong setting of FF_USE_LFN
557 #endif /* FF_USE_LFN == 1 */
558 #endif /* FF_USE_LFN == 0 */
562 /*--------------------------------*/
563 /* Code conversion tables */
564 /*--------------------------------*/
566 #if FF_CODE_PAGE == 0 /* Run-time code page configuration */
567 #define CODEPAGE CodePage
568 static WORD CodePage
; /* Current code page */
569 static const BYTE
* ExCvt
; /* Ptr to SBCS up-case table Ct???[] (null:not used) */
570 static const BYTE
* DbcTbl
; /* Ptr to DBCS code range table Dc???[] (null:not used) */
572 static const BYTE Ct437
[] = TBL_CT437
;
573 static const BYTE Ct720
[] = TBL_CT720
;
574 static const BYTE Ct737
[] = TBL_CT737
;
575 static const BYTE Ct771
[] = TBL_CT771
;
576 static const BYTE Ct775
[] = TBL_CT775
;
577 static const BYTE Ct850
[] = TBL_CT850
;
578 static const BYTE Ct852
[] = TBL_CT852
;
579 static const BYTE Ct855
[] = TBL_CT855
;
580 static const BYTE Ct857
[] = TBL_CT857
;
581 static const BYTE Ct860
[] = TBL_CT860
;
582 static const BYTE Ct861
[] = TBL_CT861
;
583 static const BYTE Ct862
[] = TBL_CT862
;
584 static const BYTE Ct863
[] = TBL_CT863
;
585 static const BYTE Ct864
[] = TBL_CT864
;
586 static const BYTE Ct865
[] = TBL_CT865
;
587 static const BYTE Ct866
[] = TBL_CT866
;
588 static const BYTE Ct869
[] = TBL_CT869
;
589 static const BYTE Dc932
[] = TBL_DC932
;
590 static const BYTE Dc936
[] = TBL_DC936
;
591 static const BYTE Dc949
[] = TBL_DC949
;
592 static const BYTE Dc950
[] = TBL_DC950
;
594 #elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
595 #define CODEPAGE FF_CODE_PAGE
596 static const BYTE ExCvt
[] = MKCVTBL ( TBL_CT
, FF_CODE_PAGE
);
598 #else /* Static code page configuration (DBCS) */
599 #define CODEPAGE FF_CODE_PAGE
600 static const BYTE DbcTbl
[] = MKCVTBL ( TBL_DC
, FF_CODE_PAGE
);
607 /*--------------------------------------------------------------------------
609 Module Private Functions
611 ---------------------------------------------------------------------------*/
614 /*-----------------------------------------------------------------------*/
615 /* Load/Store multi-byte word in the FAT structure */
616 /*-----------------------------------------------------------------------*/
618 static WORD
ld_word ( const BYTE
* ptr
) /* Load a 2-byte little-endian word */
623 rv
= rv
<< 8 | ptr
[ 0 ];
627 static DWORD
ld_dword ( const BYTE
* ptr
) /* Load a 4-byte little-endian word */
632 rv
= rv
<< 8 | ptr
[ 2 ];
633 rv
= rv
<< 8 | ptr
[ 1 ];
634 rv
= rv
<< 8 | ptr
[ 0 ];
639 static QWORD
ld_qword ( const BYTE
* ptr
) /* Load an 8-byte little-endian word */
644 rv
= rv
<< 8 | ptr
[ 6 ];
645 rv
= rv
<< 8 | ptr
[ 5 ];
646 rv
= rv
<< 8 | ptr
[ 4 ];
647 rv
= rv
<< 8 | ptr
[ 3 ];
648 rv
= rv
<< 8 | ptr
[ 2 ];
649 rv
= rv
<< 8 | ptr
[ 1 ];
650 rv
= rv
<< 8 | ptr
[ 0 ];
656 static void st_word ( BYTE
* ptr
, WORD val
) /* Store a 2-byte word in little-endian */
658 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
662 static void st_dword ( BYTE
* ptr
, DWORD val
) /* Store a 4-byte word in little-endian */
664 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
665 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
666 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
671 static void st_qword ( BYTE
* ptr
, QWORD val
) /* Store an 8-byte word in little-endian */
673 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
674 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
675 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
676 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
677 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
678 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
679 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
683 #endif /* !FF_FS_READONLY */
687 /*-----------------------------------------------------------------------*/
688 /* String functions */
689 /*-----------------------------------------------------------------------*/
691 /* Test if the byte is DBC 1st byte */
692 static int dbc_1st ( BYTE c
)
694 #if FF_CODE_PAGE == 0 /* Variable code page */
695 if ( DbcTbl
&& c
>= DbcTbl
[ 0 ]) {
696 if ( c
<= DbcTbl
[ 1 ]) return 1 ; /* 1st byte range 1 */
697 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ; /* 1st byte range 2 */
699 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
700 if ( c
>= DbcTbl
[ 0 ]) {
701 if ( c
<= DbcTbl
[ 1 ]) return 1 ;
702 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ;
704 #else /* SBCS fixed code page */
705 if ( c
!= 0 ) return 0 ; /* Always false */
711 /* Test if the byte is DBC 2nd byte */
712 static int dbc_2nd ( BYTE c
)
714 #if FF_CODE_PAGE == 0 /* Variable code page */
715 if ( DbcTbl
&& c
>= DbcTbl
[ 4 ]) {
716 if ( c
<= DbcTbl
[ 5 ]) return 1 ; /* 2nd byte range 1 */
717 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ; /* 2nd byte range 2 */
718 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ; /* 2nd byte range 3 */
720 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
721 if ( c
>= DbcTbl
[ 4 ]) {
722 if ( c
<= DbcTbl
[ 5 ]) return 1 ;
723 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ;
724 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ;
726 #else /* SBCS fixed code page */
727 if ( c
!= 0 ) return 0 ; /* Always false */
735 /* Get a Unicode code point from the TCHAR string in defined API encodeing */
736 static DWORD
tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */
737 const TCHAR
** str
/* Pointer to pointer to TCHAR string in configured encoding */
741 const TCHAR
* p
= * str
;
743 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
746 uc
= * p
++; /* Get a unit */
747 if ( IsSurrogate ( uc
)) { /* Surrogate? */
748 wc
= * p
++; /* Get low surrogate */
749 if (! IsSurrogateH ( uc
) || ! IsSurrogateL ( wc
)) return 0xFFFFFFFF ; /* Wrong surrogate? */
753 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
757 uc
= ( BYTE
)* p
++; /* Get an encoding unit */
758 if ( uc
& 0x80 ) { /* Multiple byte code? */
759 if (( uc
& 0xE0 ) == 0xC0 ) { /* 2-byte sequence? */
761 } else if (( uc
& 0xF0 ) == 0xE0 ) { /* 3-byte sequence? */
763 } else if (( uc
& 0xF8 ) == 0xF0 ) { /* 4-byte sequence? */
765 } else { /* Wrong sequence */
768 do { /* Get trailing bytes */
770 if (( b
& 0xC0 ) != 0x80 ) return 0xFFFFFFFF ; /* Wrong sequence? */
771 uc
= uc
<< 6 | ( b
& 0x3F );
773 if ( uc
< 0x80 || IsSurrogate ( uc
) || uc
>= 0x110000 ) return 0xFFFFFFFF ; /* Wrong code? */
774 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
777 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
778 uc
= ( TCHAR
)* p
++; /* Get a unit */
779 if ( uc
>= 0x110000 || IsSurrogate ( uc
)) return 0xFFFFFFFF ; /* Wrong code? */
780 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
782 #else /* ANSI/OEM input */
786 wc
= ( BYTE
)* p
++; /* Get a byte */
787 if ( dbc_1st (( BYTE
) wc
)) { /* Is it a DBC 1st byte? */
788 b
= ( BYTE
)* p
++; /* Get 2nd byte */
789 if (! dbc_2nd ( b
)) return 0xFFFFFFFF ; /* Invalid code? */
790 wc
= ( wc
<< 8 ) + b
; /* Make a DBC */
793 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM ==> Unicode */
794 if ( wc
== 0 ) return 0xFFFFFFFF ; /* Invalid code? */
799 * str
= p
; /* Next read pointer */
804 /* Store a Unicode char in defined API encoding */
805 static UINT
put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
806 DWORD chr
, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */
807 TCHAR
* buf
, /* Output buffer */
808 UINT szb
/* Size of the buffer */
811 #if FF_LFN_UNICODE == 1 /* UTF-16 output */
814 hs
= ( WCHAR
)( chr
>> 16 );
816 if ( hs
== 0 ) { /* Single encoding unit? */
817 if ( szb
< 1 || IsSurrogate ( wc
)) return 0 ; /* Buffer overflow or wrong code? */
821 if ( szb
< 2 || ! IsSurrogateH ( hs
) || ! IsSurrogateL ( wc
)) return 0 ; /* Buffer overflow or wrong surrogate? */
826 #elif FF_LFN_UNICODE == 2 /* UTF-8 output */
829 if ( chr
< 0x80 ) { /* Single byte code? */
830 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
834 if ( chr
< 0x800 ) { /* 2-byte sequence? */
835 if ( szb
< 2 ) return 0 ; /* Buffer overflow? */
836 * buf
++ = ( TCHAR
)( 0xC0 | ( chr
>> 6 & 0x1F ));
837 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
840 if ( chr
< 0x10000 ) { /* 3-byte sequence? */
841 if ( szb
< 3 || IsSurrogate ( chr
)) return 0 ; /* Buffer overflow or wrong code? */
842 * buf
++ = ( TCHAR
)( 0xE0 | ( chr
>> 12 & 0x0F ));
843 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
844 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
847 /* 4-byte sequence */
848 if ( szb
< 4 ) return 0 ; /* Buffer overflow? */
849 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
850 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
851 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
852 chr
= ( hc
| chr
) + 0x10000 ;
853 * buf
++ = ( TCHAR
)( 0xF0 | ( chr
>> 18 & 0x07 ));
854 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 12 & 0x3F ));
855 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
856 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
859 #elif FF_LFN_UNICODE == 3 /* UTF-32 output */
862 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
863 if ( chr
>= 0x10000 ) { /* Out of BMP? */
864 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
865 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
866 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
867 chr
= ( hc
| chr
) + 0x10000 ;
872 #else /* ANSI/OEM output */
875 wc
= ff_uni2oem ( chr
, CODEPAGE
);
876 if ( wc
>= 0x100 ) { /* Is this a DBC? */
877 if ( szb
< 2 ) return 0 ;
878 * buf
++ = ( char )( wc
>> 8 ); /* Store DBC 1st byte */
879 * buf
++ = ( TCHAR
) wc
; /* Store DBC 2nd byte */
882 if ( wc
== 0 || szb
< 1 ) return 0 ; /* Invalid char or buffer overflow? */
883 * buf
++ = ( TCHAR
) wc
; /* Store the character */
887 #endif /* FF_USE_LFN */
891 /*-----------------------------------------------------------------------*/
892 /* Request/Release grant to access the volume */
893 /*-----------------------------------------------------------------------*/
895 static int lock_volume ( /* 1:Ok, 0:timeout */
896 FATFS
* fs
, /* Filesystem object to lock */
897 int syslock
/* System lock required */
904 rv
= ff_mutex_take ( fs
-> ldrv
); /* Lock the volume */
905 if ( rv
&& syslock
) { /* System lock reqiered? */
906 rv
= ff_mutex_take ( FF_VOLUMES
); /* Lock the system */
908 SysLock
= 2 ; /* System lock succeeded */
910 ff_mutex_give ( fs
-> ldrv
); /* Failed system lock */
914 rv
= syslock
? ff_mutex_take ( fs
-> ldrv
) : ff_mutex_take ( fs
-> ldrv
); /* Lock the volume (this is to prevent compiler warning) */
920 static void unlock_volume (
921 FATFS
* fs
, /* Filesystem object */
922 FRESULT res
/* Result code to be returned */
925 if ( fs
&& res
!= FR_NOT_ENABLED
&& res
!= FR_INVALID_DRIVE
&& res
!= FR_TIMEOUT
) {
927 if ( SysLock
== 2 ) { /* Is the system locked? */
929 ff_mutex_give ( FF_VOLUMES
);
932 ff_mutex_give ( fs
-> ldrv
); /* Unlock the volume */
941 /*-----------------------------------------------------------------------*/
942 /* File shareing control functions */
943 /*-----------------------------------------------------------------------*/
945 static FRESULT
chk_share ( /* Check if the file can be accessed */
946 DIR * dp
, /* Directory object pointing the file to be checked */
947 int acc
/* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
952 /* Search open object table for the object */
954 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
955 if ( Files
[ i
]. fs
) { /* Existing entry */
956 if ( Files
[ i
]. fs
== dp
-> obj
. fs
&& /* Check if the object matches with an open object */
957 Files
[ i
]. clu
== dp
-> obj
. sclust
&&
958 Files
[ i
]. ofs
== dp
-> dptr
) break ;
959 } else { /* Blank entry */
963 if ( i
== FF_FS_LOCK
) { /* The object has not been opened */
964 return (! be
&& acc
!= 2 ) ? FR_TOO_MANY_OPEN_FILES
: FR_OK
; /* Is there a blank entry for new object? */
967 /* The object was opened. Reject any open against writing file and all write mode open */
968 return ( acc
!= 0 || Files
[ i
]. ctr
== 0x100 ) ? FR_LOCKED
: FR_OK
;
972 static int enq_share ( void ) /* Check if an entry is available for a new object */
976 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ; /* Find a free entry */
977 return ( i
== FF_FS_LOCK
) ? 0 : 1 ;
981 static UINT
inc_share ( /* Increment object open counter and returns its index (0:Internal error) */
982 DIR * dp
, /* Directory object pointing the file to register or increment */
983 int acc
/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
989 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) { /* Find the object */
990 if ( Files
[ i
]. fs
== dp
-> obj
. fs
991 && Files
[ i
]. clu
== dp
-> obj
. sclust
992 && Files
[ i
]. ofs
== dp
-> dptr
) break ;
995 if ( i
== FF_FS_LOCK
) { /* Not opened. Register it as new. */
996 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ; /* Find a free entry */
997 if ( i
== FF_FS_LOCK
) return 0 ; /* No free entry to register (int err) */
998 Files
[ i
]. fs
= dp
-> obj
. fs
;
999 Files
[ i
]. clu
= dp
-> obj
. sclust
;
1000 Files
[ i
]. ofs
= dp
-> dptr
;
1004 if ( acc
>= 1 && Files
[ i
]. ctr
) return 0 ; /* Access violation (int err) */
1006 Files
[ i
]. ctr
= acc
? 0x100 : Files
[ i
]. ctr
+ 1 ; /* Set semaphore value */
1008 return i
+ 1 ; /* Index number origin from 1 */
1012 static FRESULT
dec_share ( /* Decrement object open counter */
1013 UINT i
/* Semaphore index (1..) */
1020 if (-- i
< FF_FS_LOCK
) { /* Index number origin from 0 */
1022 if ( n
== 0x100 ) n
= 0 ; /* If write mode open, delete the object semaphore */
1023 if ( n
> 0 ) n
--; /* Decrement read mode open count */
1025 if ( n
== 0 ) { /* Delete the object semaphore if open count becomes zero */
1026 Files
[ i
]. fs
= 0 ; /* Free the entry <<<If this memory write operation is not in atomic, FF_FS_REENTRANT == 1 and FF_VOLUMES > 1, there is a potential error in this process >>> */
1030 res
= FR_INT_ERR
; /* Invalid index number */
1036 static void clear_share ( /* Clear all lock entries of the volume */
1042 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
1043 if ( Files
[ i
]. fs
== fs
) Files
[ i
]. fs
= 0 ;
1047 #endif /* FF_FS_LOCK */
1051 /*-----------------------------------------------------------------------*/
1052 /* Move/Flush disk access window in the filesystem object */
1053 /*-----------------------------------------------------------------------*/
1055 static FRESULT
sync_window ( /* Returns FR_OK or FR_DISK_ERR */
1056 FATFS
* fs
/* Filesystem object */
1059 FRESULT res
= FR_OK
;
1062 if ( fs
-> wflag
) { /* Is the disk access window dirty? */
1063 if ( disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 ) == RES_OK
) { /* Write it back into the volume */
1064 fs
-> wflag
= 0 ; /* Clear window dirty flag */
1065 if ( fs
-> winsect
- fs
-> fatbase
< fs
-> fsize
) { /* Is it in the 1st FAT? */
1066 if ( fs
-> n_fats
== 2 ) disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
+ fs
-> fsize
, 1 ); /* Reflect it to 2nd FAT if needed */
1077 static FRESULT
move_window ( /* Returns FR_OK or FR_DISK_ERR */
1078 FATFS
* fs
, /* Filesystem object */
1079 LBA_t sect
/* Sector LBA to make appearance in the fs->win[] */
1082 FRESULT res
= FR_OK
;
1085 if ( sect
!= fs
-> winsect
) { /* Window offset changed? */
1087 res
= sync_window ( fs
); /* Flush the window */
1089 if ( res
== FR_OK
) { /* Fill sector window with new data */
1090 if ( disk_read ( fs
-> pdrv
, fs
-> win
, sect
, 1 ) != RES_OK
) {
1091 sect
= ( LBA_t
) 0 - 1 ; /* Invalidate window if read data is not valid */
1104 /*-----------------------------------------------------------------------*/
1105 /* Synchronize filesystem and data on the storage */
1106 /*-----------------------------------------------------------------------*/
1108 static FRESULT
sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
1109 FATFS
* fs
/* Filesystem object */
1115 res
= sync_window ( fs
);
1117 if ( fs
-> fs_type
== FS_FAT32
&& fs
-> fsi_flag
== 1 ) { /* FAT32: Update FSInfo sector if needed */
1118 /* Create FSInfo structure */
1119 memset ( fs
-> win
, 0 , sizeof fs
-> win
);
1120 st_word ( fs
-> win
+ BS_55AA
, 0xAA55 ); /* Boot signature */
1121 st_dword ( fs
-> win
+ FSI_LeadSig
, 0x41615252 ); /* Leading signature */
1122 st_dword ( fs
-> win
+ FSI_StrucSig
, 0x61417272 ); /* Structure signature */
1123 st_dword ( fs
-> win
+ FSI_Free_Count
, fs
-> free_clst
); /* Number of free clusters */
1124 st_dword ( fs
-> win
+ FSI_Nxt_Free
, fs
-> last_clst
); /* Last allocated culuster */
1125 fs
-> winsect
= fs
-> volbase
+ 1 ; /* Write it into the FSInfo sector (Next to VBR) */
1126 disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 );
1129 /* Make sure that no pending write process in the lower layer */
1130 if ( disk_ioctl ( fs
-> pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) res
= FR_DISK_ERR
;
1140 /*-----------------------------------------------------------------------*/
1141 /* Get physical sector number from cluster number */
1142 /*-----------------------------------------------------------------------*/
1144 static LBA_t
clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
1145 FATFS
* fs
, /* Filesystem object */
1146 DWORD clst
/* Cluster# to be converted */
1149 clst
-= 2 ; /* Cluster number is origin from 2 */
1150 if ( clst
>= fs
-> n_fatent
- 2 ) return 0 ; /* Is it invalid cluster number? */
1151 return fs
-> database
+ ( LBA_t
) fs
-> csize
* clst
; /* Start sector number of the cluster */
1157 /*-----------------------------------------------------------------------*/
1158 /* FAT access - Read value of an FAT entry */
1159 /*-----------------------------------------------------------------------*/
1161 static DWORD
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
1162 FFOBJID
* obj
, /* Corresponding object */
1163 DWORD clst
/* Cluster number to get the value */
1168 FATFS
* fs
= obj
-> fs
;
1171 if ( clst
< 2 || clst
>= fs
-> n_fatent
) { /* Check if in valid range */
1172 val
= 1 ; /* Internal error */
1175 val
= 0xFFFFFFFF ; /* Default value falls on disk error */
1177 switch ( fs
-> fs_type
) {
1179 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ;
1180 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1181 wc
= fs
-> win
[ bc
++ % SS ( fs
)]; /* Get 1st byte of the entry */
1182 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1183 wc
|= fs
-> win
[ bc
% SS ( fs
)] << 8 ; /* Merge 2nd byte of the entry */
1184 val
= ( clst
& 1 ) ? ( wc
>> 4 ) : ( wc
& 0xFFF ); /* Adjust bit position */
1188 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 ))) != FR_OK
) break ;
1189 val
= ld_word ( fs
-> win
+ clst
* 2 % SS ( fs
)); /* Simple WORD array */
1193 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1194 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x0FFFFFFF ; /* Simple DWORD array but mask out upper 4 bits */
1198 if (( obj
-> objsize
!= 0 && obj
-> sclust
!= 0 ) || obj
-> stat
== 0 ) { /* Object except root dir must have valid data length */
1199 DWORD cofs
= clst
- obj
-> sclust
; /* Offset from start cluster */
1200 DWORD clen
= ( DWORD
)(( LBA_t
)(( obj
-> objsize
- 1 ) / SS ( fs
)) / fs
-> csize
); /* Number of clusters - 1 */
1202 if ( obj
-> stat
== 2 && cofs
<= clen
) { /* Is it a contiguous chain? */
1203 val
= ( cofs
== clen
) ? 0x7FFFFFFF : clst
+ 1 ; /* No data on the FAT, generate the value */
1206 if ( obj
-> stat
== 3 && cofs
< obj
-> n_cont
) { /* Is it in the 1st fragment? */
1207 val
= clst
+ 1 ; /* Generate the value */
1210 if ( obj
-> stat
!= 2 ) { /* Get value from FAT if FAT chain is valid */
1211 if ( obj
-> n_frag
!= 0 ) { /* Is it on the growing edge? */
1212 val
= 0x7FFFFFFF ; /* Generate EOC */
1214 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1215 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x7FFFFFFF ;
1220 val
= 1 ; /* Internal error */
1224 val
= 1 ; /* Internal error */
1235 /*-----------------------------------------------------------------------*/
1236 /* FAT access - Change value of an FAT entry */
1237 /*-----------------------------------------------------------------------*/
1239 static FRESULT
put_fat ( /* FR_OK(0):succeeded, !=0:error */
1240 FATFS
* fs
, /* Corresponding filesystem object */
1241 DWORD clst
, /* FAT index number (cluster number) to be changed */
1242 DWORD val
/* New value to be set to the entry */
1247 FRESULT res
= FR_INT_ERR
;
1250 if ( clst
>= 2 && clst
< fs
-> n_fatent
) { /* Check if in valid range */
1251 switch ( fs
-> fs_type
) {
1253 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ; /* bc: byte offset of the entry */
1254 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1255 if ( res
!= FR_OK
) break ;
1256 p
= fs
-> win
+ bc
++ % SS ( fs
);
1257 * p
= ( clst
& 1 ) ? ((* p
& 0x0F ) | (( BYTE
) val
<< 4 )) : ( BYTE
) val
; /* Update 1st byte */
1259 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1260 if ( res
!= FR_OK
) break ;
1261 p
= fs
-> win
+ bc
% SS ( fs
);
1262 * p
= ( clst
& 1 ) ? ( BYTE
)( val
>> 4 ) : ((* p
& 0xF0 ) | (( BYTE
)( val
>> 8 ) & 0x0F )); /* Update 2nd byte */
1267 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 )));
1268 if ( res
!= FR_OK
) break ;
1269 st_word ( fs
-> win
+ clst
* 2 % SS ( fs
), ( WORD
) val
); /* Simple WORD array */
1277 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 )));
1278 if ( res
!= FR_OK
) break ;
1279 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1280 val
= ( val
& 0x0FFFFFFF ) | ( ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0xF0000000 );
1282 st_dword ( fs
-> win
+ clst
* 4 % SS ( fs
), val
);
1290 #endif /* !FF_FS_READONLY */
1295 #if FF_FS_EXFAT && !FF_FS_READONLY
1296 /*-----------------------------------------------------------------------*/
1297 /* exFAT: Accessing FAT and Allocation Bitmap */
1298 /*-----------------------------------------------------------------------*/
1300 /*--------------------------------------*/
1301 /* Find a contiguous free cluster block */
1302 /*--------------------------------------*/
1304 static DWORD
find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
1305 FATFS
* fs
, /* Filesystem object */
1306 DWORD clst
, /* Cluster number to scan from */
1307 DWORD ncl
/* Number of contiguous clusters to find (1..) */
1312 DWORD val
, scl
, ctr
;
1315 clst
-= 2 ; /* The first bit in the bitmap corresponds to cluster #2 */
1316 if ( clst
>= fs
-> n_fatent
- 2 ) clst
= 0 ;
1317 scl
= val
= clst
; ctr
= 0 ;
1319 if ( move_window ( fs
, fs
-> bitbase
+ val
/ 8 / SS ( fs
)) != FR_OK
) return 0xFFFFFFFF ;
1320 i
= val
/ 8 % SS ( fs
); bm
= 1 << ( val
% 8 );
1323 bv
= fs
-> win
[ i
] & bm
; bm
<<= 1 ; /* Get bit value */
1324 if (++ val
>= fs
-> n_fatent
- 2 ) { /* Next cluster (with wrap-around) */
1325 val
= 0 ; bm
= 0 ; i
= SS ( fs
);
1327 if ( bv
== 0 ) { /* Is it a free cluster? */
1328 if (++ ctr
== ncl
) return scl
+ 2 ; /* Check if run length is sufficient for required */
1330 scl
= val
; ctr
= 0 ; /* Encountered a cluster in-use, restart to scan */
1332 if ( val
== clst
) return 0 ; /* All cluster scanned? */
1335 } while (++ i
< SS ( fs
));
1340 /*----------------------------------------*/
1341 /* Set/Clear a block of allocation bitmap */
1342 /*----------------------------------------*/
1344 static FRESULT
change_bitmap (
1345 FATFS
* fs
, /* Filesystem object */
1346 DWORD clst
, /* Cluster number to change from */
1347 DWORD ncl
, /* Number of clusters to be changed */
1348 int bv
/* bit value to be set (0 or 1) */
1356 clst
-= 2 ; /* The first bit corresponds to cluster #2 */
1357 sect
= fs
-> bitbase
+ clst
/ 8 / SS ( fs
); /* Sector address */
1358 i
= clst
/ 8 % SS ( fs
); /* Byte offset in the sector */
1359 bm
= 1 << ( clst
% 8 ); /* Bit mask in the byte */
1361 if ( move_window ( fs
, sect
++) != FR_OK
) return FR_DISK_ERR
;
1364 if ( bv
== ( int )(( fs
-> win
[ i
] & bm
) != 0 )) return FR_INT_ERR
; /* Is the bit expected value? */
1365 fs
-> win
[ i
] ^= bm
; /* Flip the bit */
1367 if (-- ncl
== 0 ) return FR_OK
; /* All bits processed? */
1368 } while ( bm
<<= 1 ); /* Next bit */
1370 } while (++ i
< SS ( fs
)); /* Next byte */
1376 /*---------------------------------------------*/
1377 /* Fill the first fragment of the FAT chain */
1378 /*---------------------------------------------*/
1380 static FRESULT
fill_first_frag (
1381 FFOBJID
* obj
/* Pointer to the corresponding object */
1388 if ( obj
-> stat
== 3 ) { /* Has the object been changed 'fragmented' in this session? */
1389 for ( cl
= obj
-> sclust
, n
= obj
-> n_cont
; n
; cl
++, n
--) { /* Create cluster chain on the FAT */
1390 res
= put_fat ( obj
-> fs
, cl
, cl
+ 1 );
1391 if ( res
!= FR_OK
) return res
;
1393 obj
-> stat
= 0 ; /* Change status 'FAT chain is valid' */
1399 /*---------------------------------------------*/
1400 /* Fill the last fragment of the FAT chain */
1401 /*---------------------------------------------*/
1403 static FRESULT
fill_last_frag (
1404 FFOBJID
* obj
, /* Pointer to the corresponding object */
1405 DWORD lcl
, /* Last cluster of the fragment */
1406 DWORD term
/* Value to set the last FAT entry */
1412 while ( obj
-> n_frag
> 0 ) { /* Create the chain of last fragment */
1413 res
= put_fat ( obj
-> fs
, lcl
- obj
-> n_frag
+ 1 , ( obj
-> n_frag
> 1 ) ? lcl
- obj
-> n_frag
+ 2 : term
);
1414 if ( res
!= FR_OK
) return res
;
1420 #endif /* FF_FS_EXFAT && !FF_FS_READONLY */
1425 /*-----------------------------------------------------------------------*/
1426 /* FAT handling - Remove a cluster chain */
1427 /*-----------------------------------------------------------------------*/
1429 static FRESULT
remove_chain ( /* FR_OK(0):succeeded, !=0:error */
1430 FFOBJID
* obj
, /* Corresponding object */
1431 DWORD clst
, /* Cluster to remove a chain from */
1432 DWORD pclst
/* Previous cluster of clst (0 if entire chain) */
1435 FRESULT res
= FR_OK
;
1437 FATFS
* fs
= obj
-> fs
;
1438 #if FF_FS_EXFAT || FF_USE_TRIM
1439 DWORD scl
= clst
, ecl
= clst
;
1445 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Check if in valid range */
1447 /* Mark the previous cluster 'EOC' on the FAT if it exists */
1448 if ( pclst
!= 0 && (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
|| obj
-> stat
!= 2 )) {
1449 res
= put_fat ( fs
, pclst
, 0xFFFFFFFF );
1450 if ( res
!= FR_OK
) return res
;
1453 /* Remove the chain */
1455 nxt
= get_fat ( obj
, clst
); /* Get cluster status */
1456 if ( nxt
== 0 ) break ; /* Empty cluster? */
1457 if ( nxt
== 1 ) return FR_INT_ERR
; /* Internal error? */
1458 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error? */
1459 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1460 res
= put_fat ( fs
, clst
, 0 ); /* Mark the cluster 'free' on the FAT */
1461 if ( res
!= FR_OK
) return res
;
1463 if ( fs
-> free_clst
< fs
-> n_fatent
- 2 ) { /* Update FSINFO */
1467 #if FF_FS_EXFAT || FF_USE_TRIM
1468 if ( ecl
+ 1 == nxt
) { /* Is next cluster contiguous? */
1470 } else { /* End of contiguous cluster block */
1472 if ( fs
-> fs_type
== FS_EXFAT
) {
1473 res
= change_bitmap ( fs
, scl
, ecl
- scl
+ 1 , 0 ); /* Mark the cluster block 'free' on the bitmap */
1474 if ( res
!= FR_OK
) return res
;
1478 rt
[ 0 ] = clst2sect ( fs
, scl
); /* Start of data area to be freed */
1479 rt
[ 1 ] = clst2sect ( fs
, ecl
) + fs
-> csize
- 1 ; /* End of data area to be freed */
1480 disk_ioctl ( fs
-> pdrv
, CTRL_TRIM
, rt
); /* Inform storage device that the data in the block may be erased */
1485 clst
= nxt
; /* Next cluster */
1486 } while ( clst
< fs
-> n_fatent
); /* Repeat while not the last link */
1489 /* Some post processes for chain status */
1490 if ( fs
-> fs_type
== FS_EXFAT
) {
1491 if ( pclst
== 0 ) { /* Has the entire chain been removed? */
1492 obj
-> stat
= 0 ; /* Change the chain status 'initial' */
1494 if ( obj
-> stat
== 0 ) { /* Is it a fragmented chain from the beginning of this session? */
1495 clst
= obj
-> sclust
; /* Follow the chain to check if it gets contiguous */
1496 while ( clst
!= pclst
) {
1497 nxt
= get_fat ( obj
, clst
);
1498 if ( nxt
< 2 ) return FR_INT_ERR
;
1499 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
;
1500 if ( nxt
!= clst
+ 1 ) break ; /* Not contiguous? */
1503 if ( clst
== pclst
) { /* Has the chain got contiguous again? */
1504 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1507 if ( obj
-> stat
== 3 && pclst
>= obj
-> sclust
&& pclst
<= obj
-> sclust
+ obj
-> n_cont
) { /* Was the chain fragmented in this session and got contiguous again? */
1508 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1520 /*-----------------------------------------------------------------------*/
1521 /* FAT handling - Stretch a chain or Create a new chain */
1522 /*-----------------------------------------------------------------------*/
1524 static DWORD
create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
1525 FFOBJID
* obj
, /* Corresponding object */
1526 DWORD clst
/* Cluster# to stretch, 0:Create a new chain */
1531 FATFS
* fs
= obj
-> fs
;
1534 if ( clst
== 0 ) { /* Create a new chain */
1535 scl
= fs
-> last_clst
; /* Suggested cluster to start to find */
1536 if ( scl
== 0 || scl
>= fs
-> n_fatent
) scl
= 1 ;
1538 else { /* Stretch a chain */
1539 cs
= get_fat ( obj
, clst
); /* Check the cluster status */
1540 if ( cs
< 2 ) return 1 ; /* Test for insanity */
1541 if ( cs
== 0xFFFFFFFF ) return cs
; /* Test for disk error */
1542 if ( cs
< fs
-> n_fatent
) return cs
; /* It is already followed by next cluster */
1543 scl
= clst
; /* Cluster to start to find */
1545 if ( fs
-> free_clst
== 0 ) return 0 ; /* No free cluster */
1548 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
1549 ncl
= find_bitmap ( fs
, scl
, 1 ); /* Find a free cluster */
1550 if ( ncl
== 0 || ncl
== 0xFFFFFFFF ) return ncl
; /* No free cluster or hard error? */
1551 res
= change_bitmap ( fs
, ncl
, 1 , 1 ); /* Mark the cluster 'in use' */
1552 if ( res
== FR_INT_ERR
) return 1 ;
1553 if ( res
== FR_DISK_ERR
) return 0xFFFFFFFF ;
1554 if ( clst
== 0 ) { /* Is it a new chain? */
1555 obj
-> stat
= 2 ; /* Set status 'contiguous' */
1556 } else { /* It is a stretched chain */
1557 if ( obj
-> stat
== 2 && ncl
!= scl
+ 1 ) { /* Is the chain got fragmented? */
1558 obj
-> n_cont
= scl
- obj
-> sclust
; /* Set size of the contiguous part */
1559 obj
-> stat
= 3 ; /* Change status 'just fragmented' */
1562 if ( obj
-> stat
!= 2 ) { /* Is the file non-contiguous? */
1563 if ( ncl
== clst
+ 1 ) { /* Is the cluster next to previous one? */
1564 obj
-> n_frag
= obj
-> n_frag
? obj
-> n_frag
+ 1 : 2 ; /* Increment size of last framgent */
1565 } else { /* New fragment */
1566 if ( obj
-> n_frag
== 0 ) obj
-> n_frag
= 1 ;
1567 res
= fill_last_frag ( obj
, clst
, ncl
); /* Fill last fragment on the FAT and link it to new one */
1568 if ( res
== FR_OK
) obj
-> n_frag
= 1 ;
1573 { /* On the FAT/FAT32 volume */
1575 if ( scl
== clst
) { /* Stretching an existing chain? */
1576 ncl
= scl
+ 1 ; /* Test if next cluster is free */
1577 if ( ncl
>= fs
-> n_fatent
) ncl
= 2 ;
1578 cs
= get_fat ( obj
, ncl
); /* Get next cluster status */
1579 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1580 if ( cs
!= 0 ) { /* Not free? */
1581 cs
= fs
-> last_clst
; /* Start at suggested cluster if it is valid */
1582 if ( cs
>= 2 && cs
< fs
-> n_fatent
) scl
= cs
;
1586 if ( ncl
== 0 ) { /* The new cluster cannot be contiguous and find another fragment */
1587 ncl
= scl
; /* Start cluster */
1589 ncl
++; /* Next cluster */
1590 if ( ncl
>= fs
-> n_fatent
) { /* Check wrap-around */
1592 if ( ncl
> scl
) return 0 ; /* No free cluster found? */
1594 cs
= get_fat ( obj
, ncl
); /* Get the cluster status */
1595 if ( cs
== 0 ) break ; /* Found a free cluster? */
1596 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1597 if ( ncl
== scl
) return 0 ; /* No free cluster found? */
1600 res
= put_fat ( fs
, ncl
, 0xFFFFFFFF ); /* Mark the new cluster 'EOC' */
1601 if ( res
== FR_OK
&& clst
!= 0 ) {
1602 res
= put_fat ( fs
, clst
, ncl
); /* Link it from the previous one if needed */
1606 if ( res
== FR_OK
) { /* Update FSINFO if function succeeded. */
1607 fs
-> last_clst
= ncl
;
1608 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) fs
-> free_clst
--;
1611 ncl
= ( res
== FR_DISK_ERR
) ? 0xFFFFFFFF : 1 ; /* Failed. Generate error status */
1614 return ncl
; /* Return new cluster number or error status */
1617 #endif /* !FF_FS_READONLY */
1623 /*-----------------------------------------------------------------------*/
1624 /* FAT handling - Convert offset into cluster with link map table */
1625 /*-----------------------------------------------------------------------*/
1627 static DWORD
clmt_clust ( /* <2:Error, >=2:Cluster number */
1628 FIL
* fp
, /* Pointer to the file object */
1629 FSIZE_t ofs
/* File offset to be converted to cluster# */
1634 FATFS
* fs
= fp
-> obj
. fs
;
1637 tbl
= fp
-> cltbl
+ 1 ; /* Top of CLMT */
1638 cl
= ( DWORD
)( ofs
/ SS ( fs
) / fs
-> csize
); /* Cluster order from top of the file */
1640 ncl
= * tbl
++; /* Number of cluters in the fragment */
1641 if ( ncl
== 0 ) return 0 ; /* End of table? (error) */
1642 if ( cl
< ncl
) break ; /* In this fragment? */
1643 cl
-= ncl
; tbl
++; /* Next fragment */
1645 return cl
+ * tbl
; /* Return the cluster number */
1648 #endif /* FF_USE_FASTSEEK */
1653 /*-----------------------------------------------------------------------*/
1654 /* Directory handling - Fill a cluster with zeros */
1655 /*-----------------------------------------------------------------------*/
1658 static FRESULT
dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
1659 FATFS
* fs
, /* Filesystem object */
1660 DWORD clst
/* Directory table to clear */
1668 if ( sync_window ( fs
) != FR_OK
) return FR_DISK_ERR
; /* Flush disk access window */
1669 sect
= clst2sect ( fs
, clst
); /* Top of the cluster */
1670 fs
-> winsect
= sect
; /* Set window to top of the cluster */
1671 memset ( fs
-> win
, 0 , sizeof fs
-> win
); /* Clear window buffer */
1672 #if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
1673 /* Allocate a temporary buffer */
1674 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 ) ;
1675 if ( szb
> SS ( fs
)) { /* Buffer allocated? */
1676 memset ( ibuf
, 0 , szb
);
1677 szb
/= SS ( fs
); /* Bytes -> Sectors */
1678 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1683 ibuf
= fs
-> win
; szb
= 1 ; /* Use window buffer (many single-sector writes may take a time) */
1684 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1686 return ( n
== fs
-> csize
) ? FR_OK
: FR_DISK_ERR
;
1688 #endif /* !FF_FS_READONLY */
1693 /*-----------------------------------------------------------------------*/
1694 /* Directory handling - Set directory index */
1695 /*-----------------------------------------------------------------------*/
1697 static FRESULT
dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
1698 DIR * dp
, /* Pointer to directory object */
1699 DWORD ofs
/* Offset of directory table */
1703 FATFS
* fs
= dp
-> obj
. fs
;
1706 if ( ofs
>= ( DWORD
)(( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) ? MAX_DIR_EX
: MAX_DIR
) || ofs
% SZDIRE
) { /* Check range of offset and alignment */
1709 dp
-> dptr
= ofs
; /* Set current offset */
1710 clst
= dp
-> obj
. sclust
; /* Table start cluster (0:root) */
1711 if ( clst
== 0 && fs
-> fs_type
>= FS_FAT32
) { /* Replace cluster# 0 with root cluster# */
1712 clst
= ( DWORD
) fs
-> dirbase
;
1713 if ( FF_FS_EXFAT
) dp
-> obj
. stat
= 0 ; /* exFAT: Root dir has an FAT chain */
1716 if ( clst
== 0 ) { /* Static table (root-directory on the FAT volume) */
1717 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) return FR_INT_ERR
; /* Is index out of range? */
1718 dp
-> sect
= fs
-> dirbase
;
1720 } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
1721 csz
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Bytes per cluster */
1722 while ( ofs
>= csz
) { /* Follow cluster chain */
1723 clst
= get_fat (& dp
-> obj
, clst
); /* Get next cluster */
1724 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1725 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Reached to end of table or internal error */
1728 dp
-> sect
= clst2sect ( fs
, clst
);
1730 dp
-> clust
= clst
; /* Current cluster# */
1731 if ( dp
-> sect
== 0 ) return FR_INT_ERR
;
1732 dp
-> sect
+= ofs
/ SS ( fs
); /* Sector# of the directory entry */
1733 dp
-> dir
= fs
-> win
+ ( ofs
% SS ( fs
)); /* Pointer to the entry in the win[] */
1741 /*-----------------------------------------------------------------------*/
1742 /* Directory handling - Move directory table index next */
1743 /*-----------------------------------------------------------------------*/
1745 static FRESULT
dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
1746 DIR * dp
, /* Pointer to the directory object */
1747 int stretch
/* 0: Do not stretch table, 1: Stretch table if needed */
1751 FATFS
* fs
= dp
-> obj
. fs
;
1754 ofs
= dp
-> dptr
+ SZDIRE
; /* Next entry */
1755 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 */
1756 if ( dp
-> sect
== 0 ) return FR_NO_FILE
; /* Report EOT if it has been disabled */
1758 if ( ofs
% SS ( fs
) == 0 ) { /* Sector changed? */
1759 dp
-> sect
++; /* Next sector */
1761 if ( dp
-> clust
== 0 ) { /* Static table */
1762 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) { /* Report EOT if it reached end of static table */
1763 dp
-> sect
= 0 ; return FR_NO_FILE
;
1766 else { /* Dynamic table */
1767 if (( ofs
/ SS ( fs
) & ( fs
-> csize
- 1 )) == 0 ) { /* Cluster changed? */
1768 clst
= get_fat (& dp
-> obj
, dp
-> clust
); /* Get next cluster */
1769 if ( clst
<= 1 ) return FR_INT_ERR
; /* Internal error */
1770 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1771 if ( clst
>= fs
-> n_fatent
) { /* It reached end of dynamic table */
1773 if (! stretch
) { /* If no stretch, report EOT */
1774 dp
-> sect
= 0 ; return FR_NO_FILE
;
1776 clst
= create_chain (& dp
-> obj
, dp
-> clust
); /* Allocate a cluster */
1777 if ( clst
== 0 ) return FR_DENIED
; /* No free cluster */
1778 if ( clst
== 1 ) return FR_INT_ERR
; /* Internal error */
1779 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1780 if ( dir_clear ( fs
, clst
) != FR_OK
) return FR_DISK_ERR
; /* Clean up the stretched table */
1781 if ( FF_FS_EXFAT
) dp
-> obj
. stat
|= 4 ; /* exFAT: The directory has been stretched */
1783 if (! stretch
) dp
-> sect
= 0 ; /* (this line is to suppress compiler warning) */
1784 dp
-> sect
= 0 ; return FR_NO_FILE
; /* Report EOT */
1787 dp
-> clust
= clst
; /* Initialize data for new cluster */
1788 dp
-> sect
= clst2sect ( fs
, clst
);
1792 dp
-> dptr
= ofs
; /* Current entry */
1793 dp
-> dir
= fs
-> win
+ ofs
% SS ( fs
); /* Pointer to the entry in the win[] */
1802 /*-----------------------------------------------------------------------*/
1803 /* Directory handling - Reserve a block of directory entries */
1804 /*-----------------------------------------------------------------------*/
1806 static FRESULT
dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
1807 DIR * dp
, /* Pointer to the directory object */
1808 UINT n_ent
/* Number of contiguous entries to allocate */
1813 FATFS
* fs
= dp
-> obj
. fs
;
1816 res
= dir_sdi ( dp
, 0 );
1820 res
= move_window ( fs
, dp
-> sect
);
1821 if ( res
!= FR_OK
) break ;
1823 if (( fs
-> fs_type
== FS_EXFAT
) ? ( int )(( dp
-> dir
[ XDIR_Type
] & 0x80 ) == 0 ) : ( int )( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 )) { /* Is the entry free? */
1825 if ( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 ) { /* Is the entry free? */
1827 if (++ n
== n_ent
) break ; /* Is a block of contiguous free entries found? */
1829 n
= 0 ; /* Not a free entry, restart to search */
1831 res
= dir_next ( dp
, 1 ); /* Next entry with table stretch enabled */
1832 } while ( res
== FR_OK
);
1835 if ( res
== FR_NO_FILE
) res
= FR_DENIED
; /* No directory entry to allocate */
1839 #endif /* !FF_FS_READONLY */
1844 /*-----------------------------------------------------------------------*/
1845 /* FAT: Directory handling - Load/Store start cluster number */
1846 /*-----------------------------------------------------------------------*/
1848 static DWORD
ld_clust ( /* Returns the top cluster value of the SFN entry */
1849 FATFS
* fs
, /* Pointer to the fs object */
1850 const BYTE
* dir
/* Pointer to the key entry */
1855 cl
= ld_word ( dir
+ DIR_FstClusLO
);
1856 if ( fs
-> fs_type
== FS_FAT32
) {
1857 cl
|= ( DWORD
) ld_word ( dir
+ DIR_FstClusHI
) << 16 ;
1865 static void st_clust (
1866 FATFS
* fs
, /* Pointer to the fs object */
1867 BYTE
* dir
, /* Pointer to the key entry */
1868 DWORD cl
/* Value to be set */
1871 st_word ( dir
+ DIR_FstClusLO
, ( WORD
) cl
);
1872 if ( fs
-> fs_type
== FS_FAT32
) {
1873 st_word ( dir
+ DIR_FstClusHI
, ( WORD
)( cl
>> 16 ));
1881 /*--------------------------------------------------------*/
1882 /* FAT-LFN: Compare a part of file name with an LFN entry */
1883 /*--------------------------------------------------------*/
1885 static int cmp_lfn ( /* 1:matched, 0:not matched */
1886 const WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer to be compared */
1887 BYTE
* dir
/* Pointer to the directory entry containing the part of LFN */
1894 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO */
1896 i
= (( dir
[ LDIR_Ord
] & 0x3F ) - 1 ) * 13 ; /* Offset in the LFN buffer */
1898 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1899 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1901 if ( i
>= FF_MAX_LFN
+ 1 || ff_wtoupper ( uc
) != ff_wtoupper ( lfnbuf
[ i
++])) { /* Compare it */
1902 return 0 ; /* Not matched */
1906 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1910 if (( dir
[ LDIR_Ord
] & LLEF
) && wc
&& lfnbuf
[ i
]) return 0 ; /* Last segment matched but different length */
1912 return 1 ; /* The part of LFN matched */
1916 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
1917 /*-----------------------------------------------------*/
1918 /* FAT-LFN: Pick a part of file name from an LFN entry */
1919 /*-----------------------------------------------------*/
1921 static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
1922 WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer */
1923 BYTE
* dir
/* Pointer to the LFN entry */
1930 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO is 0 */
1932 i
= (( dir
[ LDIR_Ord
] & ~ LLEF
) - 1 ) * 13 ; /* Offset in the LFN buffer */
1934 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1935 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1937 if ( i
>= FF_MAX_LFN
+ 1 ) return 0 ; /* Buffer overflow? */
1938 lfnbuf
[ i
++] = wc
= uc
; /* Store it */
1940 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1944 if ( dir
[ LDIR_Ord
] & LLEF
&& wc
!= 0 ) { /* Put terminator if it is the last LFN part and not terminated */
1945 if ( i
>= FF_MAX_LFN
+ 1 ) return 0 ; /* Buffer overflow? */
1949 return 1 ; /* The part of LFN is valid */
1955 /*-----------------------------------------*/
1956 /* FAT-LFN: Create an entry of LFN entries */
1957 /*-----------------------------------------*/
1959 static void put_lfn (
1960 const WCHAR
* lfn
, /* Pointer to the LFN */
1961 BYTE
* dir
, /* Pointer to the LFN entry to be created */
1962 BYTE ord
, /* LFN order (1-20) */
1963 BYTE sum
/* Checksum of the corresponding SFN */
1970 dir
[ LDIR_Chksum
] = sum
; /* Set checksum */
1971 dir
[ LDIR_Attr
] = AM_LFN
; /* Set attribute. LFN entry */
1973 st_word ( dir
+ LDIR_FstClusLO
, 0 );
1975 i
= ( ord
- 1 ) * 13 ; /* Get offset in the LFN working buffer */
1978 if ( wc
!= 0xFFFF ) wc
= lfn
[ i
++]; /* Get an effective character */
1979 st_word ( dir
+ LfnOfs
[ s
], wc
); /* Put it */
1980 if ( wc
== 0 ) wc
= 0xFFFF ; /* Padding characters for following items */
1982 if ( wc
== 0xFFFF || ! lfn
[ i
]) ord
|= LLEF
; /* Last LFN part is the start of LFN sequence */
1983 dir
[ LDIR_Ord
] = ord
; /* Set the LFN order */
1986 #endif /* !FF_FS_READONLY */
1987 #endif /* FF_USE_LFN */
1991 #if FF_USE_LFN && !FF_FS_READONLY
1992 /*-----------------------------------------------------------------------*/
1993 /* FAT-LFN: Create a Numbered SFN */
1994 /*-----------------------------------------------------------------------*/
1996 static void gen_numname (
1997 BYTE
* dst
, /* Pointer to the buffer to store numbered SFN */
1998 const BYTE
* src
, /* Pointer to SFN in directory form */
1999 const WCHAR
* lfn
, /* Pointer to LFN */
2000 UINT seq
/* Sequence number */
2009 memcpy ( dst
, src
, 11 ); /* Prepare the SFN to be modified */
2011 if ( seq
> 5 ) { /* In case of many collisions, generate a hash number instead of sequential number */
2013 while (* lfn
) { /* Create a CRC as hash value */
2015 for ( i
= 0 ; i
< 16 ; i
++) {
2016 sreg
= ( sreg
<< 1 ) + ( wc
& 1 );
2018 if ( sreg
& 0x10000 ) sreg
^= 0x11021 ;
2024 /* Make suffix (~ + hexadecimal) */
2027 c
= ( BYTE
)(( seq
% 16 ) + '0' ); seq
/= 16 ;
2028 if ( c
> '9' ) c
+= 7 ;
2033 /* Append the suffix to the SFN body */
2034 for ( j
= 0 ; j
< i
&& dst
[ j
] != ' ' ; j
++) { /* Find the offset to append */
2035 if ( dbc_1st ( dst
[ j
])) { /* To avoid DBC break up */
2036 if ( j
== i
- 1 ) break ;
2040 do { /* Append the suffix */
2041 dst
[ j
++] = ( i
< 8 ) ? ns
[ i
++] : ' ' ;
2044 #endif /* FF_USE_LFN && !FF_FS_READONLY */
2049 /*-----------------------------------------------------------------------*/
2050 /* FAT-LFN: Calculate checksum of an SFN entry */
2051 /*-----------------------------------------------------------------------*/
2053 static BYTE
sum_sfn (
2054 const BYTE
* dir
/* Pointer to the SFN entry */
2061 sum
= ( sum
>> 1 ) + ( sum
<< 7 ) + * dir
++;
2066 #endif /* FF_USE_LFN */
2071 /*-----------------------------------------------------------------------*/
2072 /* exFAT: Checksum */
2073 /*-----------------------------------------------------------------------*/
2075 static WORD
xdir_sum ( /* Get checksum of the directoly entry block */
2076 const BYTE
* dir
/* Directory entry block to be calculated */
2083 szblk
= ( dir
[ XDIR_NumSec
] + 1 ) * SZDIRE
; /* Number of bytes of the entry block */
2084 for ( i
= sum
= 0 ; i
< szblk
; i
++) {
2085 if ( i
== XDIR_SetSum
) { /* Skip 2-byte sum field */
2088 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + dir
[ i
];
2096 static WORD
xname_sum ( /* Get check sum (to be used as hash) of the file name */
2097 const WCHAR
* name
/* File name to be calculated */
2104 while (( chr
= * name
++) != 0 ) {
2105 chr
= ( WCHAR
) ff_wtoupper ( chr
); /* File name needs to be up-case converted */
2106 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
& 0xFF );
2107 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
>> 8 );
2113 #if !FF_FS_READONLY && FF_USE_MKFS
2114 static DWORD
xsum32 ( /* Returns 32-bit checksum */
2115 BYTE dat
, /* Byte to be calculated (byte-by-byte processing) */
2116 DWORD sum
/* Previous sum value */
2119 sum
= (( sum
& 1 ) ? 0x80000000 : 0 ) + ( sum
>> 1 ) + dat
;
2126 /*------------------------------------*/
2127 /* exFAT: Get a directory entry block */
2128 /*------------------------------------*/
2130 static FRESULT
load_xdir ( /* FR_INT_ERR: invalid entry block */
2131 DIR * dp
/* Reading directory object pointing top of the entry block to load */
2136 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the on-memory directory entry block 85+C0+C1s */
2139 /* Load file directory entry */
2140 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2141 if ( res
!= FR_OK
) return res
;
2142 if ( dp
-> dir
[ XDIR_Type
] != ET_FILEDIR
) return FR_INT_ERR
; /* Invalid order */
2143 memcpy ( dirb
+ 0 * SZDIRE
, dp
-> dir
, SZDIRE
);
2144 sz_ent
= ( dirb
[ XDIR_NumSec
] + 1 ) * SZDIRE
;
2145 if ( sz_ent
< 3 * SZDIRE
|| sz_ent
> 19 * SZDIRE
) return FR_INT_ERR
;
2147 /* Load stream extension entry */
2148 res
= dir_next ( dp
, 0 );
2149 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2150 if ( res
!= FR_OK
) return res
;
2151 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2152 if ( res
!= FR_OK
) return res
;
2153 if ( dp
-> dir
[ XDIR_Type
] != ET_STREAM
) return FR_INT_ERR
; /* Invalid order */
2154 memcpy ( dirb
+ 1 * SZDIRE
, dp
-> dir
, SZDIRE
);
2155 if ( MAXDIRB ( dirb
[ XDIR_NumName
]) > sz_ent
) return FR_INT_ERR
;
2157 /* Load file name entries */
2158 i
= 2 * SZDIRE
; /* Name offset to load */
2160 res
= dir_next ( dp
, 0 );
2161 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2162 if ( res
!= FR_OK
) return res
;
2163 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2164 if ( res
!= FR_OK
) return res
;
2165 if ( dp
-> dir
[ XDIR_Type
] != ET_FILENAME
) return FR_INT_ERR
; /* Invalid order */
2166 if ( i
< MAXDIRB ( FF_MAX_LFN
)) memcpy ( dirb
+ i
, dp
-> dir
, SZDIRE
);
2167 } while (( i
+= SZDIRE
) < sz_ent
);
2169 /* Sanity check (do it for only accessible object) */
2170 if ( i
<= MAXDIRB ( FF_MAX_LFN
)) {
2171 if ( xdir_sum ( dirb
) != ld_word ( dirb
+ XDIR_SetSum
)) return FR_INT_ERR
;
2177 /*------------------------------------------------------------------*/
2178 /* exFAT: Initialize object allocation info with loaded entry block */
2179 /*------------------------------------------------------------------*/
2181 static void init_alloc_info (
2182 FATFS
* fs
, /* Filesystem object */
2183 FFOBJID
* obj
/* Object allocation information to be initialized */
2186 obj
-> sclust
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Start cluster */
2187 obj
-> objsize
= ld_qword ( fs
-> dirbuf
+ XDIR_FileSize
); /* Size */
2188 obj
-> stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ; /* Allocation status */
2189 obj
-> n_frag
= 0 ; /* No last fragment info */
2194 #if !FF_FS_READONLY || FF_FS_RPATH != 0
2195 /*------------------------------------------------*/
2196 /* exFAT: Load the object's directory entry block */
2197 /*------------------------------------------------*/
2199 static FRESULT
load_obj_xdir (
2200 DIR * dp
, /* Blank directory object to be used to access containing directory */
2201 const FFOBJID
* obj
/* Object with its containing directory information */
2206 /* Open object containing directory */
2207 dp
-> obj
. fs
= obj
-> fs
;
2208 dp
-> obj
. sclust
= obj
-> c_scl
;
2209 dp
-> obj
. stat
= ( BYTE
) obj
-> c_size
;
2210 dp
-> obj
. objsize
= obj
-> c_size
& 0xFFFFFF00 ;
2212 dp
-> blk_ofs
= obj
-> c_ofs
;
2214 res
= dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto object's entry block */
2216 res
= load_xdir ( dp
); /* Load the object's entry block */
2224 /*----------------------------------------*/
2225 /* exFAT: Store the directory entry block */
2226 /*----------------------------------------*/
2228 static FRESULT
store_xdir (
2229 DIR * dp
/* Pointer to the directory object */
2234 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the directory entry block 85+C0+C1s */
2236 /* Create set sum */
2237 st_word ( dirb
+ XDIR_SetSum
, xdir_sum ( dirb
));
2238 nent
= dirb
[ XDIR_NumSec
] + 1 ;
2240 /* Store the directory entry block to the directory */
2241 res
= dir_sdi ( dp
, dp
-> blk_ofs
);
2242 while ( res
== FR_OK
) {
2243 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2244 if ( res
!= FR_OK
) break ;
2245 memcpy ( dp
-> dir
, dirb
, SZDIRE
);
2246 dp
-> obj
. fs
-> wflag
= 1 ;
2247 if (-- nent
== 0 ) break ;
2249 res
= dir_next ( dp
, 0 );
2251 return ( res
== FR_OK
|| res
== FR_DISK_ERR
) ? res
: FR_INT_ERR
;
2256 /*-------------------------------------------*/
2257 /* exFAT: Create a new directory entry block */
2258 /*-------------------------------------------*/
2260 static void create_xdir (
2261 BYTE
* dirb
, /* Pointer to the directory entry block buffer */
2262 const WCHAR
* lfn
/* Pointer to the object name */
2270 /* Create file-directory and stream-extension entry */
2271 memset ( dirb
, 0 , 2 * SZDIRE
);
2272 dirb
[ 0 * SZDIRE
+ XDIR_Type
] = ET_FILEDIR
;
2273 dirb
[ 1 * SZDIRE
+ XDIR_Type
] = ET_STREAM
;
2275 /* Create file-name entries */
2276 i
= SZDIRE
* 2 ; /* Top of file_name entries */
2277 nlen
= nc1
= 0 ; wc
= 1 ;
2279 dirb
[ i
++] = ET_FILENAME
; dirb
[ i
++] = 0 ;
2280 do { /* Fill name field */
2281 if ( wc
!= 0 && ( wc
= lfn
[ nlen
]) != 0 ) nlen
++; /* Get a character if exist */
2282 st_word ( dirb
+ i
, wc
); /* Store it */
2284 } while ( i
% SZDIRE
!= 0 );
2286 } while ( lfn
[ nlen
]); /* Fill next entry if any char follows */
2288 dirb
[ XDIR_NumName
] = nlen
; /* Set name length */
2289 dirb
[ XDIR_NumSec
] = 1 + nc1
; /* Set secondary count (C0 + C1s) */
2290 st_word ( dirb
+ XDIR_NameHash
, xname_sum ( lfn
)); /* Set name hash */
2293 #endif /* !FF_FS_READONLY */
2294 #endif /* FF_FS_EXFAT */
2298 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
2299 /*-----------------------------------------------------------------------*/
2300 /* Read an object from the directory */
2301 /*-----------------------------------------------------------------------*/
2303 #define DIR_READ_FILE(dp) dir_read(dp, 0)
2304 #define DIR_READ_LABEL(dp) dir_read(dp, 1)
2306 static FRESULT
dir_read (
2307 DIR * dp
, /* Pointer to the directory object */
2308 int vol
/* Filtered by 0:file/directory or 1:volume label */
2311 FRESULT res
= FR_NO_FILE
;
2312 FATFS
* fs
= dp
-> obj
. fs
;
2315 BYTE ord
= 0xFF , sum
= 0xFF ;
2319 res
= move_window ( fs
, dp
-> sect
);
2320 if ( res
!= FR_OK
) break ;
2321 b
= dp
-> dir
[ DIR_Name
]; /* Test for the entry type */
2323 res
= FR_NO_FILE
; break ; /* Reached to end of the directory */
2326 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2327 if ( FF_USE_LABEL
&& vol
) {
2328 if ( b
== ET_VLABEL
) break ; /* Volume label entry? */
2330 if ( b
== ET_FILEDIR
) { /* Start of the file entry block? */
2331 dp
-> blk_ofs
= dp
-> dptr
; /* Get location of the block */
2332 res
= load_xdir ( dp
); /* Load the entry block */
2334 dp
-> obj
. attr
= fs
-> dirbuf
[ XDIR_Attr
] & AM_MASK
; /* Get attribute */
2341 { /* On the FAT/FAT32 volume */
2342 dp
-> obj
. attr
= attr
= dp
-> dir
[ DIR_Attr
] & AM_MASK
; /* Get attribute */
2343 #if FF_USE_LFN /* LFN configuration */
2344 if ( b
== DDEM
|| b
== '.' || ( int )(( attr
& ~ AM_ARC
) == AM_VOL
) != vol
) { /* An entry without valid data */
2347 if ( attr
== AM_LFN
) { /* An LFN entry is found */
2348 if ( b
& LLEF
) { /* Is it start of an LFN sequence? */
2349 sum
= dp
-> dir
[ LDIR_Chksum
];
2350 b
&= ( BYTE
)~ LLEF
; ord
= b
;
2351 dp
-> blk_ofs
= dp
-> dptr
;
2353 /* Check LFN validity and capture it */
2354 ord
= ( b
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && pick_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2355 } else { /* An SFN entry is found */
2356 if ( ord
!= 0 || sum
!= sum_sfn ( dp
-> dir
)) { /* Is there a valid LFN? */
2357 dp
-> blk_ofs
= 0xFFFFFFFF ; /* It has no LFN. */
2362 #else /* Non LFN configuration */
2363 if ( b
!= DDEM
&& b
!= '.' && attr
!= AM_LFN
&& ( int )(( attr
& ~ AM_ARC
) == AM_VOL
) == vol
) { /* Is it a valid entry? */
2368 res
= dir_next ( dp
, 0 ); /* Next entry */
2369 if ( res
!= FR_OK
) break ;
2372 if ( res
!= FR_OK
) dp
-> sect
= 0 ; /* Terminate the read operation on error or EOT */
2376 #endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
2380 /*-----------------------------------------------------------------------*/
2381 /* Directory handling - Find an object in the directory */
2382 /*-----------------------------------------------------------------------*/
2384 static FRESULT
dir_find ( /* FR_OK(0):succeeded, !=0:error */
2385 DIR * dp
/* Pointer to the directory object with the file name */
2389 FATFS
* fs
= dp
-> obj
. fs
;
2395 res
= dir_sdi ( dp
, 0 ); /* Rewind directory object */
2396 if ( res
!= FR_OK
) return res
;
2398 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2401 WORD hash
= xname_sum ( fs
-> lfnbuf
); /* Hash value of the name to find */
2403 while (( res
= DIR_READ_FILE ( dp
)) == FR_OK
) { /* Read an item */
2404 #if FF_MAX_LFN < 255
2405 if ( fs
-> dirbuf
[ XDIR_NumName
] > FF_MAX_LFN
) continue ; /* Skip comparison if inaccessible object name */
2407 if ( ld_word ( fs
-> dirbuf
+ XDIR_NameHash
) != hash
) continue ; /* Skip comparison if hash mismatched */
2408 for ( nc
= fs
-> dirbuf
[ XDIR_NumName
], di
= SZDIRE
* 2 , ni
= 0 ; nc
; nc
--, di
+= 2 , ni
++) { /* Compare the name */
2409 if (( di
% SZDIRE
) == 0 ) di
+= 2 ;
2410 if ( ff_wtoupper ( ld_word ( fs
-> dirbuf
+ di
)) != ff_wtoupper ( fs
-> lfnbuf
[ ni
])) break ;
2412 if ( nc
== 0 && ! fs
-> lfnbuf
[ ni
]) break ; /* Name matched? */
2417 /* On the FAT/FAT32 volume */
2419 ord
= sum
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2422 res
= move_window ( fs
, dp
-> sect
);
2423 if ( res
!= FR_OK
) break ;
2424 c
= dp
-> dir
[ DIR_Name
];
2425 if ( c
== 0 ) { res
= FR_NO_FILE
; break ; } /* Reached to end of table */
2426 #if FF_USE_LFN /* LFN configuration */
2427 dp
-> obj
. attr
= a
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2428 if ( c
== DDEM
|| (( a
& AM_VOL
) && a
!= AM_LFN
)) { /* An entry without valid data */
2429 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2431 if ( a
== AM_LFN
) { /* An LFN entry is found */
2432 if (!( dp
-> fn
[ NSFLAG
] & NS_NOLFN
)) {
2433 if ( c
& LLEF
) { /* Is it start of LFN sequence? */
2434 sum
= dp
-> dir
[ LDIR_Chksum
];
2435 c
&= ( BYTE
)~ LLEF
; ord
= c
; /* LFN start order */
2436 dp
-> blk_ofs
= dp
-> dptr
; /* Start offset of LFN */
2438 /* Check validity of the LFN entry and compare it with given name */
2439 ord
= ( c
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && cmp_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2441 } else { /* An SFN entry is found */
2442 if ( ord
== 0 && sum
== sum_sfn ( dp
-> dir
)) break ; /* LFN matched? */
2443 if (!( dp
-> fn
[ NSFLAG
] & NS_LOSS
) && ! memcmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* SFN matched? */
2444 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2447 #else /* Non LFN configuration */
2448 dp
-> obj
. attr
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2449 if (!( dp
-> dir
[ DIR_Attr
] & AM_VOL
) && ! memcmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* Is it a valid entry? */
2451 res
= dir_next ( dp
, 0 ); /* Next entry */
2452 } while ( res
== FR_OK
);
2461 /*-----------------------------------------------------------------------*/
2462 /* Register an object to the directory */
2463 /*-----------------------------------------------------------------------*/
2465 static FRESULT
dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
2466 DIR * dp
/* Target directory with object name to be created */
2470 FATFS
* fs
= dp
-> obj
. fs
;
2471 #if FF_USE_LFN /* LFN configuration */
2476 if ( dp
-> fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
)) return FR_INVALID_NAME
; /* Check name validity */
2477 for ( len
= 0 ; fs
-> lfnbuf
[ len
]; len
++) ; /* Get lfn length */
2480 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2481 n_ent
= ( len
+ 14 ) / 15 + 2 ; /* Number of entries to allocate (85+C0+C1s) */
2482 res
= dir_alloc ( dp
, n_ent
); /* Allocate directory entries */
2483 if ( res
!= FR_OK
) return res
;
2484 dp
-> blk_ofs
= dp
-> dptr
- SZDIRE
* ( n_ent
- 1 ); /* Set the allocated entry block offset */
2486 if ( dp
-> obj
. stat
& 4 ) { /* Has the directory been stretched by new allocation? */
2488 res
= fill_first_frag (& dp
-> obj
); /* Fill the first fragment on the FAT if needed */
2489 if ( res
!= FR_OK
) return res
;
2490 res
= fill_last_frag (& dp
-> obj
, dp
-> clust
, 0xFFFFFFFF ); /* Fill the last fragment on the FAT if needed */
2491 if ( res
!= FR_OK
) return res
;
2492 if ( dp
-> obj
. sclust
!= 0 ) { /* Is it a sub-directory? */
2495 res
= load_obj_xdir (& dj
, & dp
-> obj
); /* Load the object status */
2496 if ( res
!= FR_OK
) return res
;
2497 dp
-> obj
. objsize
+= ( DWORD
) fs
-> csize
* SS ( fs
); /* Increase the directory size by cluster size */
2498 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, dp
-> obj
. objsize
);
2499 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, dp
-> obj
. objsize
);
2500 fs
-> dirbuf
[ XDIR_GenFlags
] = dp
-> obj
. stat
| 1 ; /* Update the allocation status */
2501 res
= store_xdir (& dj
); /* Store the object status */
2502 if ( res
!= FR_OK
) return res
;
2506 create_xdir ( fs
-> dirbuf
, fs
-> lfnbuf
); /* Create on-memory directory block to be written later */
2510 /* On the FAT/FAT32 volume */
2511 memcpy ( sn
, dp
-> fn
, 12 );
2512 if ( sn
[ NSFLAG
] & NS_LOSS
) { /* When LFN is out of 8.3 format, generate a numbered name */
2513 dp
-> fn
[ NSFLAG
] = NS_NOLFN
; /* Find only SFN */
2514 for ( n
= 1 ; n
< 100 ; n
++) {
2515 gen_numname ( dp
-> fn
, sn
, fs
-> lfnbuf
, n
); /* Generate a numbered name */
2516 res
= dir_find ( dp
); /* Check if the name collides with existing SFN */
2517 if ( res
!= FR_OK
) break ;
2519 if ( n
== 100 ) return FR_DENIED
; /* Abort if too many collisions */
2520 if ( res
!= FR_NO_FILE
) return res
; /* Abort if the result is other than 'not collided' */
2521 dp
-> fn
[ NSFLAG
] = sn
[ NSFLAG
];
2524 /* Create an SFN with/without LFNs. */
2525 n_ent
= ( sn
[ NSFLAG
] & NS_LFN
) ? ( len
+ 12 ) / 13 + 1 : 1 ; /* Number of entries to allocate */
2526 res
= dir_alloc ( dp
, n_ent
); /* Allocate entries */
2527 if ( res
== FR_OK
&& -- n_ent
) { /* Set LFN entry if needed */
2528 res
= dir_sdi ( dp
, dp
-> dptr
- n_ent
* SZDIRE
);
2530 sum
= sum_sfn ( dp
-> fn
); /* Checksum value of the SFN tied to the LFN */
2531 do { /* Store LFN entries in bottom first */
2532 res
= move_window ( fs
, dp
-> sect
);
2533 if ( res
!= FR_OK
) break ;
2534 put_lfn ( fs
-> lfnbuf
, dp
-> dir
, ( BYTE
) n_ent
, sum
);
2536 res
= dir_next ( dp
, 0 ); /* Next entry */
2537 } while ( res
== FR_OK
&& -- n_ent
);
2541 #else /* Non LFN configuration */
2542 res
= dir_alloc ( dp
, 1 ); /* Allocate an entry for SFN */
2548 res
= move_window ( fs
, dp
-> sect
);
2550 memset ( dp
-> dir
, 0 , SZDIRE
); /* Clean the entry */
2551 memcpy ( dp
-> dir
+ DIR_Name
, dp
-> fn
, 11 ); /* Put SFN */
2553 dp
-> dir
[ DIR_NTres
] = dp
-> fn
[ NSFLAG
] & ( NS_BODY
| NS_EXT
); /* Put NT flag */
2562 #endif /* !FF_FS_READONLY */
2566 #if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
2567 /*-----------------------------------------------------------------------*/
2568 /* Remove an object from the directory */
2569 /*-----------------------------------------------------------------------*/
2571 static FRESULT
dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
2572 DIR * dp
/* Directory object pointing the entry to be removed */
2576 FATFS
* fs
= dp
-> obj
. fs
;
2577 #if FF_USE_LFN /* LFN configuration */
2578 DWORD last
= dp
-> dptr
;
2580 res
= ( dp
-> blk_ofs
== 0xFFFFFFFF ) ? FR_OK
: dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto top of the entry block if LFN is exist */
2583 res
= move_window ( fs
, dp
-> sect
);
2584 if ( res
!= FR_OK
) break ;
2585 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2586 dp
-> dir
[ XDIR_Type
] &= 0x7F ; /* Clear the entry InUse flag. */
2587 } else { /* On the FAT/FAT32 volume */
2588 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'. */
2591 if ( dp
-> dptr
>= last
) break ; /* If reached last entry then all entries of the object has been deleted. */
2592 res
= dir_next ( dp
, 0 ); /* Next entry */
2593 } while ( res
== FR_OK
);
2594 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
;
2596 #else /* Non LFN configuration */
2598 res
= move_window ( fs
, dp
-> sect
);
2600 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'.*/
2608 #endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
2612 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2613 /*-----------------------------------------------------------------------*/
2614 /* Get file information from directory entry */
2615 /*-----------------------------------------------------------------------*/
2617 static void get_fileinfo (
2618 DIR * dp
, /* Pointer to the directory object */
2619 FILINFO
* fno
/* Pointer to the file information to be filled */
2626 FATFS
* fs
= dp
-> obj
. fs
;
2633 fno
-> fname
[ 0 ] = 0 ; /* Invaidate file info */
2634 if ( dp
-> sect
== 0 ) return ; /* Exit if read pointer has reached end of directory */
2636 #if FF_USE_LFN /* LFN configuration */
2638 if ( fs
-> fs_type
== FS_EXFAT
) { /* exFAT volume */
2641 si
= SZDIRE
* 2 ; di
= 0 ; /* 1st C1 entry in the entry block */
2643 while ( nc
< fs
-> dirbuf
[ XDIR_NumName
]) {
2644 if ( si
>= MAXDIRB ( FF_MAX_LFN
)) { /* Truncated directory block? */
2647 if (( si
% SZDIRE
) == 0 ) si
+= 2 ; /* Skip entry type field */
2648 wc
= ld_word ( fs
-> dirbuf
+ si
); si
+= 2 ; nc
++; /* Get a character */
2649 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2650 hs
= wc
; continue ; /* Get low surrogate */
2652 nw
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in API encoding */
2653 if ( nw
== 0 ) { /* Buffer overflow or wrong char? */
2659 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2660 if ( di
== 0 ) fno
-> fname
[ di
++] = ' \? ' ; /* Inaccessible object name? */
2661 fno
-> fname
[ di
] = 0 ; /* Terminate the name */
2662 fno
-> altname
[ 0 ] = 0 ; /* exFAT does not support SFN */
2664 fno
-> fattrib
= fs
-> dirbuf
[ XDIR_Attr
] & AM_MASKX
; /* Attribute */
2665 fno
-> fsize
= ( fno
-> fattrib
& AM_DIR
) ? 0 : ld_qword ( fs
-> dirbuf
+ XDIR_FileSize
); /* Size */
2666 fno
-> ftime
= ld_word ( fs
-> dirbuf
+ XDIR_ModTime
+ 0 ); /* Time */
2667 fno
-> fdate
= ld_word ( fs
-> dirbuf
+ XDIR_ModTime
+ 2 ); /* Date */
2671 { /* FAT/FAT32 volume */
2672 if ( dp
-> blk_ofs
!= 0xFFFFFFFF ) { /* Get LFN if available */
2675 while ( fs
-> lfnbuf
[ si
] != 0 ) {
2676 wc
= fs
-> lfnbuf
[ si
++]; /* Get an LFN character (UTF-16) */
2677 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2678 hs
= wc
; continue ; /* Get low surrogate */
2680 nw
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in API encoding */
2681 if ( nw
== 0 ) { /* Buffer overflow or wrong char? */
2687 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2688 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN (null string means LFN is invalid) */
2693 while ( si
< 11 ) { /* Get SFN from SFN entry */
2694 wc
= dp
-> dir
[ si
++]; /* Get a char */
2695 if ( wc
== ' ' ) continue ; /* Skip padding spaces */
2696 if ( wc
== RDDEM
) wc
= DDEM
; /* Restore replaced DDEM character */
2697 if ( si
== 9 && di
< FF_SFN_BUF
) fno
-> altname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2698 #if FF_LFN_UNICODE >= 1 /* Unicode output */
2699 if ( dbc_1st (( BYTE
) wc
) && si
!= 8 && si
!= 11 && dbc_2nd ( dp
-> dir
[ si
])) { /* Make a DBC if needed */
2700 wc
= wc
<< 8 | dp
-> dir
[ si
++];
2702 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM -> Unicode */
2703 if ( wc
== 0 ) { /* Wrong char in the current code page? */
2706 nw
= put_utf ( wc
, & fno
-> altname
[ di
], FF_SFN_BUF
- di
); /* Store it in API encoding */
2707 if ( nw
== 0 ) { /* Buffer overflow? */
2711 #else /* ANSI/OEM output */
2712 fno
-> altname
[ di
++] = ( TCHAR
) wc
; /* Store it without any conversion */
2715 fno
-> altname
[ di
] = 0 ; /* Terminate the SFN (null string means SFN is invalid) */
2717 if ( fno
-> fname
[ 0 ] == 0 ) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
2718 if ( di
== 0 ) { /* If LFN and SFN both are invalid, this object is inaccessible */
2719 fno
-> fname
[ di
++] = ' \? ' ;
2721 for ( si
= di
= 0 , lcf
= NS_BODY
; fno
-> altname
[ si
]; si
++, di
++) { /* Copy altname[] to fname[] with case information */
2722 wc
= ( WCHAR
) fno
-> altname
[ si
];
2723 if ( wc
== '.' ) lcf
= NS_EXT
;
2724 if ( IsUpper ( wc
) && ( dp
-> dir
[ DIR_NTres
] & lcf
)) wc
+= 0x20 ;
2725 fno
-> fname
[ di
] = ( TCHAR
) wc
;
2728 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN */
2729 if (! dp
-> dir
[ DIR_NTres
]) fno
-> altname
[ 0 ] = 0 ; /* Altname is not needed if neither LFN nor case info is exist. */
2732 #else /* Non-LFN configuration */
2734 while ( si
< 11 ) { /* Copy name body and extension */
2735 c
= ( TCHAR
) dp
-> dir
[ si
++];
2736 if ( c
== ' ' ) continue ; /* Skip padding spaces */
2737 if ( c
== RDDEM
) c
= DDEM
; /* Restore replaced DDEM character */
2738 if ( si
== 9 ) fno
-> fname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2739 fno
-> fname
[ di
++] = c
;
2741 fno
-> fname
[ di
] = 0 ; /* Terminate the SFN */
2744 fno
-> fattrib
= dp
-> dir
[ DIR_Attr
] & AM_MASK
; /* Attribute */
2745 fno
-> fsize
= ld_dword ( dp
-> dir
+ DIR_FileSize
); /* Size */
2746 fno
-> ftime
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 0 ); /* Time */
2747 fno
-> fdate
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 2 ); /* Date */
2750 #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2754 #if FF_USE_FIND && FF_FS_MINIMIZE <= 1
2755 /*-----------------------------------------------------------------------*/
2756 /* Pattern matching */
2757 /*-----------------------------------------------------------------------*/
2759 #define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */
2762 static DWORD
get_achar ( /* Get a character and advance ptr */
2763 const TCHAR
** ptr
/* Pointer to pointer to the ANSI/OEM or Unicode string */
2769 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
2770 chr
= tchar2uni ( ptr
);
2771 if ( chr
== 0xFFFFFFFF ) chr
= 0 ; /* Wrong UTF encoding is recognized as end of the string */
2772 chr
= ff_wtoupper ( chr
);
2774 #else /* ANSI/OEM input */
2775 chr
= ( BYTE
)*(* ptr
)++; /* Get a byte */
2776 if ( IsLower ( chr
)) chr
-= 0x20 ; /* To upper ASCII char */
2777 #if FF_CODE_PAGE == 0
2778 if ( ExCvt
&& chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2779 #elif FF_CODE_PAGE < 900
2780 if ( chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2782 #if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
2783 if ( dbc_1st (( BYTE
) chr
)) { /* Get DBC 2nd byte if needed */
2784 chr
= dbc_2nd (( BYTE
)** ptr
) ? chr
<< 8 | ( BYTE
)*(* ptr
)++ : 0 ;
2793 static int pattern_match ( /* 0:mismatched, 1:matched */
2794 const TCHAR
* pat
, /* Matching pattern */
2795 const TCHAR
* nam
, /* String to be tested */
2796 UINT skip
, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */
2797 UINT recur
/* Recursion count */
2806 while (( skip
& 0xFF ) != 0 ) { /* Pre-skip name chars */
2807 if (! get_achar (& nam
)) return 0 ; /* Branch mismatched if less name chars */
2810 if (* pat
== 0 && skip
) return 1 ; /* Matched? (short circuit) */
2813 pptr
= pat
; nptr
= nam
; /* Top of pattern and name to match */
2815 if (* pptr
== ' \? ' || * pptr
== '*' ) { /* Wildcard term? */
2816 if ( recur
== 0 ) return 0 ; /* Too many wildcard terms? */
2818 do { /* Analyze the wildcard term */
2819 if (* pptr
++ == ' \? ' ) {
2824 } while (* pptr
== ' \? ' || * pptr
== '*' );
2825 if ( pattern_match ( pptr
, nptr
, sk
, recur
- 1 )) return 1 ; /* Test new branch (recursive call) */
2826 nchr
= * nptr
; break ; /* Branch mismatched */
2828 pchr
= get_achar (& pptr
); /* Get a pattern char */
2829 nchr
= get_achar (& nptr
); /* Get a name char */
2830 if ( pchr
!= nchr
) break ; /* Branch mismatched? */
2831 if ( pchr
== 0 ) return 1 ; /* Branch matched? (matched at end of both strings) */
2833 get_achar (& nam
); /* nam++ */
2834 } while ( skip
&& nchr
); /* Retry until end of name if infinite search is specified */
2839 #endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
2843 /*-----------------------------------------------------------------------*/
2844 /* Pick a top segment and create the object name in directory form */
2845 /*-----------------------------------------------------------------------*/
2847 static FRESULT
create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
2848 DIR * dp
, /* Pointer to the directory object */
2849 const TCHAR
** path
/* Pointer to pointer to the segment in the path string */
2852 #if FF_USE_LFN /* LFN configuration */
2861 /* Create LFN into LFN working buffer */
2862 p
= * path
; lfn
= dp
-> obj
. fs
-> lfnbuf
; di
= 0 ;
2864 uc
= tchar2uni (& p
); /* Get a character */
2865 if ( uc
== 0xFFFFFFFF ) return FR_INVALID_NAME
; /* Invalid code or UTF decode error */
2866 if ( uc
>= 0x10000 ) lfn
[ di
++] = ( WCHAR
)( uc
>> 16 ); /* Store high surrogate if needed */
2868 if ( wc
< ' ' || IsSeparator ( wc
)) break ; /* Break if end of the path or a separator is found */
2869 if ( wc
< 0x80 && strchr ( "*:<>| \"\?\x7F " , ( int ) wc
)) return FR_INVALID_NAME
; /* Reject illegal characters for LFN */
2870 if ( di
>= FF_MAX_LFN
) return FR_INVALID_NAME
; /* Reject too long name */
2871 lfn
[ di
++] = wc
; /* Store the Unicode character */
2873 if ( wc
< ' ' ) { /* Stopped at end of the path? */
2874 cf
= NS_LAST
; /* Last segment */
2875 } else { /* Stopped at a separator */
2876 while ( IsSeparator (* p
)) p
++; /* Skip duplicated separators if exist */
2877 cf
= 0 ; /* Next segment may follow */
2878 if ( IsTerminator (* p
)) cf
= NS_LAST
; /* Ignore terminating separator */
2880 * path
= p
; /* Return pointer to the next segment */
2882 #if FF_FS_RPATH != 0
2883 if (( di
== 1 && lfn
[ di
- 1 ] == '.' ) ||
2884 ( di
== 2 && lfn
[ di
- 1 ] == '.' && lfn
[ di
- 2 ] == '.' )) { /* Is this segment a dot name? */
2886 for ( i
= 0 ; i
< 11 ; i
++) { /* Create dot name for SFN entry */
2887 dp
-> fn
[ i
] = ( i
< di
) ? '.' : ' ' ;
2889 dp
-> fn
[ i
] = cf
| NS_DOT
; /* This is a dot entry */
2893 while ( di
) { /* Snip off trailing spaces and dots if exist */
2895 if ( wc
!= ' ' && wc
!= '.' ) break ;
2898 lfn
[ di
] = 0 ; /* LFN is created into the working buffer */
2899 if ( di
== 0 ) return FR_INVALID_NAME
; /* Reject null name */
2901 /* Create SFN in directory form */
2902 for ( si
= 0 ; lfn
[ si
] == ' ' ; si
++) ; /* Remove leading spaces */
2903 if ( si
> 0 || lfn
[ si
] == '.' ) cf
|= NS_LOSS
| NS_LFN
; /* Is there any leading space or dot? */
2904 while ( di
> 0 && lfn
[ di
- 1 ] != '.' ) di
--; /* Find last dot (di<=si: no extension) */
2906 memset ( dp
-> fn
, ' ' , 11 );
2909 wc
= lfn
[ si
++]; /* Get an LFN character */
2910 if ( wc
== 0 ) break ; /* Break on end of the LFN */
2911 if ( wc
== ' ' || ( wc
== '.' && si
!= di
)) { /* Remove embedded spaces and dots */
2912 cf
|= NS_LOSS
| NS_LFN
;
2916 if ( i
>= ni
|| si
== di
) { /* End of field? */
2917 if ( ni
== 11 ) { /* Name extension overflow? */
2918 cf
|= NS_LOSS
| NS_LFN
;
2921 if ( si
!= di
) cf
|= NS_LOSS
| NS_LFN
; /* Name body overflow? */
2922 if ( si
> di
) break ; /* No name extension? */
2923 si
= di
; i
= 8 ; ni
= 11 ; b
<<= 2 ; /* Enter name extension */
2927 if ( wc
>= 0x80 ) { /* Is this an extended character? */
2928 cf
|= NS_LFN
; /* LFN entry needs to be created */
2929 #if FF_CODE_PAGE == 0
2930 if ( ExCvt
) { /* In SBCS cfg */
2931 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2932 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2933 } else { /* In DBCS cfg */
2934 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Up-convert ==> ANSI/OEM code */
2936 #elif FF_CODE_PAGE < 900 /* In SBCS cfg */
2937 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2938 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2939 #else /* In DBCS cfg */
2940 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Up-convert ==> ANSI/OEM code */
2944 if ( wc
>= 0x100 ) { /* Is this a DBC? */
2945 if ( i
>= ni
- 1 ) { /* Field overflow? */
2946 cf
|= NS_LOSS
| NS_LFN
;
2947 i
= ni
; continue ; /* Next field */
2949 dp
-> fn
[ i
++] = ( BYTE
)( wc
>> 8 ); /* Put 1st byte */
2951 if ( wc
== 0 || strchr ( "+,;=[]" , ( int ) wc
)) { /* Replace illegal characters for SFN */
2952 wc
= '_' ; cf
|= NS_LOSS
| NS_LFN
; /* Lossy conversion */
2954 if ( IsUpper ( wc
)) { /* ASCII upper case? */
2957 if ( IsLower ( wc
)) { /* ASCII lower case? */
2962 dp
-> fn
[ i
++] = ( BYTE
) wc
;
2965 if ( dp
-> fn
[ 0 ] == DDEM
) dp
-> fn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
2967 if ( ni
== 8 ) b
<<= 2 ; /* Shift capital flags if no extension */
2968 if (( b
& 0x0C ) == 0x0C || ( b
& 0x03 ) == 0x03 ) cf
|= NS_LFN
; /* LFN entry needs to be created if composite capitals */
2969 if (!( cf
& NS_LFN
)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
2970 if ( b
& 0x01 ) cf
|= NS_EXT
; /* NT flag (Extension has small capital letters only) */
2971 if ( b
& 0x04 ) cf
|= NS_BODY
; /* NT flag (Body has small capital letters only) */
2974 dp
-> fn
[ NSFLAG
] = cf
; /* SFN is created into dp->fn[] */
2979 #else /* FF_USE_LFN : Non-LFN configuration */
2985 /* Create file name in directory form */
2986 p
= * path
; sfn
= dp
-> fn
;
2987 memset ( sfn
, ' ' , 11 );
2989 #if FF_FS_RPATH != 0
2990 if ( p
[ si
] == '.' ) { /* Is this a dot entry? */
2993 if ( c
!= '.' || si
>= 3 ) break ;
2996 if (! IsSeparator ( c
) && c
> ' ' ) return FR_INVALID_NAME
;
2997 * path
= p
+ si
; /* Return pointer to the next segment */
2998 sfn
[ NSFLAG
] = ( c
<= ' ' ) ? NS_LAST
| NS_DOT
: NS_DOT
; /* Set last segment flag if end of the path */
3003 c
= ( BYTE
) p
[ si
++]; /* Get a byte */
3004 if ( c
<= ' ' ) break ; /* Break if end of the path name */
3005 if ( IsSeparator ( c
)) { /* Break if a separator is found */
3006 while ( IsSeparator ( p
[ si
])) si
++; /* Skip duplicated separator if exist */
3009 if ( c
== '.' || i
>= ni
) { /* End of body or field overflow? */
3010 if ( ni
== 11 || c
!= '.' ) return FR_INVALID_NAME
; /* Field overflow or invalid dot? */
3011 i
= 8 ; ni
= 11 ; /* Enter file extension field */
3014 #if FF_CODE_PAGE == 0
3015 if ( ExCvt
&& c
>= 0x80 ) { /* Is SBC extended character? */
3016 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
3018 #elif FF_CODE_PAGE < 900
3019 if ( c
>= 0x80 ) { /* Is SBC extended character? */
3020 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
3023 if ( dbc_1st ( c
)) { /* Check if it is a DBC 1st byte */
3024 d
= ( BYTE
) p
[ si
++]; /* Get 2nd byte */
3025 if (! dbc_2nd ( d
) || i
>= ni
- 1 ) return FR_INVALID_NAME
; /* Reject invalid DBC */
3029 if ( strchr ( "*+,:;<=>[]| \"\?\x7F " , ( int ) c
)) return FR_INVALID_NAME
; /* Reject illegal chrs for SFN */
3030 if ( IsLower ( c
)) c
-= 0x20 ; /* To upper */
3034 * path
= & p
[ si
]; /* Return pointer to the next segment */
3035 if ( i
== 0 ) return FR_INVALID_NAME
; /* Reject nul string */
3037 if ( sfn
[ 0 ] == DDEM
) sfn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
3038 sfn
[ NSFLAG
] = ( c
<= ' ' || p
[ si
] <= ' ' ) ? NS_LAST
: 0 ; /* Set last segment flag if end of the path */
3041 #endif /* FF_USE_LFN */
3047 /*-----------------------------------------------------------------------*/
3048 /* Follow a file path */
3049 /*-----------------------------------------------------------------------*/
3051 static FRESULT
follow_path ( /* FR_OK(0): successful, !=0: error code */
3052 DIR * dp
, /* Directory object to return last directory and found object */
3053 const TCHAR
* path
/* Full-path string to find a file or directory */
3058 FATFS
* fs
= dp
-> obj
. fs
;
3061 #if FF_FS_RPATH != 0
3062 if (! IsSeparator (* path
) && ( FF_STR_VOLUME_ID
!= 2 || ! IsTerminator (* path
))) { /* Without heading separator */
3063 dp
-> obj
. sclust
= fs
-> cdir
; /* Start at the current directory */
3066 { /* With heading separator */
3067 while ( IsSeparator (* path
)) path
++; /* Strip separators */
3068 dp
-> obj
. sclust
= 0 ; /* Start from the root directory */
3071 dp
-> obj
. n_frag
= 0 ; /* Invalidate last fragment counter of the object */
3072 #if FF_FS_RPATH != 0
3073 if ( fs
-> fs_type
== FS_EXFAT
&& dp
-> obj
. sclust
) { /* exFAT: Retrieve the sub-directory's status */
3076 dp
-> obj
. c_scl
= fs
-> cdc_scl
;
3077 dp
-> obj
. c_size
= fs
-> cdc_size
;
3078 dp
-> obj
. c_ofs
= fs
-> cdc_ofs
;
3079 res
= load_obj_xdir (& dj
, & dp
-> obj
);
3080 if ( res
!= FR_OK
) return res
;
3081 dp
-> obj
. objsize
= ld_dword ( fs
-> dirbuf
+ XDIR_FileSize
);
3082 dp
-> obj
. stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ;
3087 if (( UINT
)* path
< ' ' ) { /* Null path name is the origin directory itself */
3088 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3089 res
= dir_sdi ( dp
, 0 );
3091 } else { /* Follow path */
3093 res
= create_name ( dp
, & path
); /* Get a segment name of the path */
3094 if ( res
!= FR_OK
) break ;
3095 res
= dir_find ( dp
); /* Find an object with the segment name */
3096 ns
= dp
-> fn
[ NSFLAG
];
3097 if ( res
!= FR_OK
) { /* Failed to find the object */
3098 if ( res
== FR_NO_FILE
) { /* Object is not found */
3099 if ( FF_FS_RPATH
&& ( ns
& NS_DOT
)) { /* If dot entry is not exist, stay there */
3100 if (!( ns
& NS_LAST
)) continue ; /* Continue to follow if not last segment */
3101 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3103 } else { /* Could not find the object */
3104 if (!( ns
& NS_LAST
)) res
= FR_NO_PATH
; /* Adjust error code if not last segment */
3109 if ( ns
& NS_LAST
) break ; /* Last segment matched. Function completed. */
3110 /* Get into the sub-directory */
3111 if (!( dp
-> obj
. attr
& AM_DIR
)) { /* It is not a sub-directory and cannot follow */
3112 res
= FR_NO_PATH
; break ;
3115 if ( fs
-> fs_type
== FS_EXFAT
) { /* Save containing directory information for next dir */
3116 dp
-> obj
. c_scl
= dp
-> obj
. sclust
;
3117 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
3118 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
3119 init_alloc_info ( fs
, & dp
-> obj
); /* Open next directory */
3123 dp
-> obj
. sclust
= ld_clust ( fs
, fs
-> win
+ dp
-> dptr
% SS ( fs
)); /* Open next directory */
3134 /*-----------------------------------------------------------------------*/
3135 /* Get logical drive number from path name */
3136 /*-----------------------------------------------------------------------*/
3138 static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
3139 const TCHAR
** path
/* Pointer to pointer to the path name */
3147 #if FF_STR_VOLUME_ID /* Find string volume ID */
3153 if (! tp
) return vol
; /* Invalid path name? */
3154 do { /* Find a colon in the path */
3156 } while (! IsTerminator ( tc
) && tc
!= ':' );
3158 if ( tc
== ':' ) { /* DOS/Windows style volume ID? */
3160 if ( IsDigit (* tp
) && tp
+ 2 == tt
) { /* Is there a numeric volume ID + colon? */
3161 i
= ( int )* tp
- '0' ; /* Get the LD number */
3163 #if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
3167 sp
= VolumeStr
[ i
]; tp
= * path
; /* This string volume ID and path name */
3168 do { /* Compare the volume ID with path name */
3169 c
= * sp
++; tc
= * tp
++;
3170 if ( IsLower ( c
)) c
-= 0x20 ;
3171 if ( IsLower ( tc
)) tc
-= 0x20 ;
3172 } while ( c
&& ( TCHAR
) c
== tc
);
3173 } while (( c
|| tp
!= tt
) && ++ i
< FF_VOLUMES
); /* Repeat for each id until pattern match */
3176 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3177 vol
= i
; /* Drive number */
3178 * path
= tt
; /* Snip the drive prefix off */
3182 #if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
3183 if (* tp
== '/' ) { /* Is there a volume ID? */
3184 while (*( tp
+ 1 ) == '/' ) tp
++; /* Skip duplicated separator */
3187 tt
= tp
; sp
= VolumeStr
[ i
]; /* Path name and this string volume ID */
3188 do { /* Compare the volume ID with path name */
3189 c
= * sp
++; tc
= *(++ tt
);
3190 if ( IsLower ( c
)) c
-= 0x20 ;
3191 if ( IsLower ( tc
)) tc
-= 0x20 ;
3192 } while ( c
&& ( TCHAR
) c
== tc
);
3193 } while (( c
|| ( tc
!= '/' && ! IsTerminator ( tc
))) && ++ i
< FF_VOLUMES
); /* Repeat for each ID until pattern match */
3194 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3195 vol
= i
; /* Drive number */
3196 * path
= tt
; /* Snip the drive prefix off */
3201 /* No drive prefix is found */
3202 #if FF_FS_RPATH != 0
3203 vol
= CurrVol
; /* Default drive is current drive */
3205 vol
= 0 ; /* Default drive is 0 */
3207 return vol
; /* Return the default drive */
3213 /*-----------------------------------------------------------------------*/
3214 /* GPT support functions */
3215 /*-----------------------------------------------------------------------*/
3219 /* Calculate CRC32 in byte-by-byte */
3221 static DWORD
crc32 ( /* Returns next CRC value */
3222 DWORD crc
, /* Current CRC value */
3223 BYTE d
/* A byte to be processed */
3229 for ( b
= 1 ; b
; b
<<= 1 ) {
3230 crc
^= ( d
& b
) ? 1 : 0 ;
3231 crc
= ( crc
& 1 ) ? crc
>> 1 ^ 0xEDB88320 : crc
>> 1 ;
3237 /* Check validity of GPT header */
3239 static int test_gpt_header ( /* 0:Invalid, 1:Valid */
3240 const BYTE
* gpth
/* Pointer to the GPT header */
3247 if ( memcmp ( gpth
+ GPTH_Sign
, "EFI PART" "\0\0\1" , 12 )) return 0 ; /* Check signature and version (1.0) */
3248 hlen
= ld_dword ( gpth
+ GPTH_Size
); /* Check header size */
3249 if ( hlen
< 92 || hlen
> FF_MIN_SS
) return 0 ;
3250 for ( i
= 0 , bcc
= 0xFFFFFFFF ; i
< hlen
; i
++) { /* Check header BCC */
3251 bcc
= crc32 ( bcc
, i
- GPTH_Bcc
< 4 ? 0 : gpth
[ i
]);
3253 if (~ bcc
!= ld_dword ( gpth
+ GPTH_Bcc
)) return 0 ;
3254 if ( ld_dword ( gpth
+ GPTH_PteSize
) != SZ_GPTE
) return 0 ; /* Table entry size (must be SZ_GPTE bytes) */
3255 if ( ld_dword ( gpth
+ GPTH_PtNum
) > 128 ) return 0 ; /* Table size (must be 128 entries or less) */
3260 #if !FF_FS_READONLY && FF_USE_MKFS
3262 /* Generate random value */
3263 static DWORD
make_rand (
3264 DWORD seed
, /* Seed value */
3265 BYTE
* buff
, /* Output buffer */
3266 UINT n
/* Data length */
3272 if ( seed
== 0 ) seed
= 1 ;
3274 for ( r
= 0 ; r
< 8 ; r
++) seed
= seed
& 1 ? seed
>> 1 ^ 0xA3000000 : seed
>> 1 ; /* Shift 8 bits the 32-bit LFSR */
3275 * buff
++ = ( BYTE
) seed
;
3285 /*-----------------------------------------------------------------------*/
3286 /* Load a sector and check if it is an FAT VBR */
3287 /*-----------------------------------------------------------------------*/
3289 /* Check what the sector is */
3291 static UINT
check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
3292 FATFS
* fs
, /* Filesystem object */
3293 LBA_t sect
/* Sector to load and check if it is an FAT-VBR or not */
3300 fs
-> wflag
= 0 ; fs
-> winsect
= ( LBA_t
) 0 - 1 ; /* Invaidate window */
3301 if ( move_window ( fs
, sect
) != FR_OK
) return 4 ; /* Load the boot sector */
3302 sign
= ld_word ( fs
-> win
+ BS_55AA
);
3304 if ( sign
== 0xAA55 && ! memcmp ( fs
-> win
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 )) return 1 ; /* It is an exFAT VBR */
3306 b
= fs
-> win
[ BS_JmpBoot
];
3307 if ( b
== 0xEB || b
== 0xE9 || b
== 0xE8 ) { /* Valid JumpBoot code? (short jump, near jump or near call) */
3308 if ( sign
== 0xAA55 && ! memcmp ( fs
-> win
+ BS_FilSysType32
, "FAT32 " , 8 )) {
3309 return 0 ; /* It is an FAT32 VBR */
3311 /* FAT volumes formatted with early MS-DOS lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */
3312 w
= ld_word ( fs
-> win
+ BPB_BytsPerSec
);
3313 b
= fs
-> win
[ BPB_SecPerClus
];
3314 if (( w
& ( w
- 1 )) == 0 && w
>= FF_MIN_SS
&& w
<= FF_MAX_SS
/* Properness of sector size (512-4096 and 2^n) */
3315 && b
!= 0 && ( b
& ( b
- 1 )) == 0 /* Properness of cluster size (2^n) */
3316 && ld_word ( fs
-> win
+ BPB_RsvdSecCnt
) != 0 /* Properness of reserved sectors (MNBZ) */
3317 && ( UINT
) fs
-> win
[ BPB_NumFATs
] - 1 <= 1 /* Properness of FATs (1 or 2) */
3318 && ld_word ( fs
-> win
+ BPB_RootEntCnt
) != 0 /* Properness of root dir entries (MNBZ) */
3319 && ( ld_word ( fs
-> win
+ BPB_TotSec16
) >= 128 || ld_dword ( fs
-> win
+ BPB_TotSec32
) >= 0x10000 ) /* Properness of volume sectors (>=128) */
3320 && ld_word ( fs
-> win
+ BPB_FATSz16
) != 0 ) { /* Properness of FAT size (MNBZ) */
3321 return 0 ; /* It can be presumed an FAT VBR */
3324 return sign
== 0xAA55 ? 2 : 3 ; /* Not an FAT VBR (valid or invalid BS) */
3328 /* Find an FAT volume */
3329 /* (It supports only generic partitioning rules, MBR, GPT and SFD) */
3331 static UINT
find_volume ( /* Returns BS status found in the hosting drive */
3332 FATFS
* fs
, /* Filesystem object */
3333 UINT part
/* Partition to fined = 0:find as SFD and partitions, >0:forced partition number */
3340 fmt
= check_fs ( fs
, 0 ); /* Load sector 0 and check if it is an FAT VBR as SFD format */
3341 if ( fmt
!= 2 && ( fmt
>= 3 || part
== 0 )) return fmt
; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error */
3343 /* Sector 0 is not an FAT VBR or forced partition number wants a partition */
3346 if ( fs
-> win
[ MBR_Table
+ PTE_System
] == 0xEE ) { /* GPT protective MBR? */
3347 DWORD n_ent
, v_ent
, ofs
;
3350 if ( move_window ( fs
, 1 ) != FR_OK
) return 4 ; /* Load GPT header sector (next to MBR) */
3351 if (! test_gpt_header ( fs
-> win
)) return 3 ; /* Check if GPT header is valid */
3352 n_ent
= ld_dword ( fs
-> win
+ GPTH_PtNum
); /* Number of entries */
3353 pt_lba
= ld_qword ( fs
-> win
+ GPTH_PtOfs
); /* Table location */
3354 for ( v_ent
= i
= 0 ; i
< n_ent
; i
++) { /* Find FAT partition */
3355 if ( move_window ( fs
, pt_lba
+ i
* SZ_GPTE
/ SS ( fs
)) != FR_OK
) return 4 ; /* PT sector */
3356 ofs
= i
* SZ_GPTE
% SS ( fs
); /* Offset in the sector */
3357 if (! memcmp ( fs
-> win
+ ofs
+ GPTE_PtGuid
, GUID_MS_Basic
, 16 )) { /* MS basic data partition? */
3359 fmt
= check_fs ( fs
, ld_qword ( fs
-> win
+ ofs
+ GPTE_FstLba
)); /* Load VBR and check status */
3360 if ( part
== 0 && fmt
<= 1 ) return fmt
; /* Auto search (valid FAT volume found first) */
3361 if ( part
!= 0 && v_ent
== part
) return fmt
; /* Forced partition order (regardless of it is valid or not) */
3364 return 3 ; /* Not found */
3367 if ( FF_MULTI_PARTITION
&& part
> 4 ) return 3 ; /* MBR has 4 partitions max */
3368 for ( i
= 0 ; i
< 4 ; i
++) { /* Load partition offset in the MBR */
3369 mbr_pt
[ i
] = ld_dword ( fs
-> win
+ MBR_Table
+ i
* SZ_PTE
+ PTE_StLba
);
3371 i
= part
? part
- 1 : 0 ; /* Table index to find first */
3372 do { /* Find an FAT volume */
3373 fmt
= mbr_pt
[ i
] ? check_fs ( fs
, mbr_pt
[ i
]) : 3 ; /* Check if the partition is FAT */
3374 } while ( part
== 0 && fmt
>= 2 && ++ i
< 4 );
3381 /*-----------------------------------------------------------------------*/
3382 /* Determine logical drive number and mount the volume if needed */
3383 /*-----------------------------------------------------------------------*/
3385 static FRESULT
mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
3386 const TCHAR
** path
, /* Pointer to pointer to the path name (drive number) */
3387 FATFS
** rfs
, /* Pointer to pointer to the found filesystem object */
3388 BYTE mode
/* Desiered access mode to check write protection */
3395 DWORD tsect
, sysect
, fasize
, nclst
, szbfat
;
3400 /* Get logical drive number */
3402 vol
= get_ldnumber ( path
);
3403 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3405 /* Check if the filesystem object is valid or not */
3406 fs
= FatFs
[ vol
]; /* Get pointer to the filesystem object */
3407 if (! fs
) return FR_NOT_ENABLED
; /* Is the filesystem object available? */
3409 if (! lock_volume ( fs
, 1 )) return FR_TIMEOUT
; /* Lock the volume, and system if needed */
3411 * rfs
= fs
; /* Return pointer to the filesystem object */
3413 mode
&= ( BYTE
)~ FA_READ
; /* Desired access mode, write access or not */
3414 if ( fs
-> fs_type
!= 0 ) { /* If the volume has been mounted */
3415 stat
= disk_status ( fs
-> pdrv
);
3416 if (!( stat
& STA_NOINIT
)) { /* and the physical drive is kept initialized */
3417 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check write protection if needed */
3418 return FR_WRITE_PROTECTED
;
3420 return FR_OK
; /* The filesystem object is already valid */
3424 /* The filesystem object is not valid. */
3425 /* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */
3427 fs
-> fs_type
= 0 ; /* Invalidate the filesystem object */
3428 stat
= disk_initialize ( fs
-> pdrv
); /* Initialize the volume hosting physical drive */
3429 if ( stat
& STA_NOINIT
) { /* Check if the initialization succeeded */
3430 return FR_NOT_READY
; /* Failed to initialize due to no medium or hard error */
3432 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check disk write protection if needed */
3433 return FR_WRITE_PROTECTED
;
3435 #if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
3436 if ( disk_ioctl ( fs
-> pdrv
, GET_SECTOR_SIZE
, & SS ( fs
)) != RES_OK
) return FR_DISK_ERR
;
3437 if ( SS ( fs
) > FF_MAX_SS
|| SS ( fs
) < FF_MIN_SS
|| ( SS ( fs
) & ( SS ( fs
) - 1 ))) return FR_DISK_ERR
;
3440 /* Find an FAT volume on the hosting drive */
3441 fmt
= find_volume ( fs
, LD2PT ( vol
));
3442 if ( fmt
== 4 ) return FR_DISK_ERR
; /* An error occurred in the disk I/O layer */
3443 if ( fmt
>= 2 ) return FR_NO_FILESYSTEM
; /* No FAT volume is found */
3444 bsect
= fs
-> winsect
; /* Volume offset in the hosting physical drive */
3446 /* An FAT volume is found (bsect). Following code initializes the filesystem object */
3451 DWORD so
, cv
, bcl
, i
;
3453 for ( i
= BPB_ZeroedEx
; i
< BPB_ZeroedEx
+ 53 && fs
-> win
[ i
] == 0 ; i
++) ; /* Check zero filler */
3454 if ( i
< BPB_ZeroedEx
+ 53 ) return FR_NO_FILESYSTEM
;
3456 if ( ld_word ( fs
-> win
+ BPB_FSVerEx
) != 0x100 ) return FR_NO_FILESYSTEM
; /* Check exFAT version (must be version 1.0) */
3458 if ( 1 << fs
-> win
[ BPB_BytsPerSecEx
] != SS ( fs
)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
3459 return FR_NO_FILESYSTEM
;
3462 maxlba
= ld_qword ( fs
-> win
+ BPB_TotSecEx
) + bsect
; /* Last LBA of the volume + 1 */
3463 if (! FF_LBA64
&& maxlba
>= 0x100000000 ) return FR_NO_FILESYSTEM
; /* (It cannot be accessed in 32-bit LBA) */
3465 fs
-> fsize
= ld_dword ( fs
-> win
+ BPB_FatSzEx
); /* Number of sectors per FAT */
3467 fs
-> n_fats
= fs
-> win
[ BPB_NumFATsEx
]; /* Number of FATs */
3468 if ( fs
-> n_fats
!= 1 ) return FR_NO_FILESYSTEM
; /* (Supports only 1 FAT) */
3470 fs
-> csize
= 1 << fs
-> win
[ BPB_SecPerClusEx
]; /* Cluster size */
3471 if ( fs
-> csize
== 0 ) return FR_NO_FILESYSTEM
; /* (Must be 1..32768 sectors) */
3473 nclst
= ld_dword ( fs
-> win
+ BPB_NumClusEx
); /* Number of clusters */
3474 if ( nclst
> MAX_EXFAT
) return FR_NO_FILESYSTEM
; /* (Too many clusters) */
3475 fs
-> n_fatent
= nclst
+ 2 ;
3477 /* Boundaries and Limits */
3478 fs
-> volbase
= bsect
;
3479 fs
-> database
= bsect
+ ld_dword ( fs
-> win
+ BPB_DataOfsEx
);
3480 fs
-> fatbase
= bsect
+ ld_dword ( fs
-> win
+ BPB_FatOfsEx
);
3481 if ( maxlba
< ( QWORD
) fs
-> database
+ nclst
* fs
-> csize
) return FR_NO_FILESYSTEM
; /* (Volume size must not be smaller than the size required) */
3482 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClusEx
);
3484 /* Get bitmap location and check if it is contiguous (implementation assumption) */
3486 for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */
3488 if ( so
>= fs
-> csize
) return FR_NO_FILESYSTEM
; /* Not found? */
3489 if ( move_window ( fs
, clst2sect ( fs
, ( DWORD
) fs
-> dirbase
) + so
) != FR_OK
) return FR_DISK_ERR
;
3492 if ( fs
-> win
[ i
] == ET_BITMAP
) break ; /* Is it a bitmap entry? */
3493 i
= ( i
+ SZDIRE
) % SS ( fs
); /* Next entry */
3495 bcl
= ld_dword ( fs
-> win
+ i
+ 20 ); /* Bitmap cluster */
3496 if ( bcl
< 2 || bcl
>= fs
-> n_fatent
) return FR_NO_FILESYSTEM
; /* (Wrong cluster#) */
3497 fs
-> bitbase
= fs
-> database
+ fs
-> csize
* ( bcl
- 2 ); /* Bitmap sector */
3498 for (;;) { /* Check if bitmap is contiguous */
3499 if ( move_window ( fs
, fs
-> fatbase
+ bcl
/ ( SS ( fs
) / 4 )) != FR_OK
) return FR_DISK_ERR
;
3500 cv
= ld_dword ( fs
-> win
+ bcl
% ( SS ( fs
) / 4 ) * 4 );
3501 if ( cv
== 0xFFFFFFFF ) break ; /* Last link? */
3502 if ( cv
!= ++ bcl
) return FR_NO_FILESYSTEM
; /* Fragmented bitmap? */
3506 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3508 fmt
= FS_EXFAT
; /* FAT sub-type */
3510 #endif /* FF_FS_EXFAT */
3512 if ( ld_word ( fs
-> win
+ BPB_BytsPerSec
) != SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_BytsPerSec must be equal to the physical sector size) */
3514 fasize
= ld_word ( fs
-> win
+ BPB_FATSz16
); /* Number of sectors per FAT */
3515 if ( fasize
== 0 ) fasize
= ld_dword ( fs
-> win
+ BPB_FATSz32
);
3518 fs
-> n_fats
= fs
-> win
[ BPB_NumFATs
]; /* Number of FATs */
3519 if ( fs
-> n_fats
!= 1 && fs
-> n_fats
!= 2 ) return FR_NO_FILESYSTEM
; /* (Must be 1 or 2) */
3520 fasize
*= fs
-> n_fats
; /* Number of sectors for FAT area */
3522 fs
-> csize
= fs
-> win
[ BPB_SecPerClus
]; /* Cluster size */
3523 if ( fs
-> csize
== 0 || ( fs
-> csize
& ( fs
-> csize
- 1 ))) return FR_NO_FILESYSTEM
; /* (Must be power of 2) */
3525 fs
-> n_rootdir
= ld_word ( fs
-> win
+ BPB_RootEntCnt
); /* Number of root directory entries */
3526 if ( fs
-> n_rootdir
% ( SS ( fs
) / SZDIRE
)) return FR_NO_FILESYSTEM
; /* (Must be sector aligned) */
3528 tsect
= ld_word ( fs
-> win
+ BPB_TotSec16
); /* Number of sectors on the volume */
3529 if ( tsect
== 0 ) tsect
= ld_dword ( fs
-> win
+ BPB_TotSec32
);
3531 nrsv
= ld_word ( fs
-> win
+ BPB_RsvdSecCnt
); /* Number of reserved sectors */
3532 if ( nrsv
== 0 ) return FR_NO_FILESYSTEM
; /* (Must not be 0) */
3534 /* Determine the FAT sub type */
3535 sysect
= nrsv
+ fasize
+ fs
-> n_rootdir
/ ( SS ( fs
) / SZDIRE
); /* RSV + FAT + DIR */
3536 if ( tsect
< sysect
) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3537 nclst
= ( tsect
- sysect
) / fs
-> csize
; /* Number of clusters */
3538 if ( nclst
== 0 ) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3540 if ( nclst
<= MAX_FAT32
) fmt
= FS_FAT32
;
3541 if ( nclst
<= MAX_FAT16
) fmt
= FS_FAT16
;
3542 if ( nclst
<= MAX_FAT12
) fmt
= FS_FAT12
;
3543 if ( fmt
== 0 ) return FR_NO_FILESYSTEM
;
3545 /* Boundaries and Limits */
3546 fs
-> n_fatent
= nclst
+ 2 ; /* Number of FAT entries */
3547 fs
-> volbase
= bsect
; /* Volume start sector */
3548 fs
-> fatbase
= bsect
+ nrsv
; /* FAT start sector */
3549 fs
-> database
= bsect
+ sysect
; /* Data start sector */
3550 if ( fmt
== FS_FAT32
) {
3551 if ( ld_word ( fs
-> win
+ BPB_FSVer32
) != 0 ) return FR_NO_FILESYSTEM
; /* (Must be FAT32 revision 0.0) */
3552 if ( fs
-> n_rootdir
!= 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must be 0) */
3553 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClus32
); /* Root directory start cluster */
3554 szbfat
= fs
-> n_fatent
* 4 ; /* (Needed FAT size) */
3556 if ( fs
-> n_rootdir
== 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must not be 0) */
3557 fs
-> dirbase
= fs
-> fatbase
+ fasize
; /* Root directory start sector */
3558 szbfat
= ( fmt
== FS_FAT16
) ? /* (Needed FAT size) */
3559 fs
-> n_fatent
* 2 : fs
-> n_fatent
* 3 / 2 + ( fs
-> n_fatent
& 1 );
3561 if ( fs
-> fsize
< ( szbfat
+ ( SS ( fs
) - 1 )) / SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_FATSz must not be less than the size needed) */
3564 /* Get FSInfo if available */
3565 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3566 fs
-> fsi_flag
= 0x80 ;
3567 #if (FF_FS_NOFSINFO & 3) != 3
3568 if ( fmt
== FS_FAT32
/* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
3569 && ld_word ( fs
-> win
+ BPB_FSInfo32
) == 1
3570 && move_window ( fs
, bsect
+ 1 ) == FR_OK
)
3573 if ( ld_word ( fs
-> win
+ BS_55AA
) == 0xAA55 /* Load FSInfo data if available */
3574 && ld_dword ( fs
-> win
+ FSI_LeadSig
) == 0x41615252
3575 && ld_dword ( fs
-> win
+ FSI_StrucSig
) == 0x61417272 )
3577 #if (FF_FS_NOFSINFO & 1) == 0
3578 fs
-> free_clst
= ld_dword ( fs
-> win
+ FSI_Free_Count
);
3580 #if (FF_FS_NOFSINFO & 2) == 0
3581 fs
-> last_clst
= ld_dword ( fs
-> win
+ FSI_Nxt_Free
);
3585 #endif /* (FF_FS_NOFSINFO & 3) != 3 */
3586 #endif /* !FF_FS_READONLY */
3589 fs
-> fs_type
= ( BYTE
) fmt
; /* FAT sub-type (the filesystem object gets valid) */
3590 fs
-> id
= ++ Fsid
; /* Volume mount ID */
3592 fs
-> lfnbuf
= LfnBuf
; /* Static LFN working buffer */
3594 fs
-> dirbuf
= DirBuf
; /* Static directory block scratchpad buuffer */
3597 #if FF_FS_RPATH != 0
3598 fs
-> cdir
= 0 ; /* Initialize current directory */
3600 #if FF_FS_LOCK /* Clear file lock semaphores */
3609 /*-----------------------------------------------------------------------*/
3610 /* Check if the file/directory object is valid or not */
3611 /*-----------------------------------------------------------------------*/
3613 static FRESULT
validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
3614 FFOBJID
* obj
, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR structure, to check validity */
3615 FATFS
** rfs
/* Pointer to pointer to the owner filesystem object to return */
3618 FRESULT res
= FR_INVALID_OBJECT
;
3621 if ( obj
&& obj
-> fs
&& obj
-> fs
-> fs_type
&& obj
-> id
== obj
-> fs
-> id
) { /* Test if the object is valid */
3623 if ( lock_volume ( obj
-> fs
, 0 )) { /* Take a grant to access the volume */
3624 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the hosting phsical drive is kept initialized */
3627 unlock_volume ( obj
-> fs
, FR_OK
); /* Invalidated volume, abort to access */
3629 } else { /* Could not take */
3633 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the hosting phsical drive is kept initialized */
3638 * rfs
= ( res
== FR_OK
) ? obj
-> fs
: 0 ; /* Return corresponding filesystem object if it is valid */
3645 /*---------------------------------------------------------------------------
3647 Public Functions (FatFs API)
3649 ----------------------------------------------------------------------------*/
3653 /*-----------------------------------------------------------------------*/
3654 /* Mount/Unmount a Logical Drive */
3655 /*-----------------------------------------------------------------------*/
3658 FATFS
* fs
, /* Pointer to the filesystem object to be registered (NULL:unmount)*/
3659 const TCHAR
* path
, /* Logical drive number to be mounted/unmounted */
3660 BYTE opt
/* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */
3666 const TCHAR
* rp
= path
;
3669 /* Get volume ID (logical drive number) */
3670 vol
= get_ldnumber (& rp
);
3671 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3672 cfs
= FatFs
[ vol
]; /* Pointer to the filesystem object of the volume */
3674 if ( cfs
) { /* Unregister current filesystem object if regsitered */
3679 #if FF_FS_REENTRANT /* Discard mutex of the current volume */
3680 ff_mutex_delete ( vol
);
3682 cfs
-> fs_type
= 0 ; /* Invalidate the filesystem object to be unregistered */
3685 if ( fs
) { /* Register new filesystem object */
3686 fs
-> pdrv
= LD2PD ( vol
); /* Volume hosting physical drive */
3687 #if FF_FS_REENTRANT /* Create a volume mutex */
3688 fs
-> ldrv
= ( BYTE
) vol
; /* Owner volume ID */
3689 if (! ff_mutex_create ( vol
)) return FR_INT_ERR
;
3691 if ( SysLock
== 0 ) { /* Create a system mutex if needed */
3692 if (! ff_mutex_create ( FF_VOLUMES
)) {
3693 ff_mutex_delete ( vol
);
3696 SysLock
= 1 ; /* System mutex is ready */
3700 fs
-> fs_type
= 0 ; /* Invalidate the new filesystem object */
3701 FatFs
[ vol
] = fs
; /* Register new fs object */
3704 if ( opt
== 0 ) return FR_OK
; /* Do not mount now, it will be mounted in subsequent file functions */
3706 res
= mount_volume (& path
, & fs
, 0 ); /* Force mounted the volume */
3713 /*-----------------------------------------------------------------------*/
3714 /* Open or Create a File */
3715 /*-----------------------------------------------------------------------*/
3718 FIL
* fp
, /* Pointer to the blank file object */
3719 const TCHAR
* path
, /* Pointer to the file name */
3720 BYTE mode
/* Access mode and open mode flags */
3727 DWORD cl
, bcs
, clst
, tm
;
3734 if (! fp
) return FR_INVALID_OBJECT
;
3736 /* Get logical drive number */
3737 mode
&= FF_FS_READONLY
? FA_READ
: FA_READ
| FA_WRITE
| FA_CREATE_ALWAYS
| FA_CREATE_NEW
| FA_OPEN_ALWAYS
| FA_OPEN_APPEND
;
3738 res
= mount_volume (& path
, & fs
, mode
);
3742 res
= follow_path (& dj
, path
); /* Follow the file path */
3743 #if !FF_FS_READONLY /* Read/Write configuration */
3745 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Origin directory itself? */
3746 res
= FR_INVALID_NAME
;
3750 res
= chk_share (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Check if the file can be used */
3754 /* Create or Open a file */
3755 if ( mode
& ( FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
)) {
3756 if ( res
!= FR_OK
) { /* No file, create new */
3757 if ( res
== FR_NO_FILE
) { /* There is no file to open, create a new entry */
3759 res
= enq_share () ? dir_register (& dj
) : FR_TOO_MANY_OPEN_FILES
;
3761 res
= dir_register (& dj
);
3764 mode
|= FA_CREATE_ALWAYS
; /* File is created */
3766 else { /* Any object with the same name is already existing */
3767 if ( dj
. obj
. attr
& ( AM_RDO
| AM_DIR
)) { /* Cannot overwrite it (R/O or DIR) */
3770 if ( mode
& FA_CREATE_NEW
) res
= FR_EXIST
; /* Cannot create as new file */
3773 if ( res
== FR_OK
&& ( mode
& FA_CREATE_ALWAYS
)) { /* Truncate the file if overwrite mode */
3775 if ( fs
-> fs_type
== FS_EXFAT
) {
3776 /* Get current allocation info */
3778 init_alloc_info ( fs
, & fp
-> obj
);
3779 /* Set directory entry block initial state */
3780 memset ( fs
-> dirbuf
+ 2 , 0 , 30 ); /* Clear 85 entry except for NumSec */
3781 memset ( fs
-> dirbuf
+ 38 , 0 , 26 ); /* Clear C0 entry except for NumName and NameHash */
3782 fs
-> dirbuf
[ XDIR_Attr
] = AM_ARC
;
3783 st_dword ( fs
-> dirbuf
+ XDIR_CrtTime
, GET_FATTIME ());
3784 fs
-> dirbuf
[ XDIR_GenFlags
] = 1 ;
3785 res
= store_xdir (& dj
);
3786 if ( res
== FR_OK
&& fp
-> obj
. sclust
!= 0 ) { /* Remove the cluster chain if exist */
3787 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
3788 fs
-> last_clst
= fp
-> obj
. sclust
- 1 ; /* Reuse the cluster hole */
3793 /* Set directory entry initial state */
3794 tm
= GET_FATTIME (); /* Set created time */
3795 st_dword ( dj
. dir
+ DIR_CrtTime
, tm
);
3796 st_dword ( dj
. dir
+ DIR_ModTime
, tm
);
3797 cl
= ld_clust ( fs
, dj
. dir
); /* Get current cluster chain */
3798 dj
. dir
[ DIR_Attr
] = AM_ARC
; /* Reset attribute */
3799 st_clust ( fs
, dj
. dir
, 0 ); /* Reset file allocation info */
3800 st_dword ( dj
. dir
+ DIR_FileSize
, 0 );
3802 if ( cl
!= 0 ) { /* Remove the cluster chain if exist */
3804 res
= remove_chain (& dj
. obj
, cl
, 0 );
3806 res
= move_window ( fs
, sc
);
3807 fs
-> last_clst
= cl
- 1 ; /* Reuse the cluster hole */
3813 else { /* Open an existing file */
3814 if ( res
== FR_OK
) { /* Is the object exsiting? */
3815 if ( dj
. obj
. attr
& AM_DIR
) { /* File open against a directory */
3818 if (( mode
& FA_WRITE
) && ( dj
. obj
. attr
& AM_RDO
)) { /* Write mode open against R/O file */
3825 if ( mode
& FA_CREATE_ALWAYS
) mode
|= FA_MODIFIED
; /* Set file change flag if created or overwritten */
3826 fp
-> dir_sect
= fs
-> winsect
; /* Pointer to the directory entry */
3827 fp
-> dir_ptr
= dj
. dir
;
3829 fp
-> obj
. lockid
= inc_share (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Lock the file for this session */
3830 if ( fp
-> obj
. lockid
== 0 ) res
= FR_INT_ERR
;
3833 #else /* R/O configuration */
3835 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it origin directory itself? */
3836 res
= FR_INVALID_NAME
;
3838 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a directory? */
3847 if ( fs
-> fs_type
== FS_EXFAT
) {
3848 fp
-> obj
. c_scl
= dj
. obj
. sclust
; /* Get containing directory info */
3849 fp
-> obj
. c_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
3850 fp
-> obj
. c_ofs
= dj
. blk_ofs
;
3851 init_alloc_info ( fs
, & fp
-> obj
);
3855 fp
-> obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Get object allocation info */
3856 fp
-> obj
. objsize
= ld_dword ( dj
. dir
+ DIR_FileSize
);
3859 fp
-> cltbl
= 0 ; /* Disable fast seek mode */
3861 fp
-> obj
. fs
= fs
; /* Validate the file object */
3862 fp
-> obj
. id
= fs
-> id
;
3863 fp
-> flag
= mode
; /* Set file access mode */
3864 fp
-> err
= 0 ; /* Clear error flag */
3865 fp
-> sect
= 0 ; /* Invalidate current data sector */
3866 fp
-> fptr
= 0 ; /* Set file pointer top of the file */
3869 memset ( fp
-> buf
, 0 , sizeof fp
-> buf
); /* Clear sector buffer */
3871 if (( mode
& FA_SEEKEND
) && fp
-> obj
. objsize
> 0 ) { /* Seek to end of file if FA_OPEN_APPEND is specified */
3872 fp
-> fptr
= fp
-> obj
. objsize
; /* Offset to seek */
3873 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size in byte */
3874 clst
= fp
-> obj
. sclust
; /* Follow the cluster chain */
3875 for ( ofs
= fp
-> obj
. objsize
; res
== FR_OK
&& ofs
> bcs
; ofs
-= bcs
) {
3876 clst
= get_fat (& fp
-> obj
, clst
);
3877 if ( clst
<= 1 ) res
= FR_INT_ERR
;
3878 if ( clst
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
3881 if ( res
== FR_OK
&& ofs
% SS ( fs
)) { /* Fill sector buffer if not on the sector boundary */
3882 sc
= clst2sect ( fs
, clst
);
3886 fp
-> sect
= sc
+ ( DWORD
)( ofs
/ SS ( fs
));
3888 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) res
= FR_DISK_ERR
;
3893 if ( res
!= FR_OK
) dec_share ( fp
-> obj
. lockid
); /* Decrement file open counter if seek failed */
3902 if ( res
!= FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object on error */
3910 /*-----------------------------------------------------------------------*/
3912 /*-----------------------------------------------------------------------*/
3915 FIL
* fp
, /* Open file to be read */
3916 void * buff
, /* Data buffer to store the read data */
3917 UINT btr
, /* Number of bytes to read */
3918 UINT
* br
/* Number of bytes read */
3926 UINT rcnt
, cc
, csect
;
3927 BYTE
* rbuff
= ( BYTE
*) buff
;
3930 * br
= 0 ; /* Clear read byte counter */
3931 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3932 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
3933 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
3934 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
3935 if ( btr
> remain
) btr
= ( UINT
) remain
; /* Truncate btr by remaining bytes */
3937 for ( ; btr
> 0 ; btr
-= rcnt
, * br
+= rcnt
, rbuff
+= rcnt
, fp
-> fptr
+= rcnt
) { /* Repeat until btr bytes read */
3938 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
3939 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
3940 if ( csect
== 0 ) { /* On the cluster boundary? */
3941 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
3942 clst
= fp
-> obj
. sclust
; /* Follow cluster chain from the origin */
3943 } else { /* Middle or end of the file */
3946 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
3950 clst
= get_fat (& fp
-> obj
, fp
-> clust
); /* Follow cluster chain on the FAT */
3953 if ( clst
< 2 ) ABORT ( fs
, FR_INT_ERR
);
3954 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
3955 fp
-> clust
= clst
; /* Update current cluster */
3957 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
3958 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
3960 cc
= btr
/ SS ( fs
); /* When remaining bytes >= sector size, */
3961 if ( cc
> 0 ) { /* Read maximum contiguous sectors directly */
3962 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
3963 cc
= fs
-> csize
- csect
;
3965 if ( disk_read ( fs
-> pdrv
, rbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3966 #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
3968 if ( fs
-> wflag
&& fs
-> winsect
- sect
< cc
) {
3969 memcpy ( rbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), fs
-> win
, SS ( fs
));
3972 if (( fp
-> flag
& FA_DIRTY
) && fp
-> sect
- sect
< cc
) {
3973 memcpy ( rbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), fp
-> buf
, SS ( fs
));
3977 rcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
3981 if ( fp
-> sect
!= sect
) { /* Load data sector if not in cache */
3983 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
3984 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3985 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3988 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
3993 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes remains in the sector */
3994 if ( rcnt
> btr
) rcnt
= btr
; /* Clip it by btr if needed */
3996 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
3997 memcpy ( rbuff
, fs
-> win
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
3999 memcpy ( rbuff
, fp
-> buf
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
4003 LEAVE_FF ( fs
, FR_OK
);
4010 /*-----------------------------------------------------------------------*/
4012 /*-----------------------------------------------------------------------*/
4015 FIL
* fp
, /* Open file to be written */
4016 const void * buff
, /* Data to be written */
4017 UINT btw
, /* Number of bytes to write */
4018 UINT
* bw
/* Number of bytes written */
4025 UINT wcnt
, cc
, csect
;
4026 const BYTE
* wbuff
= ( const BYTE
*) buff
;
4029 * bw
= 0 ; /* Clear write byte counter */
4030 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4031 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
4032 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
4034 /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
4035 if ((! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) && ( DWORD
)( fp
-> fptr
+ btw
) < ( DWORD
) fp
-> fptr
) {
4036 btw
= ( UINT
)( 0xFFFFFFFF - ( DWORD
) fp
-> fptr
);
4039 for ( ; btw
> 0 ; btw
-= wcnt
, * bw
+= wcnt
, wbuff
+= wcnt
, fp
-> fptr
+= wcnt
, fp
-> obj
. objsize
= ( fp
-> fptr
> fp
-> obj
. objsize
) ? fp
-> fptr
: fp
-> obj
. objsize
) { /* Repeat until all data written */
4040 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
4041 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
)) & ( fs
-> csize
- 1 ); /* Sector offset in the cluster */
4042 if ( csect
== 0 ) { /* On the cluster boundary? */
4043 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
4044 clst
= fp
-> obj
. sclust
; /* Follow from the origin */
4045 if ( clst
== 0 ) { /* If no cluster is allocated, */
4046 clst
= create_chain (& fp
-> obj
, 0 ); /* create a new cluster chain */
4048 } else { /* On the middle or end of the file */
4051 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
4055 clst
= create_chain (& fp
-> obj
, fp
-> clust
); /* Follow or stretch cluster chain on the FAT */
4058 if ( clst
== 0 ) break ; /* Could not allocate a new cluster (disk full) */
4059 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
4060 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4061 fp
-> clust
= clst
; /* Update current cluster */
4062 if ( fp
-> obj
. sclust
== 0 ) fp
-> obj
. sclust
= clst
; /* Set start cluster if the first write */
4065 if ( fs
-> winsect
== fp
-> sect
&& sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Write-back sector cache */
4067 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back sector cache */
4068 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4069 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4072 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
4073 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4075 cc
= btw
/ SS ( fs
); /* When remaining bytes >= sector size, */
4076 if ( cc
> 0 ) { /* Write maximum contiguous sectors directly */
4077 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
4078 cc
= fs
-> csize
- csect
;
4080 if ( disk_write ( fs
-> pdrv
, wbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4081 #if FF_FS_MINIMIZE <= 2
4083 if ( fs
-> winsect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
4084 memcpy ( fs
-> win
, wbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), SS ( fs
));
4088 if ( fp
-> sect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
4089 memcpy ( fp
-> buf
, wbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), SS ( fs
));
4090 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4094 wcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
4098 if ( fp
-> fptr
>= fp
-> obj
. objsize
) { /* Avoid silly cache filling on the growing edge */
4099 if ( sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
);
4103 if ( fp
-> sect
!= sect
&& /* Fill sector cache with file data */
4104 fp
-> fptr
< fp
-> obj
. objsize
&&
4105 disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) {
4106 ABORT ( fs
, FR_DISK_ERR
);
4111 wcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes remains in the sector */
4112 if ( wcnt
> btw
) wcnt
= btw
; /* Clip it by btw if needed */
4114 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
4115 memcpy ( fs
-> win
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
4118 memcpy ( fp
-> buf
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
4119 fp
-> flag
|= FA_DIRTY
;
4123 fp
-> flag
|= FA_MODIFIED
; /* Set file change flag */
4125 LEAVE_FF ( fs
, FR_OK
);
4131 /*-----------------------------------------------------------------------*/
4132 /* Synchronize the File */
4133 /*-----------------------------------------------------------------------*/
4136 FIL
* fp
/* Open file to be synced */
4145 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4147 if ( fp
-> flag
& FA_MODIFIED
) { /* Is there any change to the file? */
4149 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back cached data if needed */
4150 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) LEAVE_FF ( fs
, FR_DISK_ERR
);
4151 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4154 /* Update the directory entry */
4155 tm
= GET_FATTIME (); /* Modified time */
4157 if ( fs
-> fs_type
== FS_EXFAT
) {
4158 res
= fill_first_frag (& fp
-> obj
); /* Fill first fragment on the FAT if needed */
4160 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
4167 res
= load_obj_xdir (& dj
, & fp
-> obj
); /* Load directory entry block */
4169 fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
4170 fs
-> dirbuf
[ XDIR_GenFlags
] = fp
-> obj
. stat
| 1 ; /* Update file allocation information */
4171 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, fp
-> obj
. sclust
); /* Update start cluster */
4172 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, fp
-> obj
. objsize
); /* Update file size */
4173 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, fp
-> obj
. objsize
); /* (FatFs does not support Valid File Size feature) */
4174 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Update modified time */
4175 fs
-> dirbuf
[ XDIR_ModTime10
] = 0 ;
4176 st_dword ( fs
-> dirbuf
+ XDIR_AccTime
, 0 );
4177 res
= store_xdir (& dj
); /* Restore it to the directory */
4180 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
4188 res
= move_window ( fs
, fp
-> dir_sect
);
4191 dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
4192 st_clust ( fp
-> obj
. fs
, dir
, fp
-> obj
. sclust
); /* Update file allocation information */
4193 st_dword ( dir
+ DIR_FileSize
, ( DWORD
) fp
-> obj
. objsize
); /* Update file size */
4194 st_dword ( dir
+ DIR_ModTime
, tm
); /* Update modified time */
4195 st_word ( dir
+ DIR_LstAccDate
, 0 );
4197 res
= sync_fs ( fs
); /* Restore it to the directory */
4198 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
4207 #endif /* !FF_FS_READONLY */
4212 /*-----------------------------------------------------------------------*/
4214 /*-----------------------------------------------------------------------*/
4217 FIL
* fp
/* Open file to be closed */
4224 res
= f_sync ( fp
); /* Flush cached data */
4228 res
= validate (& fp
-> obj
, & fs
); /* Lock volume */
4231 res
= dec_share ( fp
-> obj
. lockid
); /* Decrement file open counter */
4232 if ( res
== FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4234 fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4237 unlock_volume ( fs
, FR_OK
); /* Unlock volume */
4247 #if FF_FS_RPATH >= 1
4248 /*-----------------------------------------------------------------------*/
4249 /* Change Current Directory or Current Drive, Get Current Directory */
4250 /*-----------------------------------------------------------------------*/
4253 const TCHAR
* path
/* Drive number to set */
4259 /* Get logical drive number */
4260 vol
= get_ldnumber (& path
);
4261 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
4262 CurrVol
= ( BYTE
) vol
; /* Set it as current volume */
4270 const TCHAR
* path
/* Pointer to the directory path */
4273 #if FF_STR_VOLUME_ID == 2
4282 /* Get logical drive */
4283 res
= mount_volume (& path
, & fs
, 0 );
4287 res
= follow_path (& dj
, path
); /* Follow the path */
4288 if ( res
== FR_OK
) { /* Follow completed */
4289 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it the start directory itself? */
4290 fs
-> cdir
= dj
. obj
. sclust
;
4292 if ( fs
-> fs_type
== FS_EXFAT
) {
4293 fs
-> cdc_scl
= dj
. obj
. c_scl
;
4294 fs
-> cdc_size
= dj
. obj
. c_size
;
4295 fs
-> cdc_ofs
= dj
. obj
. c_ofs
;
4299 if ( dj
. obj
. attr
& AM_DIR
) { /* It is a sub-directory */
4301 if ( fs
-> fs_type
== FS_EXFAT
) {
4302 fs
-> cdir
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Sub-directory cluster */
4303 fs
-> cdc_scl
= dj
. obj
. sclust
; /* Save containing directory information */
4304 fs
-> cdc_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
4305 fs
-> cdc_ofs
= dj
. blk_ofs
;
4309 fs
-> cdir
= ld_clust ( fs
, dj
. dir
); /* Sub-directory cluster */
4312 res
= FR_NO_PATH
; /* Reached but a file */
4317 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4318 #if FF_STR_VOLUME_ID == 2 /* Also current drive is changed if in Unix style volume ID */
4320 for ( i
= FF_VOLUMES
- 1 ; i
&& fs
!= FatFs
[ i
]; i
--) ; /* Set current drive */
4330 #if FF_FS_RPATH >= 2
4332 TCHAR
* buff
, /* Pointer to the directory path */
4333 UINT len
/* Size of buff in unit of TCHAR */
4344 #if FF_STR_VOLUME_ID
4352 /* Get logical drive */
4353 buff
[ 0 ] = 0 ; /* Set null string to get current volume */
4354 res
= mount_volume (( const TCHAR
**)& buff
, & fs
, 0 ); /* Get current volume */
4359 /* Follow parent directories and create the path */
4360 i
= len
; /* Bottom of buffer (directory stack base) */
4361 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) { /* (Cannot do getcwd on exFAT and returns root path) */
4362 dj
. obj
. sclust
= fs
-> cdir
; /* Start to follow upper directory from current directory */
4363 while (( ccl
= dj
. obj
. sclust
) != 0 ) { /* Repeat while current directory is a sub-directory */
4364 res
= dir_sdi (& dj
, 1 * SZDIRE
); /* Get parent directory */
4365 if ( res
!= FR_OK
) break ;
4366 res
= move_window ( fs
, dj
. sect
);
4367 if ( res
!= FR_OK
) break ;
4368 dj
. obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Goto parent directory */
4369 res
= dir_sdi (& dj
, 0 );
4370 if ( res
!= FR_OK
) break ;
4371 do { /* Find the entry links to the child directory */
4372 res
= DIR_READ_FILE (& dj
);
4373 if ( res
!= FR_OK
) break ;
4374 if ( ccl
== ld_clust ( fs
, dj
. dir
)) break ; /* Found the entry */
4375 res
= dir_next (& dj
, 0 );
4376 } while ( res
== FR_OK
);
4377 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be 'not found'. */
4378 if ( res
!= FR_OK
) break ;
4379 get_fileinfo (& dj
, & fno
); /* Get the directory name and push it to the buffer */
4380 for ( n
= 0 ; fno
. fname
[ n
]; n
++) ; /* Name length */
4381 if ( i
< n
+ 1 ) { /* Insufficient space to store the path name? */
4382 res
= FR_NOT_ENOUGH_CORE
; break ;
4384 while ( n
) buff
[-- i
] = fno
. fname
[-- n
]; /* Stack the name */
4389 if ( i
== len
) buff
[-- i
] = '/' ; /* Is it the root-directory? */
4390 #if FF_VOLUMES >= 2 /* Put drive prefix */
4392 #if FF_STR_VOLUME_ID >= 1 /* String volume ID */
4393 for ( n
= 0 , vp
= ( const char *) VolumeStr
[ CurrVol
]; vp
[ n
]; n
++) ;
4395 if ( FF_STR_VOLUME_ID
== 2 ) * tp
++ = ( TCHAR
) '/' ;
4396 for ( vl
= 0 ; vl
< n
; * tp
++ = ( TCHAR
) vp
[ vl
], vl
++) ;
4397 if ( FF_STR_VOLUME_ID
== 1 ) * tp
++ = ( TCHAR
) ':' ;
4400 #else /* Numeric volume ID */
4402 * tp
++ = ( TCHAR
) '0' + CurrVol
;
4407 if ( vl
== 0 ) res
= FR_NOT_ENOUGH_CORE
;
4409 /* Add current directory path */
4411 do { /* Copy stacked path string */
4423 #endif /* FF_FS_RPATH >= 2 */
4424 #endif /* FF_FS_RPATH >= 1 */
4428 #if FF_FS_MINIMIZE <= 2
4429 /*-----------------------------------------------------------------------*/
4430 /* Seek File Read/Write Pointer */
4431 /*-----------------------------------------------------------------------*/
4434 FIL
* fp
, /* Pointer to the file object */
4435 FSIZE_t ofs
/* File pointer from top of file */
4444 DWORD cl
, pcl
, ncl
, tcl
, tlen
, ulen
;
4449 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4450 if ( res
== FR_OK
) res
= ( FRESULT
) fp
-> err
;
4451 #if FF_FS_EXFAT && !FF_FS_READONLY
4452 if ( res
== FR_OK
&& fs
-> fs_type
== FS_EXFAT
) {
4453 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
4456 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
4459 if ( fp
-> cltbl
) { /* Fast seek */
4460 if ( ofs
== CREATE_LINKMAP
) { /* Create CLMT */
4462 tlen
= * tbl
++; ulen
= 2 ; /* Given table size and required table size */
4463 cl
= fp
-> obj
. sclust
; /* Origin of the chain */
4466 /* Get a fragment */
4467 tcl
= cl
; ncl
= 0 ; ulen
+= 2 ; /* Top, length and used items */
4470 cl
= get_fat (& fp
-> obj
, cl
);
4471 if ( cl
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
4472 if ( cl
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4473 } while ( cl
== pcl
+ 1 );
4474 if ( ulen
<= tlen
) { /* Store the length and top of the fragment */
4475 * tbl
++ = ncl
; * tbl
++ = tcl
;
4477 } while ( cl
< fs
-> n_fatent
); /* Repeat until end of chain */
4479 * fp
-> cltbl
= ulen
; /* Number of items used */
4481 * tbl
= 0 ; /* Terminate table */
4483 res
= FR_NOT_ENOUGH_CORE
; /* Given table size is smaller than required */
4485 } else { /* Fast seek */
4486 if ( ofs
> fp
-> obj
. objsize
) ofs
= fp
-> obj
. objsize
; /* Clip offset at the file size */
4487 fp
-> fptr
= ofs
; /* Set file pointer */
4489 fp
-> clust
= clmt_clust ( fp
, ofs
- 1 );
4490 dsc
= clst2sect ( fs
, fp
-> clust
);
4491 if ( dsc
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4492 dsc
+= ( DWORD
)(( ofs
- 1 ) / SS ( fs
)) & ( fs
-> csize
- 1 );
4493 if ( fp
-> fptr
% SS ( fs
) && dsc
!= fp
-> sect
) { /* Refill sector cache if needed */
4496 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4497 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4498 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4501 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, dsc
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Load current sector */
4513 if ( fs
-> fs_type
!= FS_EXFAT
&& ofs
>= 0x100000000 ) ofs
= 0xFFFFFFFF ; /* Clip at 4 GiB - 1 if at FATxx */
4515 if ( ofs
> fp
-> obj
. objsize
&& ( FF_FS_READONLY
|| !( fp
-> flag
& FA_WRITE
))) { /* In read-only mode, clip offset with the file size */
4516 ofs
= fp
-> obj
. objsize
;
4519 fp
-> fptr
= nsect
= 0 ;
4521 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size (byte) */
4523 ( ofs
- 1 ) / bcs
>= ( ifptr
- 1 ) / bcs
) { /* When seek to same or following cluster, */
4524 fp
-> fptr
= ( ifptr
- 1 ) & ~( FSIZE_t
)( bcs
- 1 ); /* start from the current cluster */
4527 } else { /* When seek to back cluster, */
4528 clst
= fp
-> obj
. sclust
; /* start from the first cluster */
4530 if ( clst
== 0 ) { /* If no cluster chain, create a new chain */
4531 clst
= create_chain (& fp
-> obj
, 0 );
4532 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
4533 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4534 fp
-> obj
. sclust
= clst
;
4540 while ( ofs
> bcs
) { /* Cluster following loop */
4541 ofs
-= bcs
; fp
-> fptr
+= bcs
;
4543 if ( fp
-> flag
& FA_WRITE
) { /* Check if in write mode or not */
4544 if ( FF_FS_EXFAT
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* No FAT chain object needs correct objsize to generate FAT value */
4545 fp
-> obj
. objsize
= fp
-> fptr
;
4546 fp
-> flag
|= FA_MODIFIED
;
4548 clst
= create_chain (& fp
-> obj
, clst
); /* Follow chain with forceed stretch */
4549 if ( clst
== 0 ) { /* Clip file size in case of disk full */
4555 clst
= get_fat (& fp
-> obj
, clst
); /* Follow cluster chain if not in write mode */
4557 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4558 if ( clst
<= 1 || clst
>= fs
-> n_fatent
) ABORT ( fs
, FR_INT_ERR
);
4563 nsect
= clst2sect ( fs
, clst
); /* Current sector */
4564 if ( nsect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4565 nsect
+= ( DWORD
)( ofs
/ SS ( fs
));
4569 if (! FF_FS_READONLY
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* Set file change flag if the file size is extended */
4570 fp
-> obj
. objsize
= fp
-> fptr
;
4571 fp
-> flag
|= FA_MODIFIED
;
4573 if ( fp
-> fptr
% SS ( fs
) && nsect
!= fp
-> sect
) { /* Fill sector cache if needed */
4576 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4577 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4578 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4581 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, nsect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
4592 #if FF_FS_MINIMIZE <= 1
4593 /*-----------------------------------------------------------------------*/
4594 /* Create a Directory Object */
4595 /*-----------------------------------------------------------------------*/
4598 DIR * dp
, /* Pointer to directory object to create */
4599 const TCHAR
* path
/* Pointer to the directory path */
4607 if (! dp
) return FR_INVALID_OBJECT
;
4609 /* Get logical drive */
4610 res
= mount_volume (& path
, & fs
, 0 );
4614 res
= follow_path ( dp
, path
); /* Follow the path to the directory */
4615 if ( res
== FR_OK
) { /* Follow completed */
4616 if (!( dp
-> fn
[ NSFLAG
] & NS_NONAME
)) { /* It is not the origin directory itself */
4617 if ( dp
-> obj
. attr
& AM_DIR
) { /* This object is a sub-directory */
4619 if ( fs
-> fs_type
== FS_EXFAT
) {
4620 dp
-> obj
. c_scl
= dp
-> obj
. sclust
; /* Get containing directory inforamation */
4621 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
4622 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
4623 init_alloc_info ( fs
, & dp
-> obj
); /* Get object allocation info */
4627 dp
-> obj
. sclust
= ld_clust ( fs
, dp
-> dir
); /* Get object allocation info */
4629 } else { /* This object is a file */
4634 dp
-> obj
. id
= fs
-> id
;
4635 res
= dir_sdi ( dp
, 0 ); /* Rewind directory */
4638 if ( dp
-> obj
. sclust
!= 0 ) {
4639 dp
-> obj
. lockid
= inc_share ( dp
, 0 ); /* Lock the sub directory */
4640 if (! dp
-> obj
. lockid
) res
= FR_TOO_MANY_OPEN_FILES
;
4642 dp
-> obj
. lockid
= 0 ; /* Root directory need not to be locked */
4649 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4651 if ( res
!= FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate the directory object if function failed */
4659 /*-----------------------------------------------------------------------*/
4660 /* Close Directory */
4661 /*-----------------------------------------------------------------------*/
4663 FRESULT
f_closedir (
4664 DIR * dp
/* Pointer to the directory object to be closed */
4671 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the file object */
4674 if ( dp
-> obj
. lockid
) res
= dec_share ( dp
-> obj
. lockid
); /* Decrement sub-directory open counter */
4675 if ( res
== FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4677 dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4680 unlock_volume ( fs
, FR_OK
); /* Unlock volume */
4689 /*-----------------------------------------------------------------------*/
4690 /* Read Directory Entries in Sequence */
4691 /*-----------------------------------------------------------------------*/
4694 DIR * dp
, /* Pointer to the open directory object */
4695 FILINFO
* fno
/* Pointer to file information to return */
4703 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the directory object */
4706 res
= dir_sdi ( dp
, 0 ); /* Rewind the directory object */
4709 res
= DIR_READ_FILE ( dp
); /* Read an item */
4710 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory */
4711 if ( res
== FR_OK
) { /* A valid entry is found */
4712 get_fileinfo ( dp
, fno
); /* Get the object information */
4713 res
= dir_next ( dp
, 0 ); /* Increment index for next */
4714 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory now */
4725 /*-----------------------------------------------------------------------*/
4726 /* Find Next File */
4727 /*-----------------------------------------------------------------------*/
4729 FRESULT
f_findnext (
4730 DIR * dp
, /* Pointer to the open directory object */
4731 FILINFO
* fno
/* Pointer to the file information structure */
4738 res
= f_readdir ( dp
, fno
); /* Get a directory item */
4739 if ( res
!= FR_OK
|| ! fno
|| ! fno
-> fname
[ 0 ]) break ; /* Terminate if any error or end of directory */
4740 if ( pattern_match ( dp
-> pat
, fno
-> fname
, 0 , FIND_RECURS
)) break ; /* Test for the file name */
4741 #if FF_USE_LFN && FF_USE_FIND == 2
4742 if ( pattern_match ( dp
-> pat
, fno
-> altname
, 0 , FIND_RECURS
)) break ; /* Test for alternative name if exist */
4750 /*-----------------------------------------------------------------------*/
4751 /* Find First File */
4752 /*-----------------------------------------------------------------------*/
4754 FRESULT
f_findfirst (
4755 DIR * dp
, /* Pointer to the blank directory object */
4756 FILINFO
* fno
, /* Pointer to the file information structure */
4757 const TCHAR
* path
, /* Pointer to the directory to open */
4758 const TCHAR
* pattern
/* Pointer to the matching pattern */
4764 dp
-> pat
= pattern
; /* Save pointer to pattern string */
4765 res
= f_opendir ( dp
, path
); /* Open the target directory */
4767 res
= f_findnext ( dp
, fno
); /* Find the first item */
4772 #endif /* FF_USE_FIND */
4776 #if FF_FS_MINIMIZE == 0
4777 /*-----------------------------------------------------------------------*/
4778 /* Get File Status */
4779 /*-----------------------------------------------------------------------*/
4782 const TCHAR
* path
, /* Pointer to the file path */
4783 FILINFO
* fno
/* Pointer to file information to return */
4791 /* Get logical drive */
4792 res
= mount_volume (& path
, & dj
. obj
. fs
, 0 );
4794 INIT_NAMBUF ( dj
. obj
. fs
);
4795 res
= follow_path (& dj
, path
); /* Follow the file path */
4796 if ( res
== FR_OK
) { /* Follow completed */
4797 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* It is origin directory */
4798 res
= FR_INVALID_NAME
;
4799 } else { /* Found an object */
4800 if ( fno
) get_fileinfo (& dj
, fno
);
4806 LEAVE_FF ( dj
. obj
. fs
, res
);
4812 /*-----------------------------------------------------------------------*/
4813 /* Get Number of Free Clusters */
4814 /*-----------------------------------------------------------------------*/
4817 const TCHAR
* path
, /* Logical drive number */
4818 DWORD
* nclst
, /* Pointer to a variable to return number of free clusters */
4819 FATFS
** fatfs
/* Pointer to return pointer to corresponding filesystem object */
4824 DWORD nfree
, clst
, stat
;
4830 /* Get logical drive */
4831 res
= mount_volume (& path
, & fs
, 0 );
4833 * fatfs
= fs
; /* Return ptr to the fs object */
4834 /* If free_clst is valid, return it without full FAT scan */
4835 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) {
4836 * nclst
= fs
-> free_clst
;
4838 /* Scan FAT to obtain number of free clusters */
4840 if ( fs
-> fs_type
== FS_FAT12
) { /* FAT12: Scan bit field FAT entries */
4841 clst
= 2 ; obj
. fs
= fs
;
4843 stat
= get_fat (& obj
, clst
);
4844 if ( stat
== 0xFFFFFFFF ) {
4845 res
= FR_DISK_ERR
; break ;
4848 res
= FR_INT_ERR
; break ;
4850 if ( stat
== 0 ) nfree
++;
4851 } while (++ clst
< fs
-> n_fatent
);
4854 if ( fs
-> fs_type
== FS_EXFAT
) { /* exFAT: Scan allocation bitmap */
4858 clst
= fs
-> n_fatent
- 2 ; /* Number of clusters */
4859 sect
= fs
-> bitbase
; /* Bitmap sector */
4860 i
= 0 ; /* Offset in the sector */
4861 do { /* Counts numbuer of bits with zero in the bitmap */
4862 if ( i
== 0 ) { /* New sector? */
4863 res
= move_window ( fs
, sect
++);
4864 if ( res
!= FR_OK
) break ;
4866 for ( b
= 8 , bm
= ~ fs
-> win
[ i
]; b
&& clst
; b
--, clst
--) {
4870 i
= ( i
+ 1 ) % SS ( fs
);
4874 { /* FAT16/32: Scan WORD/DWORD FAT entries */
4875 clst
= fs
-> n_fatent
; /* Number of entries */
4876 sect
= fs
-> fatbase
; /* Top of the FAT */
4877 i
= 0 ; /* Offset in the sector */
4878 do { /* Counts numbuer of entries with zero in the FAT */
4879 if ( i
== 0 ) { /* New sector? */
4880 res
= move_window ( fs
, sect
++);
4881 if ( res
!= FR_OK
) break ;
4883 if ( fs
-> fs_type
== FS_FAT16
) {
4884 if ( ld_word ( fs
-> win
+ i
) == 0 ) nfree
++;
4887 if (( ld_dword ( fs
-> win
+ i
) & 0x0FFFFFFF ) == 0 ) nfree
++;
4894 if ( res
== FR_OK
) { /* Update parameters if succeeded */
4895 * nclst
= nfree
; /* Return the free clusters */
4896 fs
-> free_clst
= nfree
; /* Now free_clst is valid */
4897 fs
-> fsi_flag
|= 1 ; /* FAT32: FSInfo is to be updated */
4908 /*-----------------------------------------------------------------------*/
4910 /*-----------------------------------------------------------------------*/
4912 FRESULT
f_truncate (
4913 FIL
* fp
/* Pointer to the file object */
4921 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4922 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
4923 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
4925 if ( fp
-> fptr
< fp
-> obj
. objsize
) { /* Process when fptr is not on the eof */
4926 if ( fp
-> fptr
== 0 ) { /* When set file size to zero, remove entire cluster chain */
4927 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
4929 } else { /* When truncate a part of the file, remove remaining clusters */
4930 ncl
= get_fat (& fp
-> obj
, fp
-> clust
);
4932 if ( ncl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
4933 if ( ncl
== 1 ) res
= FR_INT_ERR
;
4934 if ( res
== FR_OK
&& ncl
< fs
-> n_fatent
) {
4935 res
= remove_chain (& fp
-> obj
, ncl
, fp
-> clust
);
4938 fp
-> obj
. objsize
= fp
-> fptr
; /* Set file size to current read/write point */
4939 fp
-> flag
|= FA_MODIFIED
;
4941 if ( res
== FR_OK
&& ( fp
-> flag
& FA_DIRTY
)) {
4942 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) {
4945 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4949 if ( res
!= FR_OK
) ABORT ( fs
, res
);
4958 /*-----------------------------------------------------------------------*/
4959 /* Delete a File/Directory */
4960 /*-----------------------------------------------------------------------*/
4963 const TCHAR
* path
/* Pointer to the file or directory path */
4976 /* Get logical drive */
4977 res
= mount_volume (& path
, & fs
, FA_WRITE
);
4981 res
= follow_path (& dj
, path
); /* Follow the file path */
4982 if ( FF_FS_RPATH
&& res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) {
4983 res
= FR_INVALID_NAME
; /* Cannot remove dot entry */
4986 if ( res
== FR_OK
) res
= chk_share (& dj
, 2 ); /* Check if it is an open object */
4988 if ( res
== FR_OK
) { /* The object is accessible */
4989 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) {
4990 res
= FR_INVALID_NAME
; /* Cannot remove the origin directory */
4992 if ( dj
. obj
. attr
& AM_RDO
) {
4993 res
= FR_DENIED
; /* Cannot remove R/O object */
4999 if ( fs
-> fs_type
== FS_EXFAT
) {
5000 init_alloc_info ( fs
, & obj
);
5005 dclst
= ld_clust ( fs
, dj
. dir
);
5007 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a sub-directory? */
5008 #if FF_FS_RPATH != 0
5009 if ( dclst
== fs
-> cdir
) { /* Is it the current directory? */
5014 sdj
. obj
. fs
= fs
; /* Open the sub-directory */
5015 sdj
. obj
. sclust
= dclst
;
5017 if ( fs
-> fs_type
== FS_EXFAT
) {
5018 sdj
. obj
. objsize
= obj
. objsize
;
5019 sdj
. obj
. stat
= obj
. stat
;
5022 res
= dir_sdi (& sdj
, 0 );
5024 res
= DIR_READ_FILE (& sdj
); /* Test if the directory is empty */
5025 if ( res
== FR_OK
) res
= FR_DENIED
; /* Not empty? */
5026 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Empty? */
5032 res
= dir_remove (& dj
); /* Remove the directory entry */
5033 if ( res
== FR_OK
&& dclst
!= 0 ) { /* Remove the cluster chain if exist */
5035 res
= remove_chain (& obj
, dclst
, 0 );
5037 res
= remove_chain (& dj
. obj
, dclst
, 0 );
5040 if ( res
== FR_OK
) res
= sync_fs ( fs
);
5052 /*-----------------------------------------------------------------------*/
5053 /* Create a Directory */
5054 /*-----------------------------------------------------------------------*/
5057 const TCHAR
* path
/* Pointer to the directory path */
5068 res
= mount_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5072 res
= follow_path (& dj
, path
); /* Follow the file path */
5073 if ( res
== FR_OK
) res
= FR_EXIST
; /* Name collision? */
5074 if ( FF_FS_RPATH
&& res
== FR_NO_FILE
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) { /* Invalid name? */
5075 res
= FR_INVALID_NAME
;
5077 if ( res
== FR_NO_FILE
) { /* It is clear to create a new directory */
5078 sobj
. fs
= fs
; /* New object id to create a new chain */
5079 dcl
= create_chain (& sobj
, 0 ); /* Allocate a cluster for the new directory */
5081 if ( dcl
== 0 ) res
= FR_DENIED
; /* No space to allocate a new cluster? */
5082 if ( dcl
== 1 ) res
= FR_INT_ERR
; /* Any insanity? */
5083 if ( dcl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
; /* Disk error? */
5086 res
= dir_clear ( fs
, dcl
); /* Clean up the new table */
5088 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) { /* Create dot entries (FAT only) */
5089 memset ( fs
-> win
+ DIR_Name
, ' ' , 11 ); /* Create "." entry */
5090 fs
-> win
[ DIR_Name
] = '.' ;
5091 fs
-> win
[ DIR_Attr
] = AM_DIR
;
5092 st_dword ( fs
-> win
+ DIR_ModTime
, tm
);
5093 st_clust ( fs
, fs
-> win
, dcl
);
5094 memcpy ( fs
-> win
+ SZDIRE
, fs
-> win
, SZDIRE
); /* Create ".." entry */
5095 fs
-> win
[ SZDIRE
+ 1 ] = '.' ; pcl
= dj
. obj
. sclust
;
5096 st_clust ( fs
, fs
-> win
+ SZDIRE
, pcl
);
5099 res
= dir_register (& dj
); /* Register the object to the parent directoy */
5104 if ( fs
-> fs_type
== FS_EXFAT
) { /* Initialize directory entry block */
5105 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Created time */
5106 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, dcl
); /* Table start cluster */
5107 st_dword ( fs
-> dirbuf
+ XDIR_FileSize
, ( DWORD
) fs
-> csize
* SS ( fs
)); /* Directory size needs to be valid */
5108 st_dword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, ( DWORD
) fs
-> csize
* SS ( fs
));
5109 fs
-> dirbuf
[ XDIR_GenFlags
] = 3 ; /* Initialize the object flag */
5110 fs
-> dirbuf
[ XDIR_Attr
] = AM_DIR
; /* Attribute */
5111 res
= store_xdir (& dj
);
5115 st_dword ( dj
. dir
+ DIR_ModTime
, tm
); /* Created time */
5116 st_clust ( fs
, dj
. dir
, dcl
); /* Table start cluster */
5117 dj
. dir
[ DIR_Attr
] = AM_DIR
; /* Attribute */
5124 remove_chain (& sobj
, dcl
, 0 ); /* Could not register, remove the allocated cluster */
5136 /*-----------------------------------------------------------------------*/
5137 /* Rename a File/Directory */
5138 /*-----------------------------------------------------------------------*/
5141 const TCHAR
* path_old
, /* Pointer to the object name to be renamed */
5142 const TCHAR
* path_new
/* Pointer to the new name */
5148 BYTE buf
[ FF_FS_EXFAT
? SZDIRE
* 2 : SZDIRE
], * dir
;
5153 get_ldnumber (& path_new
); /* Snip the drive number of new name off */
5154 res
= mount_volume (& path_old
, & fs
, FA_WRITE
); /* Get logical drive of the old object */
5158 res
= follow_path (& djo
, path_old
); /* Check old object */
5159 if ( res
== FR_OK
&& ( djo
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check validity of name */
5162 res
= chk_share (& djo
, 2 );
5165 if ( res
== FR_OK
) { /* Object to be renamed is found */
5167 if ( fs
-> fs_type
== FS_EXFAT
) { /* At exFAT volume */
5171 memcpy ( buf
, fs
-> dirbuf
, SZDIRE
* 2 ); /* Save 85+C0 entry of old object */
5172 memcpy (& djn
, & djo
, sizeof djo
);
5173 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
5174 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
5175 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
5177 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
5178 res
= dir_register (& djn
); /* Register the new entry */
5180 nf
= fs
-> dirbuf
[ XDIR_NumSec
]; nn
= fs
-> dirbuf
[ XDIR_NumName
];
5181 nh
= ld_word ( fs
-> dirbuf
+ XDIR_NameHash
);
5182 memcpy ( fs
-> dirbuf
, buf
, SZDIRE
* 2 ); /* Restore 85+C0 entry */
5183 fs
-> dirbuf
[ XDIR_NumSec
] = nf
; fs
-> dirbuf
[ XDIR_NumName
] = nn
;
5184 st_word ( fs
-> dirbuf
+ XDIR_NameHash
, nh
);
5185 if (!( fs
-> dirbuf
[ XDIR_Attr
] & AM_DIR
)) fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
5186 /* Start of critical section where an interruption can cause a cross-link */
5187 res
= store_xdir (& djn
);
5192 { /* At FAT/FAT32 volume */
5193 memcpy ( buf
, djo
. dir
, SZDIRE
); /* Save directory entry of the object */
5194 memcpy (& djn
, & djo
, sizeof ( DIR )); /* Duplicate the directory object */
5195 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
5196 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
5197 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
5199 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
5200 res
= dir_register (& djn
); /* Register the new entry */
5202 dir
= djn
. dir
; /* Copy directory entry of the object except name */
5203 memcpy ( dir
+ 13 , buf
+ 13 , SZDIRE
- 13 );
5204 dir
[ DIR_Attr
] = buf
[ DIR_Attr
];
5205 if (!( dir
[ DIR_Attr
] & AM_DIR
)) dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
5207 if (( dir
[ DIR_Attr
] & AM_DIR
) && djo
. obj
. sclust
!= djn
. obj
. sclust
) { /* Update .. entry in the sub-directory if needed */
5208 sect
= clst2sect ( fs
, ld_clust ( fs
, dir
));
5212 /* Start of critical section where an interruption can cause a cross-link */
5213 res
= move_window ( fs
, sect
);
5214 dir
= fs
-> win
+ SZDIRE
* 1 ; /* Ptr to .. entry */
5215 if ( res
== FR_OK
&& dir
[ 1 ] == '.' ) {
5216 st_clust ( fs
, dir
, djn
. obj
. sclust
);
5225 res
= dir_remove (& djo
); /* Remove old entry */
5230 /* End of the critical section */
5238 #endif /* !FF_FS_READONLY */
5239 #endif /* FF_FS_MINIMIZE == 0 */
5240 #endif /* FF_FS_MINIMIZE <= 1 */
5241 #endif /* FF_FS_MINIMIZE <= 2 */
5245 #if FF_USE_CHMOD && !FF_FS_READONLY
5246 /*-----------------------------------------------------------------------*/
5247 /* Change Attribute */
5248 /*-----------------------------------------------------------------------*/
5251 const TCHAR
* path
, /* Pointer to the file path */
5252 BYTE attr
, /* Attribute bits */
5253 BYTE mask
/* Attribute mask to change */
5262 res
= mount_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5266 res
= follow_path (& dj
, path
); /* Follow the file path */
5267 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5269 mask
&= AM_RDO
| AM_HID
| AM_SYS
| AM_ARC
; /* Valid attribute mask */
5271 if ( fs
-> fs_type
== FS_EXFAT
) {
5272 fs
-> dirbuf
[ XDIR_Attr
] = ( attr
& mask
) | ( fs
-> dirbuf
[ XDIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5273 res
= store_xdir (& dj
);
5277 dj
. dir
[ DIR_Attr
] = ( attr
& mask
) | ( dj
. dir
[ DIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5293 /*-----------------------------------------------------------------------*/
5294 /* Change Timestamp */
5295 /*-----------------------------------------------------------------------*/
5298 const TCHAR
* path
, /* Pointer to the file/directory name */
5299 const FILINFO
* fno
/* Pointer to the timestamp to be set */
5308 res
= mount_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5312 res
= follow_path (& dj
, path
); /* Follow the file path */
5313 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5316 if ( fs
-> fs_type
== FS_EXFAT
) {
5317 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5318 res
= store_xdir (& dj
);
5322 st_dword ( dj
. dir
+ DIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5335 #endif /* FF_USE_CHMOD && !FF_FS_READONLY */
5340 /*-----------------------------------------------------------------------*/
5341 /* Get Volume Label */
5342 /*-----------------------------------------------------------------------*/
5344 FRESULT
f_getlabel (
5345 const TCHAR
* path
, /* Logical drive number */
5346 TCHAR
* label
, /* Buffer to store the volume label */
5347 DWORD
* vsn
/* Variable to store the volume serial number */
5356 /* Get logical drive */
5357 res
= mount_volume (& path
, & fs
, 0 );
5359 /* Get volume label */
5360 if ( res
== FR_OK
&& label
) {
5361 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5362 res
= dir_sdi (& dj
, 0 );
5364 res
= DIR_READ_LABEL (& dj
); /* Find a volume label entry */
5367 if ( fs
-> fs_type
== FS_EXFAT
) {
5371 for ( si
= di
= hs
= 0 ; si
< dj
. dir
[ XDIR_NumLabel
]; si
++) { /* Extract volume label from 83 entry */
5372 wc
= ld_word ( dj
. dir
+ XDIR_Label
+ si
* 2 );
5373 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is the code a surrogate? */
5376 nw
= put_utf (( DWORD
) hs
<< 16 | wc
, & label
[ di
], 4 ); /* Store it in API encoding */
5377 if ( nw
== 0 ) { /* Encode error? */
5383 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
5388 si
= di
= 0 ; /* Extract volume label from AM_VOL entry */
5391 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
5392 if ( dbc_1st (( BYTE
) wc
) && si
< 11 ) wc
= wc
<< 8 | dj
. dir
[ si
++]; /* Is it a DBC? */
5393 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* Convert it into Unicode */
5394 if ( wc
== 0 ) { /* Invalid char in current code page? */
5397 di
+= put_utf ( wc
, & label
[ di
], 4 ); /* Store it in Unicode */
5398 #else /* ANSI/OEM output */
5399 label
[ di
++] = ( TCHAR
) wc
;
5402 do { /* Truncate trailing spaces */
5405 } while ( label
[-- di
] == ' ' );
5409 if ( res
== FR_NO_FILE
) { /* No label entry and return nul string */
5415 /* Get volume serial number */
5416 if ( res
== FR_OK
&& vsn
) {
5417 res
= move_window ( fs
, fs
-> volbase
);
5419 switch ( fs
-> fs_type
) {
5431 * vsn
= ld_dword ( fs
-> win
+ di
);
5441 /*-----------------------------------------------------------------------*/
5442 /* Set Volume Label */
5443 /*-----------------------------------------------------------------------*/
5445 FRESULT
f_setlabel (
5446 const TCHAR
* label
/* Volume label to set with heading logical drive number */
5455 static const char badchr
[ 18 ] = "+.,;=[]" "/*:<>| \\\"\?\x7F " ; /* [0..16] for FAT, [7..16] for exFAT */
5460 /* Get logical drive */
5461 res
= mount_volume (& label
, & fs
, FA_WRITE
);
5462 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
5465 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
5466 memset ( dirvn
, 0 , 22 );
5468 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5469 dc
= tchar2uni (& label
); /* Get a Unicode character */
5470 if ( dc
>= 0x10000 ) {
5471 if ( dc
== 0xFFFFFFFF || di
>= 10 ) { /* Wrong surrogate or buffer overflow */
5474 st_word ( dirvn
+ di
* 2 , ( WCHAR
)( dc
>> 16 )); di
++;
5477 if ( dc
== 0 || strchr (& badchr
[ 7 ], ( int ) dc
) || di
>= 11 ) { /* Check validity of the volume label */
5478 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5480 st_word ( dirvn
+ di
* 2 , ( WCHAR
) dc
); di
++;
5484 { /* On the FAT/FAT32 volume */
5485 memset ( dirvn
, ' ' , 11 );
5487 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5489 dc
= tchar2uni (& label
);
5490 wc
= ( dc
< 0x10000 ) ? ff_uni2oem ( ff_wtoupper ( dc
), CODEPAGE
) : 0 ;
5491 #else /* ANSI/OEM input */
5492 wc
= ( BYTE
)* label
++;
5493 if ( dbc_1st (( BYTE
) wc
)) wc
= dbc_2nd (( BYTE
)* label
) ? wc
<< 8 | ( BYTE
)* label
++ : 0 ;
5494 if ( IsLower ( wc
)) wc
-= 0x20 ; /* To upper ASCII characters */
5495 #if FF_CODE_PAGE == 0
5496 if ( ExCvt
&& wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5497 #elif FF_CODE_PAGE < 900
5498 if ( wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5501 if ( wc
== 0 || strchr (& badchr
[ 0 ], ( int ) wc
) || di
>= ( UINT
)(( wc
>= 0x100 ) ? 10 : 11 )) { /* Reject invalid characters for volume label */
5502 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5504 if ( wc
>= 0x100 ) dirvn
[ di
++] = ( BYTE
)( wc
>> 8 );
5505 dirvn
[ di
++] = ( BYTE
) wc
;
5507 if ( dirvn
[ 0 ] == DDEM
) LEAVE_FF ( fs
, FR_INVALID_NAME
); /* Reject illegal name (heading DDEM) */
5508 while ( di
&& dirvn
[ di
- 1 ] == ' ' ) di
--; /* Snip trailing spaces */
5511 /* Set volume label */
5512 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5513 res
= dir_sdi (& dj
, 0 );
5515 res
= DIR_READ_LABEL (& dj
); /* Get volume label entry */
5517 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5518 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
; /* Change the volume label */
5519 memcpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5522 memcpy ( dj
. dir
, dirvn
, 11 ); /* Change the volume label */
5524 dj
. dir
[ DIR_Name
] = DDEM
; /* Remove the volume label */
5529 } else { /* No volume label entry or an error */
5530 if ( res
== FR_NO_FILE
) {
5532 if ( di
!= 0 ) { /* Create a volume label entry */
5533 res
= dir_alloc (& dj
, 1 ); /* Allocate an entry */
5535 memset ( dj
. dir
, 0 , SZDIRE
); /* Clean the entry */
5536 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5537 dj
. dir
[ XDIR_Type
] = ET_VLABEL
; /* Create volume label entry */
5538 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
;
5539 memcpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5541 dj
. dir
[ DIR_Attr
] = AM_VOL
; /* Create volume label entry */
5542 memcpy ( dj
. dir
, dirvn
, 11 );
5555 #endif /* !FF_FS_READONLY */
5556 #endif /* FF_USE_LABEL */
5560 #if FF_USE_EXPAND && !FF_FS_READONLY
5561 /*-----------------------------------------------------------------------*/
5562 /* Allocate a Contiguous Blocks to the File */
5563 /*-----------------------------------------------------------------------*/
5566 FIL
* fp
, /* Pointer to the file object */
5567 FSIZE_t fsz
, /* File size to be expanded to */
5568 BYTE opt
/* Operation mode 0:Find and prepare or 1:Find and allocate */
5573 DWORD n
, clst
, stcl
, scl
, ncl
, tcl
, lclst
;
5576 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5577 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5578 if ( fsz
== 0 || fp
-> obj
. objsize
!= 0 || !( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
);
5580 if ( fs
-> fs_type
!= FS_EXFAT
&& fsz
>= 0x100000000 ) LEAVE_FF ( fs
, FR_DENIED
); /* Check if in size limit */
5582 n
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size */
5583 tcl
= ( DWORD
)( fsz
/ n
) + (( fsz
& ( n
- 1 )) ? 1 : 0 ); /* Number of clusters required */
5584 stcl
= fs
-> last_clst
; lclst
= 0 ;
5585 if ( stcl
< 2 || stcl
>= fs
-> n_fatent
) stcl
= 2 ;
5588 if ( fs
-> fs_type
== FS_EXFAT
) {
5589 scl
= find_bitmap ( fs
, stcl
, tcl
); /* Find a contiguous cluster block */
5590 if ( scl
== 0 ) res
= FR_DENIED
; /* No contiguous cluster block was found */
5591 if ( scl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
5592 if ( res
== FR_OK
) { /* A contiguous free area is found */
5593 if ( opt
) { /* Allocate it now */
5594 res
= change_bitmap ( fs
, scl
, tcl
, 1 ); /* Mark the cluster block 'in use' */
5595 lclst
= scl
+ tcl
- 1 ;
5596 } else { /* Set it as suggested point for next allocation */
5603 scl
= clst
= stcl
; ncl
= 0 ;
5604 for (;;) { /* Find a contiguous cluster block */
5605 n
= get_fat (& fp
-> obj
, clst
);
5606 if (++ clst
>= fs
-> n_fatent
) clst
= 2 ;
5608 res
= FR_INT_ERR
; break ;
5610 if ( n
== 0xFFFFFFFF ) {
5611 res
= FR_DISK_ERR
; break ;
5613 if ( n
== 0 ) { /* Is it a free cluster? */
5614 if (++ ncl
== tcl
) break ; /* Break if a contiguous cluster block is found */
5616 scl
= clst
; ncl
= 0 ; /* Not a free cluster */
5618 if ( clst
== stcl
) { /* No contiguous cluster? */
5619 res
= FR_DENIED
; break ;
5622 if ( res
== FR_OK
) { /* A contiguous free area is found */
5623 if ( opt
) { /* Allocate it now */
5624 for ( clst
= scl
, n
= tcl
; n
; clst
++, n
--) { /* Create a cluster chain on the FAT */
5625 res
= put_fat ( fs
, clst
, ( n
== 1 ) ? 0xFFFFFFFF : clst
+ 1 );
5626 if ( res
!= FR_OK
) break ;
5629 } else { /* Set it as suggested point for next allocation */
5636 fs
-> last_clst
= lclst
; /* Set suggested start cluster to start next */
5637 if ( opt
) { /* Is it allocated now? */
5638 fp
-> obj
. sclust
= scl
; /* Update object allocation information */
5639 fp
-> obj
. objsize
= fsz
;
5640 if ( FF_FS_EXFAT
) fp
-> obj
. stat
= 2 ; /* Set status 'contiguous chain' */
5641 fp
-> flag
|= FA_MODIFIED
;
5642 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) { /* Update FSINFO */
5643 fs
-> free_clst
-= tcl
;
5652 #endif /* FF_USE_EXPAND && !FF_FS_READONLY */
5657 /*-----------------------------------------------------------------------*/
5658 /* Forward Data to the Stream Directly */
5659 /*-----------------------------------------------------------------------*/
5662 FIL
* fp
, /* Pointer to the file object */
5663 UINT (* func
)( const BYTE
*, UINT
), /* Pointer to the streaming function */
5664 UINT btf
, /* Number of bytes to forward */
5665 UINT
* bf
/* Pointer to number of bytes forwarded */
5677 * bf
= 0 ; /* Clear transfer byte counter */
5678 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5679 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5680 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
5682 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
5683 if ( btf
> remain
) btf
= ( UINT
) remain
; /* Truncate btf by remaining bytes */
5685 for ( ; btf
> 0 && (* func
)( 0 , 0 ); fp
-> fptr
+= rcnt
, * bf
+= rcnt
, btf
-= rcnt
) { /* Repeat until all data transferred or stream goes busy */
5686 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
5687 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
5688 if ( csect
== 0 ) { /* On the cluster boundary? */
5689 clst
= ( fp
-> fptr
== 0 ) ? /* On the top of the file? */
5690 fp
-> obj
. sclust
: get_fat (& fp
-> obj
, fp
-> clust
);
5691 if ( clst
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
5692 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
5693 fp
-> clust
= clst
; /* Update current cluster */
5696 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current data sector */
5697 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5700 if ( move_window ( fs
, sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window to the file data */
5703 if ( fp
-> sect
!= sect
) { /* Fill sector cache with file data */
5705 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
5706 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5707 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
5710 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5715 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes remains in the sector */
5716 if ( rcnt
> btf
) rcnt
= btf
; /* Clip it by btr if needed */
5717 rcnt
= (* func
)( dbuf
+ (( UINT
) fp
-> fptr
% SS ( fs
)), rcnt
); /* Forward the file data */
5718 if ( rcnt
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5721 LEAVE_FF ( fs
, FR_OK
);
5723 #endif /* FF_USE_FORWARD */
5727 #if !FF_FS_READONLY && FF_USE_MKFS
5728 /*-----------------------------------------------------------------------*/
5729 /* Create FAT/exFAT volume (with sub-functions) */
5730 /*-----------------------------------------------------------------------*/
5732 #define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
5733 #define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT [byte] (>=128KB) */
5734 #define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */
5737 /* Create partitions on the physical drive in format of MBR or GPT */
5739 static FRESULT
create_partition (
5740 BYTE drv
, /* Physical drive number */
5741 const LBA_t plst
[], /* Partition list */
5742 BYTE sys
, /* System ID for each partition (for only MBR) */
5743 BYTE
* buf
/* Working buffer for a sector */
5748 DWORD sz_drv32
, nxt_alloc32
, sz_part32
;
5750 BYTE hd
, n_hd
, sc
, n_sc
;
5752 /* Get physical drive size */
5753 if ( disk_ioctl ( drv
, GET_SECTOR_COUNT
, & sz_drv
) != RES_OK
) return FR_DISK_ERR
;
5756 if ( sz_drv
>= FF_MIN_GPT
) { /* Create partitions in GPT format */
5758 UINT sz_ptbl
, pi
, si
, ofs
;
5759 DWORD bcc
, rnd
, align
;
5760 QWORD nxt_alloc
, sz_part
, sz_pool
, top_bpt
;
5761 static const BYTE gpt_mbr
[ 16 ] = { 0x00 , 0x00 , 0x02 , 0x00 , 0xEE , 0xFE , 0xFF , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0xFF , 0xFF , 0xFF , 0xFF };
5763 #if FF_MAX_SS != FF_MIN_SS
5764 if ( disk_ioctl ( drv
, GET_SECTOR_SIZE
, & ss
) != RES_OK
) return FR_DISK_ERR
; /* Get sector size */
5765 if ( ss
> FF_MAX_SS
|| ss
< FF_MIN_SS
|| ( ss
& ( ss
- 1 ))) return FR_DISK_ERR
;
5769 rnd
= ( DWORD
) sz_drv
+ GET_FATTIME (); /* Random seed */
5770 align
= GPT_ALIGN
/ ss
; /* Partition alignment for GPT [sector] */
5771 sz_ptbl
= GPT_ITEMS
* SZ_GPTE
/ ss
; /* Size of partition table [sector] */
5772 top_bpt
= sz_drv
- sz_ptbl
- 1 ; /* Backup partition table start sector */
5773 nxt_alloc
= 2 + sz_ptbl
; /* First allocatable sector */
5774 sz_pool
= top_bpt
- nxt_alloc
; /* Size of allocatable area */
5775 bcc
= 0xFFFFFFFF ; sz_part
= 1 ;
5776 pi
= si
= 0 ; /* partition table index, size table index */
5778 if ( pi
* SZ_GPTE
% ss
== 0 ) memset ( buf
, 0 , ss
); /* Clean the buffer if needed */
5779 if ( sz_part
!= 0 ) { /* Is the size table not termintated? */
5780 nxt_alloc
= ( nxt_alloc
+ align
- 1 ) & (( QWORD
) 0 - align
); /* Align partition start */
5781 sz_part
= plst
[ si
++]; /* Get a partition size */
5782 if ( sz_part
<= 100 ) { /* Is the size in percentage? */
5783 sz_part
= sz_pool
* sz_part
/ 100 ;
5784 sz_part
= ( sz_part
+ align
- 1 ) & (( QWORD
) 0 - align
); /* Align partition end (only if in percentage) */
5786 if ( nxt_alloc
+ sz_part
> top_bpt
) { /* Clip the size at end of the pool */
5787 sz_part
= ( nxt_alloc
< top_bpt
) ? top_bpt
- nxt_alloc
: 0 ;
5790 if ( sz_part
!= 0 ) { /* Add a partition? */
5791 ofs
= pi
* SZ_GPTE
% ss
;
5792 memcpy ( buf
+ ofs
+ GPTE_PtGuid
, GUID_MS_Basic
, 16 ); /* Set partition GUID (Microsoft Basic Data) */
5793 rnd
= make_rand ( rnd
, buf
+ ofs
+ GPTE_UpGuid
, 16 ); /* Set unique partition GUID */
5794 st_qword ( buf
+ ofs
+ GPTE_FstLba
, nxt_alloc
); /* Set partition start sector */
5795 st_qword ( buf
+ ofs
+ GPTE_LstLba
, nxt_alloc
+ sz_part
- 1 ); /* Set partition end sector */
5796 nxt_alloc
+= sz_part
; /* Next allocatable sector */
5798 if (( pi
+ 1 ) * SZ_GPTE
% ss
== 0 ) { /* Write the buffer if it is filled up */
5799 for ( i
= 0 ; i
< ss
; bcc
= crc32 ( bcc
, buf
[ i
++])) ; /* Calculate table check sum */
5800 if ( disk_write ( drv
, buf
, 2 + pi
* SZ_GPTE
/ ss
, 1 ) != RES_OK
) return FR_DISK_ERR
; /* Write to primary table */
5801 if ( disk_write ( drv
, buf
, top_bpt
+ pi
* SZ_GPTE
/ ss
, 1 ) != RES_OK
) return FR_DISK_ERR
; /* Write to secondary table */
5803 } while (++ pi
< GPT_ITEMS
);
5805 /* Create primary GPT header */
5807 memcpy ( buf
+ GPTH_Sign
, "EFI PART" "\0\0\1\0" " \x5C \0\0" , 16 ); /* Signature, version (1.0) and size (92) */
5808 st_dword ( buf
+ GPTH_PtBcc
, ~ bcc
); /* Table check sum */
5809 st_qword ( buf
+ GPTH_CurLba
, 1 ); /* LBA of this header */
5810 st_qword ( buf
+ GPTH_BakLba
, sz_drv
- 1 ); /* LBA of secondary header */
5811 st_qword ( buf
+ GPTH_FstLba
, 2 + sz_ptbl
); /* LBA of first allocatable sector */
5812 st_qword ( buf
+ GPTH_LstLba
, top_bpt
- 1 ); /* LBA of last allocatable sector */
5813 st_dword ( buf
+ GPTH_PteSize
, SZ_GPTE
); /* Size of a table entry */
5814 st_dword ( buf
+ GPTH_PtNum
, GPT_ITEMS
); /* Number of table entries */
5815 st_dword ( buf
+ GPTH_PtOfs
, 2 ); /* LBA of this table */
5816 rnd
= make_rand ( rnd
, buf
+ GPTH_DskGuid
, 16 ); /* Disk GUID */
5817 for ( i
= 0 , bcc
= 0xFFFFFFFF ; i
< 92 ; bcc
= crc32 ( bcc
, buf
[ i
++])) ; /* Calculate header check sum */
5818 st_dword ( buf
+ GPTH_Bcc
, ~ bcc
); /* Header check sum */
5819 if ( disk_write ( drv
, buf
, 1 , 1 ) != RES_OK
) return FR_DISK_ERR
;
5821 /* Create secondary GPT header */
5822 st_qword ( buf
+ GPTH_CurLba
, sz_drv
- 1 ); /* LBA of this header */
5823 st_qword ( buf
+ GPTH_BakLba
, 1 ); /* LBA of primary header */
5824 st_qword ( buf
+ GPTH_PtOfs
, top_bpt
); /* LBA of this table */
5825 st_dword ( buf
+ GPTH_Bcc
, 0 );
5826 for ( i
= 0 , bcc
= 0xFFFFFFFF ; i
< 92 ; bcc
= crc32 ( bcc
, buf
[ i
++])) ; /* Calculate header check sum */
5827 st_dword ( buf
+ GPTH_Bcc
, ~ bcc
); /* Header check sum */
5828 if ( disk_write ( drv
, buf
, sz_drv
- 1 , 1 ) != RES_OK
) return FR_DISK_ERR
;
5830 /* Create protective MBR */
5832 memcpy ( buf
+ MBR_Table
, gpt_mbr
, 16 ); /* Create a GPT partition */
5833 st_word ( buf
+ BS_55AA
, 0xAA55 );
5834 if ( disk_write ( drv
, buf
, 0 , 1 ) != RES_OK
) return FR_DISK_ERR
;
5838 { /* Create partitions in MBR format */
5839 sz_drv32
= ( DWORD
) sz_drv
;
5840 n_sc
= N_SEC_TRACK
; /* Determine drive CHS without any consideration of the drive geometry */
5841 for ( n_hd
= 8 ; n_hd
!= 0 && sz_drv32
/ n_hd
/ n_sc
> 1024 ; n_hd
*= 2 ) ;
5842 if ( n_hd
== 0 ) n_hd
= 255 ; /* Number of heads needs to be <256 */
5844 memset ( buf
, 0 , FF_MAX_SS
); /* Clear MBR */
5845 pte
= buf
+ MBR_Table
; /* Partition table in the MBR */
5846 for ( i
= 0 , nxt_alloc32
= n_sc
; i
< 4 && nxt_alloc32
!= 0 && nxt_alloc32
< sz_drv32
; i
++, nxt_alloc32
+= sz_part32
) {
5847 sz_part32
= ( DWORD
) plst
[ i
]; /* Get partition size */
5848 if ( sz_part32
<= 100 ) sz_part32
= ( sz_part32
== 100 ) ? sz_drv32
: sz_drv32
/ 100 * sz_part32
; /* Size in percentage? */
5849 if ( nxt_alloc32
+ sz_part32
> sz_drv32
|| nxt_alloc32
+ sz_part32
< nxt_alloc32
) sz_part32
= sz_drv32
- nxt_alloc32
; /* Clip at drive size */
5850 if ( sz_part32
== 0 ) break ; /* End of table or no sector to allocate? */
5852 st_dword ( pte
+ PTE_StLba
, nxt_alloc32
); /* Start LBA */
5853 st_dword ( pte
+ PTE_SizLba
, sz_part32
); /* Number of sectors */
5854 pte
[ PTE_System
] = sys
; /* System type */
5856 cy
= ( UINT
)( nxt_alloc32
/ n_sc
/ n_hd
); /* Start cylinder */
5857 hd
= ( BYTE
)( nxt_alloc32
/ n_sc
% n_hd
); /* Start head */
5858 sc
= ( BYTE
)( nxt_alloc32
% n_sc
+ 1 ); /* Start sector */
5859 pte
[ PTE_StHead
] = hd
;
5860 pte
[ PTE_StSec
] = ( BYTE
)(( cy
>> 2 & 0xC0 ) | sc
);
5861 pte
[ PTE_StCyl
] = ( BYTE
) cy
;
5863 cy
= ( UINT
)(( nxt_alloc32
+ sz_part32
- 1 ) / n_sc
/ n_hd
); /* End cylinder */
5864 hd
= ( BYTE
)(( nxt_alloc32
+ sz_part32
- 1 ) / n_sc
% n_hd
); /* End head */
5865 sc
= ( BYTE
)(( nxt_alloc32
+ sz_part32
- 1 ) % n_sc
+ 1 ); /* End sector */
5866 pte
[ PTE_EdHead
] = hd
;
5867 pte
[ PTE_EdSec
] = ( BYTE
)(( cy
>> 2 & 0xC0 ) | sc
);
5868 pte
[ PTE_EdCyl
] = ( BYTE
) cy
;
5870 pte
+= SZ_PTE
; /* Next entry */
5873 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* MBR signature */
5874 if ( disk_write ( drv
, buf
, 0 , 1 ) != RES_OK
) return FR_DISK_ERR
; /* Write it to the MBR */
5883 const TCHAR
* path
, /* Logical drive number */
5884 const MKFS_PARM
* opt
, /* Format options */
5885 void * work
, /* Pointer to working buffer (null: use len bytes of heap memory) */
5886 UINT len
/* Size of working buffer [byte] */
5889 static const WORD cst
[] = { 1 , 4 , 16 , 64 , 256 , 512 , 0 }; /* Cluster size boundary for FAT volume (4Ks unit) */
5890 static const WORD cst32
[] = { 1 , 2 , 4 , 8 , 16 , 32 , 0 }; /* Cluster size boundary for FAT32 volume (128Ks unit) */
5891 static const MKFS_PARM defopt
= { FM_ANY
, 0 , 0 , 0 , 0 }; /* Default parameter */
5892 BYTE fsopt
, fsty
, sys
, pdrv
, ipart
;
5895 WORD ss
; /* Sector size */
5896 DWORD sz_buf
, sz_blk
, n_clst
, pau
, nsect
, n
, vsn
;
5897 LBA_t sz_vol
, b_vol
, b_fat
, b_data
; /* Size of volume, Base LBA of volume, fat, data */
5899 DWORD sz_rsv
, sz_fat
, sz_dir
, sz_au
; /* Size of reserved, fat, dir, data, cluster */
5900 UINT n_fat
, n_root
, i
; /* Index, Number of FATs and Number of roor dir entries */
5906 /* Check mounted drive and clear work area */
5907 vol
= get_ldnumber (& path
); /* Get target logical drive */
5908 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
5909 if ( FatFs
[ vol
]) FatFs
[ vol
]-> fs_type
= 0 ; /* Clear the fs object if mounted */
5910 pdrv
= LD2PD ( vol
); /* Hosting physical drive */
5911 ipart
= LD2PT ( vol
); /* Hosting partition (0:create as new, 1..:existing partition) */
5913 /* Initialize the hosting physical drive */
5914 ds
= disk_initialize ( pdrv
);
5915 if ( ds
& STA_NOINIT
) return FR_NOT_READY
;
5916 if ( ds
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
5918 /* Get physical drive parameters (sz_drv, sz_blk and ss) */
5919 if (! opt
) opt
= & defopt
; /* Use default parameter if it is not given */
5920 sz_blk
= opt
-> align
;
5921 if ( sz_blk
== 0 ) disk_ioctl ( pdrv
, GET_BLOCK_SIZE
, & sz_blk
); /* Block size from the paramter or lower layer */
5922 if ( sz_blk
== 0 || sz_blk
> 0x8000 || ( sz_blk
& ( sz_blk
- 1 ))) sz_blk
= 1 ; /* Use default if the block size is invalid */
5923 #if FF_MAX_SS != FF_MIN_SS
5924 if ( disk_ioctl ( pdrv
, GET_SECTOR_SIZE
, & ss
) != RES_OK
) return FR_DISK_ERR
;
5925 if ( ss
> FF_MAX_SS
|| ss
< FF_MIN_SS
|| ( ss
& ( ss
- 1 ))) return FR_DISK_ERR
;
5930 /* Options for FAT sub-type and FAT parameters */
5931 fsopt
= opt
-> fmt
& ( FM_ANY
| FM_SFD
);
5932 n_fat
= ( opt
-> n_fat
>= 1 && opt
-> n_fat
<= 2 ) ? opt
-> n_fat
: 1 ;
5933 n_root
= ( opt
-> n_root
>= 1 && opt
-> n_root
<= 32768 && ( opt
-> n_root
% ( ss
/ SZDIRE
)) == 0 ) ? opt
-> n_root
: 512 ;
5934 sz_au
= ( opt
-> au_size
<= 0x1000000 && ( opt
-> au_size
& ( opt
-> au_size
- 1 )) == 0 ) ? opt
-> au_size
: 0 ;
5935 sz_au
/= ss
; /* Byte --> Sector */
5937 /* Get working buffer */
5938 sz_buf
= len
/ ss
; /* Size of working buffer [sector] */
5939 if ( sz_buf
== 0 ) return FR_NOT_ENOUGH_CORE
;
5940 buf
= ( BYTE
*) work
; /* Working buffer */
5942 if (! buf
) buf
= ff_memalloc ( sz_buf
* ss
); /* Use heap memory for working buffer */
5944 if (! buf
) return FR_NOT_ENOUGH_CORE
;
5946 /* Determine where the volume to be located (b_vol, sz_vol) */
5948 if ( FF_MULTI_PARTITION
&& ipart
!= 0 ) { /* Is the volume associated with any specific partition? */
5949 /* Get partition location from the existing partition table */
5950 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Load MBR */
5951 if ( ld_word ( buf
+ BS_55AA
) != 0xAA55 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if MBR is valid */
5953 if ( buf
[ MBR_Table
+ PTE_System
] == 0xEE ) { /* GPT protective MBR? */
5957 /* Get the partition location from GPT */
5958 if ( disk_read ( pdrv
, buf
, 1 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Load GPT header sector (next to MBR) */
5959 if (! test_gpt_header ( buf
)) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if GPT header is valid */
5960 n_ent
= ld_dword ( buf
+ GPTH_PtNum
); /* Number of entries */
5961 pt_lba
= ld_qword ( buf
+ GPTH_PtOfs
); /* Table start sector */
5963 while ( n_ent
) { /* Find MS Basic partition with order of ipart */
5964 if ( ofs
== 0 && disk_read ( pdrv
, buf
, pt_lba
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Get PT sector */
5965 if (! memcmp ( buf
+ ofs
+ GPTE_PtGuid
, GUID_MS_Basic
, 16 ) && ++ i
== ipart
) { /* MS basic data partition? */
5966 b_vol
= ld_qword ( buf
+ ofs
+ GPTE_FstLba
);
5967 sz_vol
= ld_qword ( buf
+ ofs
+ GPTE_LstLba
) - b_vol
+ 1 ;
5970 n_ent
--; ofs
= ( ofs
+ SZ_GPTE
) % ss
; /* Next entry */
5972 if ( n_ent
== 0 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Partition not found */
5973 fsopt
|= 0x80 ; /* Partitioning is in GPT */
5976 { /* Get the partition location from MBR partition table */
5977 pte
= buf
+ ( MBR_Table
+ ( ipart
- 1 ) * SZ_PTE
);
5978 if ( ipart
> 4 || pte
[ PTE_System
] == 0 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* No partition? */
5979 b_vol
= ld_dword ( pte
+ PTE_StLba
); /* Get volume start sector */
5980 sz_vol
= ld_dword ( pte
+ PTE_SizLba
); /* Get volume size */
5982 } else { /* The volume is associated with a physical drive */
5983 if ( disk_ioctl ( pdrv
, GET_SECTOR_COUNT
, & sz_vol
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5984 if (!( fsopt
& FM_SFD
)) { /* To be partitioned? */
5985 /* Create a single-partition on the drive in this function */
5987 if ( sz_vol
>= FF_MIN_GPT
) { /* Which partition type to create, MBR or GPT? */
5988 fsopt
|= 0x80 ; /* Partitioning is in GPT */
5989 b_vol
= GPT_ALIGN
/ ss
; sz_vol
-= b_vol
+ GPT_ITEMS
* SZ_GPTE
/ ss
+ 1 ; /* Estimated partition offset and size */
5992 { /* Partitioning is in MBR */
5993 if ( sz_vol
> N_SEC_TRACK
) {
5994 b_vol
= N_SEC_TRACK
; sz_vol
-= b_vol
; /* Estimated partition offset and size */
5999 if ( sz_vol
< 128 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if volume size is >=128s */
6001 /* Now start to create an FAT volume at b_vol and sz_vol */
6003 do { /* Pre-determine the FAT type */
6004 if ( FF_FS_EXFAT
&& ( fsopt
& FM_EXFAT
)) { /* exFAT possible? */
6005 if (( fsopt
& FM_ANY
) == FM_EXFAT
|| sz_vol
>= 0x4000000 || sz_au
> 128 ) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */
6006 fsty
= FS_EXFAT
; break ;
6010 if ( sz_vol
>= 0x100000000 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too large volume for FAT/FAT32 */
6012 if ( sz_au
> 128 ) sz_au
= 128 ; /* Invalid AU for FAT/FAT32? */
6013 if ( fsopt
& FM_FAT32
) { /* FAT32 possible? */
6014 if (!( fsopt
& FM_FAT
)) { /* no-FAT? */
6015 fsty
= FS_FAT32
; break ;
6018 if (!( fsopt
& FM_FAT
)) LEAVE_MKFS ( FR_INVALID_PARAMETER
); /* no-FAT? */
6022 vsn
= ( DWORD
) sz_vol
+ GET_FATTIME (); /* VSN generated from current time and partitiion size */
6025 if ( fsty
== FS_EXFAT
) { /* Create an exFAT volume */
6026 DWORD szb_bit
, szb_case
, sum
, nbit
, clu
, clen
[ 3 ];
6030 if ( sz_vol
< 0x1000 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume for exFAT? */
6032 lba
[ 0 ] = b_vol
; lba
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform storage device that the volume area may be erased */
6033 disk_ioctl ( pdrv
, CTRL_TRIM
, lba
);
6035 /* Determine FAT location, data location and number of clusters */
6036 if ( sz_au
== 0 ) { /* AU auto-selection */
6038 if ( sz_vol
>= 0x80000 ) sz_au
= 64 ; /* >= 512Ks */
6039 if ( sz_vol
>= 0x4000000 ) sz_au
= 256 ; /* >= 64Ms */
6041 b_fat
= b_vol
+ 32 ; /* FAT start at offset 32 */
6042 sz_fat
= ( DWORD
)(( sz_vol
/ sz_au
+ 2 ) * 4 + ss
- 1 ) / ss
; /* Number of FAT sectors */
6043 b_data
= ( b_fat
+ sz_fat
+ sz_blk
- 1 ) & ~(( LBA_t
) sz_blk
- 1 ); /* Align data area to the erase block boundary */
6044 if ( b_data
- b_vol
>= sz_vol
/ 2 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
6045 n_clst
= ( DWORD
)(( sz_vol
- ( b_data
- b_vol
)) / sz_au
); /* Number of clusters */
6046 if ( n_clst
< 16 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too few clusters? */
6047 if ( n_clst
> MAX_EXFAT
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters? */
6049 szb_bit
= ( n_clst
+ 7 ) / 8 ; /* Size of allocation bitmap */
6050 clen
[ 0 ] = ( szb_bit
+ sz_au
* ss
- 1 ) / ( sz_au
* ss
); /* Number of allocation bitmap clusters */
6052 /* Create a compressed up-case table */
6053 sect
= b_data
+ sz_au
* clen
[ 0 ]; /* Table start sector */
6054 sum
= 0 ; /* Table checksum to be stored in the 82 entry */
6055 st
= 0 ; si
= 0 ; i
= 0 ; j
= 0 ; szb_case
= 0 ;
6059 ch
= ( WCHAR
) ff_wtoupper ( si
); /* Get an up-case char */
6061 si
++; break ; /* Store the up-case char if exist */
6063 for ( j
= 1 ; ( WCHAR
)( si
+ j
) && ( WCHAR
)( si
+ j
) == ff_wtoupper (( WCHAR
)( si
+ j
)); j
++) ; /* Get run length of no-case block */
6065 ch
= 0xFFFF ; st
= 2 ; break ; /* Compress the no-case block if run is >= 128 chars */
6067 st
= 1 ; /* Do not compress short run */
6070 ch
= si
++; /* Fill the short run */
6071 if (-- j
== 0 ) st
= 0 ;
6075 ch
= ( WCHAR
) j
; si
+= ( WCHAR
) j
; /* Number of chars to skip */
6078 sum
= xsum32 ( buf
[ i
+ 0 ] = ( BYTE
) ch
, sum
); /* Put it into the write buffer */
6079 sum
= xsum32 ( buf
[ i
+ 1 ] = ( BYTE
)( ch
>> 8 ), sum
);
6080 i
+= 2 ; szb_case
+= 2 ;
6081 if ( si
== 0 || i
== sz_buf
* ss
) { /* Write buffered data when buffer full or end of process */
6082 n
= ( i
+ ss
- 1 ) / ss
;
6083 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6087 clen
[ 1 ] = ( szb_case
+ sz_au
* ss
- 1 ) / ( sz_au
* ss
); /* Number of up-case table clusters */
6088 clen
[ 2 ] = 1 ; /* Number of root dir clusters */
6090 /* Initialize the allocation bitmap */
6091 sect
= b_data
; nsect
= ( szb_bit
+ ss
- 1 ) / ss
; /* Start of bitmap and number of bitmap sectors */
6092 nbit
= clen
[ 0 ] + clen
[ 1 ] + clen
[ 2 ]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */
6094 memset ( buf
, 0 , sz_buf
* ss
); /* Initialize bitmap buffer */
6095 for ( i
= 0 ; nbit
!= 0 && i
/ 8 < sz_buf
* ss
; buf
[ i
/ 8 ] |= 1 << ( i
% 8 ), i
++, nbit
--) ; /* Mark used clusters */
6096 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
6097 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6098 sect
+= n
; nsect
-= n
;
6101 /* Initialize the FAT */
6102 sect
= b_fat
; nsect
= sz_fat
; /* Start of FAT and number of FAT sectors */
6105 memset ( buf
, 0 , sz_buf
* ss
); i
= 0 ; /* Clear work area and reset write offset */
6106 if ( clu
== 0 ) { /* Initialize FAT [0] and FAT[1] */
6107 st_dword ( buf
+ i
, 0xFFFFFFF8 ); i
+= 4 ; clu
++;
6108 st_dword ( buf
+ i
, 0xFFFFFFFF ); i
+= 4 ; clu
++;
6110 do { /* Create chains of bitmap, up-case and root dir */
6111 while ( nbit
!= 0 && i
< sz_buf
* ss
) { /* Create a chain */
6112 st_dword ( buf
+ i
, ( nbit
> 1 ) ? clu
+ 1 : 0xFFFFFFFF );
6113 i
+= 4 ; clu
++; nbit
--;
6115 if ( nbit
== 0 && j
< 3 ) nbit
= clen
[ j
++]; /* Get next chain length */
6116 } while ( nbit
!= 0 && i
< sz_buf
* ss
);
6117 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
6118 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6119 sect
+= n
; nsect
-= n
;
6122 /* Initialize the root directory */
6123 memset ( buf
, 0 , sz_buf
* ss
);
6124 buf
[ SZDIRE
* 0 + 0 ] = ET_VLABEL
; /* Volume label entry (no label) */
6125 buf
[ SZDIRE
* 1 + 0 ] = ET_BITMAP
; /* Bitmap entry */
6126 st_dword ( buf
+ SZDIRE
* 1 + 20 , 2 ); /* cluster */
6127 st_dword ( buf
+ SZDIRE
* 1 + 24 , szb_bit
); /* size */
6128 buf
[ SZDIRE
* 2 + 0 ] = ET_UPCASE
; /* Up-case table entry */
6129 st_dword ( buf
+ SZDIRE
* 2 + 4 , sum
); /* sum */
6130 st_dword ( buf
+ SZDIRE
* 2 + 20 , 2 + clen
[ 0 ]); /* cluster */
6131 st_dword ( buf
+ SZDIRE
* 2 + 24 , szb_case
); /* size */
6132 sect
= b_data
+ sz_au
* ( clen
[ 0 ] + clen
[ 1 ]); nsect
= sz_au
; /* Start of the root directory and number of sectors */
6133 do { /* Fill root directory sectors */
6134 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
6135 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6136 memset ( buf
, 0 , ss
); /* Rest of entries are filled with zero */
6137 sect
+= n
; nsect
-= n
;
6140 /* Create two set of the exFAT VBR blocks */
6142 for ( n
= 0 ; n
< 2 ; n
++) {
6143 /* Main record (+0) */
6145 memcpy ( buf
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 ); /* Boot jump code (x86), OEM name */
6146 st_qword ( buf
+ BPB_VolOfsEx
, b_vol
); /* Volume offset in the physical drive [sector] */
6147 st_qword ( buf
+ BPB_TotSecEx
, sz_vol
); /* Volume size [sector] */
6148 st_dword ( buf
+ BPB_FatOfsEx
, ( DWORD
)( b_fat
- b_vol
)); /* FAT offset [sector] */
6149 st_dword ( buf
+ BPB_FatSzEx
, sz_fat
); /* FAT size [sector] */
6150 st_dword ( buf
+ BPB_DataOfsEx
, ( DWORD
)( b_data
- b_vol
)); /* Data offset [sector] */
6151 st_dword ( buf
+ BPB_NumClusEx
, n_clst
); /* Number of clusters */
6152 st_dword ( buf
+ BPB_RootClusEx
, 2 + clen
[ 0 ] + clen
[ 1 ]); /* Root dir cluster # */
6153 st_dword ( buf
+ BPB_VolIDEx
, vsn
); /* VSN */
6154 st_word ( buf
+ BPB_FSVerEx
, 0x100 ); /* Filesystem version (1.00) */
6155 for ( buf
[ BPB_BytsPerSecEx
] = 0 , i
= ss
; i
>>= 1 ; buf
[ BPB_BytsPerSecEx
]++) ; /* Log2 of sector size [byte] */
6156 for ( buf
[ BPB_SecPerClusEx
] = 0 , i
= sz_au
; i
>>= 1 ; buf
[ BPB_SecPerClusEx
]++) ; /* Log2 of cluster size [sector] */
6157 buf
[ BPB_NumFATsEx
] = 1 ; /* Number of FATs */
6158 buf
[ BPB_DrvNumEx
] = 0x80 ; /* Drive number (for int13) */
6159 st_word ( buf
+ BS_BootCodeEx
, 0xFEEB ); /* Boot code (x86) */
6160 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (placed here regardless of sector size) */
6161 for ( i
= sum
= 0 ; i
< ss
; i
++) { /* VBR checksum */
6162 if ( i
!= BPB_VolFlagEx
&& i
!= BPB_VolFlagEx
+ 1 && i
!= BPB_PercInUseEx
) sum
= xsum32 ( buf
[ i
], sum
);
6164 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6165 /* Extended bootstrap record (+1..+8) */
6167 st_word ( buf
+ ss
- 2 , 0xAA55 ); /* Signature (placed at end of sector) */
6168 for ( j
= 1 ; j
< 9 ; j
++) {
6169 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
6170 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6172 /* OEM/Reserved record (+9..+10) */
6174 for ( ; j
< 11 ; j
++) {
6175 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
6176 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6178 /* Sum record (+11) */
6179 for ( i
= 0 ; i
< ss
; i
+= 4 ) st_dword ( buf
+ i
, sum
); /* Fill with checksum value */
6180 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6184 #endif /* FF_FS_EXFAT */
6185 { /* Create an FAT/FAT32 volume */
6188 /* Pre-determine number of clusters and FAT sub-type */
6189 if ( fsty
== FS_FAT32
) { /* FAT32 volume */
6190 if ( pau
== 0 ) { /* AU auto-selection */
6191 n
= ( DWORD
) sz_vol
/ 0x20000 ; /* Volume size in unit of 128KS */
6192 for ( i
= 0 , pau
= 1 ; cst32
[ i
] && cst32
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
6194 n_clst
= ( DWORD
) sz_vol
/ pau
; /* Number of clusters */
6195 sz_fat
= ( n_clst
* 4 + 8 + ss
- 1 ) / ss
; /* FAT size [sector] */
6196 sz_rsv
= 32 ; /* Number of reserved sectors */
6197 sz_dir
= 0 ; /* No static directory */
6198 if ( n_clst
<= MAX_FAT16
|| n_clst
> MAX_FAT32
) LEAVE_MKFS ( FR_MKFS_ABORTED
);
6199 } else { /* FAT volume */
6200 if ( pau
== 0 ) { /* au auto-selection */
6201 n
= ( DWORD
) sz_vol
/ 0x1000 ; /* Volume size in unit of 4KS */
6202 for ( i
= 0 , pau
= 1 ; cst
[ i
] && cst
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
6204 n_clst
= ( DWORD
) sz_vol
/ pau
;
6205 if ( n_clst
> MAX_FAT12
) {
6206 n
= n_clst
* 2 + 4 ; /* FAT size [byte] */
6209 n
= ( n_clst
* 3 + 1 ) / 2 + 3 ; /* FAT size [byte] */
6211 sz_fat
= ( n
+ ss
- 1 ) / ss
; /* FAT size [sector] */
6212 sz_rsv
= 1 ; /* Number of reserved sectors */
6213 sz_dir
= ( DWORD
) n_root
* SZDIRE
/ ss
; /* Root dir size [sector] */
6215 b_fat
= b_vol
+ sz_rsv
; /* FAT base */
6216 b_data
= b_fat
+ sz_fat
* n_fat
+ sz_dir
; /* Data base */
6218 /* Align data area to erase block boundary (for flash memory media) */
6219 n
= ( DWORD
)((( b_data
+ sz_blk
- 1 ) & ~( sz_blk
- 1 )) - b_data
); /* Sectors to next nearest from current data base */
6220 if ( fsty
== FS_FAT32
) { /* FAT32: Move FAT */
6221 sz_rsv
+= n
; b_fat
+= n
;
6222 } else { /* FAT: Expand FAT */
6223 if ( n
% n_fat
) { /* Adjust fractional error if needed */
6224 n
--; sz_rsv
++; b_fat
++;
6226 sz_fat
+= n
/ n_fat
;
6229 /* Determine number of clusters and final check of validity of the FAT sub-type */
6230 if ( sz_vol
< b_data
+ pau
* 16 - b_vol
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
6231 n_clst
= (( DWORD
) sz_vol
- sz_rsv
- sz_fat
* n_fat
- sz_dir
) / pau
;
6232 if ( fsty
== FS_FAT32
) {
6233 if ( n_clst
<= MAX_FAT16
) { /* Too few clusters for FAT32? */
6234 if ( sz_au
== 0 && ( sz_au
= pau
/ 2 ) != 0 ) continue ; /* Adjust cluster size and retry */
6235 LEAVE_MKFS ( FR_MKFS_ABORTED
);
6238 if ( fsty
== FS_FAT16
) {
6239 if ( n_clst
> MAX_FAT16
) { /* Too many clusters for FAT16 */
6240 if ( sz_au
== 0 && ( pau
* 2 ) <= 64 ) {
6241 sz_au
= pau
* 2 ; continue ; /* Adjust cluster size and retry */
6243 if (( fsopt
& FM_FAT32
)) {
6244 fsty
= FS_FAT32
; continue ; /* Switch type to FAT32 and retry */
6246 if ( sz_au
== 0 && ( sz_au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
6247 LEAVE_MKFS ( FR_MKFS_ABORTED
);
6249 if ( n_clst
<= MAX_FAT12
) { /* Too few clusters for FAT16 */
6250 if ( sz_au
== 0 && ( sz_au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
6251 LEAVE_MKFS ( FR_MKFS_ABORTED
);
6254 if ( fsty
== FS_FAT12
&& n_clst
> MAX_FAT12
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters for FAT12 */
6256 /* Ok, it is the valid cluster configuration */
6261 lba
[ 0 ] = b_vol
; lba
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform storage device that the volume area may be erased */
6262 disk_ioctl ( pdrv
, CTRL_TRIM
, lba
);
6264 /* Create FAT VBR */
6266 memcpy ( buf
+ BS_JmpBoot
, " \xEB\xFE\x90 " "MSDOS5.0" , 11 ); /* Boot jump code (x86), OEM name */
6267 st_word ( buf
+ BPB_BytsPerSec
, ss
); /* Sector size [byte] */
6268 buf
[ BPB_SecPerClus
] = ( BYTE
) pau
; /* Cluster size [sector] */
6269 st_word ( buf
+ BPB_RsvdSecCnt
, ( WORD
) sz_rsv
); /* Size of reserved area */
6270 buf
[ BPB_NumFATs
] = ( BYTE
) n_fat
; /* Number of FATs */
6271 st_word ( buf
+ BPB_RootEntCnt
, ( WORD
)(( fsty
== FS_FAT32
) ? 0 : n_root
)); /* Number of root directory entries */
6272 if ( sz_vol
< 0x10000 ) {
6273 st_word ( buf
+ BPB_TotSec16
, ( WORD
) sz_vol
); /* Volume size in 16-bit LBA */
6275 st_dword ( buf
+ BPB_TotSec32
, ( DWORD
) sz_vol
); /* Volume size in 32-bit LBA */
6277 buf
[ BPB_Media
] = 0xF8 ; /* Media descriptor byte */
6278 st_word ( buf
+ BPB_SecPerTrk
, 63 ); /* Number of sectors per track (for int13) */
6279 st_word ( buf
+ BPB_NumHeads
, 255 ); /* Number of heads (for int13) */
6280 st_dword ( buf
+ BPB_HiddSec
, ( DWORD
) b_vol
); /* Volume offset in the physical drive [sector] */
6281 if ( fsty
== FS_FAT32
) {
6282 st_dword ( buf
+ BS_VolID32
, vsn
); /* VSN */
6283 st_dword ( buf
+ BPB_FATSz32
, sz_fat
); /* FAT size [sector] */
6284 st_dword ( buf
+ BPB_RootClus32
, 2 ); /* Root directory cluster # (2) */
6285 st_word ( buf
+ BPB_FSInfo32
, 1 ); /* Offset of FSINFO sector (VBR + 1) */
6286 st_word ( buf
+ BPB_BkBootSec32
, 6 ); /* Offset of backup VBR (VBR + 6) */
6287 buf
[ BS_DrvNum32
] = 0x80 ; /* Drive number (for int13) */
6288 buf
[ BS_BootSig32
] = 0x29 ; /* Extended boot signature */
6289 memcpy ( buf
+ BS_VolLab32
, "NO NAME " "FAT32 " , 19 ); /* Volume label, FAT signature */
6291 st_dword ( buf
+ BS_VolID
, vsn
); /* VSN */
6292 st_word ( buf
+ BPB_FATSz16
, ( WORD
) sz_fat
); /* FAT size [sector] */
6293 buf
[ BS_DrvNum
] = 0x80 ; /* Drive number (for int13) */
6294 buf
[ BS_BootSig
] = 0x29 ; /* Extended boot signature */
6295 memcpy ( buf
+ BS_VolLab
, "NO NAME " "FAT " , 19 ); /* Volume label, FAT signature */
6297 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (offset is fixed here regardless of sector size) */
6298 if ( disk_write ( pdrv
, buf
, b_vol
, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it to the VBR sector */
6300 /* Create FSINFO record if needed */
6301 if ( fsty
== FS_FAT32
) {
6302 disk_write ( pdrv
, buf
, b_vol
+ 6 , 1 ); /* Write backup VBR (VBR + 6) */
6304 st_dword ( buf
+ FSI_LeadSig
, 0x41615252 );
6305 st_dword ( buf
+ FSI_StrucSig
, 0x61417272 );
6306 st_dword ( buf
+ FSI_Free_Count
, n_clst
- 1 ); /* Number of free clusters */
6307 st_dword ( buf
+ FSI_Nxt_Free
, 2 ); /* Last allocated cluster# */
6308 st_word ( buf
+ BS_55AA
, 0xAA55 );
6309 disk_write ( pdrv
, buf
, b_vol
+ 7 , 1 ); /* Write backup FSINFO (VBR + 7) */
6310 disk_write ( pdrv
, buf
, b_vol
+ 1 , 1 ); /* Write original FSINFO (VBR + 1) */
6313 /* Initialize FAT area */
6314 memset ( buf
, 0 , sz_buf
* ss
);
6315 sect
= b_fat
; /* FAT start sector */
6316 for ( i
= 0 ; i
< n_fat
; i
++) { /* Initialize FATs each */
6317 if ( fsty
== FS_FAT32
) {
6318 st_dword ( buf
+ 0 , 0xFFFFFFF8 ); /* FAT[0] */
6319 st_dword ( buf
+ 4 , 0xFFFFFFFF ); /* FAT[1] */
6320 st_dword ( buf
+ 8 , 0x0FFFFFFF ); /* FAT[2] (root directory) */
6322 st_dword ( buf
+ 0 , ( fsty
== FS_FAT12
) ? 0xFFFFF8 : 0xFFFFFFF8 ); /* FAT[0] and FAT[1] */
6324 nsect
= sz_fat
; /* Number of FAT sectors */
6325 do { /* Fill FAT sectors */
6326 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
6327 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6328 memset ( buf
, 0 , ss
); /* Rest of FAT all are cleared */
6329 sect
+= n
; nsect
-= n
;
6333 /* Initialize root directory (fill with zero) */
6334 nsect
= ( fsty
== FS_FAT32
) ? pau
: sz_dir
; /* Number of root directory sectors */
6336 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
6337 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6338 sect
+= n
; nsect
-= n
;
6342 /* A FAT volume has been created here */
6344 /* Determine system ID in the MBR partition table */
6345 if ( FF_FS_EXFAT
&& fsty
== FS_EXFAT
) {
6346 sys
= 0x07 ; /* exFAT */
6347 } else if ( fsty
== FS_FAT32
) {
6348 sys
= 0x0C ; /* FAT32X */
6349 } else if ( sz_vol
>= 0x10000 ) {
6350 sys
= 0x06 ; /* FAT12/16 (large) */
6351 } else if ( fsty
== FS_FAT16
) {
6352 sys
= 0x04 ; /* FAT16 */
6354 sys
= 0x01 ; /* FAT12 */
6357 /* Update partition information */
6358 if ( FF_MULTI_PARTITION
&& ipart
!= 0 ) { /* Volume is in the existing partition */
6359 if (! FF_LBA64
|| !( fsopt
& 0x80 )) { /* Is the partition in MBR? */
6360 /* Update system ID in the partition table */
6361 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Read the MBR */
6362 buf
[ MBR_Table
+ ( ipart
- 1 ) * SZ_PTE
+ PTE_System
] = sys
; /* Set system ID */
6363 if ( disk_write ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it back to the MBR */
6365 } else { /* Volume as a new single partition */
6366 if (!( fsopt
& FM_SFD
)) { /* Create partition table if not in SFD format */
6367 lba
[ 0 ] = sz_vol
; lba
[ 1 ] = 0 ;
6368 res
= create_partition ( pdrv
, lba
, sys
, buf
);
6369 if ( res
!= FR_OK
) LEAVE_MKFS ( res
);
6373 if ( disk_ioctl ( pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
6381 #if FF_MULTI_PARTITION
6382 /*-----------------------------------------------------------------------*/
6383 /* Create Partition Table on the Physical Drive */
6384 /*-----------------------------------------------------------------------*/
6387 BYTE pdrv
, /* Physical drive number */
6388 const LBA_t ptbl
[], /* Pointer to the size table for each partitions */
6389 void * work
/* Pointer to the working buffer (null: use heap memory) */
6392 BYTE
* buf
= ( BYTE
*) work
;
6397 /* Initialize the physical drive */
6398 stat
= disk_initialize ( pdrv
);
6399 if ( stat
& STA_NOINIT
) return FR_NOT_READY
;
6400 if ( stat
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
6403 if (! buf
) buf
= ff_memalloc ( FF_MAX_SS
); /* Use heap memory for working buffer */
6405 if (! buf
) return FR_NOT_ENOUGH_CORE
;
6407 res
= create_partition ( pdrv
, ptbl
, 0x07 , buf
); /* Create partitions (system ID is temporary setting and determined by f_mkfs) */
6412 #endif /* FF_MULTI_PARTITION */
6413 #endif /* !FF_FS_READONLY && FF_USE_MKFS */
6419 #if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
6420 #error Wrong FF_STRF_ENCODE setting
6422 /*-----------------------------------------------------------------------*/
6423 /* Get a String from the File */
6424 /*-----------------------------------------------------------------------*/
6427 TCHAR
* buff
, /* Pointer to the buffer to store read string */
6428 int len
, /* Size of string buffer (items) */
6429 FIL
* fp
/* Pointer to the file object */
6437 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
6440 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
6444 #if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
6445 /* Make a room for the character and terminator */
6446 if ( FF_LFN_UNICODE
== 1 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 1 : 2 ;
6447 if ( FF_LFN_UNICODE
== 2 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 3 : 4 ;
6448 if ( FF_LFN_UNICODE
== 3 ) len
-= 1 ;
6450 #if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
6451 f_read ( fp
, s
, 1 , & rc
); /* Get a code unit */
6452 if ( rc
!= 1 ) break ; /* EOF? */
6454 if ( dbc_1st (( BYTE
) wc
)) { /* DBC 1st byte? */
6455 f_read ( fp
, s
, 1 , & rc
); /* Get 2nd byte */
6456 if ( rc
!= 1 || ! dbc_2nd ( s
[ 0 ])) continue ; /* Wrong code? */
6457 wc
= wc
<< 8 | s
[ 0 ];
6459 dc
= ff_oem2uni ( wc
, CODEPAGE
); /* Convert ANSI/OEM into Unicode */
6460 if ( dc
== 0 ) continue ; /* Conversion error? */
6461 #elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
6462 f_read ( fp
, s
, 2 , & rc
); /* Get a code unit */
6463 if ( rc
!= 2 ) break ; /* EOF? */
6464 dc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6465 if ( IsSurrogateL ( dc
)) continue ; /* Broken surrogate pair? */
6466 if ( IsSurrogateH ( dc
)) { /* High surrogate? */
6467 f_read ( fp
, s
, 2 , & rc
); /* Get low surrogate */
6468 if ( rc
!= 2 ) break ; /* EOF? */
6469 wc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6470 if (! IsSurrogateL ( wc
)) continue ; /* Broken surrogate pair? */
6471 dc
= (( dc
& 0x3FF ) + 0x40 ) << 10 | ( wc
& 0x3FF ); /* Merge surrogate pair */
6473 #else /* Read a character in UTF-8 */
6474 f_read ( fp
, s
, 1 , & rc
); /* Get a code unit */
6475 if ( rc
!= 1 ) break ; /* EOF? */
6477 if ( dc
>= 0x80 ) { /* Multi-byte sequence? */
6479 if (( dc
& 0xE0 ) == 0xC0 ) { /* 2-byte sequence? */
6482 if (( dc
& 0xF0 ) == 0xE0 ) { /* 3-byte sequence? */
6485 if (( dc
& 0xF8 ) == 0xF0 ) { /* 4-byte sequence? */
6488 if ( ct
== 0 ) continue ;
6489 f_read ( fp
, s
, ct
, & rc
); /* Get trailing bytes */
6490 if ( rc
!= ct
) break ;
6492 do { /* Merge the byte sequence */
6493 if (( s
[ rc
] & 0xC0 ) != 0x80 ) break ;
6494 dc
= dc
<< 6 | ( s
[ rc
] & 0x3F );
6495 } while (++ rc
< ct
);
6496 if ( rc
!= ct
|| dc
< 0x80 || IsSurrogate ( dc
) || dc
>= 0x110000 ) continue ; /* Wrong encoding? */
6499 /* A code point is avaialble in dc to be output */
6501 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ; /* Strip \r off if needed */
6502 #if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
6503 if ( FF_LFN_UNICODE
== 1 && dc
>= 0x10000 ) { /* Out of BMP at UTF-16? */
6504 * p
++ = ( TCHAR
)( 0xD800 | (( dc
>> 10 ) - 0x40 )); nc
++; /* Make and output high surrogate */
6505 dc
= 0xDC00 | ( dc
& 0x3FF ); /* Make low surrogate */
6507 * p
++ = ( TCHAR
) dc
; nc
++;
6508 if ( dc
== ' \n ' ) break ; /* End of line? */
6509 #elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
6510 if ( dc
< 0x80 ) { /* Single byte? */
6513 if ( dc
== ' \n ' ) break ; /* End of line? */
6514 } else if ( dc
< 0x800 ) { /* 2-byte sequence? */
6515 * p
++ = ( TCHAR
)( 0xC0 | ( dc
>> 6 & 0x1F ));
6516 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6518 } else if ( dc
< 0x10000 ) { /* 3-byte sequence? */
6519 * p
++ = ( TCHAR
)( 0xE0 | ( dc
>> 12 & 0x0F ));
6520 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6521 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6523 } else { /* 4-byte sequence */
6524 * p
++ = ( TCHAR
)( 0xF0 | ( dc
>> 18 & 0x07 ));
6525 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 12 & 0x3F ));
6526 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6527 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6533 #else /* Byte-by-byte read without any conversion (ANSI/OEM API) */
6534 len
-= 1 ; /* Make a room for the terminator */
6536 f_read ( fp
, s
, 1 , & rc
); /* Get a byte */
6537 if ( rc
!= 1 ) break ; /* EOF? */
6539 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ;
6540 * p
++ = ( TCHAR
) dc
; nc
++;
6541 if ( dc
== ' \n ' ) break ;
6545 * p
= 0 ; /* Terminate the string */
6546 return nc
? buff
: 0 ; /* When no data read due to EOF or error, return with error. */
6554 #define SZ_PUTC_BUF 64
6555 #define SZ_NUM_BUF 32
6557 /*-----------------------------------------------------------------------*/
6558 /* Put a Character to the File (with sub-functions) */
6559 /*-----------------------------------------------------------------------*/
6561 /* Output buffer and work area */
6564 FIL
* fp
; /* Ptr to the writing file */
6565 int idx
, nchr
; /* Write index of buf[] (-1:error), number of encoding units written */
6566 #if FF_USE_LFN && FF_LFN_UNICODE == 1
6568 #elif FF_USE_LFN && FF_LFN_UNICODE == 2
6572 BYTE buf
[ SZ_PUTC_BUF
]; /* Write buffer */
6576 /* Buffered file write with code conversion */
6578 static void putc_bfd ( putbuff
* pb
, TCHAR c
)
6582 #if FF_USE_LFN && FF_LFN_UNICODE
6584 #if FF_LFN_UNICODE == 2
6590 if ( FF_USE_STRFUNC
== 2 && c
== ' \n ' ) { /* LF -> CRLF conversion */
6594 i
= pb
-> idx
; /* Write index of pb->buf[] */
6595 if ( i
< 0 ) return ; /* In write error? */
6596 nc
= pb
-> nchr
; /* Write unit counter */
6598 #if FF_USE_LFN && FF_LFN_UNICODE
6599 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
6600 if ( IsSurrogateH ( c
)) { /* Is this a high-surrogate? */
6601 pb
-> hs
= c
; return ; /* Save it for next */
6603 hs
= pb
-> hs
; pb
-> hs
= 0 ;
6604 if ( hs
!= 0 ) { /* Is there a leading high-surrogate? */
6605 if (! IsSurrogateL ( c
)) hs
= 0 ; /* Discard high-surrogate if not a surrogate pair */
6607 if ( IsSurrogateL ( c
)) return ; /* Discard stray low-surrogate */
6610 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
6612 if ( pb
-> ct
== 0 ) { /* Out of multi-byte sequence? */
6613 pb
-> bs
[ pb
-> wi
= 0 ] = ( BYTE
) c
; /* Save 1st byte */
6614 if (( BYTE
) c
< 0x80 ) break ; /* Single byte code? */
6615 if ((( BYTE
) c
& 0xE0 ) == 0xC0 ) pb
-> ct
= 1 ; /* 2-byte sequence? */
6616 if ((( BYTE
) c
& 0xF0 ) == 0xE0 ) pb
-> ct
= 2 ; /* 3-byte sequence? */
6617 if ((( BYTE
) c
& 0xF8 ) == 0xF0 ) pb
-> ct
= 3 ; /* 4-byte sequence? */
6618 return ; /* Wrong leading byte (discard it) */
6619 } else { /* In the multi-byte sequence */
6620 if ((( BYTE
) c
& 0xC0 ) != 0x80 ) { /* Broken sequence? */
6621 pb
-> ct
= 0 ; continue ; /* Discard the sequense */
6623 pb
-> bs
[++ pb
-> wi
] = ( BYTE
) c
; /* Save the trailing byte */
6624 if (-- pb
-> ct
== 0 ) break ; /* End of the sequence? */
6628 tp
= ( const TCHAR
*) pb
-> bs
;
6629 dc
= tchar2uni (& tp
); /* UTF-8 ==> UTF-16 */
6630 if ( dc
== 0xFFFFFFFF ) return ; /* Wrong code? */
6631 hs
= ( WCHAR
)( dc
>> 16 );
6633 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
6634 if ( IsSurrogate ( c
) || c
>= 0x110000 ) return ; /* Discard invalid code */
6635 if ( c
>= 0x10000 ) { /* Out of BMP? */
6636 hs
= ( WCHAR
)( 0xD800 | (( c
>> 10 ) - 0x40 )); /* Make high surrogate */
6637 wc
= 0xDC00 | ( c
& 0x3FF ); /* Make low surrogate */
6643 /* A code point in UTF-16 is available in hs and wc */
6645 #if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */
6646 if ( hs
!= 0 ) { /* Surrogate pair? */
6647 st_word (& pb
-> buf
[ i
], hs
);
6651 st_word (& pb
-> buf
[ i
], wc
);
6653 #elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */
6654 if ( hs
!= 0 ) { /* Surrogate pair? */
6655 pb
-> buf
[ i
++] = ( BYTE
)( hs
>> 8 );
6656 pb
-> buf
[ i
++] = ( BYTE
) hs
;
6659 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 );
6660 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6661 #elif FF_STRF_ENCODE == 3 /* Write a code point in UTF-8 */
6662 if ( hs
!= 0 ) { /* 4-byte sequence? */
6664 hs
= ( hs
& 0x3FF ) + 0x40 ;
6665 pb
-> buf
[ i
++] = ( BYTE
)( 0xF0 | hs
>> 8 );
6666 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
>> 2 & 0x3F ));
6667 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
& 3 ) << 4 | ( wc
>> 6 & 0x0F ));
6668 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6670 if ( wc
< 0x80 ) { /* Single byte? */
6671 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6673 if ( wc
< 0x800 ) { /* 2-byte sequence? */
6675 pb
-> buf
[ i
++] = ( BYTE
)( 0xC0 | wc
>> 6 );
6676 } else { /* 3-byte sequence */
6678 pb
-> buf
[ i
++] = ( BYTE
)( 0xE0 | wc
>> 12 );
6679 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
>> 6 & 0x3F ));
6681 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6684 #else /* Write a code point in ANSI/OEM */
6685 if ( hs
!= 0 ) return ;
6686 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* UTF-16 ==> ANSI/OEM */
6687 if ( wc
== 0 ) return ;
6689 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 ); nc
++;
6691 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6694 #else /* ANSI/OEM input (without re-encoding) */
6695 pb
-> buf
[ i
++] = ( BYTE
) c
;
6698 if ( i
>= ( int )( sizeof pb
-> buf
) - 4 ) { /* Write buffered characters to the file */
6699 f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) i
, & n
);
6700 i
= ( n
== ( UINT
) i
) ? 0 : - 1 ;
6707 /* Flush remaining characters in the buffer */
6709 static int putc_flush ( putbuff
* pb
)
6713 if ( pb
-> idx
>= 0 /* Flush buffered characters to the file */
6714 && f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) pb
-> idx
, & nw
) == FR_OK
6715 && ( UINT
) pb
-> idx
== nw
) return pb
-> nchr
;
6720 /* Initialize write buffer */
6722 static void putc_init ( putbuff
* pb
, FIL
* fp
)
6724 memset ( pb
, 0 , sizeof ( putbuff
));
6731 TCHAR c
, /* A character to be output */
6732 FIL
* fp
/* Pointer to the file object */
6739 putc_bfd (& pb
, c
); /* Put the character */
6740 return putc_flush (& pb
);
6746 /*-----------------------------------------------------------------------*/
6747 /* Put a String to the File */
6748 /*-----------------------------------------------------------------------*/
6751 const TCHAR
* str
, /* Pointer to the string to be output */
6752 FIL
* fp
/* Pointer to the file object */
6759 while (* str
) putc_bfd (& pb
, * str
++); /* Put the string */
6760 return putc_flush (& pb
);
6766 /*-----------------------------------------------------------------------*/
6767 /* Put a Formatted String to the File (with sub-functions) */
6768 /*-----------------------------------------------------------------------*/
6769 #if FF_PRINT_FLOAT && FF_INTDEF == 2
6772 static int ilog10 ( double n
) /* Calculate log10(n) in integer output */
6776 while ( n
>= 10 ) { /* Decimate digit in right shift */
6778 n
/= 100000 ; rv
+= 5 ;
6783 while ( n
< 1 ) { /* Decimate digit in left shift */
6785 n
*= 100000 ; rv
-= 5 ;
6794 static double i10x ( int n
) /* Calculate 10^n in integer input */
6798 while ( n
> 0 ) { /* Left shift */
6800 rv
*= 100000 ; n
-= 5 ;
6805 while ( n
< 0 ) { /* Right shift */
6807 rv
/= 100000 ; n
+= 5 ;
6817 char * buf
, /* Buffer to output the floating point string */
6818 double val
, /* Value to output */
6819 int prec
, /* Number of fractional digits */
6820 TCHAR fmt
/* Notation */
6828 const char ds
= FF_PRINT_FLOAT
== 2 ? ',' : '.' ;
6831 if ( isnan ( val
)) { /* Not a number? */
6834 if ( prec
< 0 ) prec
= 6 ; /* Default precision? (6 fractional digits) */
6835 if ( val
< 0 ) { /* Negative? */
6836 val
= 0 - val
; sign
= '-' ;
6840 if ( isinf ( val
)) { /* Infinite? */
6843 if ( fmt
== 'f' ) { /* Decimal notation? */
6844 val
+= i10x ( 0 - prec
) / 2 ; /* Round (nearest) */
6847 if ( m
+ prec
+ 3 >= SZ_NUM_BUF
) er
= "OV" ; /* Buffer overflow? */
6848 } else { /* E notation */
6849 if ( val
!= 0 ) { /* Not a true zero? */
6850 val
+= i10x ( ilog10 ( val
) - prec
) / 2 ; /* Round (nearest) */
6852 if ( e
> 99 || prec
+ 7 >= SZ_NUM_BUF
) { /* Buffer overflow or E > +99? */
6855 if ( e
< - 99 ) e
= - 99 ;
6856 val
/= i10x ( e
); /* Normalize */
6861 if (! er
) { /* Not error condition */
6862 if ( sign
== '-' ) * buf
++ = sign
; /* Add a - if negative value */
6863 do { /* Put decimal number */
6864 if ( m
== - 1 ) * buf
++ = ds
; /* Insert a decimal separator when get into fractional part */
6865 w
= i10x ( m
); /* Snip the highest digit d */
6866 d
= ( int )( val
/ w
); val
-= d
* w
;
6867 * buf
++ = ( char )( '0' + d
); /* Put the digit */
6868 } while (-- m
>= - prec
); /* Output all digits specified by prec */
6869 if ( fmt
!= 'f' ) { /* Put exponent if needed */
6872 e
= 0 - e
; * buf
++ = '-' ;
6876 * buf
++ = ( char )( '0' + e
/ 10 );
6877 * buf
++ = ( char )( '0' + e
% 10 );
6881 if ( er
) { /* Error condition */
6882 if ( sign
) * buf
++ = sign
; /* Add sign if needed */
6883 do { /* Put error symbol */
6887 * buf
= 0 ; /* Term */
6889 #endif /* FF_PRINT_FLOAT && FF_INTDEF == 2 */
6894 FIL
* fp
, /* Pointer to the file object */
6895 const TCHAR
* fmt
, /* Pointer to the format string */
6896 ... /* Optional arguments... */
6903 #if FF_PRINT_LLI && FF_INTDEF == 2
6911 char d
, str
[ SZ_NUM_BUF
];
6920 if ( tc
== 0 ) break ; /* End of format string */
6921 if ( tc
!= '%' ) { /* Not an escape character (pass-through) */
6925 f
= w
= 0 ; pad
= ' ' ; prec
= - 1 ; /* Initialize parms */
6927 if ( tc
== '0' ) { /* Flag: '0' padded */
6928 pad
= '0' ; tc
= * fmt
++;
6929 } else if ( tc
== '-' ) { /* Flag: Left aligned */
6932 if ( tc
== '*' ) { /* Minimum width from an argument */
6933 w
= va_arg ( arp
, int );
6936 while ( IsDigit ( tc
)) { /* Minimum width */
6937 w
= w
* 10 + tc
- '0' ;
6941 if ( tc
== '.' ) { /* Precision */
6943 if ( tc
== '*' ) { /* Precision from an argument */
6944 prec
= va_arg ( arp
, int );
6948 while ( IsDigit ( tc
)) { /* Precision */
6949 prec
= prec
* 10 + tc
- '0' ;
6954 if ( tc
== 'l' ) { /* Size: long int */
6955 f
|= 4 ; tc
= * fmt
++;
6956 #if FF_PRINT_LLI && FF_INTDEF == 2
6957 if ( tc
== 'l' ) { /* Size: long long int */
6958 f
|= 8 ; tc
= * fmt
++;
6962 if ( tc
== 0 ) break ; /* End of format string */
6963 switch ( tc
) { /* Atgument type is... */
6964 case 'b' : /* Unsigned binary */
6967 case 'o' : /* Unsigned octal */
6970 case 'd' : /* Signed decimal */
6971 case 'u' : /* Unsigned decimal */
6974 case 'x' : /* Unsigned hexadecimal (lower case) */
6975 case 'X' : /* Unsigned hexadecimal (upper case) */
6978 case 'c' : /* Character */
6979 putc_bfd (& pb
, ( TCHAR
) va_arg ( arp
, int ));
6982 case 's' : /* String */
6983 tp
= va_arg ( arp
, TCHAR
*); /* Get a pointer argument */
6984 if (! tp
) tp
= & nul
; /* Null ptr generates a null string */
6985 for ( j
= 0 ; tp
[ j
]; j
++) ; /* j = tcslen(tp) */
6986 if ( prec
>= 0 && j
> ( UINT
) prec
) j
= prec
; /* Limited length of string body */
6987 for ( ; !( f
& 2 ) && j
< w
; j
++) putc_bfd (& pb
, pad
); /* Left pads */
6988 while (* tp
&& prec
--) putc_bfd (& pb
, * tp
++); /* Body */
6989 while ( j
++ < w
) putc_bfd (& pb
, ' ' ); /* Right pads */
6991 #if FF_PRINT_FLOAT && FF_INTDEF == 2
6992 case 'f' : /* Floating point (decimal) */
6993 case 'e' : /* Floating point (e) */
6994 case 'E' : /* Floating point (E) */
6995 ftoa ( str
, va_arg ( arp
, double ), prec
, tc
); /* Make a floating point string */
6996 for ( j
= strlen ( str
); !( f
& 2 ) && j
< w
; j
++) putc_bfd (& pb
, pad
); /* Left pads */
6997 for ( i
= 0 ; str
[ i
]; putc_bfd (& pb
, str
[ i
++])) ; /* Body */
6998 while ( j
++ < w
) putc_bfd (& pb
, ' ' ); /* Right pads */
7001 default : /* Unknown type (pass-through) */
7002 putc_bfd (& pb
, tc
); continue ;
7005 /* Get an integer argument and put it in numeral */
7006 #if FF_PRINT_LLI && FF_INTDEF == 2
7007 if ( f
& 8 ) { /* long long argument? */
7008 v
= ( QWORD
) va_arg ( arp
, long long );
7009 } else if ( f
& 4 ) { /* long argument? */
7010 v
= ( tc
== 'd' ) ? ( QWORD
)( long long ) va_arg ( arp
, long ) : ( QWORD
) va_arg ( arp
, unsigned long );
7011 } else { /* int/short/char argument */
7012 v
= ( tc
== 'd' ) ? ( QWORD
)( long long ) va_arg ( arp
, int ) : ( QWORD
) va_arg ( arp
, unsigned int );
7014 if ( tc
== 'd' && ( v
& 0x8000000000000000 )) { /* Negative value? */
7018 if ( f
& 4 ) { /* long argument? */
7019 v
= ( DWORD
) va_arg ( arp
, long );
7020 } else { /* int/short/char argument */
7021 v
= ( tc
== 'd' ) ? ( DWORD
)( long ) va_arg ( arp
, int ) : ( DWORD
) va_arg ( arp
, unsigned int );
7023 if ( tc
== 'd' && ( v
& 0x80000000 )) { /* Negative value? */
7028 do { /* Make an integer number string */
7029 d
= ( char )( v
% r
); v
/= r
;
7030 if ( d
> 9 ) d
+= ( tc
== 'x' ) ? 0x27 : 0x07 ;
7032 } while ( v
&& i
< SZ_NUM_BUF
);
7033 if ( f
& 1 ) str
[ i
++] = '-' ; /* Sign */
7035 for ( j
= i
; !( f
& 2 ) && j
< w
; j
++) { /* Left pads */
7039 putc_bfd (& pb
, ( TCHAR
) str
[-- i
]);
7041 while ( j
++ < w
) { /* Right pads */
7048 return putc_flush (& pb
);
7051 #endif /* !FF_FS_READONLY */
7052 #endif /* FF_USE_STRFUNC */
7056 #if FF_CODE_PAGE == 0
7057 /*-----------------------------------------------------------------------*/
7058 /* Set Active Codepage for the Path Name */
7059 /*-----------------------------------------------------------------------*/
7062 WORD cp
/* Value to be set as active code page */
7065 static const WORD validcp
[ 22 ] = { 437 , 720 , 737 , 771 , 775 , 850 , 852 , 855 , 857 , 860 , 861 , 862 , 863 , 864 , 865 , 866 , 869 , 932 , 936 , 949 , 950 , 0 };
7066 static const BYTE
* const tables
[ 22 ] = { Ct437
, Ct720
, Ct737
, Ct771
, Ct775
, Ct850
, Ct852
, Ct855
, Ct857
, Ct860
, Ct861
, Ct862
, Ct863
, Ct864
, Ct865
, Ct866
, Ct869
, Dc932
, Dc936
, Dc949
, Dc950
, 0 };
7070 for ( i
= 0 ; validcp
[ i
] != 0 && validcp
[ i
] != cp
; i
++) ; /* Find the code page */
7071 if ( validcp
[ i
] != cp
) return FR_INVALID_PARAMETER
; /* Not found? */
7074 if ( cp
>= 900 ) { /* DBCS */
7083 #endif /* FF_CODE_PAGE == 0 */