]>
cloudbase.mooo.com Git - z180-stamp.git/blob - fatfs/source/ff.c
1 /*----------------------------------------------------------------------------/
2 / FatFs - Generic FAT Filesystem Module R0.13b /
3 /-----------------------------------------------------------------------------/
5 / Copyright (C) 2018, ChaN, all right reserved.
7 / FatFs module is an open source software. Redistribution and use of FatFs in
8 / source and binary forms, with or without modification, are permitted provided
9 / that the following condition is met:
11 / 1. Redistributions of source code must retain the above copyright notice,
12 / this condition and the following disclaimer.
14 / This software is provided by the copyright holder and contributors "AS IS"
15 / and any warranties related to this software are DISCLAIMED.
16 / The copyright owner or contributors be NOT LIABLE for any damages caused
17 / by use of this software.
19 /----------------------------------------------------------------------------*/
22 #include "ff.h" /* Declarations of FatFs API */
23 #include "diskio.h" /* Declarations of device I/O functions */
26 /*--------------------------------------------------------------------------
28 Module Private Definitions
30 ---------------------------------------------------------------------------*/
32 #if FF_DEFINED != 63463 /* Revision ID */
33 #error Wrong include file (ff.h).
37 /* Character code support macros */
38 #define IsUpper(c) ((c) >= 'A' && (c) <= 'Z' )
39 #define IsLower(c) ((c) >= 'a' && (c) <= 'z' )
40 #define IsDigit(c) ((c) >= '0' && (c) <= '9' )
41 #define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
42 #define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
43 #define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
46 /* Additional file attribute bits for internal use */
47 #define AM_VOL 0x08 /* Volume label */
48 #define AM_LFN 0x0F /* LFN entry */
49 #define AM_MASK 0x3F /* Mask of defined bits */
52 /* Additional file access control and file status flags for internal use */
53 #define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
54 #define FA_MODIFIED 0x40 /* File has been modified */
55 #define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
58 /* Name status flags in fn[11] */
59 #define NSFLAG 11 /* Index of the name status byte */
60 #define NS_LOSS 0x01 /* Out of 8.3 format */
61 #define NS_LFN 0x02 /* Force to create LFN entry */
62 #define NS_LAST 0x04 /* Last segment */
63 #define NS_BODY 0x08 /* Lower case flag (body) */
64 #define NS_EXT 0x10 /* Lower case flag (ext) */
65 #define NS_DOT 0x20 /* Dot entry */
66 #define NS_NOLFN 0x40 /* Do not find LFN */
67 #define NS_NONAME 0x80 /* Not followed */
70 /* Limits and boundaries */
71 #define MAX_DIR 0x200000 /* Max size of FAT directory */
72 #define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
73 #define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
74 #define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
75 #define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
76 #define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
79 /* FatFs refers the FAT structure as simple byte array instead of structure member
80 / because the C structure is not binary compatible between different platforms */
82 #define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
83 #define BS_OEMName 3 /* OEM name (8-byte) */
84 #define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
85 #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
86 #define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
87 #define BPB_NumFATs 16 /* Number of FATs (BYTE) */
88 #define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
89 #define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
90 #define BPB_Media 21 /* Media descriptor byte (BYTE) */
91 #define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
92 #define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
93 #define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
94 #define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
95 #define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
96 #define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
97 #define BS_NTres 37 /* WindowsNT error flag (BYTE) */
98 #define BS_BootSig 38 /* Extended boot signature (BYTE) */
99 #define BS_VolID 39 /* Volume serial number (DWORD) */
100 #define BS_VolLab 43 /* Volume label string (8-byte) */
101 #define BS_FilSysType 54 /* Filesystem type string (8-byte) */
102 #define BS_BootCode 62 /* Boot code (448-byte) */
103 #define BS_55AA 510 /* Signature word (WORD) */
105 #define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
106 #define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
107 #define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
108 #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
109 #define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
110 #define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
111 #define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
112 #define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
113 #define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
114 #define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
115 #define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
116 #define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
117 #define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
119 #define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
120 #define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
121 #define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
122 #define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
123 #define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
124 #define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
125 #define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
126 #define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
127 #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
128 #define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
129 #define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */
130 #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
131 #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
132 #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
133 #define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
134 #define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
135 #define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
136 #define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
138 #define DIR_Name 0 /* Short file name (11-byte) */
139 #define DIR_Attr 11 /* Attribute (BYTE) */
140 #define DIR_NTres 12 /* Lower case flag (BYTE) */
141 #define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
142 #define DIR_CrtTime 14 /* Created time (DWORD) */
143 #define DIR_LstAccDate 18 /* Last accessed date (WORD) */
144 #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
145 #define DIR_ModTime 22 /* Modified time (DWORD) */
146 #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
147 #define DIR_FileSize 28 /* File size (DWORD) */
148 #define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
149 #define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
150 #define LDIR_Type 12 /* LFN: Entry type (BYTE) */
151 #define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
152 #define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
153 #define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
154 #define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
155 #define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
156 #define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
157 #define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
158 #define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
159 #define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
160 #define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
161 #define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
162 #define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
163 #define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
164 #define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
165 #define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
166 #define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
167 #define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
168 #define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
169 #define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
170 #define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
171 #define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
172 #define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
173 #define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
175 #define SZDIRE 32 /* Size of a directory entry */
176 #define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
177 #define RDDEM 0x05 /* Replacement of the character collides with DDEM */
178 #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
180 #define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
181 #define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
182 #define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
183 #define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
185 #define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
186 #define SZ_PTE 16 /* MBR: Size of a partition table entry */
187 #define PTE_Boot 0 /* MBR PTE: Boot indicator */
188 #define PTE_StHead 1 /* MBR PTE: Start head */
189 #define PTE_StSec 2 /* MBR PTE: Start sector */
190 #define PTE_StCyl 3 /* MBR PTE: Start cylinder */
191 #define PTE_System 4 /* MBR PTE: System ID */
192 #define PTE_EdHead 5 /* MBR PTE: End head */
193 #define PTE_EdSec 6 /* MBR PTE: End sector */
194 #define PTE_EdCyl 7 /* MBR PTE: End cylinder */
195 #define PTE_StLba 8 /* MBR PTE: Start in LBA */
196 #define PTE_SizLba 12 /* MBR PTE: Size in LBA */
199 /* Post process on fatal error in the file operations */
200 #define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
203 /* Re-entrancy related */
206 #error Static LFN work area cannot be used at thread-safe configuration
208 #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
210 #define LEAVE_FF(fs, res) return res
214 /* Definitions of volume - physical location conversion */
215 #if FF_MULTI_PARTITION
216 #define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
217 #define LD2PT(vol) VolToPart[vol].pt /* Get partition index */
219 #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
220 #define LD2PT(vol) 0 /* Find first valid partition or in SFD */
224 /* Definitions of sector size */
225 #if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
226 #error Wrong sector size configuration
228 #if FF_MAX_SS == FF_MIN_SS
229 #define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
231 #define SS(fs) ((fs)->ssize) /* Variable sector size */
237 #if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
238 #error Invalid FF_FS_NORTC settings
240 #define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
242 #define GET_FATTIME() get_fattime()
246 /* File lock controls */
249 #error FF_FS_LOCK must be 0 at read-only configuration
252 FATFS
* fs
; /* Object ID 1, volume (NULL:blank entry) */
253 DWORD clu
; /* Object ID 2, containing directory (0:root) */
254 DWORD ofs
; /* Object ID 3, offset in the directory */
255 WORD ctr
; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
260 /* SBCS up-case tables (\x80-\xFF) */
261 #define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
262 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
263 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
264 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
265 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
266 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
267 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
268 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
269 #define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
270 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
271 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
272 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
273 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
274 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
275 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
276 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
277 #define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
278 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
279 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
280 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
281 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
282 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
283 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
284 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
285 #define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
286 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
287 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
288 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
289 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
290 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
291 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
292 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
293 #define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
294 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
295 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
296 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
297 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
298 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
299 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
300 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
301 #define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
302 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
303 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
304 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
305 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
306 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
307 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
308 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
309 #define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
310 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
311 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
312 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
313 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
314 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
315 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
316 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
317 #define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
318 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
319 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
320 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
321 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
322 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
323 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
324 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
325 #define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
326 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
327 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
328 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
329 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
330 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
331 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
332 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
333 #define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
334 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
335 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
336 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
337 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
338 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
339 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
340 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
341 #define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
342 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
343 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
344 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
345 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
346 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
347 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
348 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
349 #define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
350 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
351 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
352 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
353 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
354 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
355 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
356 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
357 #define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
358 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
359 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
360 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
361 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
362 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
363 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
364 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
365 #define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
366 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
367 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
368 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
369 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
370 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
371 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
372 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
373 #define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
374 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
375 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
376 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
377 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
378 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
379 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
380 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
381 #define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
382 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
383 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
384 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
385 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
386 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
387 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
388 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
389 #define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
390 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
391 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
392 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
393 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
394 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
395 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
396 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
399 /* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
400 #define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
401 #define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
402 #define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
403 #define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
406 /* Macros for table definitions */
407 #define MERGE_2STR(a, b) a ## b
408 #define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
413 /*--------------------------------------------------------------------------
415 Module Private Work Area
417 ---------------------------------------------------------------------------*/
418 /* Remark: Variables defined here without initial value shall be guaranteed
419 / zero/null at start-up. If not, the linker option or start-up routine is
420 / not compliance with C standard. */
422 /*--------------------------------*/
423 /* File/Volume controls */
424 /*--------------------------------*/
426 #if FF_VOLUMES < 1 || FF_VOLUMES > 10
427 #error Wrong FF_VOLUMES setting
429 static FATFS
* FatFs
[ FF_VOLUMES
]; /* Pointer to the filesystem objects (logical drives) */
430 static WORD Fsid
; /* Filesystem mount ID */
433 static BYTE CurrVol
; /* Current drive */
437 static FILESEM Files
[ FF_FS_LOCK
]; /* Open object lock semaphores */
441 #ifdef FF_VOLUME_STRS
442 static const char * const VolumeStr
[ FF_VOLUMES
] = { FF_VOLUME_STRS
}; /* Pre-defined volume ID */
447 /*--------------------------------*/
448 /* LFN/Directory working buffer */
449 /*--------------------------------*/
451 #if FF_USE_LFN == 0 /* Non-LFN configuration */
453 #error LFN must be enabled when enable exFAT
456 #define INIT_NAMBUF(fs)
457 #define FREE_NAMBUF()
458 #define LEAVE_MKFS(res) return res
460 #else /* LFN configurations */
461 #if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
462 #error Wrong setting of FF_MAX_LFN
464 #if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
465 #error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
467 #if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
468 #error Wrong setting of FF_LFN_UNICODE
470 static const BYTE LfnOfs
[] = { 1 , 3 , 5 , 7 , 9 , 14 , 16 , 18 , 20 , 22 , 24 , 28 , 30 }; /* FAT: Offset of LFN characters in the directory entry */
471 #define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
473 #if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
475 static BYTE DirBuf
[ MAXDIRB ( FF_MAX_LFN
)]; /* Directory entry block scratchpad buffer */
477 static WCHAR LfnBuf
[ FF_MAX_LFN
+ 1 ]; /* LFN working buffer */
479 #define INIT_NAMBUF(fs)
480 #define FREE_NAMBUF()
481 #define LEAVE_MKFS(res) return res
483 #elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
485 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
486 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
487 #define FREE_NAMBUF()
489 #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
490 #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; }
491 #define FREE_NAMBUF()
493 #define LEAVE_MKFS(res) return res
495 #elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
497 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
498 #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); }
499 #define FREE_NAMBUF() ff_memfree(lfn)
501 #define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */
502 #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
503 #define FREE_NAMBUF() ff_memfree(lfn)
505 #define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
506 #define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
509 #error Wrong setting of FF_USE_LFN
511 #endif /* FF_USE_LFN == 1 */
512 #endif /* FF_USE_LFN == 0 */
516 /*--------------------------------*/
517 /* Code conversion tables */
518 /*--------------------------------*/
520 #if FF_CODE_PAGE == 0 /* Run-time code page configuration */
521 #define CODEPAGE CodePage
522 static WORD CodePage
; /* Current code page */
523 static const BYTE
* ExCvt
, * DbcTbl
; /* Pointer to current SBCS up-case table and DBCS code range table below */
524 static const BYTE Ct437
[] = TBL_CT437
;
525 static const BYTE Ct720
[] = TBL_CT720
;
526 static const BYTE Ct737
[] = TBL_CT737
;
527 static const BYTE Ct771
[] = TBL_CT771
;
528 static const BYTE Ct775
[] = TBL_CT775
;
529 static const BYTE Ct850
[] = TBL_CT850
;
530 static const BYTE Ct852
[] = TBL_CT852
;
531 static const BYTE Ct855
[] = TBL_CT855
;
532 static const BYTE Ct857
[] = TBL_CT857
;
533 static const BYTE Ct860
[] = TBL_CT860
;
534 static const BYTE Ct861
[] = TBL_CT861
;
535 static const BYTE Ct862
[] = TBL_CT862
;
536 static const BYTE Ct863
[] = TBL_CT863
;
537 static const BYTE Ct864
[] = TBL_CT864
;
538 static const BYTE Ct865
[] = TBL_CT865
;
539 static const BYTE Ct866
[] = TBL_CT866
;
540 static const BYTE Ct869
[] = TBL_CT869
;
541 static const BYTE Dc932
[] = TBL_DC932
;
542 static const BYTE Dc936
[] = TBL_DC936
;
543 static const BYTE Dc949
[] = TBL_DC949
;
544 static const BYTE Dc950
[] = TBL_DC950
;
546 #elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
547 #define CODEPAGE FF_CODE_PAGE
548 static const BYTE ExCvt
[] = MKCVTBL ( TBL_CT
, FF_CODE_PAGE
);
550 #else /* Static code page configuration (DBCS) */
551 #define CODEPAGE FF_CODE_PAGE
552 static const BYTE DbcTbl
[] = MKCVTBL ( TBL_DC
, FF_CODE_PAGE
);
559 /*--------------------------------------------------------------------------
561 Module Private Functions
563 ---------------------------------------------------------------------------*/
566 /*-----------------------------------------------------------------------*/
567 /* Load/Store multi-byte word in the FAT structure */
568 /*-----------------------------------------------------------------------*/
570 static WORD
ld_word ( const BYTE
* ptr
) /* Load a 2-byte little-endian word */
575 rv
= rv
<< 8 | ptr
[ 0 ];
579 static DWORD
ld_dword ( const BYTE
* ptr
) /* Load a 4-byte little-endian word */
584 rv
= rv
<< 8 | ptr
[ 2 ];
585 rv
= rv
<< 8 | ptr
[ 1 ];
586 rv
= rv
<< 8 | ptr
[ 0 ];
591 static QWORD
ld_qword ( const BYTE
* ptr
) /* Load an 8-byte little-endian word */
596 rv
= rv
<< 8 | ptr
[ 6 ];
597 rv
= rv
<< 8 | ptr
[ 5 ];
598 rv
= rv
<< 8 | ptr
[ 4 ];
599 rv
= rv
<< 8 | ptr
[ 3 ];
600 rv
= rv
<< 8 | ptr
[ 2 ];
601 rv
= rv
<< 8 | ptr
[ 1 ];
602 rv
= rv
<< 8 | ptr
[ 0 ];
608 static void st_word ( BYTE
* ptr
, WORD val
) /* Store a 2-byte word in little-endian */
610 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
614 static void st_dword ( BYTE
* ptr
, DWORD val
) /* Store a 4-byte word in little-endian */
616 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
617 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
618 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
623 static void st_qword ( BYTE
* ptr
, QWORD val
) /* Store an 8-byte word in little-endian */
625 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
626 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
627 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
628 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
629 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
630 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
631 * ptr
++ = ( BYTE
) val
; val
>>= 8 ;
635 #endif /* !FF_FS_READONLY */
639 /*-----------------------------------------------------------------------*/
640 /* String functions */
641 /*-----------------------------------------------------------------------*/
643 /* Copy memory to memory */
644 static void mem_cpy ( void * dst
, const void * src
, UINT cnt
)
646 BYTE
* d
= ( BYTE
*) dst
;
647 const BYTE
* s
= ( const BYTE
*) src
;
657 /* Fill memory block */
658 static void mem_set ( void * dst
, int val
, UINT cnt
)
660 BYTE
* d
= ( BYTE
*) dst
;
668 /* Compare memory block */
669 static int mem_cmp ( const void * dst
, const void * src
, UINT cnt
) /* ZR:same, NZ:different */
671 const BYTE
* d
= ( const BYTE
*) dst
, * s
= ( const BYTE
*) src
;
676 } while (-- cnt
&& r
== 0 );
682 /* Check if chr is contained in the string */
683 static int chk_chr ( const char * str
, int chr
) /* NZ:contained, ZR:not contained */
685 while (* str
&& * str
!= chr
) str
++;
690 /* Test if the character is DBC 1st byte */
691 static int dbc_1st ( BYTE c
)
693 #if FF_CODE_PAGE == 0 /* Variable code page */
694 if ( DbcTbl
&& c
>= DbcTbl
[ 0 ]) {
695 if ( c
<= DbcTbl
[ 1 ]) return 1 ; /* 1st byte range 1 */
696 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ; /* 1st byte range 2 */
698 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
699 if ( c
>= DbcTbl
[ 0 ]) {
700 if ( c
<= DbcTbl
[ 1 ]) return 1 ;
701 if ( c
>= DbcTbl
[ 2 ] && c
<= DbcTbl
[ 3 ]) return 1 ;
703 #else /* SBCS fixed code page */
704 if ( c
!= 0 ) return 0 ; /* Always false */
710 /* Test if the character is DBC 2nd byte */
711 static int dbc_2nd ( BYTE c
)
713 #if FF_CODE_PAGE == 0 /* Variable code page */
714 if ( DbcTbl
&& c
>= DbcTbl
[ 4 ]) {
715 if ( c
<= DbcTbl
[ 5 ]) return 1 ; /* 2nd byte range 1 */
716 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ; /* 2nd byte range 2 */
717 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ; /* 2nd byte range 3 */
719 #elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
720 if ( c
>= DbcTbl
[ 4 ]) {
721 if ( c
<= DbcTbl
[ 5 ]) return 1 ;
722 if ( c
>= DbcTbl
[ 6 ] && c
<= DbcTbl
[ 7 ]) return 1 ;
723 if ( c
>= DbcTbl
[ 8 ] && c
<= DbcTbl
[ 9 ]) return 1 ;
725 #else /* SBCS fixed code page */
726 if ( c
!= 0 ) return 0 ; /* Always false */
734 /* Get a character from TCHAR string in defined API encodeing */
735 static DWORD
tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */
736 const TCHAR
** str
/* Pointer to pointer to TCHAR string in configured encoding */
740 const TCHAR
* p
= * str
;
742 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
745 uc
= * p
++; /* Get a unit */
746 if ( IsSurrogate ( uc
)) { /* Surrogate? */
747 wc
= * p
++; /* Get low surrogate */
748 if (! IsSurrogateH ( uc
) || ! IsSurrogateL ( wc
)) return 0xFFFFFFFF ; /* Wrong surrogate? */
752 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
756 uc
= ( BYTE
)* p
++; /* Get a unit */
757 if ( uc
& 0x80 ) { /* Multiple byte code? */
758 if (( uc
& 0xE0 ) == 0xC0 ) { /* 2-byte sequence? */
761 if (( uc
& 0xF0 ) == 0xE0 ) { /* 3-byte sequence? */
764 if (( uc
& 0xF8 ) == 0xF0 ) { /* 4-byte sequence? */
766 } else { /* Wrong sequence */
771 do { /* Get trailing bytes */
773 if (( b
& 0xC0 ) != 0x80 ) return 0xFFFFFFFF ; /* Wrong sequence? */
774 uc
= uc
<< 6 | ( b
& 0x3F );
776 if ( uc
< 0x80 || IsSurrogate ( uc
) || uc
>= 0x110000 ) return 0xFFFFFFFF ; /* Wrong code? */
777 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
780 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
781 uc
= ( TCHAR
)* p
++; /* Get a unit */
782 if ( uc
>= 0x110000 ) return 0xFFFFFFFF ; /* Wrong code? */
783 if ( uc
>= 0x010000 ) uc
= 0xD800DC00 | (( uc
- 0x10000 ) << 6 & 0x3FF0000 ) | ( uc
& 0x3FF ); /* Make a surrogate pair if needed */
785 #else /* ANSI/OEM input */
789 wc
= ( BYTE
)* p
++; /* Get a byte */
790 if ( dbc_1st (( BYTE
) wc
)) { /* Is it a DBC 1st byte? */
791 b
= ( BYTE
)* p
++; /* Get 2nd byte */
792 if (! dbc_2nd ( b
)) return 0xFFFFFFFF ; /* Invalid code? */
793 wc
= ( wc
<< 8 ) + b
; /* Make a DBC */
796 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM ==> Unicode */
797 if ( wc
== 0 ) return 0xFFFFFFFF ; /* Invalid code? */
802 * str
= p
; /* Next read pointer */
807 /* Output a TCHAR string in defined API encoding */
808 static BYTE
put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
809 DWORD chr
, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */
810 TCHAR
* buf
, /* Output buffer */
811 UINT szb
/* Size of the buffer */
814 #if FF_LFN_UNICODE == 1 /* UTF-16 output */
817 hs
= ( WCHAR
)( chr
>> 16 );
819 if ( hs
== 0 ) { /* Single encoding unit? */
820 if ( szb
< 1 || IsSurrogate ( wc
)) return 0 ; /* Buffer overflow or wrong code? */
824 if ( szb
< 2 || ! IsSurrogateH ( hs
) || ! IsSurrogateL ( wc
)) return 0 ; /* Buffer overflow or wrong surrogate? */
829 #elif FF_LFN_UNICODE == 2 /* UTF-8 output */
832 if ( chr
< 0x80 ) { /* Single byte code? */
833 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
837 if ( chr
< 0x800 ) { /* 2-byte sequence? */
838 if ( szb
< 2 ) return 0 ; /* Buffer overflow? */
839 * buf
++ = ( TCHAR
)( 0xC0 | ( chr
>> 6 & 0x1F ));
840 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
843 if ( chr
< 0x10000 ) { /* 3-byte sequence? */
844 if ( szb
< 3 || IsSurrogate ( chr
)) return 0 ; /* Buffer overflow or wrong code? */
845 * buf
++ = ( TCHAR
)( 0xE0 | ( chr
>> 12 & 0x0F ));
846 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
847 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
850 /* 4-byte sequence */
851 if ( szb
< 4 ) return 0 ; /* Buffer overflow? */
852 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
853 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
854 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
855 chr
= ( hc
| chr
) + 0x10000 ;
856 * buf
++ = ( TCHAR
)( 0xF0 | ( chr
>> 18 & 0x07 ));
857 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 12 & 0x3F ));
858 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 6 & 0x3F ));
859 * buf
++ = ( TCHAR
)( 0x80 | ( chr
>> 0 & 0x3F ));
862 #elif FF_LFN_UNICODE == 3 /* UTF-32 output */
865 if ( szb
< 1 ) return 0 ; /* Buffer overflow? */
866 if ( chr
>= 0x10000 ) { /* Out of BMP? */
867 hc
= (( chr
& 0xFFFF0000 ) - 0xD8000000 ) >> 6 ; /* Get high 10 bits */
868 chr
= ( chr
& 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
869 if ( hc
>= 0x100000 || chr
>= 0x400 ) return 0 ; /* Wrong surrogate? */
870 chr
= ( hc
| chr
) + 0x10000 ;
875 #else /* ANSI/OEM output */
878 wc
= ff_uni2oem ( chr
, CODEPAGE
);
879 if ( wc
>= 0x100 ) { /* Is this a DBC? */
880 if ( szb
< 2 ) return 0 ;
881 * buf
++ = ( char )( wc
>> 8 ); /* Store DBC 1st byte */
882 * buf
++ = ( TCHAR
) wc
; /* Store DBC 2nd byte */
885 if ( wc
== 0 || szb
< 1 ) return 0 ; /* Invalid char or buffer overflow? */
886 * buf
++ = ( TCHAR
) wc
; /* Store the character */
890 #endif /* FF_USE_LFN */
894 /*-----------------------------------------------------------------------*/
895 /* Request/Release grant to access the volume */
896 /*-----------------------------------------------------------------------*/
897 static int lock_fs ( /* 1:Ok, 0:timeout */
898 FATFS
* fs
/* Filesystem object */
901 return ff_req_grant ( fs
-> sobj
);
905 static void unlock_fs (
906 FATFS
* fs
, /* Filesystem object */
907 FRESULT res
/* Result code to be returned */
910 if ( fs
&& res
!= FR_NOT_ENABLED
&& res
!= FR_INVALID_DRIVE
&& res
!= FR_TIMEOUT
) {
911 ff_rel_grant ( fs
-> sobj
);
920 /*-----------------------------------------------------------------------*/
921 /* File lock control functions */
922 /*-----------------------------------------------------------------------*/
924 static FRESULT
chk_lock ( /* Check if the file can be accessed */
925 DIR * dp
, /* Directory object pointing the file to be checked */
926 int acc
/* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
931 /* Search open object table for the object */
933 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
934 if ( Files
[ i
]. fs
) { /* Existing entry */
935 if ( Files
[ i
]. fs
== dp
-> obj
. fs
&& /* Check if the object matches with an open object */
936 Files
[ i
]. clu
== dp
-> obj
. sclust
&&
937 Files
[ i
]. ofs
== dp
-> dptr
) break ;
938 } else { /* Blank entry */
942 if ( i
== FF_FS_LOCK
) { /* The object has not been opened */
943 return (! be
&& acc
!= 2 ) ? FR_TOO_MANY_OPEN_FILES
: FR_OK
; /* Is there a blank entry for new object? */
946 /* The object was opened. Reject any open against writing file and all write mode open */
947 return ( acc
!= 0 || Files
[ i
]. ctr
== 0x100 ) ? FR_LOCKED
: FR_OK
;
951 static int enq_lock ( void ) /* Check if an entry is available for a new object */
955 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ;
956 return ( i
== FF_FS_LOCK
) ? 0 : 1 ;
960 static UINT
inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
961 DIR * dp
, /* Directory object pointing the file to register or increment */
962 int acc
/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
968 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) { /* Find the object */
969 if ( Files
[ i
]. fs
== dp
-> obj
. fs
&&
970 Files
[ i
]. clu
== dp
-> obj
. sclust
&&
971 Files
[ i
]. ofs
== dp
-> dptr
) break ;
974 if ( i
== FF_FS_LOCK
) { /* Not opened. Register it as new. */
975 for ( i
= 0 ; i
< FF_FS_LOCK
&& Files
[ i
]. fs
; i
++) ;
976 if ( i
== FF_FS_LOCK
) return 0 ; /* No free entry to register (int err) */
977 Files
[ i
]. fs
= dp
-> obj
. fs
;
978 Files
[ i
]. clu
= dp
-> obj
. sclust
;
979 Files
[ i
]. ofs
= dp
-> dptr
;
983 if ( acc
>= 1 && Files
[ i
]. ctr
) return 0 ; /* Access violation (int err) */
985 Files
[ i
]. ctr
= acc
? 0x100 : Files
[ i
]. ctr
+ 1 ; /* Set semaphore value */
987 return i
+ 1 ; /* Index number origin from 1 */
991 static FRESULT
dec_lock ( /* Decrement object open counter */
992 UINT i
/* Semaphore index (1..) */
999 if (-- i
< FF_FS_LOCK
) { /* Index number origin from 0 */
1001 if ( n
== 0x100 ) n
= 0 ; /* If write mode open, delete the entry */
1002 if ( n
> 0 ) n
--; /* Decrement read mode open count */
1004 if ( n
== 0 ) Files
[ i
]. fs
= 0 ; /* Delete the entry if open count gets zero */
1007 res
= FR_INT_ERR
; /* Invalid index nunber */
1013 static void clear_lock ( /* Clear lock entries of the volume */
1019 for ( i
= 0 ; i
< FF_FS_LOCK
; i
++) {
1020 if ( Files
[ i
]. fs
== fs
) Files
[ i
]. fs
= 0 ;
1024 #endif /* FF_FS_LOCK != 0 */
1028 /*-----------------------------------------------------------------------*/
1029 /* Move/Flush disk access window in the filesystem object */
1030 /*-----------------------------------------------------------------------*/
1032 static FRESULT
sync_window ( /* Returns FR_OK or FR_DISK_ERR */
1033 FATFS
* fs
/* Filesystem object */
1036 FRESULT res
= FR_OK
;
1039 if ( fs
-> wflag
) { /* Is the disk access window dirty */
1040 if ( disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 ) == RES_OK
) { /* Write back the window */
1041 fs
-> wflag
= 0 ; /* Clear window dirty flag */
1042 if ( fs
-> winsect
- fs
-> fatbase
< fs
-> fsize
) { /* Is it in the 1st FAT? */
1043 if ( fs
-> n_fats
== 2 ) disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
+ fs
-> fsize
, 1 ); /* Reflect it to 2nd FAT if needed */
1054 static FRESULT
move_window ( /* Returns FR_OK or FR_DISK_ERR */
1055 FATFS
* fs
, /* Filesystem object */
1056 DWORD sector
/* Sector number to make appearance in the fs->win[] */
1059 FRESULT res
= FR_OK
;
1062 if ( sector
!= fs
-> winsect
) { /* Window offset changed? */
1064 res
= sync_window ( fs
); /* Write-back changes */
1066 if ( res
== FR_OK
) { /* Fill sector window with new data */
1067 if ( disk_read ( fs
-> pdrv
, fs
-> win
, sector
, 1 ) != RES_OK
) {
1068 sector
= 0xFFFFFFFF ; /* Invalidate window if read data is not valid */
1071 fs
-> winsect
= sector
;
1081 /*-----------------------------------------------------------------------*/
1082 /* Synchronize filesystem and data on the storage */
1083 /*-----------------------------------------------------------------------*/
1085 static FRESULT
sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
1086 FATFS
* fs
/* Filesystem object */
1092 res
= sync_window ( fs
);
1094 if ( fs
-> fs_type
== FS_FAT32
&& fs
-> fsi_flag
== 1 ) { /* FAT32: Update FSInfo sector if needed */
1095 /* Create FSInfo structure */
1096 mem_set ( fs
-> win
, 0 , SS ( fs
));
1097 st_word ( fs
-> win
+ BS_55AA
, 0xAA55 );
1098 st_dword ( fs
-> win
+ FSI_LeadSig
, 0x41615252 );
1099 st_dword ( fs
-> win
+ FSI_StrucSig
, 0x61417272 );
1100 st_dword ( fs
-> win
+ FSI_Free_Count
, fs
-> free_clst
);
1101 st_dword ( fs
-> win
+ FSI_Nxt_Free
, fs
-> last_clst
);
1102 /* Write it into the FSInfo sector */
1103 fs
-> winsect
= fs
-> volbase
+ 1 ;
1104 disk_write ( fs
-> pdrv
, fs
-> win
, fs
-> winsect
, 1 );
1107 /* Make sure that no pending write process in the lower layer */
1108 if ( disk_ioctl ( fs
-> pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) res
= FR_DISK_ERR
;
1118 /*-----------------------------------------------------------------------*/
1119 /* Get physical sector number from cluster number */
1120 /*-----------------------------------------------------------------------*/
1122 static DWORD
clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
1123 FATFS
* fs
, /* Filesystem object */
1124 DWORD clst
/* Cluster# to be converted */
1127 clst
-= 2 ; /* Cluster number is origin from 2 */
1128 if ( clst
>= fs
-> n_fatent
- 2 ) return 0 ; /* Is it invalid cluster number? */
1129 return fs
-> database
+ fs
-> csize
* clst
; /* Start sector number of the cluster */
1135 /*-----------------------------------------------------------------------*/
1136 /* FAT access - Read value of a FAT entry */
1137 /*-----------------------------------------------------------------------*/
1139 static DWORD
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
1140 FFOBJID
* obj
, /* Corresponding object */
1141 DWORD clst
/* Cluster number to get the value */
1146 FATFS
* fs
= obj
-> fs
;
1149 if ( clst
< 2 || clst
>= fs
-> n_fatent
) { /* Check if in valid range */
1150 val
= 1 ; /* Internal error */
1153 val
= 0xFFFFFFFF ; /* Default value falls on disk error */
1155 switch ( fs
-> fs_type
) {
1157 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ;
1158 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1159 wc
= fs
-> win
[ bc
++ % SS ( fs
)]; /* Get 1st byte of the entry */
1160 if ( move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
))) != FR_OK
) break ;
1161 wc
|= fs
-> win
[ bc
% SS ( fs
)] << 8 ; /* Merge 2nd byte of the entry */
1162 val
= ( clst
& 1 ) ? ( wc
>> 4 ) : ( wc
& 0xFFF ); /* Adjust bit position */
1166 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 ))) != FR_OK
) break ;
1167 val
= ld_word ( fs
-> win
+ clst
* 2 % SS ( fs
)); /* Simple WORD array */
1171 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1172 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x0FFFFFFF ; /* Simple DWORD array but mask out upper 4 bits */
1176 if (( obj
-> objsize
!= 0 && obj
-> sclust
!= 0 ) || obj
-> stat
== 0 ) { /* Object except root dir must have valid data length */
1177 DWORD cofs
= clst
- obj
-> sclust
; /* Offset from start cluster */
1178 DWORD clen
= ( DWORD
)(( obj
-> objsize
- 1 ) / SS ( fs
)) / fs
-> csize
; /* Number of clusters - 1 */
1180 if ( obj
-> stat
== 2 && cofs
<= clen
) { /* Is it a contiguous chain? */
1181 val
= ( cofs
== clen
) ? 0x7FFFFFFF : clst
+ 1 ; /* No data on the FAT, generate the value */
1184 if ( obj
-> stat
== 3 && cofs
< obj
-> n_cont
) { /* Is it in the 1st fragment? */
1185 val
= clst
+ 1 ; /* Generate the value */
1188 if ( obj
-> stat
!= 2 ) { /* Get value from FAT if FAT chain is valid */
1189 if ( obj
-> n_frag
!= 0 ) { /* Is it on the growing edge? */
1190 val
= 0x7FFFFFFF ; /* Generate EOC */
1192 if ( move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 ))) != FR_OK
) break ;
1193 val
= ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0x7FFFFFFF ;
1201 val
= 1 ; /* Internal error */
1212 /*-----------------------------------------------------------------------*/
1213 /* FAT access - Change value of a FAT entry */
1214 /*-----------------------------------------------------------------------*/
1216 static FRESULT
put_fat ( /* FR_OK(0):succeeded, !=0:error */
1217 FATFS
* fs
, /* Corresponding filesystem object */
1218 DWORD clst
, /* FAT index number (cluster number) to be changed */
1219 DWORD val
/* New value to be set to the entry */
1224 FRESULT res
= FR_INT_ERR
;
1227 if ( clst
>= 2 && clst
< fs
-> n_fatent
) { /* Check if in valid range */
1228 switch ( fs
-> fs_type
) {
1230 bc
= ( UINT
) clst
; bc
+= bc
/ 2 ; /* bc: byte offset of the entry */
1231 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1232 if ( res
!= FR_OK
) break ;
1233 p
= fs
-> win
+ bc
++ % SS ( fs
);
1234 * p
= ( clst
& 1 ) ? ((* p
& 0x0F ) | (( BYTE
) val
<< 4 )) : ( BYTE
) val
; /* Put 1st byte */
1236 res
= move_window ( fs
, fs
-> fatbase
+ ( bc
/ SS ( fs
)));
1237 if ( res
!= FR_OK
) break ;
1238 p
= fs
-> win
+ bc
% SS ( fs
);
1239 * p
= ( clst
& 1 ) ? ( BYTE
)( val
>> 4 ) : ((* p
& 0xF0 ) | (( BYTE
)( val
>> 8 ) & 0x0F )); /* Put 2nd byte */
1244 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 2 )));
1245 if ( res
!= FR_OK
) break ;
1246 st_word ( fs
-> win
+ clst
* 2 % SS ( fs
), ( WORD
) val
); /* Simple WORD array */
1254 res
= move_window ( fs
, fs
-> fatbase
+ ( clst
/ ( SS ( fs
) / 4 )));
1255 if ( res
!= FR_OK
) break ;
1256 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1257 val
= ( val
& 0x0FFFFFFF ) | ( ld_dword ( fs
-> win
+ clst
* 4 % SS ( fs
)) & 0xF0000000 );
1259 st_dword ( fs
-> win
+ clst
* 4 % SS ( fs
), val
);
1267 #endif /* !FF_FS_READONLY */
1272 #if FF_FS_EXFAT && !FF_FS_READONLY
1273 /*-----------------------------------------------------------------------*/
1274 /* exFAT: Accessing FAT and Allocation Bitmap */
1275 /*-----------------------------------------------------------------------*/
1277 /*--------------------------------------*/
1278 /* Find a contiguous free cluster block */
1279 /*--------------------------------------*/
1281 static DWORD
find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
1282 FATFS
* fs
, /* Filesystem object */
1283 DWORD clst
, /* Cluster number to scan from */
1284 DWORD ncl
/* Number of contiguous clusters to find (1..) */
1289 DWORD val
, scl
, ctr
;
1292 clst
-= 2 ; /* The first bit in the bitmap corresponds to cluster #2 */
1293 if ( clst
>= fs
-> n_fatent
- 2 ) clst
= 0 ;
1294 scl
= val
= clst
; ctr
= 0 ;
1296 if ( move_window ( fs
, fs
-> database
+ val
/ 8 / SS ( fs
)) != FR_OK
) return 0xFFFFFFFF ; /* (assuming bitmap is located top of the cluster heap) */
1297 i
= val
/ 8 % SS ( fs
); bm
= 1 << ( val
% 8 );
1300 bv
= fs
-> win
[ i
] & bm
; bm
<<= 1 ; /* Get bit value */
1301 if (++ val
>= fs
-> n_fatent
- 2 ) { /* Next cluster (with wrap-around) */
1302 val
= 0 ; bm
= 0 ; i
= SS ( fs
);
1304 if ( bv
== 0 ) { /* Is it a free cluster? */
1305 if (++ ctr
== ncl
) return scl
+ 2 ; /* Check if run length is sufficient for required */
1307 scl
= val
; ctr
= 0 ; /* Encountered a cluster in-use, restart to scan */
1309 if ( val
== clst
) return 0 ; /* All cluster scanned? */
1312 } while (++ i
< SS ( fs
));
1317 /*----------------------------------------*/
1318 /* Set/Clear a block of allocation bitmap */
1319 /*----------------------------------------*/
1321 static FRESULT
change_bitmap (
1322 FATFS
* fs
, /* Filesystem object */
1323 DWORD clst
, /* Cluster number to change from */
1324 DWORD ncl
, /* Number of clusters to be changed */
1325 int bv
/* bit value to be set (0 or 1) */
1333 clst
-= 2 ; /* The first bit corresponds to cluster #2 */
1334 sect
= fs
-> database
+ clst
/ 8 / SS ( fs
); /* Sector address (assuming bitmap is located top of the cluster heap) */
1335 i
= clst
/ 8 % SS ( fs
); /* Byte offset in the sector */
1336 bm
= 1 << ( clst
% 8 ); /* Bit mask in the byte */
1338 if ( move_window ( fs
, sect
++) != FR_OK
) return FR_DISK_ERR
;
1341 if ( bv
== ( int )(( fs
-> win
[ i
] & bm
) != 0 )) return FR_INT_ERR
; /* Is the bit expected value? */
1342 fs
-> win
[ i
] ^= bm
; /* Flip the bit */
1344 if (-- ncl
== 0 ) return FR_OK
; /* All bits processed? */
1345 } while ( bm
<<= 1 ); /* Next bit */
1347 } while (++ i
< SS ( fs
)); /* Next byte */
1353 /*---------------------------------------------*/
1354 /* Fill the first fragment of the FAT chain */
1355 /*---------------------------------------------*/
1357 static FRESULT
fill_first_frag (
1358 FFOBJID
* obj
/* Pointer to the corresponding object */
1365 if ( obj
-> stat
== 3 ) { /* Has the object been changed 'fragmented' in this session? */
1366 for ( cl
= obj
-> sclust
, n
= obj
-> n_cont
; n
; cl
++, n
--) { /* Create cluster chain on the FAT */
1367 res
= put_fat ( obj
-> fs
, cl
, cl
+ 1 );
1368 if ( res
!= FR_OK
) return res
;
1370 obj
-> stat
= 0 ; /* Change status 'FAT chain is valid' */
1376 /*---------------------------------------------*/
1377 /* Fill the last fragment of the FAT chain */
1378 /*---------------------------------------------*/
1380 static FRESULT
fill_last_frag (
1381 FFOBJID
* obj
, /* Pointer to the corresponding object */
1382 DWORD lcl
, /* Last cluster of the fragment */
1383 DWORD term
/* Value to set the last FAT entry */
1389 while ( obj
-> n_frag
> 0 ) { /* Create the chain of last fragment */
1390 res
= put_fat ( obj
-> fs
, lcl
- obj
-> n_frag
+ 1 , ( obj
-> n_frag
> 1 ) ? lcl
- obj
-> n_frag
+ 2 : term
);
1391 if ( res
!= FR_OK
) return res
;
1397 #endif /* FF_FS_EXFAT && !FF_FS_READONLY */
1402 /*-----------------------------------------------------------------------*/
1403 /* FAT handling - Remove a cluster chain */
1404 /*-----------------------------------------------------------------------*/
1406 static FRESULT
remove_chain ( /* FR_OK(0):succeeded, !=0:error */
1407 FFOBJID
* obj
, /* Corresponding object */
1408 DWORD clst
, /* Cluster to remove a chain from */
1409 DWORD pclst
/* Previous cluster of clst (0:entire chain) */
1412 FRESULT res
= FR_OK
;
1414 FATFS
* fs
= obj
-> fs
;
1415 #if FF_FS_EXFAT || FF_USE_TRIM
1416 DWORD scl
= clst
, ecl
= clst
;
1422 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Check if in valid range */
1424 /* Mark the previous cluster 'EOC' on the FAT if it exists */
1425 if ( pclst
!= 0 && (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
|| obj
-> stat
!= 2 )) {
1426 res
= put_fat ( fs
, pclst
, 0xFFFFFFFF );
1427 if ( res
!= FR_OK
) return res
;
1430 /* Remove the chain */
1432 nxt
= get_fat ( obj
, clst
); /* Get cluster status */
1433 if ( nxt
== 0 ) break ; /* Empty cluster? */
1434 if ( nxt
== 1 ) return FR_INT_ERR
; /* Internal error? */
1435 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error? */
1436 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) {
1437 res
= put_fat ( fs
, clst
, 0 ); /* Mark the cluster 'free' on the FAT */
1438 if ( res
!= FR_OK
) return res
;
1440 if ( fs
-> free_clst
< fs
-> n_fatent
- 2 ) { /* Update FSINFO */
1444 #if FF_FS_EXFAT || FF_USE_TRIM
1445 if ( ecl
+ 1 == nxt
) { /* Is next cluster contiguous? */
1447 } else { /* End of contiguous cluster block */
1449 if ( fs
-> fs_type
== FS_EXFAT
) {
1450 res
= change_bitmap ( fs
, scl
, ecl
- scl
+ 1 , 0 ); /* Mark the cluster block 'free' on the bitmap */
1451 if ( res
!= FR_OK
) return res
;
1455 rt
[ 0 ] = clst2sect ( fs
, scl
); /* Start of data area freed */
1456 rt
[ 1 ] = clst2sect ( fs
, ecl
) + fs
-> csize
- 1 ; /* End of data area freed */
1457 disk_ioctl ( fs
-> pdrv
, CTRL_TRIM
, rt
); /* Inform device the data in the block is no longer needed */
1462 clst
= nxt
; /* Next cluster */
1463 } while ( clst
< fs
-> n_fatent
); /* Repeat while not the last link */
1466 /* Some post processes for chain status */
1467 if ( fs
-> fs_type
== FS_EXFAT
) {
1468 if ( pclst
== 0 ) { /* Has the entire chain been removed? */
1469 obj
-> stat
= 0 ; /* Change the chain status 'initial' */
1471 if ( obj
-> stat
== 0 ) { /* Is it a fragmented chain from the beginning of this session? */
1472 clst
= obj
-> sclust
; /* Follow the chain to check if it gets contiguous */
1473 while ( clst
!= pclst
) {
1474 nxt
= get_fat ( obj
, clst
);
1475 if ( nxt
< 2 ) return FR_INT_ERR
;
1476 if ( nxt
== 0xFFFFFFFF ) return FR_DISK_ERR
;
1477 if ( nxt
!= clst
+ 1 ) break ; /* Not contiguous? */
1480 if ( clst
== pclst
) { /* Has the chain got contiguous again? */
1481 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1484 if ( obj
-> stat
== 3 && pclst
>= obj
-> sclust
&& pclst
<= obj
-> sclust
+ obj
-> n_cont
) { /* Was the chain fragmented in this session and got contiguous again? */
1485 obj
-> stat
= 2 ; /* Change the chain status 'contiguous' */
1497 /*-----------------------------------------------------------------------*/
1498 /* FAT handling - Stretch a chain or Create a new chain */
1499 /*-----------------------------------------------------------------------*/
1501 static DWORD
create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
1502 FFOBJID
* obj
, /* Corresponding object */
1503 DWORD clst
/* Cluster# to stretch, 0:Create a new chain */
1508 FATFS
* fs
= obj
-> fs
;
1511 if ( clst
== 0 ) { /* Create a new chain */
1512 scl
= fs
-> last_clst
; /* Suggested cluster to start to find */
1513 if ( scl
== 0 || scl
>= fs
-> n_fatent
) scl
= 1 ;
1515 else { /* Stretch a chain */
1516 cs
= get_fat ( obj
, clst
); /* Check the cluster status */
1517 if ( cs
< 2 ) return 1 ; /* Test for insanity */
1518 if ( cs
== 0xFFFFFFFF ) return cs
; /* Test for disk error */
1519 if ( cs
< fs
-> n_fatent
) return cs
; /* It is already followed by next cluster */
1520 scl
= clst
; /* Cluster to start to find */
1522 if ( fs
-> free_clst
== 0 ) return 0 ; /* No free cluster */
1525 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
1526 ncl
= find_bitmap ( fs
, scl
, 1 ); /* Find a free cluster */
1527 if ( ncl
== 0 || ncl
== 0xFFFFFFFF ) return ncl
; /* No free cluster or hard error? */
1528 res
= change_bitmap ( fs
, ncl
, 1 , 1 ); /* Mark the cluster 'in use' */
1529 if ( res
== FR_INT_ERR
) return 1 ;
1530 if ( res
== FR_DISK_ERR
) return 0xFFFFFFFF ;
1531 if ( clst
== 0 ) { /* Is it a new chain? */
1532 obj
-> stat
= 2 ; /* Set status 'contiguous' */
1533 } else { /* It is a stretched chain */
1534 if ( obj
-> stat
== 2 && ncl
!= scl
+ 1 ) { /* Is the chain got fragmented? */
1535 obj
-> n_cont
= scl
- obj
-> sclust
; /* Set size of the contiguous part */
1536 obj
-> stat
= 3 ; /* Change status 'just fragmented' */
1539 if ( obj
-> stat
!= 2 ) { /* Is the file non-contiguous? */
1540 if ( ncl
== clst
+ 1 ) { /* Is the cluster next to previous one? */
1541 obj
-> n_frag
= obj
-> n_frag
? obj
-> n_frag
+ 1 : 2 ; /* Increment size of last framgent */
1542 } else { /* New fragment */
1543 if ( obj
-> n_frag
== 0 ) obj
-> n_frag
= 1 ;
1544 res
= fill_last_frag ( obj
, clst
, ncl
); /* Fill last fragment on the FAT and link it to new one */
1545 if ( res
== FR_OK
) obj
-> n_frag
= 1 ;
1550 { /* On the FAT/FAT32 volume */
1552 if ( scl
== clst
) { /* Stretching an existing chain? */
1553 ncl
= scl
+ 1 ; /* Test if next cluster is free */
1554 if ( ncl
>= fs
-> n_fatent
) ncl
= 2 ;
1555 cs
= get_fat ( obj
, ncl
); /* Get next cluster status */
1556 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1557 if ( cs
!= 0 ) { /* Not free? */
1558 cs
= fs
-> last_clst
; /* Start at suggested cluster if it is valid */
1559 if ( cs
>= 2 && cs
< fs
-> n_fatent
) scl
= cs
;
1563 if ( ncl
== 0 ) { /* The new cluster cannot be contiguous and find another fragment */
1564 ncl
= scl
; /* Start cluster */
1566 ncl
++; /* Next cluster */
1567 if ( ncl
>= fs
-> n_fatent
) { /* Check wrap-around */
1569 if ( ncl
> scl
) return 0 ; /* No free cluster found? */
1571 cs
= get_fat ( obj
, ncl
); /* Get the cluster status */
1572 if ( cs
== 0 ) break ; /* Found a free cluster? */
1573 if ( cs
== 1 || cs
== 0xFFFFFFFF ) return cs
; /* Test for error */
1574 if ( ncl
== scl
) return 0 ; /* No free cluster found? */
1577 res
= put_fat ( fs
, ncl
, 0xFFFFFFFF ); /* Mark the new cluster 'EOC' */
1578 if ( res
== FR_OK
&& clst
!= 0 ) {
1579 res
= put_fat ( fs
, clst
, ncl
); /* Link it from the previous one if needed */
1583 if ( res
== FR_OK
) { /* Update FSINFO if function succeeded. */
1584 fs
-> last_clst
= ncl
;
1585 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) fs
-> free_clst
--;
1588 ncl
= ( res
== FR_DISK_ERR
) ? 0xFFFFFFFF : 1 ; /* Failed. Generate error status */
1591 return ncl
; /* Return new cluster number or error status */
1594 #endif /* !FF_FS_READONLY */
1600 /*-----------------------------------------------------------------------*/
1601 /* FAT handling - Convert offset into cluster with link map table */
1602 /*-----------------------------------------------------------------------*/
1604 static DWORD
clmt_clust ( /* <2:Error, >=2:Cluster number */
1605 FIL
* fp
, /* Pointer to the file object */
1606 FSIZE_t ofs
/* File offset to be converted to cluster# */
1609 DWORD cl
, ncl
, * tbl
;
1610 FATFS
* fs
= fp
-> obj
. fs
;
1613 tbl
= fp
-> cltbl
+ 1 ; /* Top of CLMT */
1614 cl
= ( DWORD
)( ofs
/ SS ( fs
) / fs
-> csize
); /* Cluster order from top of the file */
1616 ncl
= * tbl
++; /* Number of cluters in the fragment */
1617 if ( ncl
== 0 ) return 0 ; /* End of table? (error) */
1618 if ( cl
< ncl
) break ; /* In this fragment? */
1619 cl
-= ncl
; tbl
++; /* Next fragment */
1621 return cl
+ * tbl
; /* Return the cluster number */
1624 #endif /* FF_USE_FASTSEEK */
1629 /*-----------------------------------------------------------------------*/
1630 /* Directory handling - Fill a cluster with zeros */
1631 /*-----------------------------------------------------------------------*/
1634 static FRESULT
dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
1635 FATFS
* fs
, /* Filesystem object */
1636 DWORD clst
/* Directory table to clear */
1644 if ( sync_window ( fs
) != FR_OK
) return FR_DISK_ERR
; /* Flush disk access window */
1645 sect
= clst2sect ( fs
, clst
); /* Top of the cluster */
1646 fs
-> winsect
= sect
; /* Set window to top of the cluster */
1647 mem_set ( fs
-> win
, 0 , SS ( fs
)); /* Clear window buffer */
1648 #if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
1649 /* Allocate a temporary buffer */
1650 for ( szb
= (( DWORD
) fs
-> csize
* SS ( fs
) >= MAX_MALLOC
) ? MAX_MALLOC
: fs
-> csize
* SS ( fs
), ibuf
= 0 ; szb
> SS ( fs
) && ( ibuf
= ff_memalloc ( szb
)) == 0 ; szb
/= 2 ) ;
1651 if ( szb
> SS ( fs
)) { /* Buffer allocated? */
1652 mem_set ( ibuf
, 0 , szb
);
1653 szb
/= SS ( fs
); /* Bytes -> Sectors */
1654 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1659 ibuf
= fs
-> win
; szb
= 1 ; /* Use window buffer (many single-sector writes may take a time) */
1660 for ( n
= 0 ; n
< fs
-> csize
&& disk_write ( fs
-> pdrv
, ibuf
, sect
+ n
, szb
) == RES_OK
; n
+= szb
) ; /* Fill the cluster with 0 */
1662 return ( n
== fs
-> csize
) ? FR_OK
: FR_DISK_ERR
;
1664 #endif /* !FF_FS_READONLY */
1669 /*-----------------------------------------------------------------------*/
1670 /* Directory handling - Set directory index */
1671 /*-----------------------------------------------------------------------*/
1673 static FRESULT
dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
1674 DIR * dp
, /* Pointer to directory object */
1675 DWORD ofs
/* Offset of directory table */
1679 FATFS
* fs
= dp
-> obj
. fs
;
1682 if ( ofs
>= ( DWORD
)(( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) ? MAX_DIR_EX
: MAX_DIR
) || ofs
% SZDIRE
) { /* Check range of offset and alignment */
1685 dp
-> dptr
= ofs
; /* Set current offset */
1686 clst
= dp
-> obj
. sclust
; /* Table start cluster (0:root) */
1687 if ( clst
== 0 && fs
-> fs_type
>= FS_FAT32
) { /* Replace cluster# 0 with root cluster# */
1689 if ( FF_FS_EXFAT
) dp
-> obj
. stat
= 0 ; /* exFAT: Root dir has an FAT chain */
1692 if ( clst
== 0 ) { /* Static table (root-directory on the FAT volume) */
1693 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) return FR_INT_ERR
; /* Is index out of range? */
1694 dp
-> sect
= fs
-> dirbase
;
1696 } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
1697 csz
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Bytes per cluster */
1698 while ( ofs
>= csz
) { /* Follow cluster chain */
1699 clst
= get_fat (& dp
-> obj
, clst
); /* Get next cluster */
1700 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1701 if ( clst
< 2 || clst
>= fs
-> n_fatent
) return FR_INT_ERR
; /* Reached to end of table or internal error */
1704 dp
-> sect
= clst2sect ( fs
, clst
);
1706 dp
-> clust
= clst
; /* Current cluster# */
1707 if ( dp
-> sect
== 0 ) return FR_INT_ERR
;
1708 dp
-> sect
+= ofs
/ SS ( fs
); /* Sector# of the directory entry */
1709 dp
-> dir
= fs
-> win
+ ( ofs
% SS ( fs
)); /* Pointer to the entry in the win[] */
1717 /*-----------------------------------------------------------------------*/
1718 /* Directory handling - Move directory table index next */
1719 /*-----------------------------------------------------------------------*/
1721 static FRESULT
dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
1722 DIR * dp
, /* Pointer to the directory object */
1723 int stretch
/* 0: Do not stretch table, 1: Stretch table if needed */
1727 FATFS
* fs
= dp
-> obj
. fs
;
1730 ofs
= dp
-> dptr
+ SZDIRE
; /* Next entry */
1731 if ( dp
-> sect
== 0 || ofs
>= ( DWORD
)(( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) ? MAX_DIR_EX
: MAX_DIR
)) return FR_NO_FILE
; /* Report EOT when offset has reached max value */
1733 if ( ofs
% SS ( fs
) == 0 ) { /* Sector changed? */
1734 dp
-> sect
++; /* Next sector */
1736 if ( dp
-> clust
== 0 ) { /* Static table */
1737 if ( ofs
/ SZDIRE
>= fs
-> n_rootdir
) { /* Report EOT if it reached end of static table */
1738 dp
-> sect
= 0 ; return FR_NO_FILE
;
1741 else { /* Dynamic table */
1742 if (( ofs
/ SS ( fs
) & ( fs
-> csize
- 1 )) == 0 ) { /* Cluster changed? */
1743 clst
= get_fat (& dp
-> obj
, dp
-> clust
); /* Get next cluster */
1744 if ( clst
<= 1 ) return FR_INT_ERR
; /* Internal error */
1745 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1746 if ( clst
>= fs
-> n_fatent
) { /* It reached end of dynamic table */
1748 if (! stretch
) { /* If no stretch, report EOT */
1749 dp
-> sect
= 0 ; return FR_NO_FILE
;
1751 clst
= create_chain (& dp
-> obj
, dp
-> clust
); /* Allocate a cluster */
1752 if ( clst
== 0 ) return FR_DENIED
; /* No free cluster */
1753 if ( clst
== 1 ) return FR_INT_ERR
; /* Internal error */
1754 if ( clst
== 0xFFFFFFFF ) return FR_DISK_ERR
; /* Disk error */
1755 if ( dir_clear ( fs
, clst
) != FR_OK
) return FR_DISK_ERR
; /* Clean up the stretched table */
1756 if ( FF_FS_EXFAT
) dp
-> obj
. stat
|= 4 ; /* exFAT: The directory has been stretched */
1758 if (! stretch
) dp
-> sect
= 0 ; /* (this line is to suppress compiler warning) */
1759 dp
-> sect
= 0 ; return FR_NO_FILE
; /* Report EOT */
1762 dp
-> clust
= clst
; /* Initialize data for new cluster */
1763 dp
-> sect
= clst2sect ( fs
, clst
);
1767 dp
-> dptr
= ofs
; /* Current entry */
1768 dp
-> dir
= fs
-> win
+ ofs
% SS ( fs
); /* Pointer to the entry in the win[] */
1777 /*-----------------------------------------------------------------------*/
1778 /* Directory handling - Reserve a block of directory entries */
1779 /*-----------------------------------------------------------------------*/
1781 static FRESULT
dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
1782 DIR * dp
, /* Pointer to the directory object */
1783 UINT nent
/* Number of contiguous entries to allocate */
1788 FATFS
* fs
= dp
-> obj
. fs
;
1791 res
= dir_sdi ( dp
, 0 );
1795 res
= move_window ( fs
, dp
-> sect
);
1796 if ( res
!= FR_OK
) break ;
1798 if (( fs
-> fs_type
== FS_EXFAT
) ? ( int )(( dp
-> dir
[ XDIR_Type
] & 0x80 ) == 0 ) : ( int )( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 )) {
1800 if ( dp
-> dir
[ DIR_Name
] == DDEM
|| dp
-> dir
[ DIR_Name
] == 0 ) {
1802 if (++ n
== nent
) break ; /* A block of contiguous free entries is found */
1804 n
= 0 ; /* Not a blank entry. Restart to search */
1806 res
= dir_next ( dp
, 1 );
1807 } while ( res
== FR_OK
); /* Next entry with table stretch enabled */
1810 if ( res
== FR_NO_FILE
) res
= FR_DENIED
; /* No directory entry to allocate */
1814 #endif /* !FF_FS_READONLY */
1819 /*-----------------------------------------------------------------------*/
1820 /* FAT: Directory handling - Load/Store start cluster number */
1821 /*-----------------------------------------------------------------------*/
1823 static DWORD
ld_clust ( /* Returns the top cluster value of the SFN entry */
1824 FATFS
* fs
, /* Pointer to the fs object */
1825 const BYTE
* dir
/* Pointer to the key entry */
1830 cl
= ld_word ( dir
+ DIR_FstClusLO
);
1831 if ( fs
-> fs_type
== FS_FAT32
) {
1832 cl
|= ( DWORD
) ld_word ( dir
+ DIR_FstClusHI
) << 16 ;
1840 static void st_clust (
1841 FATFS
* fs
, /* Pointer to the fs object */
1842 BYTE
* dir
, /* Pointer to the key entry */
1843 DWORD cl
/* Value to be set */
1846 st_word ( dir
+ DIR_FstClusLO
, ( WORD
) cl
);
1847 if ( fs
-> fs_type
== FS_FAT32
) {
1848 st_word ( dir
+ DIR_FstClusHI
, ( WORD
)( cl
>> 16 ));
1856 /*--------------------------------------------------------*/
1857 /* FAT-LFN: Compare a part of file name with an LFN entry */
1858 /*--------------------------------------------------------*/
1860 static int cmp_lfn ( /* 1:matched, 0:not matched */
1861 const WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer to be compared */
1862 BYTE
* dir
/* Pointer to the directory entry containing the part of LFN */
1869 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO */
1871 i
= (( dir
[ LDIR_Ord
] & 0x3F ) - 1 ) * 13 ; /* Offset in the LFN buffer */
1873 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1874 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1876 if ( i
>= FF_MAX_LFN
|| ff_wtoupper ( uc
) != ff_wtoupper ( lfnbuf
[ i
++])) { /* Compare it */
1877 return 0 ; /* Not matched */
1881 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1885 if (( dir
[ LDIR_Ord
] & LLEF
) && wc
&& lfnbuf
[ i
]) return 0 ; /* Last segment matched but different length */
1887 return 1 ; /* The part of LFN matched */
1891 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
1892 /*-----------------------------------------------------*/
1893 /* FAT-LFN: Pick a part of file name from an LFN entry */
1894 /*-----------------------------------------------------*/
1896 static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
1897 WCHAR
* lfnbuf
, /* Pointer to the LFN working buffer */
1898 BYTE
* dir
/* Pointer to the LFN entry */
1905 if ( ld_word ( dir
+ LDIR_FstClusLO
) != 0 ) return 0 ; /* Check LDIR_FstClusLO is 0 */
1907 i
= (( dir
[ LDIR_Ord
] & ~ LLEF
) - 1 ) * 13 ; /* Offset in the LFN buffer */
1909 for ( wc
= 1 , s
= 0 ; s
< 13 ; s
++) { /* Process all characters in the entry */
1910 uc
= ld_word ( dir
+ LfnOfs
[ s
]); /* Pick an LFN character */
1912 if ( i
>= FF_MAX_LFN
) return 0 ; /* Buffer overflow? */
1913 lfnbuf
[ i
++] = wc
= uc
; /* Store it */
1915 if ( uc
!= 0xFFFF ) return 0 ; /* Check filler */
1919 if ( dir
[ LDIR_Ord
] & LLEF
) { /* Put terminator if it is the last LFN part */
1920 if ( i
>= FF_MAX_LFN
) return 0 ; /* Buffer overflow? */
1924 return 1 ; /* The part of LFN is valid */
1930 /*-----------------------------------------*/
1931 /* FAT-LFN: Create an entry of LFN entries */
1932 /*-----------------------------------------*/
1934 static void put_lfn (
1935 const WCHAR
* lfn
, /* Pointer to the LFN */
1936 BYTE
* dir
, /* Pointer to the LFN entry to be created */
1937 BYTE ord
, /* LFN order (1-20) */
1938 BYTE sum
/* Checksum of the corresponding SFN */
1945 dir
[ LDIR_Chksum
] = sum
; /* Set checksum */
1946 dir
[ LDIR_Attr
] = AM_LFN
; /* Set attribute. LFN entry */
1948 st_word ( dir
+ LDIR_FstClusLO
, 0 );
1950 i
= ( ord
- 1 ) * 13 ; /* Get offset in the LFN working buffer */
1953 if ( wc
!= 0xFFFF ) wc
= lfn
[ i
++]; /* Get an effective character */
1954 st_word ( dir
+ LfnOfs
[ s
], wc
); /* Put it */
1955 if ( wc
== 0 ) wc
= 0xFFFF ; /* Padding characters for left locations */
1957 if ( wc
== 0xFFFF || ! lfn
[ i
]) ord
|= LLEF
; /* Last LFN part is the start of LFN sequence */
1958 dir
[ LDIR_Ord
] = ord
; /* Set the LFN order */
1961 #endif /* !FF_FS_READONLY */
1962 #endif /* FF_USE_LFN */
1966 #if FF_USE_LFN && !FF_FS_READONLY
1967 /*-----------------------------------------------------------------------*/
1968 /* FAT-LFN: Create a Numbered SFN */
1969 /*-----------------------------------------------------------------------*/
1971 static void gen_numname (
1972 BYTE
* dst
, /* Pointer to the buffer to store numbered SFN */
1973 const BYTE
* src
, /* Pointer to SFN */
1974 const WCHAR
* lfn
, /* Pointer to LFN */
1975 UINT seq
/* Sequence number */
1984 mem_cpy ( dst
, src
, 11 );
1986 if ( seq
> 5 ) { /* In case of many collisions, generate a hash number instead of sequential number */
1988 while (* lfn
) { /* Create a CRC as hash value */
1990 for ( i
= 0 ; i
< 16 ; i
++) {
1991 sr
= ( sr
<< 1 ) + ( wc
& 1 );
1993 if ( sr
& 0x10000 ) sr
^= 0x11021 ;
1999 /* itoa (hexdecimal) */
2002 c
= ( BYTE
)(( seq
% 16 ) + '0' );
2003 if ( c
> '9' ) c
+= 7 ;
2009 /* Append the number to the SFN body */
2010 for ( j
= 0 ; j
< i
&& dst
[ j
] != ' ' ; j
++) {
2011 if ( dbc_1st ( dst
[ j
])) {
2012 if ( j
== i
- 1 ) break ;
2017 dst
[ j
++] = ( i
< 8 ) ? ns
[ i
++] : ' ' ;
2020 #endif /* FF_USE_LFN && !FF_FS_READONLY */
2025 /*-----------------------------------------------------------------------*/
2026 /* FAT-LFN: Calculate checksum of an SFN entry */
2027 /*-----------------------------------------------------------------------*/
2029 static BYTE
sum_sfn (
2030 const BYTE
* dir
/* Pointer to the SFN entry */
2037 sum
= ( sum
>> 1 ) + ( sum
<< 7 ) + * dir
++;
2042 #endif /* FF_USE_LFN */
2047 /*-----------------------------------------------------------------------*/
2048 /* exFAT: Checksum */
2049 /*-----------------------------------------------------------------------*/
2051 static WORD
xdir_sum ( /* Get checksum of the directoly entry block */
2052 const BYTE
* dir
/* Directory entry block to be calculated */
2059 szblk
= ( dir
[ XDIR_NumSec
] + 1 ) * SZDIRE
; /* Number of bytes of the entry block */
2060 for ( i
= sum
= 0 ; i
< szblk
; i
++) {
2061 if ( i
== XDIR_SetSum
) { /* Skip 2-byte sum field */
2064 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + dir
[ i
];
2072 static WORD
xname_sum ( /* Get check sum (to be used as hash) of the file name */
2073 const WCHAR
* name
/* File name to be calculated */
2080 while (( chr
= * name
++) != 0 ) {
2081 chr
= ( WCHAR
) ff_wtoupper ( chr
); /* File name needs to be up-case converted */
2082 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
& 0xFF );
2083 sum
= (( sum
& 1 ) ? 0x8000 : 0 ) + ( sum
>> 1 ) + ( chr
>> 8 );
2089 #if !FF_FS_READONLY && FF_USE_MKFS
2090 static DWORD
xsum32 ( /* Returns 32-bit checksum */
2091 BYTE dat
, /* Byte to be calculated (byte-by-byte processing) */
2092 DWORD sum
/* Previous sum value */
2095 sum
= (( sum
& 1 ) ? 0x80000000 : 0 ) + ( sum
>> 1 ) + dat
;
2101 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2102 /*------------------------------------------------------*/
2103 /* exFAT: Get object information from a directory block */
2104 /*------------------------------------------------------*/
2106 static void get_xfileinfo (
2107 BYTE
* dirb
, /* Pointer to the direcotry entry block 85+C0+C1s */
2108 FILINFO
* fno
/* Buffer to store the extracted file information */
2114 /* Get file name from the entry block */
2115 si
= SZDIRE
* 2 ; /* 1st C1 entry */
2116 nc
= 0 ; hs
= 0 ; di
= 0 ;
2117 while ( nc
< dirb
[ XDIR_NumName
]) {
2118 if ( si
>= MAXDIRB ( FF_MAX_LFN
)) { di
= 0 ; break ; } /* Truncated directory block? */
2119 if (( si
% SZDIRE
) == 0 ) si
+= 2 ; /* Skip entry type field */
2120 wc
= ld_word ( dirb
+ si
); si
+= 2 ; nc
++; /* Get a character */
2121 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2122 hs
= wc
; continue ; /* Get low surrogate */
2124 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in API encoding */
2125 if ( wc
== 0 ) { di
= 0 ; break ; } /* Buffer overflow or wrong encoding? */
2129 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2130 if ( di
== 0 ) fno
-> fname
[ di
++] = '?' ; /* Inaccessible object name? */
2131 fno
-> fname
[ di
] = 0 ; /* Terminate the name */
2132 fno
-> altname
[ 0 ] = 0 ; /* exFAT does not support SFN */
2134 fno
-> fattrib
= dirb
[ XDIR_Attr
]; /* Attribute */
2135 fno
-> fsize
= ( fno
-> fattrib
& AM_DIR
) ? 0 : ld_qword ( dirb
+ XDIR_FileSize
); /* Size */
2136 fno
-> ftime
= ld_word ( dirb
+ XDIR_ModTime
+ 0 ); /* Time */
2137 fno
-> fdate
= ld_word ( dirb
+ XDIR_ModTime
+ 2 ); /* Date */
2140 #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2143 /*-----------------------------------*/
2144 /* exFAT: Get a directry entry block */
2145 /*-----------------------------------*/
2147 static FRESULT
load_xdir ( /* FR_INT_ERR: invalid entry block */
2148 DIR * dp
/* Reading direcotry object pointing top of the entry block to load */
2153 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */
2157 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2158 if ( res
!= FR_OK
) return res
;
2159 if ( dp
-> dir
[ XDIR_Type
] != 0x85 ) return FR_INT_ERR
; /* Invalid order */
2160 mem_cpy ( dirb
+ 0 * SZDIRE
, dp
-> dir
, SZDIRE
);
2161 sz_ent
= ( dirb
[ XDIR_NumSec
] + 1 ) * SZDIRE
;
2162 if ( sz_ent
< 3 * SZDIRE
|| sz_ent
> 19 * SZDIRE
) return FR_INT_ERR
;
2165 res
= dir_next ( dp
, 0 );
2166 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2167 if ( res
!= FR_OK
) return res
;
2168 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2169 if ( res
!= FR_OK
) return res
;
2170 if ( dp
-> dir
[ XDIR_Type
] != 0xC0 ) return FR_INT_ERR
; /* Invalid order */
2171 mem_cpy ( dirb
+ 1 * SZDIRE
, dp
-> dir
, SZDIRE
);
2172 if ( MAXDIRB ( dirb
[ XDIR_NumName
]) > sz_ent
) return FR_INT_ERR
;
2174 /* Load C1 entries */
2175 i
= 2 * SZDIRE
; /* C1 offset to load */
2177 res
= dir_next ( dp
, 0 );
2178 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be */
2179 if ( res
!= FR_OK
) return res
;
2180 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2181 if ( res
!= FR_OK
) return res
;
2182 if ( dp
-> dir
[ XDIR_Type
] != 0xC1 ) return FR_INT_ERR
; /* Invalid order */
2183 if ( i
< MAXDIRB ( FF_MAX_LFN
)) mem_cpy ( dirb
+ i
, dp
-> dir
, SZDIRE
);
2184 } while (( i
+= SZDIRE
) < sz_ent
);
2186 /* Sanity check (do it for only accessible object) */
2187 if ( i
<= MAXDIRB ( FF_MAX_LFN
)) {
2188 if ( xdir_sum ( dirb
) != ld_word ( dirb
+ XDIR_SetSum
)) return FR_INT_ERR
;
2194 /*------------------------------------------------------------------*/
2195 /* exFAT: Initialize object allocation info with loaded entry block */
2196 /*------------------------------------------------------------------*/
2198 static void init_alloc_info (
2199 FATFS
* fs
, /* Filesystem object */
2200 FFOBJID
* obj
/* Object allocation information to be initialized */
2203 obj
-> sclust
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Start cluster */
2204 obj
-> objsize
= ld_qword ( fs
-> dirbuf
+ XDIR_FileSize
); /* Size */
2205 obj
-> stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ; /* Allocation status */
2206 obj
-> n_frag
= 0 ; /* No last fragment info */
2211 #if !FF_FS_READONLY || FF_FS_RPATH != 0
2212 /*------------------------------------------------*/
2213 /* exFAT: Load the object's directory entry block */
2214 /*------------------------------------------------*/
2216 static FRESULT
load_obj_xdir (
2217 DIR * dp
, /* Blank directory object to be used to access containing direcotry */
2218 const FFOBJID
* obj
/* Object with its containing directory information */
2223 /* Open object containing directory */
2224 dp
-> obj
. fs
= obj
-> fs
;
2225 dp
-> obj
. sclust
= obj
-> c_scl
;
2226 dp
-> obj
. stat
= ( BYTE
) obj
-> c_size
;
2227 dp
-> obj
. objsize
= obj
-> c_size
& 0xFFFFFF00 ;
2229 dp
-> blk_ofs
= obj
-> c_ofs
;
2231 res
= dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto object's entry block */
2233 res
= load_xdir ( dp
); /* Load the object's entry block */
2241 /*----------------------------------------*/
2242 /* exFAT: Store the directory entry block */
2243 /*----------------------------------------*/
2245 static FRESULT
store_xdir (
2246 DIR * dp
/* Pointer to the direcotry object */
2251 BYTE
* dirb
= dp
-> obj
. fs
-> dirbuf
; /* Pointer to the direcotry entry block 85+C0+C1s */
2253 /* Create set sum */
2254 st_word ( dirb
+ XDIR_SetSum
, xdir_sum ( dirb
));
2255 nent
= dirb
[ XDIR_NumSec
] + 1 ;
2257 /* Store the direcotry entry block to the directory */
2258 res
= dir_sdi ( dp
, dp
-> blk_ofs
);
2259 while ( res
== FR_OK
) {
2260 res
= move_window ( dp
-> obj
. fs
, dp
-> sect
);
2261 if ( res
!= FR_OK
) break ;
2262 mem_cpy ( dp
-> dir
, dirb
, SZDIRE
);
2263 dp
-> obj
. fs
-> wflag
= 1 ;
2264 if (-- nent
== 0 ) break ;
2266 res
= dir_next ( dp
, 0 );
2268 return ( res
== FR_OK
|| res
== FR_DISK_ERR
) ? res
: FR_INT_ERR
;
2273 /*-------------------------------------------*/
2274 /* exFAT: Create a new directory enrty block */
2275 /*-------------------------------------------*/
2277 static void create_xdir (
2278 BYTE
* dirb
, /* Pointer to the direcotry entry block buffer */
2279 const WCHAR
* lfn
/* Pointer to the object name */
2287 /* Create 85,C0 entry */
2288 mem_set ( dirb
, 0 , 2 * SZDIRE
);
2289 dirb
[ 0 * SZDIRE
+ XDIR_Type
] = 0x85 ; /* 85 entry */
2290 dirb
[ 1 * SZDIRE
+ XDIR_Type
] = 0xC0 ; /* C0 entry */
2292 /* Create C1 entries */
2293 i
= SZDIRE
* 2 ; /* Top of C1 entries */
2294 nlen
= nc1
= 0 ; wc
= 1 ;
2296 dirb
[ i
++] = 0xC1 ; dirb
[ i
++] = 0 ; /* Entry type C1 */
2297 do { /* Fill name field */
2298 if ( wc
!= 0 && ( wc
= lfn
[ nlen
]) != 0 ) nlen
++; /* Get a character if exist */
2299 st_word ( dirb
+ i
, wc
); /* Store it */
2301 } while ( i
% SZDIRE
!= 0 );
2303 } while ( lfn
[ nlen
]); /* Fill next entry if any char follows */
2305 dirb
[ XDIR_NumName
] = nlen
; /* Set name length */
2306 dirb
[ XDIR_NumSec
] = 1 + nc1
; /* Set secondary count (C0 + C1s) */
2307 st_word ( dirb
+ XDIR_NameHash
, xname_sum ( lfn
)); /* Set name hash */
2310 #endif /* !FF_FS_READONLY */
2311 #endif /* FF_FS_EXFAT */
2315 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
2316 /*-----------------------------------------------------------------------*/
2317 /* Read an object from the directory */
2318 /*-----------------------------------------------------------------------*/
2320 #define dir_read_file(dp) dir_read(dp, 0)
2321 #define dir_read_label(dp) dir_read(dp, 1)
2323 static FRESULT
dir_read (
2324 DIR * dp
, /* Pointer to the directory object */
2325 int vol
/* Filtered by 0:file/directory or 1:volume label */
2328 FRESULT res
= FR_NO_FILE
;
2329 FATFS
* fs
= dp
-> obj
. fs
;
2332 BYTE ord
= 0xFF , sum
= 0xFF ;
2336 res
= move_window ( fs
, dp
-> sect
);
2337 if ( res
!= FR_OK
) break ;
2338 c
= dp
-> dir
[ DIR_Name
]; /* Test for the entry type */
2340 res
= FR_NO_FILE
; break ; /* Reached to end of the directory */
2343 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2344 if ( FF_USE_LABEL
&& vol
) {
2345 if ( c
== 0x83 ) break ; /* Volume label entry? */
2347 if ( c
== 0x85 ) { /* Start of the file entry block? */
2348 dp
-> blk_ofs
= dp
-> dptr
; /* Get location of the block */
2349 res
= load_xdir ( dp
); /* Load the entry block */
2351 dp
-> obj
. attr
= fs
-> dirbuf
[ XDIR_Attr
] & AM_MASK
; /* Get attribute */
2358 { /* On the FAT/FAT32 volume */
2359 dp
-> obj
. attr
= a
= dp
-> dir
[ DIR_Attr
] & AM_MASK
; /* Get attribute */
2360 #if FF_USE_LFN /* LFN configuration */
2361 if ( c
== DDEM
|| c
== '.' || ( int )(( a
& ~ AM_ARC
) == AM_VOL
) != vol
) { /* An entry without valid data */
2364 if ( a
== AM_LFN
) { /* An LFN entry is found */
2365 if ( c
& LLEF
) { /* Is it start of an LFN sequence? */
2366 sum
= dp
-> dir
[ LDIR_Chksum
];
2367 c
&= ( BYTE
)~ LLEF
; ord
= c
;
2368 dp
-> blk_ofs
= dp
-> dptr
;
2370 /* Check LFN validity and capture it */
2371 ord
= ( c
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && pick_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2372 } else { /* An SFN entry is found */
2373 if ( ord
!= 0 || sum
!= sum_sfn ( dp
-> dir
)) { /* Is there a valid LFN? */
2374 dp
-> blk_ofs
= 0xFFFFFFFF ; /* It has no LFN. */
2379 #else /* Non LFN configuration */
2380 if ( c
!= DDEM
&& c
!= '.' && a
!= AM_LFN
&& ( int )(( a
& ~ AM_ARC
) == AM_VOL
) == vol
) { /* Is it a valid entry? */
2385 res
= dir_next ( dp
, 0 ); /* Next entry */
2386 if ( res
!= FR_OK
) break ;
2389 if ( res
!= FR_OK
) dp
-> sect
= 0 ; /* Terminate the read operation on error or EOT */
2393 #endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
2397 /*-----------------------------------------------------------------------*/
2398 /* Directory handling - Find an object in the directory */
2399 /*-----------------------------------------------------------------------*/
2401 static FRESULT
dir_find ( /* FR_OK(0):succeeded, !=0:error */
2402 DIR * dp
/* Pointer to the directory object with the file name */
2406 FATFS
* fs
= dp
-> obj
. fs
;
2412 res
= dir_sdi ( dp
, 0 ); /* Rewind directory object */
2413 if ( res
!= FR_OK
) return res
;
2415 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2418 WORD hash
= xname_sum ( fs
-> lfnbuf
); /* Hash value of the name to find */
2420 while (( res
= dir_read_file ( dp
)) == FR_OK
) { /* Read an item */
2421 #if FF_MAX_LFN < 255
2422 if ( fs
-> dirbuf
[ XDIR_NumName
] > FF_MAX_LFN
) continue ; /* Skip comparison if inaccessible object name */
2424 if ( ld_word ( fs
-> dirbuf
+ XDIR_NameHash
) != hash
) continue ; /* Skip comparison if hash mismatched */
2425 for ( nc
= fs
-> dirbuf
[ XDIR_NumName
], di
= SZDIRE
* 2 , ni
= 0 ; nc
; nc
--, di
+= 2 , ni
++) { /* Compare the name */
2426 if (( di
% SZDIRE
) == 0 ) di
+= 2 ;
2427 if ( ff_wtoupper ( ld_word ( fs
-> dirbuf
+ di
)) != ff_wtoupper ( fs
-> lfnbuf
[ ni
])) break ;
2429 if ( nc
== 0 && ! fs
-> lfnbuf
[ ni
]) break ; /* Name matched? */
2434 /* On the FAT/FAT32 volume */
2436 ord
= sum
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2439 res
= move_window ( fs
, dp
-> sect
);
2440 if ( res
!= FR_OK
) break ;
2441 c
= dp
-> dir
[ DIR_Name
];
2442 if ( c
== 0 ) { res
= FR_NO_FILE
; break ; } /* Reached to end of table */
2443 #if FF_USE_LFN /* LFN configuration */
2444 dp
-> obj
. attr
= a
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2445 if ( c
== DDEM
|| (( a
& AM_VOL
) && a
!= AM_LFN
)) { /* An entry without valid data */
2446 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2448 if ( a
== AM_LFN
) { /* An LFN entry is found */
2449 if (!( dp
-> fn
[ NSFLAG
] & NS_NOLFN
)) {
2450 if ( c
& LLEF
) { /* Is it start of LFN sequence? */
2451 sum
= dp
-> dir
[ LDIR_Chksum
];
2452 c
&= ( BYTE
)~ LLEF
; ord
= c
; /* LFN start order */
2453 dp
-> blk_ofs
= dp
-> dptr
; /* Start offset of LFN */
2455 /* Check validity of the LFN entry and compare it with given name */
2456 ord
= ( c
== ord
&& sum
== dp
-> dir
[ LDIR_Chksum
] && cmp_lfn ( fs
-> lfnbuf
, dp
-> dir
)) ? ord
- 1 : 0xFF ;
2458 } else { /* An SFN entry is found */
2459 if ( ord
== 0 && sum
== sum_sfn ( dp
-> dir
)) break ; /* LFN matched? */
2460 if (!( dp
-> fn
[ NSFLAG
] & NS_LOSS
) && ! mem_cmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* SFN matched? */
2461 ord
= 0xFF ; dp
-> blk_ofs
= 0xFFFFFFFF ; /* Reset LFN sequence */
2464 #else /* Non LFN configuration */
2465 dp
-> obj
. attr
= dp
-> dir
[ DIR_Attr
] & AM_MASK
;
2466 if (!( dp
-> dir
[ DIR_Attr
] & AM_VOL
) && ! mem_cmp ( dp
-> dir
, dp
-> fn
, 11 )) break ; /* Is it a valid entry? */
2468 res
= dir_next ( dp
, 0 ); /* Next entry */
2469 } while ( res
== FR_OK
);
2478 /*-----------------------------------------------------------------------*/
2479 /* Register an object to the directory */
2480 /*-----------------------------------------------------------------------*/
2482 static FRESULT
dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
2483 DIR * dp
/* Target directory with object name to be created */
2487 FATFS
* fs
= dp
-> obj
. fs
;
2488 #if FF_USE_LFN /* LFN configuration */
2493 if ( dp
-> fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
)) return FR_INVALID_NAME
; /* Check name validity */
2494 for ( nlen
= 0 ; fs
-> lfnbuf
[ nlen
]; nlen
++) ; /* Get lfn length */
2497 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2498 nent
= ( nlen
+ 14 ) / 15 + 2 ; /* Number of entries to allocate (85+C0+C1s) */
2499 res
= dir_alloc ( dp
, nent
); /* Allocate entries */
2500 if ( res
!= FR_OK
) return res
;
2501 dp
-> blk_ofs
= dp
-> dptr
- SZDIRE
* ( nent
- 1 ); /* Set the allocated entry block offset */
2503 if ( dp
-> obj
. stat
& 4 ) { /* Has the directory been stretched? */
2505 res
= fill_first_frag (& dp
-> obj
); /* Fill the first fragment on the FAT if needed */
2506 if ( res
!= FR_OK
) return res
;
2507 res
= fill_last_frag (& dp
-> obj
, dp
-> clust
, 0xFFFFFFFF ); /* Fill the last fragment on the FAT if needed */
2508 if ( res
!= FR_OK
) return res
;
2509 if ( dp
-> obj
. sclust
!= 0 ) { /* Is it a sub directory? */
2512 res
= load_obj_xdir (& dj
, & dp
-> obj
); /* Load the object status */
2513 if ( res
!= FR_OK
) return res
;
2514 dp
-> obj
. objsize
+= ( DWORD
) fs
-> csize
* SS ( fs
); /* Increase the directory size by cluster size */
2515 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, dp
-> obj
. objsize
); /* Update the allocation status */
2516 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, dp
-> obj
. objsize
);
2517 fs
-> dirbuf
[ XDIR_GenFlags
] = dp
-> obj
. stat
| 1 ;
2518 res
= store_xdir (& dj
); /* Store the object status */
2519 if ( res
!= FR_OK
) return res
;
2523 create_xdir ( fs
-> dirbuf
, fs
-> lfnbuf
); /* Create on-memory directory block to be written later */
2527 /* On the FAT/FAT32 volume */
2528 mem_cpy ( sn
, dp
-> fn
, 12 );
2529 if ( sn
[ NSFLAG
] & NS_LOSS
) { /* When LFN is out of 8.3 format, generate a numbered name */
2530 dp
-> fn
[ NSFLAG
] = NS_NOLFN
; /* Find only SFN */
2531 for ( n
= 1 ; n
< 100 ; n
++) {
2532 gen_numname ( dp
-> fn
, sn
, fs
-> lfnbuf
, n
); /* Generate a numbered name */
2533 res
= dir_find ( dp
); /* Check if the name collides with existing SFN */
2534 if ( res
!= FR_OK
) break ;
2536 if ( n
== 100 ) return FR_DENIED
; /* Abort if too many collisions */
2537 if ( res
!= FR_NO_FILE
) return res
; /* Abort if the result is other than 'not collided' */
2538 dp
-> fn
[ NSFLAG
] = sn
[ NSFLAG
];
2541 /* Create an SFN with/without LFNs. */
2542 nent
= ( sn
[ NSFLAG
] & NS_LFN
) ? ( nlen
+ 12 ) / 13 + 1 : 1 ; /* Number of entries to allocate */
2543 res
= dir_alloc ( dp
, nent
); /* Allocate entries */
2544 if ( res
== FR_OK
&& -- nent
) { /* Set LFN entry if needed */
2545 res
= dir_sdi ( dp
, dp
-> dptr
- nent
* SZDIRE
);
2547 sum
= sum_sfn ( dp
-> fn
); /* Checksum value of the SFN tied to the LFN */
2548 do { /* Store LFN entries in bottom first */
2549 res
= move_window ( fs
, dp
-> sect
);
2550 if ( res
!= FR_OK
) break ;
2551 put_lfn ( fs
-> lfnbuf
, dp
-> dir
, ( BYTE
) nent
, sum
);
2553 res
= dir_next ( dp
, 0 ); /* Next entry */
2554 } while ( res
== FR_OK
&& -- nent
);
2558 #else /* Non LFN configuration */
2559 res
= dir_alloc ( dp
, 1 ); /* Allocate an entry for SFN */
2565 res
= move_window ( fs
, dp
-> sect
);
2567 mem_set ( dp
-> dir
, 0 , SZDIRE
); /* Clean the entry */
2568 mem_cpy ( dp
-> dir
+ DIR_Name
, dp
-> fn
, 11 ); /* Put SFN */
2570 dp
-> dir
[ DIR_NTres
] = dp
-> fn
[ NSFLAG
] & ( NS_BODY
| NS_EXT
); /* Put NT flag */
2579 #endif /* !FF_FS_READONLY */
2583 #if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
2584 /*-----------------------------------------------------------------------*/
2585 /* Remove an object from the directory */
2586 /*-----------------------------------------------------------------------*/
2588 static FRESULT
dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
2589 DIR * dp
/* Directory object pointing the entry to be removed */
2593 FATFS
* fs
= dp
-> obj
. fs
;
2594 #if FF_USE_LFN /* LFN configuration */
2595 DWORD last
= dp
-> dptr
;
2597 res
= ( dp
-> blk_ofs
== 0xFFFFFFFF ) ? FR_OK
: dir_sdi ( dp
, dp
-> blk_ofs
); /* Goto top of the entry block if LFN is exist */
2600 res
= move_window ( fs
, dp
-> sect
);
2601 if ( res
!= FR_OK
) break ;
2602 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2603 dp
-> dir
[ XDIR_Type
] &= 0x7F ; /* Clear the entry InUse flag. */
2604 } else { /* On the FAT/FAT32 volume */
2605 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'. */
2608 if ( dp
-> dptr
>= last
) break ; /* If reached last entry then all entries of the object has been deleted. */
2609 res
= dir_next ( dp
, 0 ); /* Next entry */
2610 } while ( res
== FR_OK
);
2611 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
;
2613 #else /* Non LFN configuration */
2615 res
= move_window ( fs
, dp
-> sect
);
2617 dp
-> dir
[ DIR_Name
] = DDEM
; /* Mark the entry 'deleted'.*/
2625 #endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
2629 #if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2630 /*-----------------------------------------------------------------------*/
2631 /* Get file information from directory entry */
2632 /*-----------------------------------------------------------------------*/
2634 static void get_fileinfo (
2635 DIR * dp
, /* Pointer to the directory object */
2636 FILINFO
* fno
/* Pointer to the file information to be filled */
2642 FATFS
* fs
= dp
-> obj
. fs
;
2648 fno
-> fname
[ 0 ] = 0 ; /* Invaidate file info */
2649 if ( dp
-> sect
== 0 ) return ; /* Exit if read pointer has reached end of directory */
2651 #if FF_USE_LFN /* LFN configuration */
2653 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
2654 get_xfileinfo ( fs
-> dirbuf
, fno
);
2658 { /* On the FAT/FAT32 volume */
2659 if ( dp
-> blk_ofs
!= 0xFFFFFFFF ) { /* Get LFN if available */
2661 while ( fs
-> lfnbuf
[ si
] != 0 ) {
2662 wc
= fs
-> lfnbuf
[ si
++]; /* Get an LFN character (UTF-16) */
2663 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is it a surrogate? */
2664 hs
= wc
; continue ; /* Get low surrogate */
2666 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & fno
-> fname
[ di
], FF_LFN_BUF
- di
); /* Store it in UTF-16 or UTF-8 encoding */
2667 if ( wc
== 0 ) { di
= 0 ; break ; } /* Invalid char or buffer overflow? */
2671 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
2672 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN (null string means LFN is invalid) */
2677 while ( si
< 11 ) { /* Get SFN from SFN entry */
2678 wc
= dp
-> dir
[ si
++]; /* Get a char */
2679 if ( wc
== ' ' ) continue ; /* Skip padding spaces */
2680 if ( wc
== RDDEM
) wc
= DDEM
; /* Restore replaced DDEM character */
2681 if ( si
== 9 && di
< FF_SFN_BUF
) fno
-> altname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2682 #if FF_LFN_UNICODE >= 1 /* Unicode output */
2683 if ( dbc_1st (( BYTE
) wc
) && si
!= 8 && si
!= 11 && dbc_2nd ( dp
-> dir
[ si
])) { /* Make a DBC if needed */
2684 wc
= wc
<< 8 | dp
-> dir
[ si
++];
2686 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* ANSI/OEM -> Unicode */
2687 if ( wc
== 0 ) { di
= 0 ; break ; } /* Wrong char in the current code page? */
2688 wc
= put_utf ( wc
, & fno
-> altname
[ di
], FF_SFN_BUF
- di
); /* Store it in Unicode */
2689 if ( wc
== 0 ) { di
= 0 ; break ; } /* Buffer overflow? */
2691 #else /* ANSI/OEM output */
2692 fno
-> altname
[ di
++] = ( TCHAR
) wc
; /* Store it without any conversion */
2695 fno
-> altname
[ di
] = 0 ; /* Terminate the SFN (null string means SFN is invalid) */
2697 if ( fno
-> fname
[ 0 ] == 0 ) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
2698 if ( di
== 0 ) { /* If LFN and SFN both are invalid, this object is inaccesible */
2699 fno
-> fname
[ di
++] = '?' ;
2701 for ( si
= di
= 0 ; fno
-> altname
[ si
]; si
++, di
++) { /* Copy altname[] to fname[] with case information */
2702 wc
= ( WCHAR
) fno
-> altname
[ si
];
2703 if ( IsUpper ( wc
) && ( dp
-> dir
[ DIR_NTres
] & (( si
>= 9 ) ? NS_EXT
: NS_BODY
))) wc
+= 0x20 ;
2704 fno
-> fname
[ di
] = ( TCHAR
) wc
;
2707 fno
-> fname
[ di
] = 0 ; /* Terminate the LFN */
2708 if (! dp
-> dir
[ DIR_NTres
]) fno
-> altname
[ 0 ] = 0 ; /* Altname is not needed if neither LFN nor case info is exist. */
2711 #else /* Non-LFN configuration */
2713 while ( si
< 11 ) { /* Copy name body and extension */
2714 c
= ( TCHAR
) dp
-> dir
[ si
++];
2715 if ( c
== ' ' ) continue ; /* Skip padding spaces */
2716 if ( c
== RDDEM
) c
= DDEM
; /* Restore replaced DDEM character */
2717 if ( si
== 9 ) fno
-> fname
[ di
++] = '.' ; /* Insert a . if extension is exist */
2718 fno
-> fname
[ di
++] = c
;
2723 fno
-> fattrib
= dp
-> dir
[ DIR_Attr
]; /* Attribute */
2724 fno
-> fsize
= ld_dword ( dp
-> dir
+ DIR_FileSize
); /* Size */
2725 fno
-> ftime
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 0 ); /* Time */
2726 fno
-> fdate
= ld_word ( dp
-> dir
+ DIR_ModTime
+ 2 ); /* Date */
2729 #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2733 #if FF_USE_FIND && FF_FS_MINIMIZE <= 1
2734 /*-----------------------------------------------------------------------*/
2735 /* Pattern matching */
2736 /*-----------------------------------------------------------------------*/
2738 static DWORD
get_achar ( /* Get a character and advances ptr */
2739 const TCHAR
** ptr
/* Pointer to pointer to the ANSI/OEM or Unicode string */
2745 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
2746 chr
= tchar2uni ( ptr
);
2747 if ( chr
== 0xFFFFFFFF ) chr
= 0 ; /* Wrong UTF encoding is recognized as end of the string */
2748 chr
= ff_wtoupper ( chr
);
2750 #else /* ANSI/OEM input */
2751 chr
= ( BYTE
)*(* ptr
)++; /* Get a byte */
2752 if ( IsLower ( chr
)) chr
-= 0x20 ; /* To upper ASCII char */
2753 #if FF_CODE_PAGE == 0
2754 if ( ExCvt
&& chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2755 #elif FF_CODE_PAGE < 900
2756 if ( chr
>= 0x80 ) chr
= ExCvt
[ chr
- 0x80 ]; /* To upper SBCS extended char */
2758 #if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
2759 if ( dbc_1st (( BYTE
) chr
)) { /* Get DBC 2nd byte if needed */
2760 chr
= dbc_2nd (( BYTE
)** ptr
) ? chr
<< 8 | ( BYTE
)*(* ptr
)++ : 0 ;
2769 static int pattern_matching ( /* 0:not matched, 1:matched */
2770 const TCHAR
* pat
, /* Matching pattern */
2771 const TCHAR
* nam
, /* String to be tested */
2772 int skip
, /* Number of pre-skip chars (number of ?s) */
2773 int inf
/* Infinite search (* specified) */
2776 const TCHAR
* pp
, * np
;
2781 while ( skip
--) { /* Pre-skip name chars */
2782 if (! get_achar (& nam
)) return 0 ; /* Branch mismatched if less name chars */
2784 if (* pat
== 0 && inf
) return 1 ; /* (short circuit) */
2787 pp
= pat
; np
= nam
; /* Top of pattern and name to match */
2789 if (* pp
== '?' || * pp
== '*' ) { /* Wildcard? */
2791 do { /* Analyze the wildcard block */
2792 if (* pp
++ == '?' ) nm
++; else nx
= 1 ;
2793 } while (* pp
== '?' || * pp
== '*' );
2794 if ( pattern_matching ( pp
, np
, nm
, nx
)) return 1 ; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
2795 nc
= * np
; break ; /* Branch mismatched */
2797 pc
= get_achar (& pp
); /* Get a pattern char */
2798 nc
= get_achar (& np
); /* Get a name char */
2799 if ( pc
!= nc
) break ; /* Branch mismatched? */
2800 if ( pc
== 0 ) return 1 ; /* Branch matched? (matched at end of both strings) */
2802 get_achar (& nam
); /* nam++ */
2803 } while ( inf
&& nc
); /* Retry until end of name if infinite search is specified */
2808 #endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
2812 /*-----------------------------------------------------------------------*/
2813 /* Pick a top segment and create the object name in directory form */
2814 /*-----------------------------------------------------------------------*/
2816 static FRESULT
create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
2817 DIR * dp
, /* Pointer to the directory object */
2818 const TCHAR
** path
/* Pointer to pointer to the segment in the path string */
2821 #if FF_USE_LFN /* LFN configuration */
2829 /* Create LFN into LFN working buffer */
2830 p
= * path
; lfn
= dp
-> obj
. fs
-> lfnbuf
; di
= 0 ;
2832 uc
= tchar2uni (& p
); /* Get a character */
2833 if ( uc
== 0xFFFFFFFF ) return FR_INVALID_NAME
; /* Invalid code or UTF decode error */
2834 if ( uc
>= 0x10000 ) lfn
[ di
++] = ( WCHAR
)( uc
>> 16 ); /* Store high surrogate if needed */
2836 if ( wc
< ' ' || wc
== '/' || wc
== ' \\ ' ) break ; /* Break if end of the path or a separator is found */
2837 if ( wc
< 0x80 && chk_chr ( " \" *:<> \? | \x7F " , wc
)) return FR_INVALID_NAME
; /* Reject illegal characters for LFN */
2838 if ( di
>= FF_MAX_LFN
) return FR_INVALID_NAME
; /* Reject too long name */
2839 lfn
[ di
++] = wc
; /* Store the Unicode character */
2841 while (* p
== '/' || * p
== ' \\ ' ) p
++; /* Skip duplicated separators if exist */
2842 * path
= p
; /* Return pointer to the next segment */
2843 cf
= ( wc
< ' ' ) ? NS_LAST
: 0 ; /* Set last segment flag if end of the path */
2845 #if FF_FS_RPATH != 0
2846 if (( di
== 1 && lfn
[ di
- 1 ] == '.' ) ||
2847 ( di
== 2 && lfn
[ di
- 1 ] == '.' && lfn
[ di
- 2 ] == '.' )) { /* Is this segment a dot name? */
2849 for ( i
= 0 ; i
< 11 ; i
++) { /* Create dot name for SFN entry */
2850 dp
-> fn
[ i
] = ( i
< di
) ? '.' : ' ' ;
2852 dp
-> fn
[ i
] = cf
| NS_DOT
; /* This is a dot entry */
2856 while ( di
) { /* Snip off trailing spaces and dots if exist */
2858 if ( wc
!= ' ' && wc
!= '.' ) break ;
2861 lfn
[ di
] = 0 ; /* LFN is created into the working buffer */
2862 if ( di
== 0 ) return FR_INVALID_NAME
; /* Reject null name */
2864 /* Create SFN in directory form */
2865 for ( si
= 0 ; lfn
[ si
] == ' ' ; si
++) ; /* Remove leading spaces */
2866 if ( si
> 0 || lfn
[ si
] == '.' ) cf
|= NS_LOSS
| NS_LFN
; /* Is there any leading space or dot? */
2867 while ( di
> 0 && lfn
[ di
- 1 ] != '.' ) di
--; /* Find last dot (di<=si: no extension) */
2869 mem_set ( dp
-> fn
, ' ' , 11 );
2872 wc
= lfn
[ si
++]; /* Get an LFN character */
2873 if ( wc
== 0 ) break ; /* Break on end of the LFN */
2874 if ( wc
== ' ' || ( wc
== '.' && si
!= di
)) { /* Remove embedded spaces and dots */
2875 cf
|= NS_LOSS
| NS_LFN
;
2879 if ( i
>= ni
|| si
== di
) { /* End of field? */
2880 if ( ni
== 11 ) { /* Name extension overflow? */
2881 cf
|= NS_LOSS
| NS_LFN
;
2884 if ( si
!= di
) cf
|= NS_LOSS
| NS_LFN
; /* Name body overflow? */
2885 if ( si
> di
) break ; /* No name extension? */
2886 si
= di
; i
= 8 ; ni
= 11 ; b
<<= 2 ; /* Enter name extension */
2890 if ( wc
>= 0x80 ) { /* Is this a non-ASCII character? */
2891 cf
|= NS_LFN
; /* LFN entry needs to be created */
2892 #if FF_CODE_PAGE == 0
2893 if ( ExCvt
) { /* At SBCS */
2894 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2895 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2896 } else { /* At DBCS */
2897 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Upper convert ==> ANSI/OEM code */
2899 #elif FF_CODE_PAGE < 900 /* SBCS cfg */
2900 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* Unicode ==> ANSI/OEM code */
2901 if ( wc
& 0x80 ) wc
= ExCvt
[ wc
& 0x7F ]; /* Convert extended character to upper (SBCS) */
2902 #else /* DBCS cfg */
2903 wc
= ff_uni2oem ( ff_wtoupper ( wc
), CODEPAGE
); /* Unicode ==> Upper convert ==> ANSI/OEM code */
2907 if ( wc
>= 0x100 ) { /* Is this a DBC? */
2908 if ( i
>= ni
- 1 ) { /* Field overflow? */
2909 cf
|= NS_LOSS
| NS_LFN
;
2910 i
= ni
; continue ; /* Next field */
2912 dp
-> fn
[ i
++] = ( BYTE
)( wc
>> 8 ); /* Put 1st byte */
2914 if ( wc
== 0 || chk_chr ( "+,;=[]" , wc
)) { /* Replace illegal characters for SFN if needed */
2915 wc
= '_' ; cf
|= NS_LOSS
| NS_LFN
; /* Lossy conversion */
2917 if ( IsUpper ( wc
)) { /* ASCII upper case? */
2920 if ( IsLower ( wc
)) { /* ASCII lower case? */
2925 dp
-> fn
[ i
++] = ( BYTE
) wc
;
2928 if ( dp
-> fn
[ 0 ] == DDEM
) dp
-> fn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
2930 if ( ni
== 8 ) b
<<= 2 ; /* Shift capital flags if no extension */
2931 if (( b
& 0x0C ) == 0x0C || ( b
& 0x03 ) == 0x03 ) cf
|= NS_LFN
; /* LFN entry needs to be created if composite capitals */
2932 if (!( cf
& NS_LFN
)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
2933 if ( b
& 0x01 ) cf
|= NS_EXT
; /* NT flag (Extension has small capital letters only) */
2934 if ( b
& 0x04 ) cf
|= NS_BODY
; /* NT flag (Body has small capital letters only) */
2937 dp
-> fn
[ NSFLAG
] = cf
; /* SFN is created into dp->fn[] */
2942 #else /* FF_USE_LFN : Non-LFN configuration */
2947 /* Create file name in directory form */
2948 p
= * path
; sfn
= dp
-> fn
;
2949 mem_set ( sfn
, ' ' , 11 );
2951 #if FF_FS_RPATH != 0
2952 if ( p
[ si
] == '.' ) { /* Is this a dot entry? */
2955 if ( c
!= '.' || si
>= 3 ) break ;
2958 if ( c
!= '/' && c
!= ' \\ ' && c
> ' ' ) return FR_INVALID_NAME
;
2959 * path
= p
+ si
; /* Return pointer to the next segment */
2960 sfn
[ NSFLAG
] = ( c
<= ' ' ) ? NS_LAST
| NS_DOT
: NS_DOT
; /* Set last segment flag if end of the path */
2965 c
= ( BYTE
) p
[ si
++]; /* Get a byte */
2966 if ( c
<= ' ' ) break ; /* Break if end of the path name */
2967 if ( c
== '/' || c
== ' \\ ' ) { /* Break if a separator is found */
2968 while ( p
[ si
] == '/' || p
[ si
] == ' \\ ' ) si
++; /* Skip duplicated separator if exist */
2971 if ( c
== '.' || i
>= ni
) { /* End of body or field overflow? */
2972 if ( ni
== 11 || c
!= '.' ) return FR_INVALID_NAME
; /* Field overflow or invalid dot? */
2973 i
= 8 ; ni
= 11 ; /* Enter file extension field */
2976 #if FF_CODE_PAGE == 0
2977 if ( ExCvt
&& c
>= 0x80 ) { /* Is SBC extended character? */
2978 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
2980 #elif FF_CODE_PAGE < 900
2981 if ( c
>= 0x80 ) { /* Is SBC extended character? */
2982 c
= ExCvt
[ c
& 0x7F ]; /* To upper SBC extended character */
2985 if ( dbc_1st ( c
)) { /* Check if it is a DBC 1st byte */
2986 d
= ( BYTE
) p
[ si
++]; /* Get 2nd byte */
2987 if (! dbc_2nd ( d
) || i
>= ni
- 1 ) return FR_INVALID_NAME
; /* Reject invalid DBC */
2991 if ( chk_chr ( " \" *+,:;<=> \? []| \x7F " , c
)) return FR_INVALID_NAME
; /* Reject illegal chrs for SFN */
2992 if ( IsLower ( c
)) c
-= 0x20 ; /* To upper */
2996 * path
= p
+ si
; /* Return pointer to the next segment */
2997 if ( i
== 0 ) return FR_INVALID_NAME
; /* Reject nul string */
2999 if ( sfn
[ 0 ] == DDEM
) sfn
[ 0 ] = RDDEM
; /* If the first character collides with DDEM, replace it with RDDEM */
3000 sfn
[ NSFLAG
] = ( c
<= ' ' ) ? NS_LAST
: 0 ; /* Set last segment flag if end of the path */
3003 #endif /* FF_USE_LFN */
3009 /*-----------------------------------------------------------------------*/
3010 /* Follow a file path */
3011 /*-----------------------------------------------------------------------*/
3013 static FRESULT
follow_path ( /* FR_OK(0): successful, !=0: error code */
3014 DIR * dp
, /* Directory object to return last directory and found object */
3015 const TCHAR
* path
/* Full-path string to find a file or directory */
3020 FATFS
* fs
= dp
-> obj
. fs
;
3023 #if FF_FS_RPATH != 0
3024 if (* path
!= '/' && * path
!= ' \\ ' ) { /* Without heading separator */
3025 dp
-> obj
. sclust
= fs
-> cdir
; /* Start from current directory */
3028 { /* With heading separator */
3029 while (* path
== '/' || * path
== ' \\ ' ) path
++; /* Strip heading separator */
3030 dp
-> obj
. sclust
= 0 ; /* Start from root directory */
3033 dp
-> obj
. n_frag
= 0 ; /* Invalidate last fragment counter of the object */
3034 #if FF_FS_RPATH != 0
3035 if ( fs
-> fs_type
== FS_EXFAT
&& dp
-> obj
. sclust
) { /* exFAT: Retrieve the sub-directory's status */
3038 dp
-> obj
. c_scl
= fs
-> cdc_scl
;
3039 dp
-> obj
. c_size
= fs
-> cdc_size
;
3040 dp
-> obj
. c_ofs
= fs
-> cdc_ofs
;
3041 res
= load_obj_xdir (& dj
, & dp
-> obj
);
3042 if ( res
!= FR_OK
) return res
;
3043 dp
-> obj
. objsize
= ld_dword ( fs
-> dirbuf
+ XDIR_FileSize
);
3044 dp
-> obj
. stat
= fs
-> dirbuf
[ XDIR_GenFlags
] & 2 ;
3049 if (( UINT
)* path
< ' ' ) { /* Null path name is the origin directory itself */
3050 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3051 res
= dir_sdi ( dp
, 0 );
3053 } else { /* Follow path */
3055 res
= create_name ( dp
, & path
); /* Get a segment name of the path */
3056 if ( res
!= FR_OK
) break ;
3057 res
= dir_find ( dp
); /* Find an object with the segment name */
3058 ns
= dp
-> fn
[ NSFLAG
];
3059 if ( res
!= FR_OK
) { /* Failed to find the object */
3060 if ( res
== FR_NO_FILE
) { /* Object is not found */
3061 if ( FF_FS_RPATH
&& ( ns
& NS_DOT
)) { /* If dot entry is not exist, stay there */
3062 if (!( ns
& NS_LAST
)) continue ; /* Continue to follow if not last segment */
3063 dp
-> fn
[ NSFLAG
] = NS_NONAME
;
3065 } else { /* Could not find the object */
3066 if (!( ns
& NS_LAST
)) res
= FR_NO_PATH
; /* Adjust error code if not last segment */
3071 if ( ns
& NS_LAST
) break ; /* Last segment matched. Function completed. */
3072 /* Get into the sub-directory */
3073 if (!( dp
-> obj
. attr
& AM_DIR
)) { /* It is not a sub-directory and cannot follow */
3074 res
= FR_NO_PATH
; break ;
3077 if ( fs
-> fs_type
== FS_EXFAT
) { /* Save containing directory information for next dir */
3078 dp
-> obj
. c_scl
= dp
-> obj
. sclust
;
3079 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
3080 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
3081 init_alloc_info ( fs
, & dp
-> obj
); /* Open next directory */
3085 dp
-> obj
. sclust
= ld_clust ( fs
, fs
-> win
+ dp
-> dptr
% SS ( fs
)); /* Open next directory */
3096 /*-----------------------------------------------------------------------*/
3097 /* Get logical drive number from path name */
3098 /*-----------------------------------------------------------------------*/
3100 static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
3101 const TCHAR
** path
/* Pointer to pointer to the path name */
3104 const TCHAR
* tp
, * tt
;
3107 #if FF_STR_VOLUME_ID /* Find string volume ID */
3113 if (! tp
) return vol
; /* Invalid path name? */
3114 do tc
= * tt
++; while (( UINT
) tc
>= ( FF_USE_LFN
? ' ' : '!' ) && tc
!= ':' ); /* Find a colon in the path */
3116 if ( tc
== ':' ) { /* DOS/Windows style volume ID? */
3118 if ( IsDigit (* tp
) && tp
+ 2 == tt
) { /* Is there a numeric volume ID + colon? */
3119 i
= ( int )* tp
- '0' ; /* Get the LD number */
3121 #if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
3125 sp
= VolumeStr
[ i
]; tp
= * path
; /* This string volume ID and path name */
3126 do { /* Compare the volume ID with path name */
3127 c
= * sp
++; tc
= * tp
++;
3128 if ( IsLower ( c
)) c
-= 0x20 ;
3129 if ( IsLower ( tc
)) tc
-= 0x20 ;
3130 } while ( c
&& ( TCHAR
) c
== tc
);
3131 } while (( c
|| tp
!= tt
) && ++ i
< FF_VOLUMES
); /* Repeat for each id until pattern match */
3134 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3135 vol
= i
; /* Drive number */
3136 * path
= tt
; /* Snip the drive prefix off */
3140 #if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
3144 sp
= VolumeStr
[ i
]; tp
= * path
; /* This string volume ID and path name */
3145 do { /* Compare the volume ID with path name */
3146 c
= * sp
++; tc
= *(++ tp
);
3147 if ( IsLower ( c
)) c
-= 0x20 ;
3148 if ( IsLower ( tc
)) tc
-= 0x20 ;
3149 } while ( c
&& ( TCHAR
) c
== tc
);
3150 } while (( c
|| ( tc
!= '/' && ( UINT
) tc
>= ( FF_USE_LFN
? ' ' : '!' ))) && ++ i
< FF_VOLUMES
); /* Repeat for each ID until pattern match */
3151 if ( i
< FF_VOLUMES
) { /* If a volume ID is found, get the drive number and strip it */
3152 vol
= i
; /* Drive number */
3153 * path
= tp
; /* Snip the drive prefix off */
3158 /* No drive prefix is found */
3159 #if FF_FS_RPATH != 0
3160 vol
= CurrVol
; /* Default drive is current drive */
3162 vol
= 0 ; /* Default drive is 0 */
3164 return vol
; /* Return the default drive */
3170 /*-----------------------------------------------------------------------*/
3171 /* Load a sector and check if it is an FAT VBR */
3172 /*-----------------------------------------------------------------------*/
3174 static BYTE
check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */
3175 FATFS
* fs
, /* Filesystem object */
3176 DWORD sect
/* Sector# (lba) to load and check if it is an FAT-VBR or not */
3179 fs
-> wflag
= 0 ; fs
-> winsect
= 0xFFFFFFFF ; /* Invaidate window */
3180 if ( move_window ( fs
, sect
) != FR_OK
) return 4 ; /* Load boot record */
3182 if ( ld_word ( fs
-> win
+ BS_55AA
) != 0xAA55 ) return 3 ; /* Check boot record signature (always here regardless of the sector size) */
3185 if (! mem_cmp ( fs
-> win
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 )) return 1 ; /* Check if exFAT VBR */
3187 if ( fs
-> win
[ BS_JmpBoot
] == 0xE9 || fs
-> win
[ BS_JmpBoot
] == 0xEB || fs
-> win
[ BS_JmpBoot
] == 0xE8 ) { /* Valid JumpBoot code? */
3188 if (! mem_cmp ( fs
-> win
+ BS_FilSysType
, "FAT" , 3 )) return 0 ; /* Is it an FAT VBR? */
3189 if (! mem_cmp ( fs
-> win
+ BS_FilSysType32
, "FAT32" , 5 )) return 0 ; /* Is it an FAT32 VBR? */
3191 return 2 ; /* Valid BS but not FAT */
3197 /*-----------------------------------------------------------------------*/
3198 /* Determine logical drive number and mount the volume if needed */
3199 /*-----------------------------------------------------------------------*/
3201 static FRESULT
find_volume ( /* FR_OK(0): successful, !=0: an error occurred */
3202 const TCHAR
** path
, /* Pointer to pointer to the path name (drive number) */
3203 FATFS
** rfs
, /* Pointer to pointer to the found filesystem object */
3204 BYTE mode
/* !=0: Check write protection for write access */
3210 DWORD bsect
, fasize
, tsect
, sysect
, nclst
, szbfat
, br
[ 4 ];
3216 /* Get logical drive number */
3218 vol
= get_ldnumber ( path
);
3219 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3221 /* Check if the filesystem object is valid or not */
3222 fs
= FatFs
[ vol
]; /* Get pointer to the filesystem object */
3223 if (! fs
) return FR_NOT_ENABLED
; /* Is the filesystem object available? */
3225 if (! lock_fs ( fs
)) return FR_TIMEOUT
; /* Lock the volume */
3227 * rfs
= fs
; /* Return pointer to the filesystem object */
3229 mode
&= ( BYTE
)~ FA_READ
; /* Desired access mode, write access or not */
3230 if ( fs
-> fs_type
!= 0 ) { /* If the volume has been mounted */
3231 stat
= disk_status ( fs
-> pdrv
);
3232 if (!( stat
& STA_NOINIT
)) { /* and the physical drive is kept initialized */
3233 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check write protection if needed */
3234 return FR_WRITE_PROTECTED
;
3236 return FR_OK
; /* The filesystem object is valid */
3240 /* The filesystem object is not valid. */
3241 /* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */
3243 fs
-> fs_type
= 0 ; /* Clear the filesystem object */
3244 fs
-> pdrv
= LD2PD ( vol
); /* Bind the logical drive and a physical drive */
3245 stat
= disk_initialize ( fs
-> pdrv
); /* Initialize the physical drive */
3246 if ( stat
& STA_NOINIT
) { /* Check if the initialization succeeded */
3247 return FR_NOT_READY
; /* Failed to initialize due to no medium or hard error */
3249 if (! FF_FS_READONLY
&& mode
&& ( stat
& STA_PROTECT
)) { /* Check disk write protection if needed */
3250 return FR_WRITE_PROTECTED
;
3252 #if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
3253 if ( disk_ioctl ( fs
-> pdrv
, GET_SECTOR_SIZE
, & SS ( fs
)) != RES_OK
) return FR_DISK_ERR
;
3254 if ( SS ( fs
) > FF_MAX_SS
|| SS ( fs
) < FF_MIN_SS
|| ( SS ( fs
) & ( SS ( fs
) - 1 ))) return FR_DISK_ERR
;
3257 /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */
3259 fmt
= check_fs ( fs
, bsect
); /* Load sector 0 and check if it is an FAT-VBR as SFD */
3260 if ( fmt
== 2 || ( fmt
< 2 && LD2PT ( vol
) != 0 )) { /* Not an FAT-VBR or forced partition number */
3261 for ( i
= 0 ; i
< 4 ; i
++) { /* Get partition offset */
3262 pt
= fs
-> win
+ ( MBR_Table
+ i
* SZ_PTE
);
3263 br
[ i
] = pt
[ PTE_System
] ? ld_dword ( pt
+ PTE_StLba
) : 0 ;
3265 i
= LD2PT ( vol
); /* Partition number: 0:auto, 1-4:forced */
3267 do { /* Find an FAT volume */
3269 fmt
= bsect
? check_fs ( fs
, bsect
) : 3 ; /* Check the partition */
3270 } while ( LD2PT ( vol
) == 0 && fmt
>= 2 && ++ i
< 4 );
3272 if ( fmt
== 4 ) return FR_DISK_ERR
; /* An error occured in the disk I/O layer */
3273 if ( fmt
>= 2 ) return FR_NO_FILESYSTEM
; /* No FAT volume is found */
3275 /* An FAT volume is found (bsect). Following code initializes the filesystem object */
3281 for ( i
= BPB_ZeroedEx
; i
< BPB_ZeroedEx
+ 53 && fs
-> win
[ i
] == 0 ; i
++) ; /* Check zero filler */
3282 if ( i
< BPB_ZeroedEx
+ 53 ) return FR_NO_FILESYSTEM
;
3284 if ( ld_word ( fs
-> win
+ BPB_FSVerEx
) != 0x100 ) return FR_NO_FILESYSTEM
; /* Check exFAT version (must be version 1.0) */
3286 if ( 1 << fs
-> win
[ BPB_BytsPerSecEx
] != SS ( fs
)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
3287 return FR_NO_FILESYSTEM
;
3290 maxlba
= ld_qword ( fs
-> win
+ BPB_TotSecEx
) + bsect
; /* Last LBA + 1 of the volume */
3291 if ( maxlba
>= 0x100000000 ) return FR_NO_FILESYSTEM
; /* (It cannot be handled in 32-bit LBA) */
3293 fs
-> fsize
= ld_dword ( fs
-> win
+ BPB_FatSzEx
); /* Number of sectors per FAT */
3295 fs
-> n_fats
= fs
-> win
[ BPB_NumFATsEx
]; /* Number of FATs */
3296 if ( fs
-> n_fats
!= 1 ) return FR_NO_FILESYSTEM
; /* (Supports only 1 FAT) */
3298 fs
-> csize
= 1 << fs
-> win
[ BPB_SecPerClusEx
]; /* Cluster size */
3299 if ( fs
-> csize
== 0 ) return FR_NO_FILESYSTEM
; /* (Must be 1..32768) */
3301 nclst
= ld_dword ( fs
-> win
+ BPB_NumClusEx
); /* Number of clusters */
3302 if ( nclst
> MAX_EXFAT
) return FR_NO_FILESYSTEM
; /* (Too many clusters) */
3303 fs
-> n_fatent
= nclst
+ 2 ;
3305 /* Boundaries and Limits */
3306 fs
-> volbase
= bsect
;
3307 fs
-> database
= bsect
+ ld_dword ( fs
-> win
+ BPB_DataOfsEx
);
3308 fs
-> fatbase
= bsect
+ ld_dword ( fs
-> win
+ BPB_FatOfsEx
);
3309 if ( maxlba
< ( QWORD
) fs
-> database
+ nclst
* fs
-> csize
) return FR_NO_FILESYSTEM
; /* (Volume size must not be smaller than the size requiered) */
3310 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClusEx
);
3312 /* Check if bitmap location is in assumption (at the first cluster) */
3313 if ( move_window ( fs
, clst2sect ( fs
, fs
-> dirbase
)) != FR_OK
) return FR_DISK_ERR
;
3314 for ( i
= 0 ; i
< SS ( fs
); i
+= SZDIRE
) {
3315 if ( fs
-> win
[ i
] == 0x81 && ld_dword ( fs
-> win
+ i
+ 20 ) == 2 ) break ; /* 81 entry with cluster #2? */
3317 if ( i
== SS ( fs
)) return FR_NO_FILESYSTEM
;
3319 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3321 fmt
= FS_EXFAT
; /* FAT sub-type */
3323 #endif /* FF_FS_EXFAT */
3325 if ( ld_word ( fs
-> win
+ BPB_BytsPerSec
) != SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_BytsPerSec must be equal to the physical sector size) */
3327 fasize
= ld_word ( fs
-> win
+ BPB_FATSz16
); /* Number of sectors per FAT */
3328 if ( fasize
== 0 ) fasize
= ld_dword ( fs
-> win
+ BPB_FATSz32
);
3331 fs
-> n_fats
= fs
-> win
[ BPB_NumFATs
]; /* Number of FATs */
3332 if ( fs
-> n_fats
!= 1 && fs
-> n_fats
!= 2 ) return FR_NO_FILESYSTEM
; /* (Must be 1 or 2) */
3333 fasize
*= fs
-> n_fats
; /* Number of sectors for FAT area */
3335 fs
-> csize
= fs
-> win
[ BPB_SecPerClus
]; /* Cluster size */
3336 if ( fs
-> csize
== 0 || ( fs
-> csize
& ( fs
-> csize
- 1 ))) return FR_NO_FILESYSTEM
; /* (Must be power of 2) */
3338 fs
-> n_rootdir
= ld_word ( fs
-> win
+ BPB_RootEntCnt
); /* Number of root directory entries */
3339 if ( fs
-> n_rootdir
% ( SS ( fs
) / SZDIRE
)) return FR_NO_FILESYSTEM
; /* (Must be sector aligned) */
3341 tsect
= ld_word ( fs
-> win
+ BPB_TotSec16
); /* Number of sectors on the volume */
3342 if ( tsect
== 0 ) tsect
= ld_dword ( fs
-> win
+ BPB_TotSec32
);
3344 nrsv
= ld_word ( fs
-> win
+ BPB_RsvdSecCnt
); /* Number of reserved sectors */
3345 if ( nrsv
== 0 ) return FR_NO_FILESYSTEM
; /* (Must not be 0) */
3347 /* Determine the FAT sub type */
3348 sysect
= nrsv
+ fasize
+ fs
-> n_rootdir
/ ( SS ( fs
) / SZDIRE
); /* RSV + FAT + DIR */
3349 if ( tsect
< sysect
) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3350 nclst
= ( tsect
- sysect
) / fs
-> csize
; /* Number of clusters */
3351 if ( nclst
== 0 ) return FR_NO_FILESYSTEM
; /* (Invalid volume size) */
3353 if ( nclst
<= MAX_FAT32
) fmt
= FS_FAT32
;
3354 if ( nclst
<= MAX_FAT16
) fmt
= FS_FAT16
;
3355 if ( nclst
<= MAX_FAT12
) fmt
= FS_FAT12
;
3356 if ( fmt
== 0 ) return FR_NO_FILESYSTEM
;
3358 /* Boundaries and Limits */
3359 fs
-> n_fatent
= nclst
+ 2 ; /* Number of FAT entries */
3360 fs
-> volbase
= bsect
; /* Volume start sector */
3361 fs
-> fatbase
= bsect
+ nrsv
; /* FAT start sector */
3362 fs
-> database
= bsect
+ sysect
; /* Data start sector */
3363 if ( fmt
== FS_FAT32
) {
3364 if ( ld_word ( fs
-> win
+ BPB_FSVer32
) != 0 ) return FR_NO_FILESYSTEM
; /* (Must be FAT32 revision 0.0) */
3365 if ( fs
-> n_rootdir
!= 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must be 0) */
3366 fs
-> dirbase
= ld_dword ( fs
-> win
+ BPB_RootClus32
); /* Root directory start cluster */
3367 szbfat
= fs
-> n_fatent
* 4 ; /* (Needed FAT size) */
3369 if ( fs
-> n_rootdir
== 0 ) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must not be 0) */
3370 fs
-> dirbase
= fs
-> fatbase
+ fasize
; /* Root directory start sector */
3371 szbfat
= ( fmt
== FS_FAT16
) ? /* (Needed FAT size) */
3372 fs
-> n_fatent
* 2 : fs
-> n_fatent
* 3 / 2 + ( fs
-> n_fatent
& 1 );
3374 if ( fs
-> fsize
< ( szbfat
+ ( SS ( fs
) - 1 )) / SS ( fs
)) return FR_NO_FILESYSTEM
; /* (BPB_FATSz must not be less than the size needed) */
3377 /* Get FSInfo if available */
3378 fs
-> last_clst
= fs
-> free_clst
= 0xFFFFFFFF ; /* Initialize cluster allocation information */
3379 fs
-> fsi_flag
= 0x80 ;
3380 #if (FF_FS_NOFSINFO & 3) != 3
3381 if ( fmt
== FS_FAT32
/* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
3382 && ld_word ( fs
-> win
+ BPB_FSInfo32
) == 1
3383 && move_window ( fs
, bsect
+ 1 ) == FR_OK
)
3386 if ( ld_word ( fs
-> win
+ BS_55AA
) == 0xAA55 /* Load FSInfo data if available */
3387 && ld_dword ( fs
-> win
+ FSI_LeadSig
) == 0x41615252
3388 && ld_dword ( fs
-> win
+ FSI_StrucSig
) == 0x61417272 )
3390 #if (FF_FS_NOFSINFO & 1) == 0
3391 fs
-> free_clst
= ld_dword ( fs
-> win
+ FSI_Free_Count
);
3393 #if (FF_FS_NOFSINFO & 2) == 0
3394 fs
-> last_clst
= ld_dword ( fs
-> win
+ FSI_Nxt_Free
);
3398 #endif /* (FF_FS_NOFSINFO & 3) != 3 */
3399 #endif /* !FF_FS_READONLY */
3402 fs
-> fs_type
= fmt
; /* FAT sub-type */
3403 fs
-> id
= ++ Fsid
; /* Volume mount ID */
3405 fs
-> lfnbuf
= LfnBuf
; /* Static LFN working buffer */
3407 fs
-> dirbuf
= DirBuf
; /* Static directory block scratchpad buuffer */
3410 #if FF_FS_RPATH != 0
3411 fs
-> cdir
= 0 ; /* Initialize current directory */
3413 #if FF_FS_LOCK != 0 /* Clear file lock semaphores */
3422 /*-----------------------------------------------------------------------*/
3423 /* Check if the file/directory object is valid or not */
3424 /*-----------------------------------------------------------------------*/
3426 static FRESULT
validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
3427 FFOBJID
* obj
, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
3428 FATFS
** rfs
/* Pointer to pointer to the owner filesystem object to return */
3431 FRESULT res
= FR_INVALID_OBJECT
;
3434 if ( obj
&& obj
-> fs
&& obj
-> fs
-> fs_type
&& obj
-> id
== obj
-> fs
-> id
) { /* Test if the object is valid */
3436 if ( lock_fs ( obj
-> fs
)) { /* Obtain the filesystem object */
3437 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the phsical drive is kept initialized */
3440 unlock_fs ( obj
-> fs
, FR_OK
);
3446 if (!( disk_status ( obj
-> fs
-> pdrv
) & STA_NOINIT
)) { /* Test if the phsical drive is kept initialized */
3451 * rfs
= ( res
== FR_OK
) ? obj
-> fs
: 0 ; /* Corresponding filesystem object */
3458 /*---------------------------------------------------------------------------
3460 Public Functions (FatFs API)
3462 ----------------------------------------------------------------------------*/
3466 /*-----------------------------------------------------------------------*/
3467 /* Mount/Unmount a Logical Drive */
3468 /*-----------------------------------------------------------------------*/
3471 FATFS
* fs
, /* Pointer to the filesystem object (NULL:unmount)*/
3472 const TCHAR
* path
, /* Logical drive number to be mounted/unmounted */
3473 BYTE opt
/* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
3479 const TCHAR
* rp
= path
;
3482 /* Get logical drive number */
3483 vol
= get_ldnumber (& rp
);
3484 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
3485 cfs
= FatFs
[ vol
]; /* Pointer to fs object */
3491 #if FF_FS_REENTRANT /* Discard sync object of the current volume */
3492 if (! ff_del_syncobj ( cfs
-> sobj
)) return FR_INT_ERR
;
3494 cfs
-> fs_type
= 0 ; /* Clear old fs object */
3498 fs
-> fs_type
= 0 ; /* Clear new fs object */
3499 #if FF_FS_REENTRANT /* Create sync object for the new volume */
3500 if (! ff_cre_syncobj (( BYTE
) vol
, & fs
-> sobj
)) return FR_INT_ERR
;
3503 FatFs
[ vol
] = fs
; /* Register new fs object */
3505 if ( opt
== 0 ) return FR_OK
; /* Do not mount now, it will be mounted later */
3507 res
= find_volume (& path
, & fs
, 0 ); /* Force mounted the volume */
3514 /*-----------------------------------------------------------------------*/
3515 /* Open or Create a File */
3516 /*-----------------------------------------------------------------------*/
3519 FIL
* fp
, /* Pointer to the blank file object */
3520 const TCHAR
* path
, /* Pointer to the file name */
3521 BYTE mode
/* Access mode and file open mode flags */
3528 DWORD dw
, cl
, bcs
, clst
, sc
;
3534 if (! fp
) return FR_INVALID_OBJECT
;
3536 /* Get logical drive number */
3537 mode
&= FF_FS_READONLY
? FA_READ
: FA_READ
| FA_WRITE
| FA_CREATE_ALWAYS
| FA_CREATE_NEW
| FA_OPEN_ALWAYS
| FA_OPEN_APPEND
;
3538 res
= find_volume (& path
, & fs
, mode
);
3542 res
= follow_path (& dj
, path
); /* Follow the file path */
3543 #if !FF_FS_READONLY /* Read/Write configuration */
3545 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Origin directory itself? */
3546 res
= FR_INVALID_NAME
;
3550 res
= chk_lock (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Check if the file can be used */
3554 /* Create or Open a file */
3555 if ( mode
& ( FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
)) {
3556 if ( res
!= FR_OK
) { /* No file, create new */
3557 if ( res
== FR_NO_FILE
) { /* There is no file to open, create a new entry */
3559 res
= enq_lock () ? dir_register (& dj
) : FR_TOO_MANY_OPEN_FILES
;
3561 res
= dir_register (& dj
);
3564 mode
|= FA_CREATE_ALWAYS
; /* File is created */
3566 else { /* Any object with the same name is already existing */
3567 if ( dj
. obj
. attr
& ( AM_RDO
| AM_DIR
)) { /* Cannot overwrite it (R/O or DIR) */
3570 if ( mode
& FA_CREATE_NEW
) res
= FR_EXIST
; /* Cannot create as new file */
3573 if ( res
== FR_OK
&& ( mode
& FA_CREATE_ALWAYS
)) { /* Truncate the file if overwrite mode */
3575 if ( fs
-> fs_type
== FS_EXFAT
) {
3576 /* Get current allocation info */
3578 init_alloc_info ( fs
, & fp
-> obj
);
3579 /* Set directory entry block initial state */
3580 mem_set ( fs
-> dirbuf
+ 2 , 0 , 30 ); /* Clear 85 entry except for NumSec */
3581 mem_set ( fs
-> dirbuf
+ 38 , 0 , 26 ); /* Clear C0 entry except for NumName and NameHash */
3582 fs
-> dirbuf
[ XDIR_Attr
] = AM_ARC
;
3583 st_dword ( fs
-> dirbuf
+ XDIR_CrtTime
, GET_FATTIME ());
3584 fs
-> dirbuf
[ XDIR_GenFlags
] = 1 ;
3585 res
= store_xdir (& dj
);
3586 if ( res
== FR_OK
&& fp
-> obj
. sclust
!= 0 ) { /* Remove the cluster chain if exist */
3587 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
3588 fs
-> last_clst
= fp
-> obj
. sclust
- 1 ; /* Reuse the cluster hole */
3593 /* Set directory entry initial state */
3594 cl
= ld_clust ( fs
, dj
. dir
); /* Get current cluster chain */
3595 st_dword ( dj
. dir
+ DIR_CrtTime
, GET_FATTIME ()); /* Set created time */
3596 dj
. dir
[ DIR_Attr
] = AM_ARC
; /* Reset attribute */
3597 st_clust ( fs
, dj
. dir
, 0 ); /* Reset file allocation info */
3598 st_dword ( dj
. dir
+ DIR_FileSize
, 0 );
3600 if ( cl
!= 0 ) { /* Remove the cluster chain if exist */
3602 res
= remove_chain (& dj
. obj
, cl
, 0 );
3604 res
= move_window ( fs
, dw
);
3605 fs
-> last_clst
= cl
- 1 ; /* Reuse the cluster hole */
3611 else { /* Open an existing file */
3612 if ( res
== FR_OK
) { /* Is the object exsiting? */
3613 if ( dj
. obj
. attr
& AM_DIR
) { /* File open against a directory */
3616 if (( mode
& FA_WRITE
) && ( dj
. obj
. attr
& AM_RDO
)) { /* Write mode open against R/O file */
3623 if ( mode
& FA_CREATE_ALWAYS
) mode
|= FA_MODIFIED
; /* Set file change flag if created or overwritten */
3624 fp
-> dir_sect
= fs
-> winsect
; /* Pointer to the directory entry */
3625 fp
-> dir_ptr
= dj
. dir
;
3627 fp
-> obj
. lockid
= inc_lock (& dj
, ( mode
& ~ FA_READ
) ? 1 : 0 ); /* Lock the file for this session */
3628 if ( fp
-> obj
. lockid
== 0 ) res
= FR_INT_ERR
;
3631 #else /* R/O configuration */
3633 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it origin directory itself? */
3634 res
= FR_INVALID_NAME
;
3636 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a directory? */
3645 if ( fs
-> fs_type
== FS_EXFAT
) {
3646 fp
-> obj
. c_scl
= dj
. obj
. sclust
; /* Get containing directory info */
3647 fp
-> obj
. c_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
3648 fp
-> obj
. c_ofs
= dj
. blk_ofs
;
3649 init_alloc_info ( fs
, & fp
-> obj
);
3653 fp
-> obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Get object allocation info */
3654 fp
-> obj
. objsize
= ld_dword ( dj
. dir
+ DIR_FileSize
);
3657 fp
-> cltbl
= 0 ; /* Disable fast seek mode */
3659 fp
-> obj
. fs
= fs
; /* Validate the file object */
3660 fp
-> obj
. id
= fs
-> id
;
3661 fp
-> flag
= mode
; /* Set file access mode */
3662 fp
-> err
= 0 ; /* Clear error flag */
3663 fp
-> sect
= 0 ; /* Invalidate current data sector */
3664 fp
-> fptr
= 0 ; /* Set file pointer top of the file */
3667 mem_set ( fp
-> buf
, 0 , FF_MAX_SS
); /* Clear sector buffer */
3669 if (( mode
& FA_SEEKEND
) && fp
-> obj
. objsize
> 0 ) { /* Seek to end of file if FA_OPEN_APPEND is specified */
3670 fp
-> fptr
= fp
-> obj
. objsize
; /* Offset to seek */
3671 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size in byte */
3672 clst
= fp
-> obj
. sclust
; /* Follow the cluster chain */
3673 for ( ofs
= fp
-> obj
. objsize
; res
== FR_OK
&& ofs
> bcs
; ofs
-= bcs
) {
3674 clst
= get_fat (& fp
-> obj
, clst
);
3675 if ( clst
<= 1 ) res
= FR_INT_ERR
;
3676 if ( clst
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
3679 if ( res
== FR_OK
&& ofs
% SS ( fs
)) { /* Fill sector buffer if not on the sector boundary */
3680 if (( sc
= clst2sect ( fs
, clst
)) == 0 ) {
3683 fp
-> sect
= sc
+ ( DWORD
)( ofs
/ SS ( fs
));
3685 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) res
= FR_DISK_ERR
;
3696 if ( res
!= FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object on error */
3704 /*-----------------------------------------------------------------------*/
3706 /*-----------------------------------------------------------------------*/
3709 FIL
* fp
, /* Pointer to the file object */
3710 void * buff
, /* Pointer to data buffer */
3711 UINT btr
, /* Number of bytes to read */
3712 UINT
* br
/* Pointer to number of bytes read */
3719 UINT rcnt
, cc
, csect
;
3720 BYTE
* rbuff
= ( BYTE
*) buff
;
3723 * br
= 0 ; /* Clear read byte counter */
3724 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3725 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
3726 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
3727 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
3728 if ( btr
> remain
) btr
= ( UINT
) remain
; /* Truncate btr by remaining bytes */
3730 for ( ; btr
; /* Repeat until btr bytes read */
3731 btr
-= rcnt
, * br
+= rcnt
, rbuff
+= rcnt
, fp
-> fptr
+= rcnt
) {
3732 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
3733 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
3734 if ( csect
== 0 ) { /* On the cluster boundary? */
3735 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
3736 clst
= fp
-> obj
. sclust
; /* Follow cluster chain from the origin */
3737 } else { /* Middle or end of the file */
3740 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
3744 clst
= get_fat (& fp
-> obj
, fp
-> clust
); /* Follow cluster chain on the FAT */
3747 if ( clst
< 2 ) ABORT ( fs
, FR_INT_ERR
);
3748 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
3749 fp
-> clust
= clst
; /* Update current cluster */
3751 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
3752 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
3754 cc
= btr
/ SS ( fs
); /* When remaining bytes >= sector size, */
3755 if ( cc
> 0 ) { /* Read maximum contiguous sectors directly */
3756 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
3757 cc
= fs
-> csize
- csect
;
3759 if ( disk_read ( fs
-> pdrv
, rbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3760 #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
3762 if ( fs
-> wflag
&& fs
-> winsect
- sect
< cc
) {
3763 mem_cpy ( rbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), fs
-> win
, SS ( fs
));
3766 if (( fp
-> flag
& FA_DIRTY
) && fp
-> sect
- sect
< cc
) {
3767 mem_cpy ( rbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), fp
-> buf
, SS ( fs
));
3771 rcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
3775 if ( fp
-> sect
!= sect
) { /* Load data sector if not in cache */
3777 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
3778 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3779 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3782 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
3787 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes left in the sector */
3788 if ( rcnt
> btr
) rcnt
= btr
; /* Clip it by btr if needed */
3790 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
3791 mem_cpy ( rbuff
, fs
-> win
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
3793 mem_cpy ( rbuff
, fp
-> buf
+ fp
-> fptr
% SS ( fs
), rcnt
); /* Extract partial sector */
3797 LEAVE_FF ( fs
, FR_OK
);
3804 /*-----------------------------------------------------------------------*/
3806 /*-----------------------------------------------------------------------*/
3809 FIL
* fp
, /* Pointer to the file object */
3810 const void * buff
, /* Pointer to the data to be written */
3811 UINT btw
, /* Number of bytes to write */
3812 UINT
* bw
/* Pointer to number of bytes written */
3818 UINT wcnt
, cc
, csect
;
3819 const BYTE
* wbuff
= ( const BYTE
*) buff
;
3822 * bw
= 0 ; /* Clear write byte counter */
3823 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3824 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
); /* Check validity */
3825 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
3827 /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
3828 if ((! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) && ( DWORD
)( fp
-> fptr
+ btw
) < ( DWORD
) fp
-> fptr
) {
3829 btw
= ( UINT
)( 0xFFFFFFFF - ( DWORD
) fp
-> fptr
);
3832 for ( ; btw
; /* Repeat until all data written */
3833 btw
-= wcnt
, * bw
+= wcnt
, wbuff
+= wcnt
, fp
-> fptr
+= wcnt
, fp
-> obj
. objsize
= ( fp
-> fptr
> fp
-> obj
. objsize
) ? fp
-> fptr
: fp
-> obj
. objsize
) {
3834 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
3835 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
)) & ( fs
-> csize
- 1 ); /* Sector offset in the cluster */
3836 if ( csect
== 0 ) { /* On the cluster boundary? */
3837 if ( fp
-> fptr
== 0 ) { /* On the top of the file? */
3838 clst
= fp
-> obj
. sclust
; /* Follow from the origin */
3839 if ( clst
== 0 ) { /* If no cluster is allocated, */
3840 clst
= create_chain (& fp
-> obj
, 0 ); /* create a new cluster chain */
3842 } else { /* On the middle or end of the file */
3845 clst
= clmt_clust ( fp
, fp
-> fptr
); /* Get cluster# from the CLMT */
3849 clst
= create_chain (& fp
-> obj
, fp
-> clust
); /* Follow or stretch cluster chain on the FAT */
3852 if ( clst
== 0 ) break ; /* Could not allocate a new cluster (disk full) */
3853 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
3854 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
3855 fp
-> clust
= clst
; /* Update current cluster */
3856 if ( fp
-> obj
. sclust
== 0 ) fp
-> obj
. sclust
= clst
; /* Set start cluster if the first write */
3859 if ( fs
-> winsect
== fp
-> sect
&& sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Write-back sector cache */
3861 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back sector cache */
3862 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3863 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3866 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current sector */
3867 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
3869 cc
= btw
/ SS ( fs
); /* When remaining bytes >= sector size, */
3870 if ( cc
> 0 ) { /* Write maximum contiguous sectors directly */
3871 if ( csect
+ cc
> fs
-> csize
) { /* Clip at cluster boundary */
3872 cc
= fs
-> csize
- csect
;
3874 if ( disk_write ( fs
-> pdrv
, wbuff
, sect
, cc
) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
3875 #if FF_FS_MINIMIZE <= 2
3877 if ( fs
-> winsect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
3878 mem_cpy ( fs
-> win
, wbuff
+ (( fs
-> winsect
- sect
) * SS ( fs
)), SS ( fs
));
3882 if ( fp
-> sect
- sect
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */
3883 mem_cpy ( fp
-> buf
, wbuff
+ (( fp
-> sect
- sect
) * SS ( fs
)), SS ( fs
));
3884 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3888 wcnt
= SS ( fs
) * cc
; /* Number of bytes transferred */
3892 if ( fp
-> fptr
>= fp
-> obj
. objsize
) { /* Avoid silly cache filling on the growing edge */
3893 if ( sync_window ( fs
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
);
3897 if ( fp
-> sect
!= sect
&& /* Fill sector cache with file data */
3898 fp
-> fptr
< fp
-> obj
. objsize
&&
3899 disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) {
3900 ABORT ( fs
, FR_DISK_ERR
);
3905 wcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes left in the sector */
3906 if ( wcnt
> btw
) wcnt
= btw
; /* Clip it by btw if needed */
3908 if ( move_window ( fs
, fp
-> sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window */
3909 mem_cpy ( fs
-> win
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
3912 mem_cpy ( fp
-> buf
+ fp
-> fptr
% SS ( fs
), wbuff
, wcnt
); /* Fit data to the sector */
3913 fp
-> flag
|= FA_DIRTY
;
3917 fp
-> flag
|= FA_MODIFIED
; /* Set file change flag */
3919 LEAVE_FF ( fs
, FR_OK
);
3925 /*-----------------------------------------------------------------------*/
3926 /* Synchronize the File */
3927 /*-----------------------------------------------------------------------*/
3930 FIL
* fp
/* Pointer to the file object */
3939 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
3941 if ( fp
-> flag
& FA_MODIFIED
) { /* Is there any change to the file? */
3943 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back cached data if needed */
3944 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) LEAVE_FF ( fs
, FR_DISK_ERR
);
3945 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
3948 /* Update the directory entry */
3949 tm
= GET_FATTIME (); /* Modified time */
3951 if ( fs
-> fs_type
== FS_EXFAT
) {
3952 res
= fill_first_frag (& fp
-> obj
); /* Fill first fragment on the FAT if needed */
3954 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
3961 res
= load_obj_xdir (& dj
, & fp
-> obj
); /* Load directory entry block */
3963 fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
3964 fs
-> dirbuf
[ XDIR_GenFlags
] = fp
-> obj
. stat
| 1 ; /* Update file allocation information */
3965 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, fp
-> obj
. sclust
);
3966 st_qword ( fs
-> dirbuf
+ XDIR_FileSize
, fp
-> obj
. objsize
);
3967 st_qword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, fp
-> obj
. objsize
);
3968 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Update modified time */
3969 fs
-> dirbuf
[ XDIR_ModTime10
] = 0 ;
3970 st_dword ( fs
-> dirbuf
+ XDIR_AccTime
, 0 );
3971 res
= store_xdir (& dj
); /* Restore it to the directory */
3974 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
3982 res
= move_window ( fs
, fp
-> dir_sect
);
3985 dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute to indicate that the file has been changed */
3986 st_clust ( fp
-> obj
. fs
, dir
, fp
-> obj
. sclust
); /* Update file allocation information */
3987 st_dword ( dir
+ DIR_FileSize
, ( DWORD
) fp
-> obj
. objsize
); /* Update file size */
3988 st_dword ( dir
+ DIR_ModTime
, tm
); /* Update modified time */
3989 st_word ( dir
+ DIR_LstAccDate
, 0 );
3991 res
= sync_fs ( fs
); /* Restore it to the directory */
3992 fp
-> flag
&= ( BYTE
)~ FA_MODIFIED
;
4001 #endif /* !FF_FS_READONLY */
4006 /*-----------------------------------------------------------------------*/
4008 /*-----------------------------------------------------------------------*/
4011 FIL
* fp
/* Pointer to the file object to be closed */
4018 res
= f_sync ( fp
); /* Flush cached data */
4022 res
= validate (& fp
-> obj
, & fs
); /* Lock volume */
4025 res
= dec_lock ( fp
-> obj
. lockid
); /* Decrement file open counter */
4026 if ( res
== FR_OK
) fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4028 fp
-> obj
. fs
= 0 ; /* Invalidate file object */
4031 unlock_fs ( fs
, FR_OK
); /* Unlock volume */
4041 #if FF_FS_RPATH >= 1
4042 /*-----------------------------------------------------------------------*/
4043 /* Change Current Directory or Current Drive, Get Current Directory */
4044 /*-----------------------------------------------------------------------*/
4047 const TCHAR
* path
/* Drive number to set */
4053 /* Get logical drive number */
4054 vol
= get_ldnumber (& path
);
4055 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
4056 CurrVol
= ( BYTE
) vol
; /* Set it as current volume */
4064 const TCHAR
* path
/* Pointer to the directory path */
4067 #if FF_STR_VOLUME_ID == 2
4076 /* Get logical drive */
4077 res
= find_volume (& path
, & fs
, 0 );
4081 res
= follow_path (& dj
, path
); /* Follow the path */
4082 if ( res
== FR_OK
) { /* Follow completed */
4083 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* Is it the start directory itself? */
4084 fs
-> cdir
= dj
. obj
. sclust
;
4086 if ( fs
-> fs_type
== FS_EXFAT
) {
4087 fs
-> cdc_scl
= dj
. obj
. c_scl
;
4088 fs
-> cdc_size
= dj
. obj
. c_size
;
4089 fs
-> cdc_ofs
= dj
. obj
. c_ofs
;
4093 if ( dj
. obj
. attr
& AM_DIR
) { /* It is a sub-directory */
4095 if ( fs
-> fs_type
== FS_EXFAT
) {
4096 fs
-> cdir
= ld_dword ( fs
-> dirbuf
+ XDIR_FstClus
); /* Sub-directory cluster */
4097 fs
-> cdc_scl
= dj
. obj
. sclust
; /* Save containing directory information */
4098 fs
-> cdc_size
= (( DWORD
) dj
. obj
. objsize
& 0xFFFFFF00 ) | dj
. obj
. stat
;
4099 fs
-> cdc_ofs
= dj
. blk_ofs
;
4103 fs
-> cdir
= ld_clust ( fs
, dj
. dir
); /* Sub-directory cluster */
4106 res
= FR_NO_PATH
; /* Reached but a file */
4111 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4112 #if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */
4114 for ( i
= FF_VOLUMES
- 1 ; i
&& fs
!= FatFs
[ i
]; i
--) ; /* Set current drive */
4124 #if FF_FS_RPATH >= 2
4126 TCHAR
* buff
, /* Pointer to the directory path */
4127 UINT len
/* Size of buff in unit of TCHAR */
4139 #if FF_STR_VOLUME_ID
4146 /* Get logical drive */
4147 res
= find_volume (( const TCHAR
**)& buff
, & fs
, 0 ); /* Get current volume */
4152 /* Follow parent directories and create the path */
4153 i
= len
; /* Bottom of buffer (directory stack base) */
4154 if (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
) { /* (Cannot do getcwd on exFAT and returns root path) */
4155 dj
. obj
. sclust
= fs
-> cdir
; /* Start to follow upper directory from current directory */
4156 while (( ccl
= dj
. obj
. sclust
) != 0 ) { /* Repeat while current directory is a sub-directory */
4157 res
= dir_sdi (& dj
, 1 * SZDIRE
); /* Get parent directory */
4158 if ( res
!= FR_OK
) break ;
4159 res
= move_window ( fs
, dj
. sect
);
4160 if ( res
!= FR_OK
) break ;
4161 dj
. obj
. sclust
= ld_clust ( fs
, dj
. dir
); /* Goto parent directory */
4162 res
= dir_sdi (& dj
, 0 );
4163 if ( res
!= FR_OK
) break ;
4164 do { /* Find the entry links to the child directory */
4165 res
= dir_read_file (& dj
);
4166 if ( res
!= FR_OK
) break ;
4167 if ( ccl
== ld_clust ( fs
, dj
. dir
)) break ; /* Found the entry */
4168 res
= dir_next (& dj
, 0 );
4169 } while ( res
== FR_OK
);
4170 if ( res
== FR_NO_FILE
) res
= FR_INT_ERR
; /* It cannot be 'not found'. */
4171 if ( res
!= FR_OK
) break ;
4172 get_fileinfo (& dj
, & fno
); /* Get the directory name and push it to the buffer */
4173 for ( n
= 0 ; fno
. fname
[ n
]; n
++) ; /* Name length */
4174 if ( i
< n
+ 1 ) { /* Insufficient space to store the path name? */
4175 res
= FR_NOT_ENOUGH_CORE
; break ;
4177 while ( n
) buff
[-- i
] = fno
. fname
[-- n
]; /* Stack the name */
4182 if ( i
== len
) buff
[-- i
] = '/' ; /* Is it the root-directory? */
4183 #if FF_VOLUMES >= 2 /* Put drive prefix */
4185 #if FF_STR_VOLUME_ID >= 1 /* String volume ID */
4186 for ( n
= 0 , vp
= ( const char *) VolumeStr
[ CurrVol
]; vp
[ n
]; n
++) ;
4188 if ( FF_STR_VOLUME_ID
== 2 ) * tp
++ = ( TCHAR
) '/' ;
4189 for ( vl
= 0 ; vl
< n
; * tp
++ = ( TCHAR
) vp
[ vl
], vl
++) ;
4190 if ( FF_STR_VOLUME_ID
== 1 ) * tp
++ = ( TCHAR
) ':' ;
4193 #else /* Numeric volume ID */
4195 * tp
++ = ( TCHAR
) '0' + CurrVol
;
4200 if ( vl
== 0 ) res
= FR_NOT_ENOUGH_CORE
;
4202 /* Add current directory path */
4204 do * tp
++ = buff
[ i
++]; while ( i
< len
); /* Copy stacked path string */
4214 #endif /* FF_FS_RPATH >= 2 */
4215 #endif /* FF_FS_RPATH >= 1 */
4219 #if FF_FS_MINIMIZE <= 2
4220 /*-----------------------------------------------------------------------*/
4221 /* Seek File Read/Write Pointer */
4222 /*-----------------------------------------------------------------------*/
4225 FIL
* fp
, /* Pointer to the file object */
4226 FSIZE_t ofs
/* File pointer from top of file */
4231 DWORD clst
, bcs
, nsect
;
4234 DWORD cl
, pcl
, ncl
, tcl
, dsc
, tlen
, ulen
, * tbl
;
4237 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4238 if ( res
== FR_OK
) res
= ( FRESULT
) fp
-> err
;
4239 #if FF_FS_EXFAT && !FF_FS_READONLY
4240 if ( res
== FR_OK
&& fs
-> fs_type
== FS_EXFAT
) {
4241 res
= fill_last_frag (& fp
-> obj
, fp
-> clust
, 0xFFFFFFFF ); /* Fill last fragment on the FAT if needed */
4244 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
4247 if ( fp
-> cltbl
) { /* Fast seek */
4248 if ( ofs
== CREATE_LINKMAP
) { /* Create CLMT */
4250 tlen
= * tbl
++; ulen
= 2 ; /* Given table size and required table size */
4251 cl
= fp
-> obj
. sclust
; /* Origin of the chain */
4254 /* Get a fragment */
4255 tcl
= cl
; ncl
= 0 ; ulen
+= 2 ; /* Top, length and used items */
4258 cl
= get_fat (& fp
-> obj
, cl
);
4259 if ( cl
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
4260 if ( cl
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4261 } while ( cl
== pcl
+ 1 );
4262 if ( ulen
<= tlen
) { /* Store the length and top of the fragment */
4263 * tbl
++ = ncl
; * tbl
++ = tcl
;
4265 } while ( cl
< fs
-> n_fatent
); /* Repeat until end of chain */
4267 * fp
-> cltbl
= ulen
; /* Number of items used */
4269 * tbl
= 0 ; /* Terminate table */
4271 res
= FR_NOT_ENOUGH_CORE
; /* Given table size is smaller than required */
4273 } else { /* Fast seek */
4274 if ( ofs
> fp
-> obj
. objsize
) ofs
= fp
-> obj
. objsize
; /* Clip offset at the file size */
4275 fp
-> fptr
= ofs
; /* Set file pointer */
4277 fp
-> clust
= clmt_clust ( fp
, ofs
- 1 );
4278 dsc
= clst2sect ( fs
, fp
-> clust
);
4279 if ( dsc
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4280 dsc
+= ( DWORD
)(( ofs
- 1 ) / SS ( fs
)) & ( fs
-> csize
- 1 );
4281 if ( fp
-> fptr
% SS ( fs
) && dsc
!= fp
-> sect
) { /* Refill sector cache if needed */
4284 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4285 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4286 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4289 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, dsc
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Load current sector */
4301 if ( fs
-> fs_type
!= FS_EXFAT
&& ofs
>= 0x100000000 ) ofs
= 0xFFFFFFFF ; /* Clip at 4 GiB - 1 if at FATxx */
4303 if ( ofs
> fp
-> obj
. objsize
&& ( FF_FS_READONLY
|| !( fp
-> flag
& FA_WRITE
))) { /* In read-only mode, clip offset with the file size */
4304 ofs
= fp
-> obj
. objsize
;
4307 fp
-> fptr
= nsect
= 0 ;
4309 bcs
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size (byte) */
4311 ( ofs
- 1 ) / bcs
>= ( ifptr
- 1 ) / bcs
) { /* When seek to same or following cluster, */
4312 fp
-> fptr
= ( ifptr
- 1 ) & ~( FSIZE_t
)( bcs
- 1 ); /* start from the current cluster */
4315 } else { /* When seek to back cluster, */
4316 clst
= fp
-> obj
. sclust
; /* start from the first cluster */
4318 if ( clst
== 0 ) { /* If no cluster chain, create a new chain */
4319 clst
= create_chain (& fp
-> obj
, 0 );
4320 if ( clst
== 1 ) ABORT ( fs
, FR_INT_ERR
);
4321 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4322 fp
-> obj
. sclust
= clst
;
4328 while ( ofs
> bcs
) { /* Cluster following loop */
4329 ofs
-= bcs
; fp
-> fptr
+= bcs
;
4331 if ( fp
-> flag
& FA_WRITE
) { /* Check if in write mode or not */
4332 if ( FF_FS_EXFAT
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* No FAT chain object needs correct objsize to generate FAT value */
4333 fp
-> obj
. objsize
= fp
-> fptr
;
4334 fp
-> flag
|= FA_MODIFIED
;
4336 clst
= create_chain (& fp
-> obj
, clst
); /* Follow chain with forceed stretch */
4337 if ( clst
== 0 ) { /* Clip file size in case of disk full */
4343 clst
= get_fat (& fp
-> obj
, clst
); /* Follow cluster chain if not in write mode */
4345 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
4346 if ( clst
<= 1 || clst
>= fs
-> n_fatent
) ABORT ( fs
, FR_INT_ERR
);
4351 nsect
= clst2sect ( fs
, clst
); /* Current sector */
4352 if ( nsect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
4353 nsect
+= ( DWORD
)( ofs
/ SS ( fs
));
4357 if (! FF_FS_READONLY
&& fp
-> fptr
> fp
-> obj
. objsize
) { /* Set file change flag if the file size is extended */
4358 fp
-> obj
. objsize
= fp
-> fptr
;
4359 fp
-> flag
|= FA_MODIFIED
;
4361 if ( fp
-> fptr
% SS ( fs
) && nsect
!= fp
-> sect
) { /* Fill sector cache if needed */
4364 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
4365 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
4366 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4369 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, nsect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Fill sector cache */
4380 #if FF_FS_MINIMIZE <= 1
4381 /*-----------------------------------------------------------------------*/
4382 /* Create a Directory Object */
4383 /*-----------------------------------------------------------------------*/
4386 DIR * dp
, /* Pointer to directory object to create */
4387 const TCHAR
* path
/* Pointer to the directory path */
4395 if (! dp
) return FR_INVALID_OBJECT
;
4397 /* Get logical drive */
4398 res
= find_volume (& path
, & fs
, 0 );
4402 res
= follow_path ( dp
, path
); /* Follow the path to the directory */
4403 if ( res
== FR_OK
) { /* Follow completed */
4404 if (!( dp
-> fn
[ NSFLAG
] & NS_NONAME
)) { /* It is not the origin directory itself */
4405 if ( dp
-> obj
. attr
& AM_DIR
) { /* This object is a sub-directory */
4407 if ( fs
-> fs_type
== FS_EXFAT
) {
4408 dp
-> obj
. c_scl
= dp
-> obj
. sclust
; /* Get containing directory inforamation */
4409 dp
-> obj
. c_size
= (( DWORD
) dp
-> obj
. objsize
& 0xFFFFFF00 ) | dp
-> obj
. stat
;
4410 dp
-> obj
. c_ofs
= dp
-> blk_ofs
;
4411 init_alloc_info ( fs
, & dp
-> obj
); /* Get object allocation info */
4415 dp
-> obj
. sclust
= ld_clust ( fs
, dp
-> dir
); /* Get object allocation info */
4417 } else { /* This object is a file */
4422 dp
-> obj
. id
= fs
-> id
;
4423 res
= dir_sdi ( dp
, 0 ); /* Rewind directory */
4426 if ( dp
-> obj
. sclust
!= 0 ) {
4427 dp
-> obj
. lockid
= inc_lock ( dp
, 0 ); /* Lock the sub directory */
4428 if (! dp
-> obj
. lockid
) res
= FR_TOO_MANY_OPEN_FILES
;
4430 dp
-> obj
. lockid
= 0 ; /* Root directory need not to be locked */
4437 if ( res
== FR_NO_FILE
) res
= FR_NO_PATH
;
4439 if ( res
!= FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate the directory object if function faild */
4447 /*-----------------------------------------------------------------------*/
4448 /* Close Directory */
4449 /*-----------------------------------------------------------------------*/
4451 FRESULT
f_closedir (
4452 DIR * dp
/* Pointer to the directory object to be closed */
4459 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the file object */
4462 if ( dp
-> obj
. lockid
) res
= dec_lock ( dp
-> obj
. lockid
); /* Decrement sub-directory open counter */
4463 if ( res
== FR_OK
) dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4465 dp
-> obj
. fs
= 0 ; /* Invalidate directory object */
4468 unlock_fs ( fs
, FR_OK
); /* Unlock volume */
4477 /*-----------------------------------------------------------------------*/
4478 /* Read Directory Entries in Sequence */
4479 /*-----------------------------------------------------------------------*/
4482 DIR * dp
, /* Pointer to the open directory object */
4483 FILINFO
* fno
/* Pointer to file information to return */
4491 res
= validate (& dp
-> obj
, & fs
); /* Check validity of the directory object */
4494 res
= dir_sdi ( dp
, 0 ); /* Rewind the directory object */
4497 res
= dir_read_file ( dp
); /* Read an item */
4498 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory */
4499 if ( res
== FR_OK
) { /* A valid entry is found */
4500 get_fileinfo ( dp
, fno
); /* Get the object information */
4501 res
= dir_next ( dp
, 0 ); /* Increment index for next */
4502 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Ignore end of directory now */
4513 /*-----------------------------------------------------------------------*/
4514 /* Find Next File */
4515 /*-----------------------------------------------------------------------*/
4517 FRESULT
f_findnext (
4518 DIR * dp
, /* Pointer to the open directory object */
4519 FILINFO
* fno
/* Pointer to the file information structure */
4526 res
= f_readdir ( dp
, fno
); /* Get a directory item */
4527 if ( res
!= FR_OK
|| ! fno
|| ! fno
-> fname
[ 0 ]) break ; /* Terminate if any error or end of directory */
4528 if ( pattern_matching ( dp
-> pat
, fno
-> fname
, 0 , 0 )) break ; /* Test for the file name */
4529 #if FF_USE_LFN && FF_USE_FIND == 2
4530 if ( pattern_matching ( dp
-> pat
, fno
-> altname
, 0 , 0 )) break ; /* Test for alternative name if exist */
4538 /*-----------------------------------------------------------------------*/
4539 /* Find First File */
4540 /*-----------------------------------------------------------------------*/
4542 FRESULT
f_findfirst (
4543 DIR * dp
, /* Pointer to the blank directory object */
4544 FILINFO
* fno
, /* Pointer to the file information structure */
4545 const TCHAR
* path
, /* Pointer to the directory to open */
4546 const TCHAR
* pattern
/* Pointer to the matching pattern */
4552 dp
-> pat
= pattern
; /* Save pointer to pattern string */
4553 res
= f_opendir ( dp
, path
); /* Open the target directory */
4555 res
= f_findnext ( dp
, fno
); /* Find the first item */
4560 #endif /* FF_USE_FIND */
4564 #if FF_FS_MINIMIZE == 0
4565 /*-----------------------------------------------------------------------*/
4566 /* Get File Status */
4567 /*-----------------------------------------------------------------------*/
4570 const TCHAR
* path
, /* Pointer to the file path */
4571 FILINFO
* fno
/* Pointer to file information to return */
4579 /* Get logical drive */
4580 res
= find_volume (& path
, & dj
. obj
. fs
, 0 );
4582 INIT_NAMBUF ( dj
. obj
. fs
);
4583 res
= follow_path (& dj
, path
); /* Follow the file path */
4584 if ( res
== FR_OK
) { /* Follow completed */
4585 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) { /* It is origin directory */
4586 res
= FR_INVALID_NAME
;
4587 } else { /* Found an object */
4588 if ( fno
) get_fileinfo (& dj
, fno
);
4594 LEAVE_FF ( dj
. obj
. fs
, res
);
4600 /*-----------------------------------------------------------------------*/
4601 /* Get Number of Free Clusters */
4602 /*-----------------------------------------------------------------------*/
4605 const TCHAR
* path
, /* Logical drive number */
4606 DWORD
* nclst
, /* Pointer to a variable to return number of free clusters */
4607 FATFS
** fatfs
/* Pointer to return pointer to corresponding filesystem object */
4612 DWORD nfree
, clst
, sect
, stat
;
4617 /* Get logical drive */
4618 res
= find_volume (& path
, & fs
, 0 );
4620 * fatfs
= fs
; /* Return ptr to the fs object */
4621 /* If free_clst is valid, return it without full FAT scan */
4622 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) {
4623 * nclst
= fs
-> free_clst
;
4625 /* Scan FAT to obtain number of free clusters */
4627 if ( fs
-> fs_type
== FS_FAT12
) { /* FAT12: Scan bit field FAT entries */
4628 clst
= 2 ; obj
. fs
= fs
;
4630 stat
= get_fat (& obj
, clst
);
4631 if ( stat
== 0xFFFFFFFF ) { res
= FR_DISK_ERR
; break ; }
4632 if ( stat
== 1 ) { res
= FR_INT_ERR
; break ; }
4633 if ( stat
== 0 ) nfree
++;
4634 } while (++ clst
< fs
-> n_fatent
);
4637 if ( fs
-> fs_type
== FS_EXFAT
) { /* exFAT: Scan allocation bitmap */
4641 clst
= fs
-> n_fatent
- 2 ; /* Number of clusters */
4642 sect
= fs
-> database
; /* Assuming bitmap starts at cluster 2 */
4643 i
= 0 ; /* Offset in the sector */
4644 do { /* Counts numbuer of bits with zero in the bitmap */
4646 res
= move_window ( fs
, sect
++);
4647 if ( res
!= FR_OK
) break ;
4649 for ( b
= 8 , bm
= fs
-> win
[ i
]; b
&& clst
; b
--, clst
--) {
4650 if (!( bm
& 1 )) nfree
++;
4653 i
= ( i
+ 1 ) % SS ( fs
);
4657 { /* FAT16/32: Scan WORD/DWORD FAT entries */
4658 clst
= fs
-> n_fatent
; /* Number of entries */
4659 sect
= fs
-> fatbase
; /* Top of the FAT */
4660 i
= 0 ; /* Offset in the sector */
4661 do { /* Counts numbuer of entries with zero in the FAT */
4663 res
= move_window ( fs
, sect
++);
4664 if ( res
!= FR_OK
) break ;
4666 if ( fs
-> fs_type
== FS_FAT16
) {
4667 if ( ld_word ( fs
-> win
+ i
) == 0 ) nfree
++;
4670 if (( ld_dword ( fs
-> win
+ i
) & 0x0FFFFFFF ) == 0 ) nfree
++;
4677 * nclst
= nfree
; /* Return the free clusters */
4678 fs
-> free_clst
= nfree
; /* Now free_clst is valid */
4679 fs
-> fsi_flag
|= 1 ; /* FAT32: FSInfo is to be updated */
4689 /*-----------------------------------------------------------------------*/
4691 /*-----------------------------------------------------------------------*/
4693 FRESULT
f_truncate (
4694 FIL
* fp
/* Pointer to the file object */
4702 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
4703 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
4704 if (!( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
4706 if ( fp
-> fptr
< fp
-> obj
. objsize
) { /* Process when fptr is not on the eof */
4707 if ( fp
-> fptr
== 0 ) { /* When set file size to zero, remove entire cluster chain */
4708 res
= remove_chain (& fp
-> obj
, fp
-> obj
. sclust
, 0 );
4710 } else { /* When truncate a part of the file, remove remaining clusters */
4711 ncl
= get_fat (& fp
-> obj
, fp
-> clust
);
4713 if ( ncl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
4714 if ( ncl
== 1 ) res
= FR_INT_ERR
;
4715 if ( res
== FR_OK
&& ncl
< fs
-> n_fatent
) {
4716 res
= remove_chain (& fp
-> obj
, ncl
, fp
-> clust
);
4719 fp
-> obj
. objsize
= fp
-> fptr
; /* Set file size to current read/write point */
4720 fp
-> flag
|= FA_MODIFIED
;
4722 if ( res
== FR_OK
&& ( fp
-> flag
& FA_DIRTY
)) {
4723 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) {
4726 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
4730 if ( res
!= FR_OK
) ABORT ( fs
, res
);
4739 /*-----------------------------------------------------------------------*/
4740 /* Delete a File/Directory */
4741 /*-----------------------------------------------------------------------*/
4744 const TCHAR
* path
/* Pointer to the file or directory path */
4757 /* Get logical drive */
4758 res
= find_volume (& path
, & fs
, FA_WRITE
);
4762 res
= follow_path (& dj
, path
); /* Follow the file path */
4763 if ( FF_FS_RPATH
&& res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) {
4764 res
= FR_INVALID_NAME
; /* Cannot remove dot entry */
4767 if ( res
== FR_OK
) res
= chk_lock (& dj
, 2 ); /* Check if it is an open object */
4769 if ( res
== FR_OK
) { /* The object is accessible */
4770 if ( dj
. fn
[ NSFLAG
] & NS_NONAME
) {
4771 res
= FR_INVALID_NAME
; /* Cannot remove the origin directory */
4773 if ( dj
. obj
. attr
& AM_RDO
) {
4774 res
= FR_DENIED
; /* Cannot remove R/O object */
4780 if ( fs
-> fs_type
== FS_EXFAT
) {
4781 init_alloc_info ( fs
, & obj
);
4786 dclst
= ld_clust ( fs
, dj
. dir
);
4788 if ( dj
. obj
. attr
& AM_DIR
) { /* Is it a sub-directory? */
4789 #if FF_FS_RPATH != 0
4790 if ( dclst
== fs
-> cdir
) { /* Is it the current directory? */
4795 sdj
. obj
. fs
= fs
; /* Open the sub-directory */
4796 sdj
. obj
. sclust
= dclst
;
4798 if ( fs
-> fs_type
== FS_EXFAT
) {
4799 sdj
. obj
. objsize
= obj
. objsize
;
4800 sdj
. obj
. stat
= obj
. stat
;
4803 res
= dir_sdi (& sdj
, 0 );
4805 res
= dir_read_file (& sdj
); /* Test if the directory is empty */
4806 if ( res
== FR_OK
) res
= FR_DENIED
; /* Not empty? */
4807 if ( res
== FR_NO_FILE
) res
= FR_OK
; /* Empty? */
4813 res
= dir_remove (& dj
); /* Remove the directory entry */
4814 if ( res
== FR_OK
&& dclst
!= 0 ) { /* Remove the cluster chain if exist */
4816 res
= remove_chain (& obj
, dclst
, 0 );
4818 res
= remove_chain (& dj
. obj
, dclst
, 0 );
4821 if ( res
== FR_OK
) res
= sync_fs ( fs
);
4833 /*-----------------------------------------------------------------------*/
4834 /* Create a Directory */
4835 /*-----------------------------------------------------------------------*/
4838 const TCHAR
* path
/* Pointer to the directory path */
4849 /* Get logical drive */
4850 res
= find_volume (& path
, & fs
, FA_WRITE
);
4854 res
= follow_path (& dj
, path
); /* Follow the file path */
4855 if ( res
== FR_OK
) res
= FR_EXIST
; /* Any object with same name is already existing */
4856 if ( FF_FS_RPATH
&& res
== FR_NO_FILE
&& ( dj
. fn
[ NSFLAG
] & NS_DOT
)) {
4857 res
= FR_INVALID_NAME
;
4859 if ( res
== FR_NO_FILE
) { /* Can create a new directory */
4860 dcl
= create_chain (& dj
. obj
, 0 ); /* Allocate a cluster for the new directory table */
4861 dj
. obj
. objsize
= ( DWORD
) fs
-> csize
* SS ( fs
);
4863 if ( dcl
== 0 ) res
= FR_DENIED
; /* No space to allocate a new cluster */
4864 if ( dcl
== 1 ) res
= FR_INT_ERR
;
4865 if ( dcl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
4866 if ( res
== FR_OK
) res
= sync_window ( fs
); /* Flush FAT */
4868 if ( res
== FR_OK
) { /* Initialize the new directory table */
4869 res
= dir_clear ( fs
, dcl
); /* Clean up the new table */
4870 if ( res
== FR_OK
&& (! FF_FS_EXFAT
|| fs
-> fs_type
!= FS_EXFAT
)) { /* Create dot entries (FAT only) */
4872 mem_set ( dir
+ DIR_Name
, ' ' , 11 ); /* Create "." entry */
4873 dir
[ DIR_Name
] = '.' ;
4874 dir
[ DIR_Attr
] = AM_DIR
;
4875 st_dword ( dir
+ DIR_ModTime
, tm
);
4876 st_clust ( fs
, dir
, dcl
);
4877 mem_cpy ( dir
+ SZDIRE
, dir
, SZDIRE
); /* Create ".." entry */
4878 dir
[ SZDIRE
+ 1 ] = '.' ; pcl
= dj
. obj
. sclust
;
4879 st_clust ( fs
, dir
+ SZDIRE
, pcl
);
4884 res
= dir_register (& dj
); /* Register the object to the directoy */
4888 if ( fs
-> fs_type
== FS_EXFAT
) { /* Initialize directory entry block */
4889 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, tm
); /* Created time */
4890 st_dword ( fs
-> dirbuf
+ XDIR_FstClus
, dcl
); /* Table start cluster */
4891 st_dword ( fs
-> dirbuf
+ XDIR_FileSize
, ( DWORD
) dj
. obj
. objsize
); /* File size needs to be valid */
4892 st_dword ( fs
-> dirbuf
+ XDIR_ValidFileSize
, ( DWORD
) dj
. obj
. objsize
);
4893 fs
-> dirbuf
[ XDIR_GenFlags
] = 3 ; /* Initialize the object flag */
4894 fs
-> dirbuf
[ XDIR_Attr
] = AM_DIR
; /* Attribute */
4895 res
= store_xdir (& dj
);
4900 st_dword ( dir
+ DIR_ModTime
, tm
); /* Created time */
4901 st_clust ( fs
, dir
, dcl
); /* Table start cluster */
4902 dir
[ DIR_Attr
] = AM_DIR
; /* Attribute */
4909 remove_chain (& dj
. obj
, dcl
, 0 ); /* Could not register, remove cluster chain */
4921 /*-----------------------------------------------------------------------*/
4922 /* Rename a File/Directory */
4923 /*-----------------------------------------------------------------------*/
4926 const TCHAR
* path_old
, /* Pointer to the object name to be renamed */
4927 const TCHAR
* path_new
/* Pointer to the new name */
4933 BYTE buf
[ FF_FS_EXFAT
? SZDIRE
* 2 : SZDIRE
], * dir
;
4938 get_ldnumber (& path_new
); /* Snip the drive number of new name off */
4939 res
= find_volume (& path_old
, & fs
, FA_WRITE
); /* Get logical drive of the old object */
4943 res
= follow_path (& djo
, path_old
); /* Check old object */
4944 if ( res
== FR_OK
&& ( djo
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check validity of name */
4947 res
= chk_lock (& djo
, 2 );
4950 if ( res
== FR_OK
) { /* Object to be renamed is found */
4952 if ( fs
-> fs_type
== FS_EXFAT
) { /* At exFAT volume */
4956 mem_cpy ( buf
, fs
-> dirbuf
, SZDIRE
* 2 ); /* Save 85+C0 entry of old object */
4957 mem_cpy (& djn
, & djo
, sizeof djo
);
4958 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
4959 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
4960 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
4962 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
4963 res
= dir_register (& djn
); /* Register the new entry */
4965 nf
= fs
-> dirbuf
[ XDIR_NumSec
]; nn
= fs
-> dirbuf
[ XDIR_NumName
];
4966 nh
= ld_word ( fs
-> dirbuf
+ XDIR_NameHash
);
4967 mem_cpy ( fs
-> dirbuf
, buf
, SZDIRE
* 2 ); /* Restore 85+C0 entry */
4968 fs
-> dirbuf
[ XDIR_NumSec
] = nf
; fs
-> dirbuf
[ XDIR_NumName
] = nn
;
4969 st_word ( fs
-> dirbuf
+ XDIR_NameHash
, nh
);
4970 if (!( fs
-> dirbuf
[ XDIR_Attr
] & AM_DIR
)) fs
-> dirbuf
[ XDIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
4971 /* Start of critical section where an interruption can cause a cross-link */
4972 res
= store_xdir (& djn
);
4977 { /* At FAT/FAT32 volume */
4978 mem_cpy ( buf
, djo
. dir
, SZDIRE
); /* Save directory entry of the object */
4979 mem_cpy (& djn
, & djo
, sizeof ( DIR )); /* Duplicate the directory object */
4980 res
= follow_path (& djn
, path_new
); /* Make sure if new object name is not in use */
4981 if ( res
== FR_OK
) { /* Is new name already in use by any other object? */
4982 res
= ( djn
. obj
. sclust
== djo
. obj
. sclust
&& djn
. dptr
== djo
. dptr
) ? FR_NO_FILE
: FR_EXIST
;
4984 if ( res
== FR_NO_FILE
) { /* It is a valid path and no name collision */
4985 res
= dir_register (& djn
); /* Register the new entry */
4987 dir
= djn
. dir
; /* Copy directory entry of the object except name */
4988 mem_cpy ( dir
+ 13 , buf
+ 13 , SZDIRE
- 13 );
4989 dir
[ DIR_Attr
] = buf
[ DIR_Attr
];
4990 if (!( dir
[ DIR_Attr
] & AM_DIR
)) dir
[ DIR_Attr
] |= AM_ARC
; /* Set archive attribute if it is a file */
4992 if (( dir
[ DIR_Attr
] & AM_DIR
) && djo
. obj
. sclust
!= djn
. obj
. sclust
) { /* Update .. entry in the sub-directory if needed */
4993 dw
= clst2sect ( fs
, ld_clust ( fs
, dir
));
4997 /* Start of critical section where an interruption can cause a cross-link */
4998 res
= move_window ( fs
, dw
);
4999 dir
= fs
-> win
+ SZDIRE
* 1 ; /* Ptr to .. entry */
5000 if ( res
== FR_OK
&& dir
[ 1 ] == '.' ) {
5001 st_clust ( fs
, dir
, djn
. obj
. sclust
);
5010 res
= dir_remove (& djo
); /* Remove old entry */
5015 /* End of the critical section */
5023 #endif /* !FF_FS_READONLY */
5024 #endif /* FF_FS_MINIMIZE == 0 */
5025 #endif /* FF_FS_MINIMIZE <= 1 */
5026 #endif /* FF_FS_MINIMIZE <= 2 */
5030 #if FF_USE_CHMOD && !FF_FS_READONLY
5031 /*-----------------------------------------------------------------------*/
5032 /* Change Attribute */
5033 /*-----------------------------------------------------------------------*/
5036 const TCHAR
* path
, /* Pointer to the file path */
5037 BYTE attr
, /* Attribute bits */
5038 BYTE mask
/* Attribute mask to change */
5047 res
= find_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5051 res
= follow_path (& dj
, path
); /* Follow the file path */
5052 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5054 mask
&= AM_RDO
| AM_HID
| AM_SYS
| AM_ARC
; /* Valid attribute mask */
5056 if ( fs
-> fs_type
== FS_EXFAT
) {
5057 fs
-> dirbuf
[ XDIR_Attr
] = ( attr
& mask
) | ( fs
-> dirbuf
[ XDIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5058 res
= store_xdir (& dj
);
5062 dj
. dir
[ DIR_Attr
] = ( attr
& mask
) | ( dj
. dir
[ DIR_Attr
] & ( BYTE
)~ mask
); /* Apply attribute change */
5078 /*-----------------------------------------------------------------------*/
5079 /* Change Timestamp */
5080 /*-----------------------------------------------------------------------*/
5083 const TCHAR
* path
, /* Pointer to the file/directory name */
5084 const FILINFO
* fno
/* Pointer to the timestamp to be set */
5093 res
= find_volume (& path
, & fs
, FA_WRITE
); /* Get logical drive */
5097 res
= follow_path (& dj
, path
); /* Follow the file path */
5098 if ( res
== FR_OK
&& ( dj
. fn
[ NSFLAG
] & ( NS_DOT
| NS_NONAME
))) res
= FR_INVALID_NAME
; /* Check object validity */
5101 if ( fs
-> fs_type
== FS_EXFAT
) {
5102 st_dword ( fs
-> dirbuf
+ XDIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5103 res
= store_xdir (& dj
);
5107 st_dword ( dj
. dir
+ DIR_ModTime
, ( DWORD
) fno
-> fdate
<< 16 | fno
-> ftime
);
5120 #endif /* FF_USE_CHMOD && !FF_FS_READONLY */
5125 /*-----------------------------------------------------------------------*/
5126 /* Get Volume Label */
5127 /*-----------------------------------------------------------------------*/
5129 FRESULT
f_getlabel (
5130 const TCHAR
* path
, /* Logical drive number */
5131 TCHAR
* label
, /* Buffer to store the volume label */
5132 DWORD
* vsn
/* Variable to store the volume serial number */
5141 /* Get logical drive */
5142 res
= find_volume (& path
, & fs
, 0 );
5144 /* Get volume label */
5145 if ( res
== FR_OK
&& label
) {
5146 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5147 res
= dir_sdi (& dj
, 0 );
5149 res
= dir_read_label (& dj
); /* Find a volume label entry */
5152 if ( fs
-> fs_type
== FS_EXFAT
) {
5155 for ( si
= di
= hs
= 0 ; si
< dj
. dir
[ XDIR_NumLabel
]; si
++) { /* Extract volume label from 83 entry */
5156 wc
= ld_word ( dj
. dir
+ XDIR_Label
+ si
* 2 );
5157 if ( hs
== 0 && IsSurrogate ( wc
)) { /* Is the code a surrogate? */
5160 wc
= put_utf (( DWORD
) hs
<< 16 | wc
, & label
[ di
], 4 );
5161 if ( wc
== 0 ) { di
= 0 ; break ; }
5165 if ( hs
!= 0 ) di
= 0 ; /* Broken surrogate pair? */
5170 si
= di
= 0 ; /* Extract volume label from AM_VOL entry */
5173 #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
5174 if ( dbc_1st (( BYTE
) wc
) && si
< 11 ) wc
= wc
<< 8 | dj
. dir
[ si
++]; /* Is it a DBC? */
5175 wc
= ff_oem2uni ( wc
, CODEPAGE
); /* Convert it into Unicode */
5176 if ( wc
!= 0 ) wc
= put_utf ( wc
, & label
[ di
], 4 ); /* Put it in Unicode */
5177 if ( wc
== 0 ) { di
= 0 ; break ; }
5179 #else /* ANSI/OEM output */
5180 label
[ di
++] = ( TCHAR
) wc
;
5183 do { /* Truncate trailing spaces */
5186 } while ( label
[-- di
] == ' ' );
5190 if ( res
== FR_NO_FILE
) { /* No label entry and return nul string */
5196 /* Get volume serial number */
5197 if ( res
== FR_OK
&& vsn
) {
5198 res
= move_window ( fs
, fs
-> volbase
);
5200 switch ( fs
-> fs_type
) {
5202 di
= BPB_VolIDEx
; break ;
5205 di
= BS_VolID32
; break ;
5210 * vsn
= ld_dword ( fs
-> win
+ di
);
5220 /*-----------------------------------------------------------------------*/
5221 /* Set Volume Label */
5222 /*-----------------------------------------------------------------------*/
5224 FRESULT
f_setlabel (
5225 const TCHAR
* label
/* Volume label to set with heading logical drive number */
5234 static const char badchr
[] = "+.,;=[]/ \\\" *:<> \? | \x7F " ; /* [0..] for FAT, [7..] for exFAT */
5239 /* Get logical drive */
5240 res
= find_volume (& label
, & fs
, FA_WRITE
);
5241 if ( res
!= FR_OK
) LEAVE_FF ( fs
, res
);
5244 if ( fs
-> fs_type
== FS_EXFAT
) { /* On the exFAT volume */
5245 mem_set ( dirvn
, 0 , 22 );
5247 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5248 dc
= tchar2uni (& label
); /* Get a Unicode character */
5249 if ( dc
>= 0x10000 ) {
5250 if ( dc
== 0xFFFFFFFF || di
>= 10 ) { /* Wrong surrogate or buffer overflow */
5253 st_word ( dirvn
+ di
* 2 , ( WCHAR
)( dc
>> 16 )); di
++;
5256 if ( dc
== 0 || chk_chr ( badchr
+ 7 , ( int ) dc
) || di
>= 11 ) { /* Check validity of the volume label */
5257 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5259 st_word ( dirvn
+ di
* 2 , ( WCHAR
) dc
); di
++;
5263 { /* On the FAT/FAT32 volume */
5264 mem_set ( dirvn
, ' ' , 11 );
5266 while (( UINT
)* label
>= ' ' ) { /* Create volume label */
5268 dc
= tchar2uni (& label
);
5269 wc
= ( dc
< 0x10000 ) ? ff_uni2oem ( ff_wtoupper ( dc
), CODEPAGE
) : 0 ;
5270 #else /* ANSI/OEM input */
5271 wc
= ( BYTE
)* label
++;
5272 if ( dbc_1st (( BYTE
) wc
)) wc
= dbc_2nd (( BYTE
)* label
) ? wc
<< 8 | ( BYTE
)* label
++ : 0 ;
5273 if ( IsLower ( wc
)) wc
-= 0x20 ; /* To upper ASCII characters */
5274 #if FF_CODE_PAGE == 0
5275 if ( ExCvt
&& wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5276 #elif FF_CODE_PAGE < 900
5277 if ( wc
>= 0x80 ) wc
= ExCvt
[ wc
- 0x80 ]; /* To upper extended characters (SBCS cfg) */
5280 if ( wc
== 0 || chk_chr ( badchr
+ 0 , ( int ) wc
) || di
>= ( UINT
)(( wc
>= 0x100 ) ? 10 : 11 )) { /* Reject invalid characters for volume label */
5281 LEAVE_FF ( fs
, FR_INVALID_NAME
);
5283 if ( wc
>= 0x100 ) dirvn
[ di
++] = ( BYTE
)( wc
>> 8 );
5284 dirvn
[ di
++] = ( BYTE
) wc
;
5286 if ( dirvn
[ 0 ] == DDEM
) LEAVE_FF ( fs
, FR_INVALID_NAME
); /* Reject illegal name (heading DDEM) */
5287 while ( di
&& dirvn
[ di
- 1 ] == ' ' ) di
--; /* Snip trailing spaces */
5290 /* Set volume label */
5291 dj
. obj
. fs
= fs
; dj
. obj
. sclust
= 0 ; /* Open root directory */
5292 res
= dir_sdi (& dj
, 0 );
5294 res
= dir_read_label (& dj
); /* Get volume label entry */
5296 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5297 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
; /* Change the volume label */
5298 mem_cpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5301 mem_cpy ( dj
. dir
, dirvn
, 11 ); /* Change the volume label */
5303 dj
. dir
[ DIR_Name
] = DDEM
; /* Remove the volume label */
5308 } else { /* No volume label entry or an error */
5309 if ( res
== FR_NO_FILE
) {
5311 if ( di
!= 0 ) { /* Create a volume label entry */
5312 res
= dir_alloc (& dj
, 1 ); /* Allocate an entry */
5314 mem_set ( dj
. dir
, 0 , SZDIRE
); /* Clean the entry */
5315 if ( FF_FS_EXFAT
&& fs
-> fs_type
== FS_EXFAT
) {
5316 dj
. dir
[ XDIR_Type
] = 0x83 ; /* Create 83 entry */
5317 dj
. dir
[ XDIR_NumLabel
] = ( BYTE
) di
;
5318 mem_cpy ( dj
. dir
+ XDIR_Label
, dirvn
, 22 );
5320 dj
. dir
[ DIR_Attr
] = AM_VOL
; /* Create volume label entry */
5321 mem_cpy ( dj
. dir
, dirvn
, 11 );
5334 #endif /* !FF_FS_READONLY */
5335 #endif /* FF_USE_LABEL */
5339 #if FF_USE_EXPAND && !FF_FS_READONLY
5340 /*-----------------------------------------------------------------------*/
5341 /* Allocate a Contiguous Blocks to the File */
5342 /*-----------------------------------------------------------------------*/
5345 FIL
* fp
, /* Pointer to the file object */
5346 FSIZE_t fsz
, /* File size to be expanded to */
5347 BYTE opt
/* Operation mode 0:Find and prepare or 1:Find and allocate */
5352 DWORD n
, clst
, stcl
, scl
, ncl
, tcl
, lclst
;
5355 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5356 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5357 if ( fsz
== 0 || fp
-> obj
. objsize
!= 0 || !( fp
-> flag
& FA_WRITE
)) LEAVE_FF ( fs
, FR_DENIED
);
5359 if ( fs
-> fs_type
!= FS_EXFAT
&& fsz
>= 0x100000000 ) LEAVE_FF ( fs
, FR_DENIED
); /* Check if in size limit */
5361 n
= ( DWORD
) fs
-> csize
* SS ( fs
); /* Cluster size */
5362 tcl
= ( DWORD
)( fsz
/ n
) + (( fsz
& ( n
- 1 )) ? 1 : 0 ); /* Number of clusters required */
5363 stcl
= fs
-> last_clst
; lclst
= 0 ;
5364 if ( stcl
< 2 || stcl
>= fs
-> n_fatent
) stcl
= 2 ;
5367 if ( fs
-> fs_type
== FS_EXFAT
) {
5368 scl
= find_bitmap ( fs
, stcl
, tcl
); /* Find a contiguous cluster block */
5369 if ( scl
== 0 ) res
= FR_DENIED
; /* No contiguous cluster block was found */
5370 if ( scl
== 0xFFFFFFFF ) res
= FR_DISK_ERR
;
5371 if ( res
== FR_OK
) { /* A contiguous free area is found */
5372 if ( opt
) { /* Allocate it now */
5373 res
= change_bitmap ( fs
, scl
, tcl
, 1 ); /* Mark the cluster block 'in use' */
5374 lclst
= scl
+ tcl
- 1 ;
5375 } else { /* Set it as suggested point for next allocation */
5382 scl
= clst
= stcl
; ncl
= 0 ;
5383 for (;;) { /* Find a contiguous cluster block */
5384 n
= get_fat (& fp
-> obj
, clst
);
5385 if (++ clst
>= fs
-> n_fatent
) clst
= 2 ;
5386 if ( n
== 1 ) { res
= FR_INT_ERR
; break ; }
5387 if ( n
== 0xFFFFFFFF ) { res
= FR_DISK_ERR
; break ; }
5388 if ( n
== 0 ) { /* Is it a free cluster? */
5389 if (++ ncl
== tcl
) break ; /* Break if a contiguous cluster block is found */
5391 scl
= clst
; ncl
= 0 ; /* Not a free cluster */
5393 if ( clst
== stcl
) { res
= FR_DENIED
; break ; } /* No contiguous cluster? */
5395 if ( res
== FR_OK
) { /* A contiguous free area is found */
5396 if ( opt
) { /* Allocate it now */
5397 for ( clst
= scl
, n
= tcl
; n
; clst
++, n
--) { /* Create a cluster chain on the FAT */
5398 res
= put_fat ( fs
, clst
, ( n
== 1 ) ? 0xFFFFFFFF : clst
+ 1 );
5399 if ( res
!= FR_OK
) break ;
5402 } else { /* Set it as suggested point for next allocation */
5409 fs
-> last_clst
= lclst
; /* Set suggested start cluster to start next */
5410 if ( opt
) { /* Is it allocated now? */
5411 fp
-> obj
. sclust
= scl
; /* Update object allocation information */
5412 fp
-> obj
. objsize
= fsz
;
5413 if ( FF_FS_EXFAT
) fp
-> obj
. stat
= 2 ; /* Set status 'contiguous chain' */
5414 fp
-> flag
|= FA_MODIFIED
;
5415 if ( fs
-> free_clst
<= fs
-> n_fatent
- 2 ) { /* Update FSINFO */
5416 fs
-> free_clst
-= tcl
;
5425 #endif /* FF_USE_EXPAND && !FF_FS_READONLY */
5430 /*-----------------------------------------------------------------------*/
5431 /* Forward Data to the Stream Directly */
5432 /*-----------------------------------------------------------------------*/
5435 FIL
* fp
, /* Pointer to the file object */
5436 UINT (* func
)( const BYTE
*, UINT
), /* Pointer to the streaming function */
5437 UINT btf
, /* Number of bytes to forward */
5438 UINT
* bf
/* Pointer to number of bytes forwarded */
5449 * bf
= 0 ; /* Clear transfer byte counter */
5450 res
= validate (& fp
-> obj
, & fs
); /* Check validity of the file object */
5451 if ( res
!= FR_OK
|| ( res
= ( FRESULT
) fp
-> err
) != FR_OK
) LEAVE_FF ( fs
, res
);
5452 if (!( fp
-> flag
& FA_READ
)) LEAVE_FF ( fs
, FR_DENIED
); /* Check access mode */
5454 remain
= fp
-> obj
. objsize
- fp
-> fptr
;
5455 if ( btf
> remain
) btf
= ( UINT
) remain
; /* Truncate btf by remaining bytes */
5457 for ( ; btf
&& (* func
)( 0 , 0 ); /* Repeat until all data transferred or stream goes busy */
5458 fp
-> fptr
+= rcnt
, * bf
+= rcnt
, btf
-= rcnt
) {
5459 csect
= ( UINT
)( fp
-> fptr
/ SS ( fs
) & ( fs
-> csize
- 1 )); /* Sector offset in the cluster */
5460 if ( fp
-> fptr
% SS ( fs
) == 0 ) { /* On the sector boundary? */
5461 if ( csect
== 0 ) { /* On the cluster boundary? */
5462 clst
= ( fp
-> fptr
== 0 ) ? /* On the top of the file? */
5463 fp
-> obj
. sclust
: get_fat (& fp
-> obj
, fp
-> clust
);
5464 if ( clst
<= 1 ) ABORT ( fs
, FR_INT_ERR
);
5465 if ( clst
== 0xFFFFFFFF ) ABORT ( fs
, FR_DISK_ERR
);
5466 fp
-> clust
= clst
; /* Update current cluster */
5469 sect
= clst2sect ( fs
, fp
-> clust
); /* Get current data sector */
5470 if ( sect
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5473 if ( move_window ( fs
, sect
) != FR_OK
) ABORT ( fs
, FR_DISK_ERR
); /* Move sector window to the file data */
5476 if ( fp
-> sect
!= sect
) { /* Fill sector cache with file data */
5478 if ( fp
-> flag
& FA_DIRTY
) { /* Write-back dirty sector cache */
5479 if ( disk_write ( fs
-> pdrv
, fp
-> buf
, fp
-> sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5480 fp
-> flag
&= ( BYTE
)~ FA_DIRTY
;
5483 if ( disk_read ( fs
-> pdrv
, fp
-> buf
, sect
, 1 ) != RES_OK
) ABORT ( fs
, FR_DISK_ERR
);
5488 rcnt
= SS ( fs
) - ( UINT
) fp
-> fptr
% SS ( fs
); /* Number of bytes left in the sector */
5489 if ( rcnt
> btf
) rcnt
= btf
; /* Clip it by btr if needed */
5490 rcnt
= (* func
)( dbuf
+ (( UINT
) fp
-> fptr
% SS ( fs
)), rcnt
); /* Forward the file data */
5491 if ( rcnt
== 0 ) ABORT ( fs
, FR_INT_ERR
);
5494 LEAVE_FF ( fs
, FR_OK
);
5496 #endif /* FF_USE_FORWARD */
5500 #if FF_USE_MKFS && !FF_FS_READONLY
5501 /*-----------------------------------------------------------------------*/
5502 /* Create an FAT/exFAT volume */
5503 /*-----------------------------------------------------------------------*/
5506 const TCHAR
* path
, /* Logical drive number */
5507 BYTE opt
, /* Format option */
5508 DWORD au
, /* Size of allocation unit (cluster) [byte] */
5509 void * work
, /* Pointer to working buffer (null: use heap memory) */
5510 UINT len
/* Size of working buffer [byte] */
5513 const UINT n_fats
= 1 ; /* Number of FATs for FAT/FAT32 volume (1 or 2) */
5514 const UINT n_rootdir
= 512 ; /* Number of root directory entries for FAT volume */
5515 static const WORD cst
[] = { 1 , 4 , 16 , 64 , 256 , 512 , 0 }; /* Cluster size boundary for FAT volume (4Ks unit) */
5516 static const WORD cst32
[] = { 1 , 2 , 4 , 8 , 16 , 32 , 0 }; /* Cluster size boundary for FAT32 volume (128Ks unit) */
5517 BYTE fmt
, sys
, * buf
, * pte
, pdrv
, part
;
5518 WORD ss
; /* Sector size */
5519 DWORD szb_buf
, sz_buf
, sz_blk
, n_clst
, pau
, sect
, nsect
, n
;
5520 DWORD b_vol
, b_fat
, b_data
; /* Base LBA for volume, fat, data */
5521 DWORD sz_vol
, sz_rsv
, sz_fat
, sz_dir
; /* Size for volume, fat, dir, data */
5525 #if FF_USE_TRIM || FF_FS_EXFAT
5530 /* Check mounted drive and clear work area */
5531 vol
= get_ldnumber (& path
); /* Get target logical drive */
5532 if ( vol
< 0 ) return FR_INVALID_DRIVE
;
5533 if ( FatFs
[ vol
]) FatFs
[ vol
]-> fs_type
= 0 ; /* Clear the volume if mounted */
5534 pdrv
= LD2PD ( vol
); /* Physical drive */
5535 part
= LD2PT ( vol
); /* Partition (0:create as new, 1-4:get from partition table) */
5537 /* Check physical drive status */
5538 stat
= disk_initialize ( pdrv
);
5539 if ( stat
& STA_NOINIT
) return FR_NOT_READY
;
5540 if ( stat
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
5541 if ( disk_ioctl ( pdrv
, GET_BLOCK_SIZE
, & sz_blk
) != RES_OK
|| ! sz_blk
|| sz_blk
> 32768 || ( sz_blk
& ( sz_blk
- 1 ))) sz_blk
= 1 ; /* Erase block to align data area */
5542 #if FF_MAX_SS != FF_MIN_SS /* Get sector size of the medium if variable sector size cfg. */
5543 if ( disk_ioctl ( pdrv
, GET_SECTOR_SIZE
, & ss
) != RES_OK
) return FR_DISK_ERR
;
5544 if ( ss
> FF_MAX_SS
|| ss
< FF_MIN_SS
|| ( ss
& ( ss
- 1 ))) return FR_DISK_ERR
;
5548 if (( au
!= 0 && au
< ss
) || au
> 0x1000000 || ( au
& ( au
- 1 ))) return FR_INVALID_PARAMETER
; /* Check if au is valid */
5549 au
/= ss
; /* Cluster size in unit of sector */
5551 /* Get working buffer */
5553 if (! work
) { /* Use heap memory for working buffer */
5554 for ( szb_buf
= MAX_MALLOC
, buf
= 0 ; szb_buf
>= ss
&& ( buf
= ff_memalloc ( szb_buf
)) == 0 ; szb_buf
/= 2 ) ;
5555 sz_buf
= szb_buf
/ ss
; /* Size of working buffer (sector) */
5559 buf
= ( BYTE
*) work
; /* Working buffer */
5560 sz_buf
= len
/ ss
; /* Size of working buffer (sector) */
5561 szb_buf
= sz_buf
* ss
; /* Size of working buffer (byte) */
5563 if (! buf
|| sz_buf
== 0 ) return FR_NOT_ENOUGH_CORE
;
5565 /* Determine where the volume to be located (b_vol, sz_vol) */
5566 if ( FF_MULTI_PARTITION
&& part
!= 0 ) {
5567 /* Get partition information from partition table in the MBR */
5568 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Load MBR */
5569 if ( ld_word ( buf
+ BS_55AA
) != 0xAA55 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if MBR is valid */
5570 pte
= buf
+ ( MBR_Table
+ ( part
- 1 ) * SZ_PTE
);
5571 if ( pte
[ PTE_System
] == 0 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* No partition? */
5572 b_vol
= ld_dword ( pte
+ PTE_StLba
); /* Get volume start sector */
5573 sz_vol
= ld_dword ( pte
+ PTE_SizLba
); /* Get volume size */
5575 /* Create a single-partition in this function */
5576 if ( disk_ioctl ( pdrv
, GET_SECTOR_COUNT
, & sz_vol
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5577 b_vol
= ( opt
& FM_SFD
) ? 0 : 63 ; /* Volume start sector */
5578 if ( sz_vol
< b_vol
) LEAVE_MKFS ( FR_MKFS_ABORTED
);
5579 sz_vol
-= b_vol
; /* Volume size */
5581 if ( sz_vol
< 128 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Check if volume size is >=128s */
5583 /* Pre-determine the FAT type */
5585 if ( FF_FS_EXFAT
&& ( opt
& FM_EXFAT
)) { /* exFAT possible? */
5586 if (( opt
& FM_ANY
) == FM_EXFAT
|| sz_vol
>= 0x4000000 || au
> 128 ) { /* exFAT only, vol >= 64Ms or au > 128s ? */
5587 fmt
= FS_EXFAT
; break ;
5590 if ( au
> 128 ) LEAVE_MKFS ( FR_INVALID_PARAMETER
); /* Too large au for FAT/FAT32 */
5591 if ( opt
& FM_FAT32
) { /* FAT32 possible? */
5592 if (( opt
& FM_ANY
) == FM_FAT32
|| !( opt
& FM_FAT
)) { /* FAT32 only or no-FAT? */
5593 fmt
= FS_FAT32
; break ;
5596 if (!( opt
& FM_FAT
)) LEAVE_MKFS ( FR_INVALID_PARAMETER
); /* no-FAT? */
5601 if ( fmt
== FS_EXFAT
) { /* Create an exFAT volume */
5602 DWORD szb_bit
, szb_case
, sum
, nb
, cl
;
5607 if ( sz_vol
< 0x1000 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
5609 tbl
[ 0 ] = b_vol
; tbl
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform the device the volume area may be erased */
5610 disk_ioctl ( pdrv
, CTRL_TRIM
, tbl
);
5612 /* Determine FAT location, data location and number of clusters */
5613 if ( au
== 0 ) { /* au auto-selection */
5615 if ( sz_vol
>= 0x80000 ) au
= 64 ; /* >= 512Ks */
5616 if ( sz_vol
>= 0x4000000 ) au
= 256 ; /* >= 64Ms */
5618 b_fat
= b_vol
+ 32 ; /* FAT start at offset 32 */
5619 sz_fat
= (( sz_vol
/ au
+ 2 ) * 4 + ss
- 1 ) / ss
; /* Number of FAT sectors */
5620 b_data
= ( b_fat
+ sz_fat
+ sz_blk
- 1 ) & ~( sz_blk
- 1 ); /* Align data area to the erase block boundary */
5621 if ( b_data
>= sz_vol
/ 2 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume? */
5622 n_clst
= ( sz_vol
- ( b_data
- b_vol
)) / au
; /* Number of clusters */
5623 if ( n_clst
< 16 ) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too few clusters? */
5624 if ( n_clst
> MAX_EXFAT
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters? */
5626 szb_bit
= ( n_clst
+ 7 ) / 8 ; /* Size of allocation bitmap */
5627 tbl
[ 0 ] = ( szb_bit
+ au
* ss
- 1 ) / ( au
* ss
); /* Number of allocation bitmap clusters */
5629 /* Create a compressed up-case table */
5630 sect
= b_data
+ au
* tbl
[ 0 ]; /* Table start sector */
5631 sum
= 0 ; /* Table checksum to be stored in the 82 entry */
5632 st
= 0 ; si
= 0 ; i
= 0 ; j
= 0 ; szb_case
= 0 ;
5636 ch
= ( WCHAR
) ff_wtoupper ( si
); /* Get an up-case char */
5638 si
++; break ; /* Store the up-case char if exist */
5640 for ( j
= 1 ; ( WCHAR
)( si
+ j
) && ( WCHAR
)( si
+ j
) == ff_wtoupper (( WCHAR
)( si
+ j
)); j
++) ; /* Get run length of no-case block */
5642 ch
= 0xFFFF ; st
= 2 ; break ; /* Compress the no-case block if run is >= 128 */
5644 st
= 1 ; /* Do not compress short run */
5645 /* go to next case */
5647 ch
= si
++; /* Fill the short run */
5648 if (-- j
== 0 ) st
= 0 ;
5652 ch
= ( WCHAR
) j
; si
+= ( WCHAR
) j
; /* Number of chars to skip */
5655 sum
= xsum32 ( buf
[ i
+ 0 ] = ( BYTE
) ch
, sum
); /* Put it into the write buffer */
5656 sum
= xsum32 ( buf
[ i
+ 1 ] = ( BYTE
)( ch
>> 8 ), sum
);
5657 i
+= 2 ; szb_case
+= 2 ;
5658 if ( si
== 0 || i
== szb_buf
) { /* Write buffered data when buffer full or end of process */
5659 n
= ( i
+ ss
- 1 ) / ss
;
5660 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5664 tbl
[ 1 ] = ( szb_case
+ au
* ss
- 1 ) / ( au
* ss
); /* Number of up-case table clusters */
5665 tbl
[ 2 ] = 1 ; /* Number of root dir clusters */
5667 /* Initialize the allocation bitmap */
5668 sect
= b_data
; nsect
= ( szb_bit
+ ss
- 1 ) / ss
; /* Start of bitmap and number of sectors */
5669 nb
= tbl
[ 0 ] + tbl
[ 1 ] + tbl
[ 2 ]; /* Number of clusters in-use by system */
5671 mem_set ( buf
, 0 , szb_buf
);
5672 for ( i
= 0 ; nb
>= 8 && i
< szb_buf
; buf
[ i
++] = 0xFF , nb
-= 8 ) ;
5673 for ( b
= 1 ; nb
!= 0 && i
< szb_buf
; buf
[ i
] |= b
, b
<<= 1 , nb
--) ;
5674 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
5675 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5676 sect
+= n
; nsect
-= n
;
5679 /* Initialize the FAT */
5680 sect
= b_fat
; nsect
= sz_fat
; /* Start of FAT and number of FAT sectors */
5683 mem_set ( buf
, 0 , szb_buf
); i
= 0 ; /* Clear work area and reset write index */
5684 if ( cl
== 0 ) { /* Set entry 0 and 1 */
5685 st_dword ( buf
+ i
, 0xFFFFFFF8 ); i
+= 4 ; cl
++;
5686 st_dword ( buf
+ i
, 0xFFFFFFFF ); i
+= 4 ; cl
++;
5688 do { /* Create chains of bitmap, up-case and root dir */
5689 while ( nb
!= 0 && i
< szb_buf
) { /* Create a chain */
5690 st_dword ( buf
+ i
, ( nb
> 1 ) ? cl
+ 1 : 0xFFFFFFFF );
5693 if ( nb
== 0 && j
< 3 ) nb
= tbl
[ j
++]; /* Next chain */
5694 } while ( nb
!= 0 && i
< szb_buf
);
5695 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
; /* Write the buffered data */
5696 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5697 sect
+= n
; nsect
-= n
;
5700 /* Initialize the root directory */
5701 mem_set ( buf
, 0 , szb_buf
);
5702 buf
[ SZDIRE
* 0 + 0 ] = 0x83 ; /* 83 entry (volume label) */
5703 buf
[ SZDIRE
* 1 + 0 ] = 0x81 ; /* 81 entry (allocation bitmap) */
5704 st_dword ( buf
+ SZDIRE
* 1 + 20 , 2 ); /* cluster */
5705 st_dword ( buf
+ SZDIRE
* 1 + 24 , szb_bit
); /* size */
5706 buf
[ SZDIRE
* 2 + 0 ] = 0x82 ; /* 82 entry (up-case table) */
5707 st_dword ( buf
+ SZDIRE
* 2 + 4 , sum
); /* sum */
5708 st_dword ( buf
+ SZDIRE
* 2 + 20 , 2 + tbl
[ 0 ]); /* cluster */
5709 st_dword ( buf
+ SZDIRE
* 2 + 24 , szb_case
); /* size */
5710 sect
= b_data
+ au
* ( tbl
[ 0 ] + tbl
[ 1 ]); nsect
= au
; /* Start of the root directory and number of sectors */
5711 do { /* Fill root directory sectors */
5712 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
5713 if ( disk_write ( pdrv
, buf
, sect
, n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5714 mem_set ( buf
, 0 , ss
);
5715 sect
+= n
; nsect
-= n
;
5718 /* Create two set of the exFAT VBR blocks */
5720 for ( n
= 0 ; n
< 2 ; n
++) {
5721 /* Main record (+0) */
5722 mem_set ( buf
, 0 , ss
);
5723 mem_cpy ( buf
+ BS_JmpBoot
, " \xEB\x76\x90 " "EXFAT " , 11 ); /* Boot jump code (x86), OEM name */
5724 st_dword ( buf
+ BPB_VolOfsEx
, b_vol
); /* Volume offset in the physical drive [sector] */
5725 st_dword ( buf
+ BPB_TotSecEx
, sz_vol
); /* Volume size [sector] */
5726 st_dword ( buf
+ BPB_FatOfsEx
, b_fat
- b_vol
); /* FAT offset [sector] */
5727 st_dword ( buf
+ BPB_FatSzEx
, sz_fat
); /* FAT size [sector] */
5728 st_dword ( buf
+ BPB_DataOfsEx
, b_data
- b_vol
); /* Data offset [sector] */
5729 st_dword ( buf
+ BPB_NumClusEx
, n_clst
); /* Number of clusters */
5730 st_dword ( buf
+ BPB_RootClusEx
, 2 + tbl
[ 0 ] + tbl
[ 1 ]); /* Root dir cluster # */
5731 st_dword ( buf
+ BPB_VolIDEx
, GET_FATTIME ()); /* VSN */
5732 st_word ( buf
+ BPB_FSVerEx
, 0x100 ); /* Filesystem version (1.00) */
5733 for ( buf
[ BPB_BytsPerSecEx
] = 0 , i
= ss
; i
>>= 1 ; buf
[ BPB_BytsPerSecEx
]++) ; /* Log2 of sector size [byte] */
5734 for ( buf
[ BPB_SecPerClusEx
] = 0 , i
= au
; i
>>= 1 ; buf
[ BPB_SecPerClusEx
]++) ; /* Log2 of cluster size [sector] */
5735 buf
[ BPB_NumFATsEx
] = 1 ; /* Number of FATs */
5736 buf
[ BPB_DrvNumEx
] = 0x80 ; /* Drive number (for int13) */
5737 st_word ( buf
+ BS_BootCodeEx
, 0xFEEB ); /* Boot code (x86) */
5738 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (placed here regardless of sector size) */
5739 for ( i
= sum
= 0 ; i
< ss
; i
++) { /* VBR checksum */
5740 if ( i
!= BPB_VolFlagEx
&& i
!= BPB_VolFlagEx
+ 1 && i
!= BPB_PercInUseEx
) sum
= xsum32 ( buf
[ i
], sum
);
5742 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5743 /* Extended bootstrap record (+1..+8) */
5744 mem_set ( buf
, 0 , ss
);
5745 st_word ( buf
+ ss
- 2 , 0xAA55 ); /* Signature (placed at end of sector) */
5746 for ( j
= 1 ; j
< 9 ; j
++) {
5747 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
5748 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5750 /* OEM/Reserved record (+9..+10) */
5751 mem_set ( buf
, 0 , ss
);
5752 for ( ; j
< 11 ; j
++) {
5753 for ( i
= 0 ; i
< ss
; sum
= xsum32 ( buf
[ i
++], sum
)) ; /* VBR checksum */
5754 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5756 /* Sum record (+11) */
5757 for ( i
= 0 ; i
< ss
; i
+= 4 ) st_dword ( buf
+ i
, sum
); /* Fill with checksum value */
5758 if ( disk_write ( pdrv
, buf
, sect
++, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5762 #endif /* FF_FS_EXFAT */
5763 { /* Create an FAT/FAT32 volume */
5766 /* Pre-determine number of clusters and FAT sub-type */
5767 if ( fmt
== FS_FAT32
) { /* FAT32 volume */
5768 if ( pau
== 0 ) { /* au auto-selection */
5769 n
= sz_vol
/ 0x20000 ; /* Volume size in unit of 128KS */
5770 for ( i
= 0 , pau
= 1 ; cst32
[ i
] && cst32
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
5772 n_clst
= sz_vol
/ pau
; /* Number of clusters */
5773 sz_fat
= ( n_clst
* 4 + 8 + ss
- 1 ) / ss
; /* FAT size [sector] */
5774 sz_rsv
= 32 ; /* Number of reserved sectors */
5775 sz_dir
= 0 ; /* No static directory */
5776 if ( n_clst
<= MAX_FAT16
|| n_clst
> MAX_FAT32
) LEAVE_MKFS ( FR_MKFS_ABORTED
);
5777 } else { /* FAT volume */
5778 if ( pau
== 0 ) { /* au auto-selection */
5779 n
= sz_vol
/ 0x1000 ; /* Volume size in unit of 4KS */
5780 for ( i
= 0 , pau
= 1 ; cst
[ i
] && cst
[ i
] <= n
; i
++, pau
<<= 1 ) ; /* Get from table */
5782 n_clst
= sz_vol
/ pau
;
5783 if ( n_clst
> MAX_FAT12
) {
5784 n
= n_clst
* 2 + 4 ; /* FAT size [byte] */
5787 n
= ( n_clst
* 3 + 1 ) / 2 + 3 ; /* FAT size [byte] */
5789 sz_fat
= ( n
+ ss
- 1 ) / ss
; /* FAT size [sector] */
5790 sz_rsv
= 1 ; /* Number of reserved sectors */
5791 sz_dir
= ( DWORD
) n_rootdir
* SZDIRE
/ ss
; /* Rootdir size [sector] */
5793 b_fat
= b_vol
+ sz_rsv
; /* FAT base */
5794 b_data
= b_fat
+ sz_fat
* n_fats
+ sz_dir
; /* Data base */
5796 /* Align data base to erase block boundary (for flash memory media) */
5797 n
= (( b_data
+ sz_blk
- 1 ) & ~( sz_blk
- 1 )) - b_data
; /* Next nearest erase block from current data base */
5798 if ( fmt
== FS_FAT32
) { /* FAT32: Move FAT base */
5799 sz_rsv
+= n
; b_fat
+= n
;
5800 } else { /* FAT: Expand FAT size */
5801 sz_fat
+= n
/ n_fats
;
5804 /* Determine number of clusters and final check of validity of the FAT sub-type */
5805 if ( sz_vol
< b_data
+ pau
* 16 - b_vol
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too small volume */
5806 n_clst
= ( sz_vol
- sz_rsv
- sz_fat
* n_fats
- sz_dir
) / pau
;
5807 if ( fmt
== FS_FAT32
) {
5808 if ( n_clst
<= MAX_FAT16
) { /* Too few clusters for FAT32 */
5809 if ( au
== 0 && ( au
= pau
/ 2 ) != 0 ) continue ; /* Adjust cluster size and retry */
5810 LEAVE_MKFS ( FR_MKFS_ABORTED
);
5813 if ( fmt
== FS_FAT16
) {
5814 if ( n_clst
> MAX_FAT16
) { /* Too many clusters for FAT16 */
5815 if ( au
== 0 && ( pau
* 2 ) <= 64 ) {
5816 au
= pau
* 2 ; continue ; /* Adjust cluster size and retry */
5818 if (( opt
& FM_FAT32
)) {
5819 fmt
= FS_FAT32
; continue ; /* Switch type to FAT32 and retry */
5821 if ( au
== 0 && ( au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
5822 LEAVE_MKFS ( FR_MKFS_ABORTED
);
5824 if ( n_clst
<= MAX_FAT12
) { /* Too few clusters for FAT16 */
5825 if ( au
== 0 && ( au
= pau
* 2 ) <= 128 ) continue ; /* Adjust cluster size and retry */
5826 LEAVE_MKFS ( FR_MKFS_ABORTED
);
5829 if ( fmt
== FS_FAT12
&& n_clst
> MAX_FAT12
) LEAVE_MKFS ( FR_MKFS_ABORTED
); /* Too many clusters for FAT12 */
5831 /* Ok, it is the valid cluster configuration */
5836 tbl
[ 0 ] = b_vol
; tbl
[ 1 ] = b_vol
+ sz_vol
- 1 ; /* Inform the device the volume area can be erased */
5837 disk_ioctl ( pdrv
, CTRL_TRIM
, tbl
);
5839 /* Create FAT VBR */
5840 mem_set ( buf
, 0 , ss
);
5841 mem_cpy ( buf
+ BS_JmpBoot
, " \xEB\xFE\x90 " "MSDOS5.0" , 11 ); /* Boot jump code (x86), OEM name */
5842 st_word ( buf
+ BPB_BytsPerSec
, ss
); /* Sector size [byte] */
5843 buf
[ BPB_SecPerClus
] = ( BYTE
) pau
; /* Cluster size [sector] */
5844 st_word ( buf
+ BPB_RsvdSecCnt
, ( WORD
) sz_rsv
); /* Size of reserved area */
5845 buf
[ BPB_NumFATs
] = ( BYTE
) n_fats
; /* Number of FATs */
5846 st_word ( buf
+ BPB_RootEntCnt
, ( WORD
)(( fmt
== FS_FAT32
) ? 0 : n_rootdir
)); /* Number of root directory entries */
5847 if ( sz_vol
< 0x10000 ) {
5848 st_word ( buf
+ BPB_TotSec16
, ( WORD
) sz_vol
); /* Volume size in 16-bit LBA */
5850 st_dword ( buf
+ BPB_TotSec32
, sz_vol
); /* Volume size in 32-bit LBA */
5852 buf
[ BPB_Media
] = 0xF8 ; /* Media descriptor byte */
5853 st_word ( buf
+ BPB_SecPerTrk
, 63 ); /* Number of sectors per track (for int13) */
5854 st_word ( buf
+ BPB_NumHeads
, 255 ); /* Number of heads (for int13) */
5855 st_dword ( buf
+ BPB_HiddSec
, b_vol
); /* Volume offset in the physical drive [sector] */
5856 if ( fmt
== FS_FAT32
) {
5857 st_dword ( buf
+ BS_VolID32
, GET_FATTIME ()); /* VSN */
5858 st_dword ( buf
+ BPB_FATSz32
, sz_fat
); /* FAT size [sector] */
5859 st_dword ( buf
+ BPB_RootClus32
, 2 ); /* Root directory cluster # (2) */
5860 st_word ( buf
+ BPB_FSInfo32
, 1 ); /* Offset of FSINFO sector (VBR + 1) */
5861 st_word ( buf
+ BPB_BkBootSec32
, 6 ); /* Offset of backup VBR (VBR + 6) */
5862 buf
[ BS_DrvNum32
] = 0x80 ; /* Drive number (for int13) */
5863 buf
[ BS_BootSig32
] = 0x29 ; /* Extended boot signature */
5864 mem_cpy ( buf
+ BS_VolLab32
, "NO NAME " "FAT32 " , 19 ); /* Volume label, FAT signature */
5866 st_dword ( buf
+ BS_VolID
, GET_FATTIME ()); /* VSN */
5867 st_word ( buf
+ BPB_FATSz16
, ( WORD
) sz_fat
); /* FAT size [sector] */
5868 buf
[ BS_DrvNum
] = 0x80 ; /* Drive number (for int13) */
5869 buf
[ BS_BootSig
] = 0x29 ; /* Extended boot signature */
5870 mem_cpy ( buf
+ BS_VolLab
, "NO NAME " "FAT " , 19 ); /* Volume label, FAT signature */
5872 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* Signature (offset is fixed here regardless of sector size) */
5873 if ( disk_write ( pdrv
, buf
, b_vol
, 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it to the VBR sector */
5875 /* Create FSINFO record if needed */
5876 if ( fmt
== FS_FAT32
) {
5877 disk_write ( pdrv
, buf
, b_vol
+ 6 , 1 ); /* Write backup VBR (VBR + 6) */
5878 mem_set ( buf
, 0 , ss
);
5879 st_dword ( buf
+ FSI_LeadSig
, 0x41615252 );
5880 st_dword ( buf
+ FSI_StrucSig
, 0x61417272 );
5881 st_dword ( buf
+ FSI_Free_Count
, n_clst
- 1 ); /* Number of free clusters */
5882 st_dword ( buf
+ FSI_Nxt_Free
, 2 ); /* Last allocated cluster# */
5883 st_word ( buf
+ BS_55AA
, 0xAA55 );
5884 disk_write ( pdrv
, buf
, b_vol
+ 7 , 1 ); /* Write backup FSINFO (VBR + 7) */
5885 disk_write ( pdrv
, buf
, b_vol
+ 1 , 1 ); /* Write original FSINFO (VBR + 1) */
5888 /* Initialize FAT area */
5889 mem_set ( buf
, 0 , ( UINT
) szb_buf
);
5890 sect
= b_fat
; /* FAT start sector */
5891 for ( i
= 0 ; i
< n_fats
; i
++) { /* Initialize FATs each */
5892 if ( fmt
== FS_FAT32
) {
5893 st_dword ( buf
+ 0 , 0xFFFFFFF8 ); /* Entry 0 */
5894 st_dword ( buf
+ 4 , 0xFFFFFFFF ); /* Entry 1 */
5895 st_dword ( buf
+ 8 , 0x0FFFFFFF ); /* Entry 2 (root directory) */
5897 st_dword ( buf
+ 0 , ( fmt
== FS_FAT12
) ? 0xFFFFF8 : 0xFFFFFFF8 ); /* Entry 0 and 1 */
5899 nsect
= sz_fat
; /* Number of FAT sectors */
5900 do { /* Fill FAT sectors */
5901 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
5902 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5903 mem_set ( buf
, 0 , ss
);
5904 sect
+= n
; nsect
-= n
;
5908 /* Initialize root directory (fill with zero) */
5909 nsect
= ( fmt
== FS_FAT32
) ? pau
: sz_dir
; /* Number of root directory sectors */
5911 n
= ( nsect
> sz_buf
) ? sz_buf
: nsect
;
5912 if ( disk_write ( pdrv
, buf
, sect
, ( UINT
) n
) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5913 sect
+= n
; nsect
-= n
;
5917 /* Determine system ID in the partition table */
5918 if ( FF_FS_EXFAT
&& fmt
== FS_EXFAT
) {
5919 sys
= 0x07 ; /* HPFS/NTFS/exFAT */
5921 if ( fmt
== FS_FAT32
) {
5922 sys
= 0x0C ; /* FAT32X */
5924 if ( sz_vol
>= 0x10000 ) {
5925 sys
= 0x06 ; /* FAT12/16 (large) */
5927 sys
= ( fmt
== FS_FAT16
) ? 0x04 : 0x01 ; /* FAT16 : FAT12 */
5932 /* Update partition information */
5933 if ( FF_MULTI_PARTITION
&& part
!= 0 ) { /* Created in the existing partition */
5934 /* Update system ID in the partition table */
5935 if ( disk_read ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Read the MBR */
5936 buf
[ MBR_Table
+ ( part
- 1 ) * SZ_PTE
+ PTE_System
] = sys
; /* Set system ID */
5937 if ( disk_write ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it back to the MBR */
5938 } else { /* Created as a new single partition */
5939 if (!( opt
& FM_SFD
)) { /* Create partition table if in FDISK format */
5940 mem_set ( buf
, 0 , ss
);
5941 st_word ( buf
+ BS_55AA
, 0xAA55 ); /* MBR signature */
5942 pte
= buf
+ MBR_Table
; /* Create partition table for single partition in the drive */
5943 pte
[ PTE_Boot
] = 0 ; /* Boot indicator */
5944 pte
[ PTE_StHead
] = 1 ; /* Start head */
5945 pte
[ PTE_StSec
] = 1 ; /* Start sector */
5946 pte
[ PTE_StCyl
] = 0 ; /* Start cylinder */
5947 pte
[ PTE_System
] = sys
; /* System type */
5948 n
= ( b_vol
+ sz_vol
) / ( 63 * 255 ); /* (End CHS may be invalid) */
5949 pte
[ PTE_EdHead
] = 254 ; /* End head */
5950 pte
[ PTE_EdSec
] = ( BYTE
)((( n
>> 2 ) & 0xC0 ) | 63 ); /* End sector */
5951 pte
[ PTE_EdCyl
] = ( BYTE
) n
; /* End cylinder */
5952 st_dword ( pte
+ PTE_StLba
, b_vol
); /* Start offset in LBA */
5953 st_dword ( pte
+ PTE_SizLba
, sz_vol
); /* Size in sectors */
5954 if ( disk_write ( pdrv
, buf
, 0 , 1 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
); /* Write it to the MBR */
5958 if ( disk_ioctl ( pdrv
, CTRL_SYNC
, 0 ) != RES_OK
) LEAVE_MKFS ( FR_DISK_ERR
);
5965 #if FF_MULTI_PARTITION
5966 /*-----------------------------------------------------------------------*/
5967 /* Create Partition Table on the Physical Drive */
5968 /*-----------------------------------------------------------------------*/
5971 BYTE pdrv
, /* Physical drive number */
5972 const DWORD
* szt
, /* Pointer to the size table for each partitions */
5973 void * work
/* Pointer to the working buffer (null: use heap memory) */
5976 UINT i
, n
, sz_cyl
, tot_cyl
, b_cyl
, e_cyl
, p_cyl
;
5977 BYTE s_hd
, e_hd
, * p
, * buf
= ( BYTE
*) work
;
5979 DWORD sz_disk
, sz_part
, s_part
;
5983 stat
= disk_initialize ( pdrv
);
5984 if ( stat
& STA_NOINIT
) return FR_NOT_READY
;
5985 if ( stat
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
5986 if ( disk_ioctl ( pdrv
, GET_SECTOR_COUNT
, & sz_disk
)) return FR_DISK_ERR
;
5990 if (! buf
) buf
= ff_memalloc ( FF_MAX_SS
); /* Use heap memory for working buffer */
5992 if (! buf
) return FR_NOT_ENOUGH_CORE
;
5994 /* Determine the CHS without any consideration of the drive geometry */
5995 for ( n
= 16 ; n
< 256 && sz_disk
/ n
/ 63 > 1024 ; n
*= 2 ) ;
5997 e_hd
= ( BYTE
)( n
- 1 );
5999 tot_cyl
= sz_disk
/ sz_cyl
;
6001 /* Create partition table */
6002 mem_set ( buf
, 0 , FF_MAX_SS
);
6003 p
= buf
+ MBR_Table
; b_cyl
= 0 ;
6004 for ( i
= 0 ; i
< 4 ; i
++, p
+= SZ_PTE
) {
6005 p_cyl
= ( szt
[ i
] <= 100U ) ? ( DWORD
) tot_cyl
* szt
[ i
] / 100 : szt
[ i
] / sz_cyl
; /* Number of cylinders */
6006 if ( p_cyl
== 0 ) continue ;
6007 s_part
= ( DWORD
) sz_cyl
* b_cyl
;
6008 sz_part
= ( DWORD
) sz_cyl
* p_cyl
;
6009 if ( i
== 0 ) { /* Exclude first track of cylinder 0 */
6011 s_part
+= 63 ; sz_part
-= 63 ;
6015 e_cyl
= b_cyl
+ p_cyl
- 1 ; /* End cylinder */
6016 if ( e_cyl
>= tot_cyl
) LEAVE_MKFS ( FR_INVALID_PARAMETER
);
6018 /* Set partition table */
6019 p
[ 1 ] = s_hd
; /* Start head */
6020 p
[ 2 ] = ( BYTE
)((( b_cyl
>> 2 ) & 0xC0 ) | 1 ); /* Start sector */
6021 p
[ 3 ] = ( BYTE
) b_cyl
; /* Start cylinder */
6022 p
[ 4 ] = 0x07 ; /* System type (temporary setting) */
6023 p
[ 5 ] = e_hd
; /* End head */
6024 p
[ 6 ] = ( BYTE
)((( e_cyl
>> 2 ) & 0xC0 ) | 63 ); /* End sector */
6025 p
[ 7 ] = ( BYTE
) e_cyl
; /* End cylinder */
6026 st_dword ( p
+ 8 , s_part
); /* Start sector in LBA */
6027 st_dword ( p
+ 12 , sz_part
); /* Number of sectors */
6029 /* Next partition */
6032 st_word ( p
, 0xAA55 ); /* MBR signature (always at offset 510) */
6034 /* Write it to the MBR */
6035 res
= ( disk_write ( pdrv
, buf
, 0 , 1 ) == RES_OK
&& disk_ioctl ( pdrv
, CTRL_SYNC
, 0 ) == RES_OK
) ? FR_OK
: FR_DISK_ERR
;
6039 #endif /* FF_MULTI_PARTITION */
6040 #endif /* FF_USE_MKFS && !FF_FS_READONLY */
6046 #if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
6047 #error Wrong FF_STRF_ENCODE setting
6049 /*-----------------------------------------------------------------------*/
6050 /* Get a String from the File */
6051 /*-----------------------------------------------------------------------*/
6054 TCHAR
* buff
, /* Pointer to the string buffer to read */
6055 int len
, /* Size of string buffer (items) */
6056 FIL
* fp
/* Pointer to the file object */
6064 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
6067 #if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
6071 #if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
6072 /* Make a room for the character and terminator */
6073 if ( FF_LFN_UNICODE
== 1 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 1 : 2 ;
6074 if ( FF_LFN_UNICODE
== 2 ) len
-= ( FF_STRF_ENCODE
== 0 ) ? 3 : 4 ;
6075 if ( FF_LFN_UNICODE
== 3 ) len
-= 1 ;
6077 #if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
6078 f_read ( fp
, s
, 1 , & rc
);
6081 if ( dbc_1st (( BYTE
) wc
)) {
6082 f_read ( fp
, s
, 1 , & rc
);
6083 if ( rc
!= 1 || ! dbc_2nd ( s
[ 0 ])) continue ;
6084 wc
= wc
<< 8 | s
[ 0 ];
6086 dc
= ff_oem2uni ( wc
, CODEPAGE
);
6087 if ( dc
== 0 ) continue ;
6088 #elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
6089 f_read ( fp
, s
, 2 , & rc
);
6091 dc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6092 if ( IsSurrogateL ( dc
)) continue ;
6093 if ( IsSurrogateH ( dc
)) {
6094 f_read ( fp
, s
, 2 , & rc
);
6096 wc
= ( FF_STRF_ENCODE
== 1 ) ? ld_word ( s
) : s
[ 0 ] << 8 | s
[ 1 ];
6097 if (! IsSurrogateL ( wc
)) continue ;
6098 dc
= (( dc
& 0x3FF ) + 0x40 ) << 10 | ( wc
& 0x3FF );
6100 #else /* Read a character in UTF-8 */
6101 f_read ( fp
, s
, 1 , & rc
);
6104 if ( dc
>= 0x80 ) { /* Multi-byte character? */
6106 if (( dc
& 0xE0 ) == 0xC0 ) { dc
&= 0x1F ; ct
= 1 ; } /* 2-byte? */
6107 if (( dc
& 0xF0 ) == 0xE0 ) { dc
&= 0x0F ; ct
= 2 ; } /* 3-byte? */
6108 if (( dc
& 0xF8 ) == 0xF0 ) { dc
&= 0x07 ; ct
= 3 ; } /* 4-byte? */
6109 if ( ct
== 0 ) continue ;
6110 f_read ( fp
, s
, ct
, & rc
); /* Get trailing bytes */
6111 if ( rc
!= ct
) break ;
6113 do { /* Merge trailing bytes */
6114 if (( s
[ rc
] & 0xC0 ) != 0x80 ) break ;
6115 dc
= dc
<< 6 | ( s
[ rc
] & 0x3F );
6116 } while (++ rc
< ct
);
6117 if ( rc
!= ct
|| dc
< 0x80 || IsSurrogate ( dc
) || dc
>= 0x110000 ) continue ; /* Wrong encoding? */
6120 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ; /* Strip \r off if needed */
6121 #if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
6122 if ( FF_LFN_UNICODE
== 1 && dc
>= 0x10000 ) { /* Out of BMP at UTF-16? */
6123 * p
++ = ( TCHAR
)( 0xD800 | (( dc
>> 10 ) - 0x40 )); nc
++; /* Make and output high surrogate */
6124 dc
= 0xDC00 | ( dc
& 0x3FF ); /* Make low surrogate */
6126 * p
++ = ( TCHAR
) dc
; nc
++;
6127 if ( dc
== ' \n ' ) break ; /* End of line? */
6128 #elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
6129 if ( dc
< 0x80 ) { /* 1-byte */
6132 if ( dc
== ' \n ' ) break ; /* End of line? */
6134 if ( dc
< 0x800 ) { /* 2-byte */
6135 * p
++ = ( TCHAR
)( 0xC0 | ( dc
>> 6 & 0x1F ));
6136 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6139 if ( dc
< 0x10000 ) { /* 3-byte */
6140 * p
++ = ( TCHAR
)( 0xE0 | ( dc
>> 12 & 0x0F ));
6141 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6142 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6144 } else { /* 4-byte */
6145 * p
++ = ( TCHAR
)( 0xF0 | ( dc
>> 18 & 0x07 ));
6146 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 12 & 0x3F ));
6147 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 6 & 0x3F ));
6148 * p
++ = ( TCHAR
)( 0x80 | ( dc
>> 0 & 0x3F ));
6156 #else /* Byte-by-byte without any conversion (ANSI/OEM API) */
6157 len
-= 1 ; /* Make a room for the terminator */
6159 f_read ( fp
, s
, 1 , & rc
);
6162 if ( FF_USE_STRFUNC
== 2 && dc
== ' \r ' ) continue ;
6163 * p
++ = ( TCHAR
) dc
; nc
++;
6164 if ( dc
== ' \n ' ) break ;
6168 * p
= 0 ; /* Terminate the string */
6169 return nc
? buff
: 0 ; /* When no data read due to EOF or error, return with error. */
6177 /*-----------------------------------------------------------------------*/
6178 /* Put a Character to the File */
6179 /*-----------------------------------------------------------------------*/
6181 typedef struct { /* Putchar output buffer and work area */
6182 FIL
* fp
; /* Ptr to the writing file */
6183 int idx
, nchr
; /* Write index of buf[] (-1:error), number of encoding units written */
6184 #if FF_USE_LFN && FF_LFN_UNICODE == 1
6186 #elif FF_USE_LFN && FF_LFN_UNICODE == 2
6190 BYTE buf
[ 64 ]; /* Write buffer */
6195 void putc_bfd ( /* Buffered write with code conversion */
6202 #if FF_USE_LFN && FF_LFN_UNICODE
6204 #if FF_LFN_UNICODE == 2
6210 if ( FF_USE_STRFUNC
== 2 && c
== ' \n ' ) { /* LF -> CRLF conversion */
6214 i
= pb
-> idx
; /* Write index of pb->buf[] */
6216 nc
= pb
-> nchr
; /* Write unit counter */
6218 #if FF_USE_LFN && FF_LFN_UNICODE
6219 #if FF_LFN_UNICODE == 1 /* UTF-16 input */
6220 if ( IsSurrogateH ( c
)) {
6223 hs
= pb
-> hs
; pb
-> hs
= 0 ;
6225 if (! IsSurrogateL ( c
)) hs
= 0 ;
6227 if ( IsSurrogateL ( c
)) return ;
6230 #elif FF_LFN_UNICODE == 2 /* UTF-8 input */
6232 if ( pb
-> ct
== 0 ) { /* Out of multi-byte sequence? */
6233 pb
-> bs
[ pb
-> wi
= 0 ] = ( BYTE
) c
; /* Save 1st byte */
6234 if (( BYTE
) c
< 0x80 ) break ; /* 1-byte? */
6235 if ((( BYTE
) c
& 0xE0 ) == 0xC0 ) pb
-> ct
= 1 ; /* 2-byte? */
6236 if ((( BYTE
) c
& 0xF0 ) == 0xE0 ) pb
-> ct
= 2 ; /* 3-byte? */
6237 if ((( BYTE
) c
& 0xF1 ) == 0xF0 ) pb
-> ct
= 3 ; /* 4-byte? */
6239 } else { /* In the multi-byte sequence */
6240 if ((( BYTE
) c
& 0xC0 ) != 0x80 ) { /* Broken sequence? */
6241 pb
-> ct
= 0 ; continue ;
6243 pb
-> bs
[++ pb
-> wi
] = ( BYTE
) c
; /* Save the trailing byte */
6244 if (-- pb
-> ct
== 0 ) break ; /* End of multi-byte sequence? */
6248 tp
= ( TCHAR
*) pb
-> bs
;
6249 dc
= tchar2uni (& tp
); /* UTF-8 ==> UTF-16 */
6250 if ( dc
== 0xFFFFFFFF ) return ;
6252 hs
= ( WCHAR
)( dc
>> 16 );
6253 #elif FF_LFN_UNICODE == 3 /* UTF-32 input */
6254 if ( IsSurrogate ( c
) || c
>= 0x110000 ) return ;
6256 hs
= ( WCHAR
)( 0xD800 | (( c
>> 10 ) - 0x40 )); /* Make high surrogate */
6257 wc
= 0xDC00 | ( c
& 0x3FF ); /* Make low surrogate */
6264 #if FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */
6266 st_word (& pb
-> buf
[ i
], hs
);
6270 st_word (& pb
-> buf
[ i
], wc
);
6272 #elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */
6274 pb
-> buf
[ i
++] = ( BYTE
)( hs
>> 8 );
6275 pb
-> buf
[ i
++] = ( BYTE
) hs
;
6278 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 );
6279 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6280 #elif FF_STRF_ENCODE == 3 /* Write it in UTF-8 */
6281 if ( hs
!= 0 ) { /* 4-byte */
6283 hs
= ( hs
& 0x3FF ) + 0x40 ;
6284 pb
-> buf
[ i
++] = ( BYTE
)( 0xF0 | hs
>> 8 );
6285 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
>> 2 & 0x3F ));
6286 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( hs
& 3 ) << 4 | ( wc
>> 6 & 0x0F ));
6287 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6289 if ( wc
< 0x80 ) { /* 1-byte */
6290 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6292 if ( wc
< 0x800 ) { /* 2-byte */
6294 pb
-> buf
[ i
++] = ( BYTE
)( 0xC0 | wc
>> 6 );
6295 } else { /* 3-byte */
6297 pb
-> buf
[ i
++] = ( BYTE
)( 0xE0 | wc
>> 12 );
6298 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
>> 6 & 0x3F ));
6300 pb
-> buf
[ i
++] = ( BYTE
)( 0x80 | ( wc
& 0x3F ));
6303 #else /* Write it in ANSI/OEM */
6304 if ( hs
!= 0 ) return ;
6305 wc
= ff_uni2oem ( wc
, CODEPAGE
); /* UTF-16 ==> ANSI/OEM */
6306 if ( wc
== 0 ) return ;;
6308 pb
-> buf
[ i
++] = ( BYTE
)( wc
>> 8 ); nc
++;
6310 pb
-> buf
[ i
++] = ( BYTE
) wc
;
6313 #else /* ANSI/OEM input (without re-encode) */
6314 pb
-> buf
[ i
++] = ( BYTE
) c
;
6317 if ( i
>= ( int )( sizeof pb
-> buf
) - 4 ) { /* Write buffered characters to the file */
6318 f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) i
, & n
);
6319 i
= ( n
== ( UINT
) i
) ? 0 : - 1 ;
6327 int putc_flush ( /* Flush left characters in the buffer */
6333 if ( pb
-> idx
>= 0 /* Flush buffered characters to the file */
6334 && f_write ( pb
-> fp
, pb
-> buf
, ( UINT
) pb
-> idx
, & nw
) == FR_OK
6335 && ( UINT
) pb
-> idx
== nw
) return pb
-> nchr
;
6341 void putc_init ( /* Initialize write buffer */
6346 mem_set ( pb
, 0 , sizeof ( putbuff
));
6353 TCHAR c
, /* A character to be output */
6354 FIL
* fp
/* Pointer to the file object */
6361 putc_bfd (& pb
, c
); /* Put the character */
6362 return putc_flush (& pb
);
6368 /*-----------------------------------------------------------------------*/
6369 /* Put a String to the File */
6370 /*-----------------------------------------------------------------------*/
6373 const TCHAR
* str
, /* Pointer to the string to be output */
6374 FIL
* fp
/* Pointer to the file object */
6381 while (* str
) putc_bfd (& pb
, * str
++); /* Put the string */
6382 return putc_flush (& pb
);
6388 /*-----------------------------------------------------------------------*/
6389 /* Put a Formatted String to the File */
6390 /*-----------------------------------------------------------------------*/
6393 FIL
* fp
, /* Pointer to the file object */
6394 const TCHAR
* fmt
, /* Pointer to the format string */
6395 ... /* Optional arguments... */
6403 TCHAR c
, d
, str
[ 32 ], * p
;
6412 if ( c
== 0 ) break ; /* End of string */
6413 if ( c
!= '%' ) { /* Non escape character */
6419 if ( c
== '0' ) { /* Flag: '0' padding */
6422 if ( c
== '-' ) { /* Flag: left justified */
6426 if ( c
== '*' ) { /* Minimum width by argument */
6427 w
= va_arg ( arp
, int );
6430 while ( IsDigit ( c
)) { /* Minimum width */
6431 w
= w
* 10 + c
- '0' ;
6435 if ( c
== 'l' || c
== 'L' ) { /* Type prefix: Size is long int */
6440 if ( IsLower ( d
)) d
-= 0x20 ;
6441 switch ( d
) { /* Atgument type is... */
6442 case 'S' : /* String */
6443 p
= va_arg ( arp
, TCHAR
*);
6444 for ( j
= 0 ; p
[ j
]; j
++) ;
6445 if (!( f
& 2 )) { /* Right padded */
6446 while ( j
++ < w
) putc_bfd (& pb
, ' ' ) ;
6448 while (* p
) putc_bfd (& pb
, * p
++) ; /* String body */
6449 while ( j
++ < w
) putc_bfd (& pb
, ' ' ) ; /* Left padded */
6452 case 'C' : /* Character */
6453 putc_bfd (& pb
, ( TCHAR
) va_arg ( arp
, int )); continue ;
6455 case 'B' : /* Unsigned binary */
6458 case 'O' : /* Unsigned octal */
6461 case 'D' : /* Signed decimal */
6462 case 'U' : /* Unsigned decimal */
6465 case 'X' : /* Unsigned hexdecimal */
6468 default : /* Unknown type (pass-through) */
6469 putc_bfd (& pb
, c
); continue ;
6472 /* Get an argument and put it in numeral */
6473 v
= ( f
& 4 ) ? ( DWORD
) va_arg ( arp
, long ) : (( d
== 'D' ) ? ( DWORD
)( long ) va_arg ( arp
, int ) : ( DWORD
) va_arg ( arp
, unsigned int ));
6474 if ( d
== 'D' && ( v
& 0x80000000 )) {
6480 d
= ( TCHAR
)( v
% r
); v
/= r
;
6481 if ( d
> 9 ) d
+= ( c
== 'x' ) ? 0x27 : 0x07 ;
6483 } while ( v
&& i
< sizeof str
/ sizeof * str
);
6484 if ( f
& 8 ) str
[ i
++] = '-' ;
6485 j
= i
; d
= ( f
& 1 ) ? '0' : ' ' ;
6487 while ( j
++ < w
) putc_bfd (& pb
, d
); /* Right pad */
6490 putc_bfd (& pb
, str
[-- i
]); /* Number body */
6492 while ( j
++ < w
) putc_bfd (& pb
, d
); /* Left pad */
6497 return putc_flush (& pb
);
6500 #endif /* !FF_FS_READONLY */
6501 #endif /* FF_USE_STRFUNC */
6505 #if FF_CODE_PAGE == 0
6506 /*-----------------------------------------------------------------------*/
6507 /* Set Active Codepage for the Path Name */
6508 /*-----------------------------------------------------------------------*/
6511 WORD cp
/* Value to be set as active code page */
6514 static const WORD validcp
[] = { 437 , 720 , 737 , 771 , 775 , 850 , 852 , 857 , 860 , 861 , 862 , 863 , 864 , 865 , 866 , 869 , 932 , 936 , 949 , 950 , 0 };
6515 static const BYTE
* const tables
[] = { Ct437
, Ct720
, Ct737
, Ct771
, Ct775
, Ct850
, Ct852
, Ct857
, Ct860
, Ct861
, Ct862
, Ct863
, Ct864
, Ct865
, Ct866
, Ct869
, Dc932
, Dc936
, Dc949
, Dc950
, 0 };
6519 for ( i
= 0 ; validcp
[ i
] != 0 && validcp
[ i
] != cp
; i
++) ; /* Find the code page */
6520 if ( validcp
[ i
] != cp
) return FR_INVALID_PARAMETER
; /* Not found? */
6523 if ( cp
>= 900 ) { /* DBCS */
6532 #endif /* FF_CODE_PAGE == 0 */