sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
ff.c
1/*----------------------------------------------------------------------------/
2/ FatFs - Generic FAT Filesystem Module R0.16 /
3/-----------------------------------------------------------------------------/
4/
5/ Copyright (C) 2025, ChaN, all right reserved.
6/
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:
10/
11/ 1. Redistributions of source code must retain the above copyright notice,
12/ this condition and the following disclaimer.
13/
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.
18/
19/----------------------------------------------------------------------------*/
20
21
22#include <string.h>
23#include <stdio.h>
24#include "ff.h" /* Basic definitions and declarations of API */
25#include "diskio.h" /* Declarations of MAI */
26#include "module_debug.h"
27
28/*--------------------------------------------------------------------------
29
30 Module Private Definitions
31
32---------------------------------------------------------------------------*/
33
34#if FF_DEFINED != 80386 /* Revision ID */
35#error Wrong include file (ff.h).
36#endif
37
38
39/* Limits and boundaries */
40#define MAX_DIR 0x200000 /* Max size of FAT directory (byte) */
41#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory (byte) */
42#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
43#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
44#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not defined in specs, practical limit) */
45#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
46
47
48/* Character code support macros */
49#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z')
50#define IsLower(c) ((c) >= 'a' && (c) <= 'z')
51#define IsDigit(c) ((c) >= '0' && (c) <= '9')
52#define IsSeparator(c) ((c) == '/' || (c) == '\\')
53#define IsTerminator(c) ((UINT)(c) < (FF_USE_LFN ? ' ' : '!'))
54#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
55#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
56#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
57
58
59/* Additional file access control and file status flags for internal use */
60#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
61#define FA_MODIFIED 0x40 /* File has been modified */
62#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
63
64
65/* Additional file attribute bits for internal use */
66#define AM_VOL 0x08 /* Volume label */
67#define AM_LFN 0x0F /* LFN entry */
68#define AM_MASK 0x3F /* Mask of defined bits in FAT */
69#define AM_MASKX 0x37 /* Mask of defined bits in exFAT */
70
71
72/* Name status flags in fn[11] */
73#define NSFLAG 11 /* Index of the name status byte */
74#define NS_LOSS 0x01 /* Out of 8.3 format */
75#define NS_LFN 0x02 /* Force to create LFN entry */
76#define NS_LAST 0x04 /* Last segment */
77#define NS_BODY 0x08 /* Lower case flag (body) */
78#define NS_EXT 0x10 /* Lower case flag (ext) */
79#define NS_DOT 0x20 /* Dot entry */
80#define NS_NOLFN 0x40 /* Do not find LFN */
81#define NS_NONAME 0x80 /* Not followed */
82
83
84/* exFAT directory entry types */
85#define ET_BITMAP 0x81 /* Allocation bitmap */
86#define ET_UPCASE 0x82 /* Up-case table */
87#define ET_VLABEL 0x83 /* Volume label */
88#define ET_FILEDIR 0x85 /* File and directory */
89#define ET_STREAM 0xC0 /* Stream extension */
90#define ET_FILENAME 0xC1 /* Name extension */
91
92
93/* FatFs refers the FAT structures as simple byte array instead of structure member
94/ because the C structure is not binary compatible between different platforms */
95
96#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
97#define BS_OEMName 3 /* OEM name (8-byte) */
98#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
99#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
100#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
101#define BPB_NumFATs 16 /* Number of FATs (BYTE) */
102#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
103#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
104#define BPB_Media 21 /* Media descriptor byte (BYTE) */
105#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
106#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
107#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
108#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
109#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
110#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
111#define BS_NTres 37 /* WindowsNT error flag (BYTE) */
112#define BS_BootSig 38 /* Extended boot signature (BYTE) */
113#define BS_VolID 39 /* Volume serial number (DWORD) */
114#define BS_VolLab 43 /* Volume label string (8-byte) */
115#define BS_FilSysType 54 /* Filesystem type string (8-byte) */
116#define BS_BootCode 62 /* Boot code (448-byte) */
117#define BS_55AA 510 /* Boot signature (WORD, for VBR and MBR) */
118
119#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
120#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
121#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
122#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
123#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
124#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
125#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
126#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
127#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
128#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
129#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
130#define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
131#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
132
133#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
134#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
135#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
136#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
137#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
138#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
139#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
140#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
141#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
142#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
143#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD, out of check sum calculation) */
144#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
145#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
146#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
147#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
148#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE, out of check sum calculation) */
149#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
150#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
151
152#define DIR_Name 0 /* Short file name (11-byte) */
153#define DIR_Attr 11 /* Attribute (BYTE) */
154#define DIR_NTres 12 /* Low case flags of SFN (BYTE) */
155#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
156#define DIR_CrtTime 14 /* Created time (DWORD) */
157#define DIR_LstAccDate 18 /* Last accessed date (WORD) */
158#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
159#define DIR_ModTime 22 /* Modified time (DWORD) */
160#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
161#define DIR_FileSize 28 /* File size (DWORD) */
162#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
163#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
164#define LDIR_Type 12 /* LFN: Entry type (BYTE) */
165#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
166#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
167#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
168#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
169#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
170#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
171#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
172#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
173#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
174#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
175#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
176#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
177#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
178#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
179#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
180#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
181#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
182#define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
183#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
184#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
185#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
186#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
187#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
188
189#define SZDIRE 32 /* Size of a directory entry */
190#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
191#define RDDEM 0x05 /* Replacement of the character collides with DDEM */
192#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
193
194#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
195#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
196#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
197#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
198#define FSI_TrailSig 508 /* FAT32 FSI: Trailing signature (DWORD) */
199
200#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
201#define SZ_PTE 16 /* MBR: Size of a partition table entry */
202#define PTE_Boot 0 /* MBR PTE: Boot indicator */
203#define PTE_StHead 1 /* MBR PTE: Start head in CHS */
204#define PTE_StSec 2 /* MBR PTE: Start sector in CHS */
205#define PTE_StCyl 3 /* MBR PTE: Start cylinder in CHS */
206#define PTE_System 4 /* MBR PTE: System ID */
207#define PTE_EdHead 5 /* MBR PTE: End head in CHS */
208#define PTE_EdSec 6 /* MBR PTE: End sector in CHS */
209#define PTE_EdCyl 7 /* MBR PTE: End cylinder in CHS */
210#define PTE_StLba 8 /* MBR PTE: Start in LBA */
211#define PTE_SizLba 12 /* MBR PTE: Size in LBA */
212
213#define GPTH_Sign 0 /* GPT HDR: Signature (8-byte) */
214#define GPTH_Rev 8 /* GPT HDR: Revision (DWORD) */
215#define GPTH_Size 12 /* GPT HDR: Header size (DWORD) */
216#define GPTH_Bcc 16 /* GPT HDR: Header BCC (DWORD) */
217#define GPTH_CurLba 24 /* GPT HDR: This header LBA (QWORD) */
218#define GPTH_BakLba 32 /* GPT HDR: Another header LBA (QWORD) */
219#define GPTH_FstLba 40 /* GPT HDR: First LBA for partition data (QWORD) */
220#define GPTH_LstLba 48 /* GPT HDR: Last LBA for partition data (QWORD) */
221#define GPTH_DskGuid 56 /* GPT HDR: Disk GUID (16-byte) */
222#define GPTH_PtOfs 72 /* GPT HDR: Partition table LBA (QWORD) */
223#define GPTH_PtNum 80 /* GPT HDR: Number of table entries (DWORD) */
224#define GPTH_PteSize 84 /* GPT HDR: Size of table entry (DWORD) */
225#define GPTH_PtBcc 88 /* GPT HDR: Partition table BCC (DWORD) */
226#define SZ_GPTE 128 /* GPT PTE: Size of a GPT partition table entry */
227#define GPTE_PtGuid 0 /* GPT PTE: Partition type GUID (16-byte) */
228#define GPTE_UpGuid 16 /* GPT PTE: Partition unique GUID (16-byte) */
229#define GPTE_FstLba 32 /* GPT PTE: First LBA of partition (QWORD) */
230#define GPTE_LstLba 40 /* GPT PTE: Last LBA of partition (QWORD) */
231#define GPTE_Flags 48 /* GPT PTE: Partition flags (QWORD) */
232#define GPTE_Name 56 /* GPT PTE: Partition name */
233
234
235/* Post process on fatal error in the file operations */
236#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
237
238
239/* Re-entrancy related */
240#if FF_FS_REENTRANT
241#if FF_USE_LFN == 1
242#error Static LFN work area cannot be used in thread-safe configuration
243#endif
244#define LEAVE_FF(fs, res) { unlock_volume(fs, res); return res; }
245#else
246#define LEAVE_FF(fs, res) return res
247#endif
248
249
250/* Definitions of logical drive to physical location conversion */
251#if FF_MULTI_PARTITION
252#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number from the mapping table */
253#define LD2PT(vol) VolToPart[vol].pt /* Get partition number from the mapping table (0:auto search, 1-:forced partition number) */
254#else
255#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */
256#define LD2PT(vol) 0 /* Auto partition search */
257#endif
258
259
260/* Definitions of sector size */
261#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)
262#error Wrong sector size configuration
263#endif
264#if FF_MAX_SS == FF_MIN_SS
265#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
266#else
267#define SS(fs) ((fs)->ssize) /* Variable sector size */
268#endif
269
270
271/* Timestamp */
272#if FF_FS_NORTC == 1
273#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
274#error Invalid FF_FS_NORTC settings
275#endif
276#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
277#else
278#define GET_FATTIME() get_fattime()
279#endif
280
281
282/* File lock controls */
283#if FF_FS_LOCK
284#if FF_FS_READONLY
285#error FF_FS_LOCK must be 0 at read-only configuration
286#endif
287typedef struct { /* Open object identifier with status */
288 FATFS* fs; /* Object ID 1, volume (NULL:blank entry) */
289 DWORD clu; /* Object ID 2, containing directory (0:root) */
290 DWORD ofs; /* Object ID 3, offset in the directory */
291 UINT ctr; /* Object open status, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
292} FILESEM;
293#endif
294
295
296/* SBCS up-case tables (\x80-\xFF) */
297#define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
298 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
299 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
300 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
301 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
302 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
303 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
304 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
305#define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
306 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
307 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
308 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
309 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
310 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
311 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
312 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
313#define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
314 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
315 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
316 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
317 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
318 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
319 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
320 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
321#define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
322 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
323 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
324 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
325 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
326 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
327 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
328 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
329#define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
330 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
331 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
332 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
333 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
334 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
335 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
336 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
337#define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
338 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
339 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
340 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
341 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
342 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
343 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
344 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
345#define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
346 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
347 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
348 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
349 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
350 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
351 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
352 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
353#define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
354 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
355 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
356 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
357 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
358 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
359 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
360 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
361#define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
362 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
363 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
364 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
365 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
366 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
367 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
368 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
369#define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
370 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
371 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
372 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
373 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
374 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
375 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
376 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
377#define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
378 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
379 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
380 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
381 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
382 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
383 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
384 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
385#define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
386 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
387 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
388 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
389 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
390 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
391 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
392 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
393#define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
394 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
395 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
396 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
397 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
398 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
399 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
400 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
401#define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
402 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
403 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
404 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
405 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
406 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
407 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
408 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
409#define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
410 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
411 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
412 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
413 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
414 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
415 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
416 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
417#define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
418 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
419 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
420 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
421 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
422 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
423 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
424 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
425#define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
426 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
427 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
428 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
429 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
430 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
431 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
432 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
433
434
435/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
436/* <------> <------> <------> <------> <------> */
437#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
438#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
439#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
440#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
441
442
443/* Macros for table definitions */
444#define MERGE_2STR(a, b) a ## b
445#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
446
447
448
449
450/*--------------------------------------------------------------------------
451
452 Module Private Work Area
453
454---------------------------------------------------------------------------*/
455/* Remark: Variables defined here without initial value shall be guaranteed
456/ zero/null at start-up. If not, the linker option or start-up routine is
457/ not compliance with C standard. */
458
459/*--------------------------------*/
460/* File/Volume controls */
461/*--------------------------------*/
462
463#if FF_VOLUMES < 1 || FF_VOLUMES > 10
464#error Wrong FF_VOLUMES setting
465#endif
466static FATFS *FatFs[FF_VOLUMES]; /* Pointer to the filesystem objects (logical drives) */
467static WORD Fsid; /* Filesystem mount ID */
468
469#if FF_FS_RPATH
470static BYTE CurrVol; /* Current drive number set by f_chdrive() */
471#endif
472
473#if FF_FS_LOCK
474static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */
475#if FF_FS_REENTRANT
476static volatile BYTE SysLock; /* System lock flag to protect Files[] (0:no mutex, 1:unlocked, 2:locked) */
477static volatile BYTE SysLockVolume; /* Volume id who is locking Files[] */
478#endif
479#endif
480
481#if FF_STR_VOLUME_ID
482#ifdef FF_VOLUME_STRS
483static const char *const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; /* Pre-defined volume ID */
484#endif
485#endif
486
487#if FF_LBA64
488#if FF_MIN_GPT > 0x100000000
489#error Wrong FF_MIN_GPT setting
490#endif
491static const BYTE GUID_MS_Basic[16] = {0xA2,0xA0,0xD0,0xEB,0xE5,0xB9,0x33,0x44,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7};
492#endif
493
494
495
496/*--------------------------------*/
497/* LFN/Directory working buffer */
498/*--------------------------------*/
499
500#if FF_USE_LFN == 0 /* Non-LFN configuration */
501#if FF_FS_EXFAT
502#error LFN must be enabled when enable exFAT
503#endif
504#define DEF_NAMEBUFF
505#define INIT_NAMEBUFF(fs)
506#define FREE_NAMEBUFF()
507#define LEAVE_MKFS(res) return res
508
509#else /* LFN configurations */
510#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
511#error Wrong setting of FF_MAX_LFN
512#endif
513#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
514#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
515#endif
516#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
517#error Wrong setting of FF_LFN_UNICODE
518#endif
519static 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 */
520#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
521
522#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
523#if FF_FS_EXFAT
524static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry block scratchpad buffer */
525#endif
526static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */
527#define DEF_NAMEBUFF
528#define INIT_NAMEBUFF(fs)
529#define FREE_NAMEBUFF()
530#define LEAVE_MKFS(res) return res
531
532#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
533#if FF_FS_EXFAT
534#define DEF_NAMEBUFF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
535#define INIT_NAMEBUFF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
536#define FREE_NAMEBUFF()
537#else
538#define DEF_NAMEBUFF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
539#define INIT_NAMEBUFF(fs) { (fs)->lfnbuf = lbuf; }
540#define FREE_NAMEBUFF()
541#endif
542#define LEAVE_MKFS(res) return res
543
544#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
545#if FF_FS_EXFAT
546#define DEF_NAMEBUFF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
547#define INIT_NAMEBUFF(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); }
548#define FREE_NAMEBUFF() ff_memfree(lfn)
549#else
550#define DEF_NAMEBUFF WCHAR *lfn; /* Pointer to LFN working buffer */
551#define INIT_NAMEBUFF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
552#define FREE_NAMEBUFF() ff_memfree(lfn)
553#endif
554#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
555#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
556
557#else
558#error Wrong setting of FF_USE_LFN
559
560#endif /* FF_USE_LFN == 1 */
561#endif /* FF_USE_LFN == 0 */
562
563
564
565/*--------------------------------*/
566/* Code conversion tables */
567/*--------------------------------*/
568
569#if FF_CODE_PAGE == 0 /* Run-time code page configuration */
570#define CODEPAGE CodePage
571static WORD CodePage; /* Current code page */
572static const BYTE* ExCvt; /* Pointer to SBCS up-case table Ct???[] (null:disabled) */
573static const BYTE* DbcTbl; /* Pointer to DBCS code range table Dc???[] (null:disabled) */
574
575static const BYTE Ct437[] = TBL_CT437;
576static const BYTE Ct720[] = TBL_CT720;
577static const BYTE Ct737[] = TBL_CT737;
578static const BYTE Ct771[] = TBL_CT771;
579static const BYTE Ct775[] = TBL_CT775;
580static const BYTE Ct850[] = TBL_CT850;
581static const BYTE Ct852[] = TBL_CT852;
582static const BYTE Ct855[] = TBL_CT855;
583static const BYTE Ct857[] = TBL_CT857;
584static const BYTE Ct860[] = TBL_CT860;
585static const BYTE Ct861[] = TBL_CT861;
586static const BYTE Ct862[] = TBL_CT862;
587static const BYTE Ct863[] = TBL_CT863;
588static const BYTE Ct864[] = TBL_CT864;
589static const BYTE Ct865[] = TBL_CT865;
590static const BYTE Ct866[] = TBL_CT866;
591static const BYTE Ct869[] = TBL_CT869;
592static const BYTE Dc932[] = TBL_DC932;
593static const BYTE Dc936[] = TBL_DC936;
594static const BYTE Dc949[] = TBL_DC949;
595static const BYTE Dc950[] = TBL_DC950;
596
597#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
598#define CODEPAGE FF_CODE_PAGE
599static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE);
600
601#else /* Static code page configuration (DBCS) */
602#define CODEPAGE FF_CODE_PAGE
603static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE);
604
605#endif
606
607
608
609
610/*--------------------------------------------------------------------------
611
612 Module Private Functions
613
614---------------------------------------------------------------------------*/
615
616
617/*-----------------------------------------------------------------------*/
618/* Load/Store multi-byte word in the FAT structure */
619/*-----------------------------------------------------------------------*/
620
621static WORD ld_16 (const BYTE* ptr) /* Load a 2-byte little-endian word */
622{
623 WORD rv;
624
625 rv = ptr[1];
626 rv = rv << 8 | ptr[0];
627 return rv;
628}
629
630static DWORD ld_32 (const BYTE* ptr) /* Load a 4-byte little-endian word */
631{
632 DWORD rv;
633
634 rv = ptr[3];
635 rv = rv << 8 | ptr[2];
636 rv = rv << 8 | ptr[1];
637 rv = rv << 8 | ptr[0];
638 return rv;
639}
640
641#if FF_FS_EXFAT
642static QWORD ld_64 (const BYTE* ptr) /* Load an 8-byte little-endian word */
643{
644 QWORD rv;
645
646 rv = ptr[7];
647 rv = rv << 8 | ptr[6];
648 rv = rv << 8 | ptr[5];
649 rv = rv << 8 | ptr[4];
650 rv = rv << 8 | ptr[3];
651 rv = rv << 8 | ptr[2];
652 rv = rv << 8 | ptr[1];
653 rv = rv << 8 | ptr[0];
654 return rv;
655}
656#endif
657
658#if !FF_FS_READONLY
659static void st_16 (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */
660{
661 *ptr++ = (BYTE)val; val >>= 8;
662 *ptr++ = (BYTE)val;
663}
664
665static void st_32 (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */
666{
667 *ptr++ = (BYTE)val; val >>= 8;
668 *ptr++ = (BYTE)val; val >>= 8;
669 *ptr++ = (BYTE)val; val >>= 8;
670 *ptr++ = (BYTE)val;
671}
672
673#if FF_FS_EXFAT
674static void st_64 (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */
675{
676 *ptr++ = (BYTE)val; val >>= 8;
677 *ptr++ = (BYTE)val; val >>= 8;
678 *ptr++ = (BYTE)val; val >>= 8;
679 *ptr++ = (BYTE)val; val >>= 8;
680 *ptr++ = (BYTE)val; val >>= 8;
681 *ptr++ = (BYTE)val; val >>= 8;
682 *ptr++ = (BYTE)val; val >>= 8;
683 *ptr++ = (BYTE)val;
684}
685#endif
686#endif /* !FF_FS_READONLY */
687
688
689
690/*-----------------------------------------------------------------------*/
691/* String functions */
692/*-----------------------------------------------------------------------*/
693
694/* Test if the byte is DBC 1st byte */
695static int dbc_1st (BYTE c)
696{
697#if FF_CODE_PAGE == 0 /* Variable code page */
698 if (DbcTbl && c >= DbcTbl[0]) {
699 if (c <= DbcTbl[1]) return 1; /* 1st byte range 1 */
700 if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st byte range 2 */
701 }
702#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
703 if (c >= DbcTbl[0]) {
704 if (c <= DbcTbl[1]) return 1;
705 if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1;
706 }
707#else /* SBCS fixed code page */
708 if (c != 0) return 0; /* Always false */
709#endif
710 return 0;
711}
712
713
714/* Test if the byte is DBC 2nd byte */
715static int dbc_2nd (BYTE c)
716{
717#if FF_CODE_PAGE == 0 /* Variable code page */
718 if (DbcTbl && c >= DbcTbl[4]) {
719 if (c <= DbcTbl[5]) return 1; /* 2nd byte range 1 */
720 if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */
721 if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */
722 }
723#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
724 if (c >= DbcTbl[4]) {
725 if (c <= DbcTbl[5]) return 1;
726 if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1;
727 if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1;
728 }
729#else /* SBCS fixed code page */
730 if (c != 0) return 0; /* Always false */
731#endif
732 return 0;
733}
734
735
736#if FF_USE_LFN
737
738/* Get a Unicode code point from the TCHAR string in defined API encodeing */
739static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */
740 const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */
741)
742{
743 DWORD uc;
744 const TCHAR *p = *str;
745
746#if FF_LFN_UNICODE == 1 /* UTF-16 input */
747 WCHAR wc;
748
749 uc = *p++; /* Get an encoding unit */
750 if (IsSurrogate(uc)) { /* Surrogate? */
751 wc = *p++; /* Get low surrogate */
752 if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */
753 uc = uc << 16 | wc;
754 }
755
756#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
757 BYTE tb;
758 int nf;
759
760 uc = (BYTE)*p++; /* Get an encoding unit */
761 if (uc & 0x80) { /* Multiple byte code? */
762 if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */
763 uc &= 0x1F; nf = 1;
764 } else if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */
765 uc &= 0x0F; nf = 2;
766 } else if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */
767 uc &= 0x07; nf = 3;
768 } else { /* Wrong sequence */
769 return 0xFFFFFFFF;
770 }
771 do { /* Get and merge trailing bytes */
772 tb = (BYTE)*p++;
773 if ((tb & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */
774 uc = uc << 6 | (tb & 0x3F);
775 } while (--nf != 0);
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 */
778 }
779
780#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
781 uc = (TCHAR)*p++; /* Get a unit */
782 if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* Wrong code? */
783 if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
784
785#else /* ANSI/OEM input */
786 BYTE sb;
787 WCHAR wc;
788
789 wc = (BYTE)*p++; /* Get a byte */
790 if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */
791 sb = (BYTE)*p++; /* Get 2nd byte */
792 if (!dbc_2nd(sb)) return 0xFFFFFFFF; /* Invalid code? */
793 wc = (wc << 8) + sb; /* Make a DBC */
794 }
795 if (wc != 0) {
796 wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */
797 if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */
798 }
799 uc = wc;
800
801#endif
802 *str = p; /* Next read pointer */
803 return uc;
804}
805
806
807/* Store a Unicode char in defined API encoding */
808static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
809 DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */
810 TCHAR* buf, /* Output buffer */
811 UINT szb /* Size of the buffer */
812)
813{
814#if FF_LFN_UNICODE == 1 /* UTF-16 output */
815 WCHAR hs, wc;
816
817 hs = (WCHAR)(chr >> 16);
818 wc = (WCHAR)chr;
819 if (hs == 0) { /* Single encoding unit? */
820 if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */
821 *buf = wc;
822 return 1;
823 }
824 if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */
825 *buf++ = hs;
826 *buf++ = wc;
827 return 2;
828
829#elif FF_LFN_UNICODE == 2 /* UTF-8 output */
830 DWORD hc;
831
832 if (chr < 0x80) { /* Single byte code? */
833 if (szb < 1) return 0; /* Buffer overflow? */
834 *buf = (TCHAR)chr;
835 return 1;
836 }
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));
841 return 2;
842 }
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));
848 return 3;
849 }
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));
860 return 4;
861
862#elif FF_LFN_UNICODE == 3 /* UTF-32 output */
863 DWORD hc;
864
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;
871 }
872 *buf++ = (TCHAR)chr;
873 return 1;
874
875#else /* ANSI/OEM output */
876 WCHAR wc;
877
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 */
883 return 2;
884 }
885 if (wc == 0 || szb < 1) return 0; /* Invalid character or buffer overflow? */
886 *buf++ = (TCHAR)wc; /* Store the character */
887 return 1;
888#endif
889}
890#endif /* FF_USE_LFN */
891
892
893#if FF_FS_REENTRANT
894/*-----------------------------------------------------------------------*/
895/* Request/Release grant to access the volume */
896/*-----------------------------------------------------------------------*/
897
898static int lock_volume ( /* 1:Ok, 0:timeout */
899 FATFS* fs, /* Filesystem object to lock */
900 int syslock /* System lock required */
901)
902{
903 int rv;
904
905
906#if FF_FS_LOCK
907 rv = ff_mutex_take(fs->ldrv); /* Lock the volume */
908 if (rv && syslock) { /* System lock reqiered? */
909 rv = ff_mutex_take(FF_VOLUMES); /* Lock the system */
910 if (rv) {
911 SysLockVolume = fs->ldrv;
912 SysLock = 2; /* System lock succeeded */
913 } else {
914 ff_mutex_give(fs->ldrv); /* Failed system lock */
915 }
916 }
917#else
918 rv = syslock ? ff_mutex_take(fs->ldrv) : ff_mutex_take(fs->ldrv); /* Lock the volume (this is to prevent compiler warning) */
919#endif
920 return rv;
921}
922
923
924static void unlock_volume (
925 FATFS* fs, /* Filesystem object */
926 FRESULT res /* Result code to be returned */
927)
928{
929 if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {
930#if FF_FS_LOCK
931 if (SysLock == 2 && SysLockVolume == fs->ldrv) { /* Unlock system if it has been locked by this task */
932 SysLock = 1;
933 ff_mutex_give(FF_VOLUMES);
934 }
935#endif
936 ff_mutex_give(fs->ldrv); /* Unlock the volume */
937 }
938}
939
940#endif
941
942
943
944#if FF_FS_LOCK
945/*-----------------------------------------------------------------------*/
946/* File sharing control functions */
947/*-----------------------------------------------------------------------*/
948
949static FRESULT chk_share ( /* Check if the file can be accessed */
950 DIR* dp, /* Directory object pointing the file to be checked */
951 int acc /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
952)
953{
954 UINT i, be;
955
956 /* Search open object table for the object */
957 be = 0;
958 for (i = 0; i < FF_FS_LOCK; i++) {
959 if (Files[i].fs) { /* Existing entry */
960 if (Files[i].fs == dp->obj.fs && /* Check if the object matches with an open object */
961 Files[i].clu == dp->obj.sclust &&
962 Files[i].ofs == dp->dptr) break;
963 } else { /* Blank entry */
964 be = 1;
965 }
966 }
967 if (i == FF_FS_LOCK) { /* The object has not been opened */
968 return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; /* Is there a blank entry for new object? */
969 }
970
971 /* The object was opened. Reject any open against writing file and all write mode open */
972 return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
973}
974
975
976static int enq_share (void) /* Check if an entry is available for a new object */
977{
978 UINT i;
979
980 for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; /* Find a free entry */
981 return (i == FF_FS_LOCK) ? 0 : 1;
982}
983
984
985static UINT inc_share ( /* Increment object open counter and returns its index (0:Internal error) */
986 DIR* dp, /* Directory object pointing the file to register or increment */
987 int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
988)
989{
990 UINT i;
991
992
993 for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */
994 if (Files[i].fs == dp->obj.fs
995 && Files[i].clu == dp->obj.sclust
996 && Files[i].ofs == dp->dptr) break;
997 }
998
999 if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */
1000 for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; /* Find a free entry */
1001 if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */
1002 Files[i].fs = dp->obj.fs;
1003 Files[i].clu = dp->obj.sclust;
1004 Files[i].ofs = dp->dptr;
1005 Files[i].ctr = 0;
1006 }
1007
1008 if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */
1009
1010 Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */
1011
1012 return i + 1; /* Index number origin from 1 */
1013}
1014
1015
1016static FRESULT dec_share ( /* Decrement object open counter */
1017 UINT i /* Semaphore index (1..) */
1018)
1019{
1020 UINT n;
1021 FRESULT res;
1022
1023
1024 if (--i < FF_FS_LOCK) { /* Index number origin from 0 */
1025 n = Files[i].ctr;
1026 if (n == 0x100) n = 0; /* If write mode open, delete the object semaphore */
1027 if (n > 0) n--; /* Decrement read mode open count */
1028 Files[i].ctr = n;
1029 if (n == 0) { /* Delete the object semaphore if open count becomes zero */
1030 Files[i].fs = 0; /* Free the entry <<<If this memory write operation is not in atomic, FF_FS_REENTRANT == 1 and FF_VOLUMES > 1, there is a potential error in this process >>> */
1031 }
1032 res = FR_OK;
1033 } else {
1034 res = FR_INT_ERR; /* Invalid index number */
1035 }
1036 return res;
1037}
1038
1039
1040static void clear_share ( /* Clear all lock entries of the volume */
1041 FATFS* fs
1042)
1043{
1044 UINT i;
1045
1046 for (i = 0; i < FF_FS_LOCK; i++) {
1047 if (Files[i].fs == fs) Files[i].fs = 0;
1048 }
1049}
1050
1051#endif /* FF_FS_LOCK */
1052
1053
1054
1055/*-----------------------------------------------------------------------*/
1056/* Move/Flush disk access window in the filesystem object */
1057/*-----------------------------------------------------------------------*/
1058#if !FF_FS_READONLY
1059static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */
1060 FATFS* fs /* Filesystem object */
1061)
1062{
1063 FRESULT res = FR_OK;
1064
1065
1066 if (fs->wflag) { /* Is the disk access window dirty? */
1067 if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) { /* Write it back into the volume */
1068 fs->wflag = 0; /* Clear window dirty flag */
1069 if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it in the 1st FAT? */
1070 if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */
1071 }
1072 } else {
1073 res = FR_DISK_ERR;
1074 }
1075 }
1076 return res;
1077}
1078#endif
1079
1080
1081static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */
1082 FATFS* fs, /* Filesystem object */
1083 LBA_t sect /* Sector LBA to make appearance in the fs->win[] */
1084)
1085{
1086 FRESULT res = FR_OK;
1087
1088
1089 if (sect != fs->winsect) { /* Window offset changed? */
1090#if !FF_FS_READONLY
1091 res = sync_window(fs); /* Flush the window */
1092#endif
1093 if (res == FR_OK) { /* Fill sector window with new data */
1094 if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) {
1095 sect = (LBA_t)0 - 1; /* Invalidate window if read data is not valid */
1096 res = FR_DISK_ERR;
1097 }
1098 fs->winsect = sect;
1099 }
1100 }
1101 return res;
1102}
1103
1104
1105
1106
1107#if !FF_FS_READONLY
1108/*-----------------------------------------------------------------------*/
1109/* Synchronize filesystem and data on the storage */
1110/*-----------------------------------------------------------------------*/
1111
1112static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
1113 FATFS* fs /* Filesystem object */
1114)
1115{
1116 FRESULT res;
1117
1118
1119 res = sync_window(fs);
1120 if (res == FR_OK) {
1121 if (fs->fsi_flag == 1) { /* Allocation changed? */
1122 fs->fsi_flag = 0;
1123 if (fs->fs_type == FS_FAT32) { /* FAT32: Update FSInfo sector */
1124 /* Create FSInfo structure */
1125 memset(fs->win, 0, sizeof fs->win);
1126 st_32(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */
1127 st_32(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */
1128 st_32(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */
1129 st_32(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */
1130 st_32(fs->win + FSI_TrailSig, 0xAA550000); /* Trailing signature */
1131 disk_write(fs->pdrv, fs->win, fs->winsect = fs->volbase + 1, 1); /* Write it into the FSInfo sector (Next to VBR) */
1132 }
1133#if FF_FS_EXFAT
1134 else if (fs->fs_type == FS_EXFAT) { /* exFAT: Update PercInUse field in BPB */
1135 if (disk_read(fs->pdrv, fs->win, fs->winsect = fs->volbase, 1) == RES_OK) { /* Load VBR */
1136 BYTE perc_inuse = (fs->free_clst <= fs->n_fatent - 2) ? (BYTE)((QWORD)(fs->n_fatent - 2 - fs->free_clst) * 100 / (fs->n_fatent - 2)) : 0xFF; /* Precent in use 0-100 or 0xFF(unknown) */
1137
1138 if (fs->win[BPB_PercInUseEx] != perc_inuse) { /* Write it back into VBR if needed */
1139 fs->win[BPB_PercInUseEx] = perc_inuse;
1140 disk_write(fs->pdrv, fs->win, fs->winsect, 1);
1141 }
1142 }
1143 }
1144#endif
1145 }
1146 /* Make sure that no pending write process in the lower layer */
1147 if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;
1148 }
1149
1150 return res;
1151}
1152
1153#endif
1154
1155
1156
1157/*-----------------------------------------------------------------------*/
1158/* Get physical sector number from cluster number */
1159/*-----------------------------------------------------------------------*/
1160
1161LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
1162 FATFS* fs, /* Filesystem object */
1163 DWORD clst /* Cluster# to be converted */
1164)
1165{
1166 clst -= 2; /* Cluster number is origin from 2 */
1167 if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */
1168 return fs->database + (LBA_t)fs->csize * clst; /* Start sector number of the cluster */
1169}
1170
1171
1172
1173
1174/*-----------------------------------------------------------------------*/
1175/* FAT access - Read value of an FAT entry */
1176/*-----------------------------------------------------------------------*/
1177
1178DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
1179 FFOBJID* obj, /* Corresponding object */
1180 DWORD clst /* Cluster number to get the value */
1181)
1182{
1183 UINT wc, bc;
1184 DWORD val;
1185 FATFS *fs = obj->fs;
1186
1187
1188 if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */
1189 val = 1; /* Internal error */
1190
1191 } else {
1192 val = 0xFFFFFFFF; /* Default value falls on disk error */
1193
1194 switch (fs->fs_type) {
1195 case FS_FAT12 :
1196 bc = (UINT)clst; bc += bc / 2;
1197 if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
1198 wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of the entry */
1199 if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
1200 wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of the entry */
1201 val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* Adjust bit position */
1202 break;
1203
1204 case FS_FAT16 :
1205 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;
1206 val = ld_16(fs->win + clst * 2 % SS(fs)); /* Simple WORD array */
1207 break;
1208
1209 case FS_FAT32 :
1210 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
1211 val = ld_32(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */
1212 break;
1213#if FF_FS_EXFAT
1214 case FS_EXFAT :
1215 if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat == 0) { /* Object except root dir must have valid data length */
1216 DWORD cofs = clst - obj->sclust; /* Offset from start cluster */
1217 DWORD clen = (DWORD)((LBA_t)((obj->objsize - 1) / SS(fs)) / fs->csize); /* Number of clusters - 1 */
1218
1219 if (obj->stat == 2 && cofs <= clen) { /* Is it a contiguous chain? */
1220 val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* No data on the FAT, generate the value */
1221 break;
1222 }
1223 if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */
1224 val = clst + 1; /* Generate the value */
1225 break;
1226 }
1227 if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */
1228 if (obj->n_frag != 0) { /* Is it on the growing edge? */
1229 val = 0x7FFFFFFF; /* Generate EOC */
1230 } else {
1231 if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
1232 val = ld_32(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF;
1233 }
1234 break;
1235 }
1236 }
1237 val = 1; /* Internal error */
1238 break;
1239#endif
1240 default:
1241 val = 1; /* Internal error */
1242 }
1243 }
1244
1245 return val;
1246}
1247
1248
1249
1250
1251#if !FF_FS_READONLY
1252/*-----------------------------------------------------------------------*/
1253/* FAT access - Change value of an FAT entry */
1254/*-----------------------------------------------------------------------*/
1255
1256static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
1257 FATFS* fs, /* Corresponding filesystem object */
1258 DWORD clst, /* FAT index number (cluster number) to be changed */
1259 DWORD val /* New value to be set to the entry */
1260)
1261{
1262 UINT bc;
1263 BYTE *p;
1264 FRESULT res = FR_INT_ERR;
1265
1266
1267 if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */
1268 switch (fs->fs_type) {
1269 case FS_FAT12:
1270 bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */
1271 res = move_window(fs, fs->fatbase + (bc / SS(fs)));
1272 if (res != FR_OK) break;
1273 p = fs->win + bc++ % SS(fs);
1274 *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */
1275 fs->wflag = 1;
1276 res = move_window(fs, fs->fatbase + (bc / SS(fs)));
1277 if (res != FR_OK) break;
1278 p = fs->win + bc % SS(fs);
1279 *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); /* Update 2nd byte */
1280 fs->wflag = 1;
1281 break;
1282
1283 case FS_FAT16:
1284 res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
1285 if (res != FR_OK) break;
1286 st_16(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */
1287 fs->wflag = 1;
1288 break;
1289
1290 case FS_FAT32:
1291#if FF_FS_EXFAT
1292 case FS_EXFAT:
1293#endif
1294 res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
1295 if (res != FR_OK) break;
1296 if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
1297 val = (val & 0x0FFFFFFF) | (ld_32(fs->win + clst * 4 % SS(fs)) & 0xF0000000);
1298 }
1299 st_32(fs->win + clst * 4 % SS(fs), val);
1300 fs->wflag = 1;
1301 break;
1302 }
1303 }
1304 return res;
1305}
1306
1307#endif /* !FF_FS_READONLY */
1308
1309
1310
1311
1312#if FF_FS_EXFAT && !FF_FS_READONLY
1313/*-----------------------------------------------------------------------*/
1314/* exFAT: Accessing FAT and Allocation Bitmap */
1315/*-----------------------------------------------------------------------*/
1316
1317/*--------------------------------------*/
1318/* Find a contiguous free cluster block */
1319/*--------------------------------------*/
1320
1321static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
1322 FATFS* fs, /* Filesystem object */
1323 DWORD clst, /* Cluster number to scan from */
1324 DWORD ncl /* Number of contiguous clusters to find (1..) */
1325)
1326{
1327 BYTE bm, bv;
1328 UINT i;
1329 DWORD val, scl, ctr;
1330
1331
1332 clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */
1333 if (clst >= fs->n_fatent - 2) clst = 0;
1334 scl = val = clst; ctr = 0;
1335 for (;;) {
1336 if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;
1337 i = val / 8 % SS(fs); bm = 1 << (val % 8);
1338 do {
1339 do {
1340 bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */
1341 if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */
1342 val = 0; bm = 0; i = SS(fs);
1343 }
1344 if (bv == 0) { /* Is it a free cluster? */
1345 if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */
1346 } else {
1347 scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */
1348 }
1349 if (val == clst) return 0; /* All cluster scanned? */
1350 } while (bm != 0);
1351 bm = 1;
1352 } while (++i < SS(fs));
1353 }
1354}
1355
1356
1357/*----------------------------------------*/
1358/* Set/Clear a block of allocation bitmap */
1359/*----------------------------------------*/
1360
1361static FRESULT change_bitmap (
1362 FATFS* fs, /* Filesystem object */
1363 DWORD clst, /* Cluster number to change from */
1364 DWORD ncl, /* Number of clusters to be changed */
1365 int bv /* bit value to be set (0 or 1) */
1366)
1367{
1368 BYTE bm;
1369 UINT i;
1370 LBA_t sect;
1371
1372
1373 clst -= 2; /* The first bit corresponds to cluster #2 */
1374 sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */
1375 i = clst / 8 % SS(fs); /* Byte offset in the sector */
1376 bm = 1 << (clst % 8); /* Bit mask in the byte */
1377 for (;;) {
1378 if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR;
1379 do {
1380 do {
1381 if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */
1382 fs->win[i] ^= bm; /* Flip the bit */
1383 fs->wflag = 1;
1384 if (--ncl == 0) return FR_OK; /* All bits processed? */
1385 } while (bm <<= 1); /* Next bit */
1386 bm = 1;
1387 } while (++i < SS(fs)); /* Next byte */
1388 i = 0;
1389 }
1390}
1391
1392
1393/*---------------------------------------------*/
1394/* Fill the first fragment of the FAT chain */
1395/*---------------------------------------------*/
1396
1397static FRESULT fill_first_frag (
1398 FFOBJID* obj /* Pointer to the corresponding object */
1399)
1400{
1401 FRESULT res;
1402 DWORD cl, n;
1403
1404
1405 if (obj->stat == 3) { /* Has the object been changed 'fragmented' in this session? */
1406 for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */
1407 res = put_fat(obj->fs, cl, cl + 1);
1408 if (res != FR_OK) return res;
1409 }
1410 obj->stat = 0; /* Change status 'FAT chain is valid' */
1411 }
1412 return FR_OK;
1413}
1414
1415
1416/*---------------------------------------------*/
1417/* Fill the last fragment of the FAT chain */
1418/*---------------------------------------------*/
1419
1420static FRESULT fill_last_frag (
1421 FFOBJID* obj, /* Pointer to the corresponding object */
1422 DWORD lcl, /* Last cluster of the fragment */
1423 DWORD term /* Value to set the last FAT entry */
1424)
1425{
1426 FRESULT res;
1427
1428
1429 while (obj->n_frag > 0) { /* Create the chain of last fragment */
1430 res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term);
1431 if (res != FR_OK) return res;
1432 obj->n_frag--;
1433 }
1434 return FR_OK;
1435}
1436
1437#endif /* FF_FS_EXFAT && !FF_FS_READONLY */
1438
1439
1440
1441#if !FF_FS_READONLY
1442/*-----------------------------------------------------------------------*/
1443/* FAT handling - Remove a cluster chain */
1444/*-----------------------------------------------------------------------*/
1445
1446static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */
1447 FFOBJID* obj, /* Corresponding object */
1448 DWORD clst, /* Cluster to remove a chain from */
1449 DWORD pclst /* Previous cluster of clst (0 if entire chain) */
1450)
1451{
1452 FRESULT res = FR_OK;
1453 DWORD nxt;
1454 FATFS *fs = obj->fs;
1455#if FF_FS_EXFAT || FF_USE_TRIM
1456 DWORD scl = clst, ecl = clst;
1457#endif
1458#if FF_USE_TRIM
1459 LBA_t rt[2];
1460#endif
1461
1462 if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */
1463
1464 /* Mark the previous cluster 'EOC' on the FAT if it exists */
1465 if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) {
1466 res = put_fat(fs, pclst, 0xFFFFFFFF);
1467 if (res != FR_OK) return res;
1468 }
1469
1470 /* Remove the chain */
1471 do {
1472 nxt = get_fat(obj, clst); /* Get cluster status */
1473 if (nxt == 0) break; /* Empty cluster? */
1474 if (nxt == 1) return FR_INT_ERR; /* Internal error? */
1475 if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */
1476 if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
1477 res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */
1478 if (res != FR_OK) return res;
1479 }
1480 if (fs->free_clst < fs->n_fatent - 2) { /* Update allocation information if it is valid */
1481 fs->free_clst++;
1482 fs->fsi_flag |= 1;
1483 }
1484#if FF_FS_EXFAT || FF_USE_TRIM
1485 if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
1486 ecl = nxt;
1487 } else { /* End of contiguous cluster block */
1488#if FF_FS_EXFAT
1489 if (fs->fs_type == FS_EXFAT) {
1490 res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
1491 if (res != FR_OK) return res;
1492 }
1493#endif
1494#if FF_USE_TRIM
1495 rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
1496 rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
1497 disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
1498#endif
1499 scl = ecl = nxt;
1500 }
1501#endif
1502 clst = nxt; /* Next cluster */
1503 } while (clst < fs->n_fatent); /* Repeat until the last link */
1504
1505#if FF_FS_EXFAT
1506 /* Some post processes for chain status */
1507 if (fs->fs_type == FS_EXFAT) {
1508 if (pclst == 0) { /* Has the entire chain been removed? */
1509 obj->stat = 0; /* Change the chain status 'initial' */
1510 } else {
1511 if (obj->stat == 0) { /* Is it a fragmented chain from the beginning of this session? */
1512 clst = obj->sclust; /* Follow the chain to check if it gets contiguous */
1513 while (clst != pclst) {
1514 nxt = get_fat(obj, clst);
1515 if (nxt < 2) return FR_INT_ERR;
1516 if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;
1517 if (nxt != clst + 1) break; /* Not contiguous? */
1518 clst++;
1519 }
1520 if (clst == pclst) { /* Has the chain got contiguous again? */
1521 obj->stat = 2; /* Change the chain status 'contiguous' */
1522 }
1523 } else {
1524 if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this session and got contiguous again? */
1525 obj->stat = 2; /* Change the chain status 'contiguous' */
1526 }
1527 }
1528 }
1529 }
1530#endif
1531 return FR_OK;
1532}
1533
1534
1535
1536
1537/*-----------------------------------------------------------------------*/
1538/* FAT handling - Stretch a chain or Create a new chain */
1539/*-----------------------------------------------------------------------*/
1540
1541static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
1542 FFOBJID* obj, /* Corresponding object */
1543 DWORD clst /* Cluster# to stretch, 0:Create a new chain */
1544)
1545{
1546 DWORD cs, ncl, scl;
1547 FRESULT res;
1548 FATFS *fs = obj->fs;
1549
1550
1551 if (clst == 0) { /* Create a new chain */
1552 scl = fs->last_clst; /* Suggested cluster to start to find */
1553 if (scl == 0 || scl >= fs->n_fatent) scl = 1;
1554 }
1555 else { /* Stretch a chain */
1556 cs = get_fat(obj, clst); /* Check the cluster status */
1557 if (cs < 2) return 1; /* Test for insanity */
1558 if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */
1559 if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */
1560 scl = clst; /* Cluster to start to find */
1561 }
1562 if (fs->free_clst == 0) return 0; /* No free cluster */
1563
1564#if FF_FS_EXFAT
1565 if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
1566 ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */
1567 if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */
1568 res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */
1569 if (res == FR_INT_ERR) return 1;
1570 if (res == FR_DISK_ERR) return 0xFFFFFFFF;
1571 if (clst == 0) { /* Is it a new chain? */
1572 obj->stat = 2; /* Set status 'contiguous' */
1573 } else { /* It is a stretched chain */
1574 if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */
1575 obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */
1576 obj->stat = 3; /* Change status 'just fragmented' */
1577 }
1578 }
1579 if (obj->stat != 2) { /* Is the file non-contiguous? */
1580 if (ncl == clst + 1) { /* Is the cluster next to previous one? */
1581 obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */
1582 } else { /* New fragment */
1583 if (obj->n_frag == 0) obj->n_frag = 1;
1584 res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */
1585 if (res == FR_OK) obj->n_frag = 1;
1586 }
1587 }
1588 } else
1589#endif
1590 { /* On the FAT/FAT32 volume */
1591 ncl = 0;
1592 if (scl == clst) { /* Stretching an existing chain? */
1593 ncl = scl + 1; /* Test if next cluster is free */
1594 if (ncl >= fs->n_fatent) ncl = 2;
1595 cs = get_fat(obj, ncl); /* Get next cluster status */
1596 if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */
1597 if (cs != 0) { /* Not free? */
1598 cs = fs->last_clst; /* Start at suggested cluster if it is valid */
1599 if (cs >= 2 && cs < fs->n_fatent) scl = cs;
1600 ncl = 0;
1601 }
1602 }
1603 if (ncl == 0) { /* The new cluster cannot be contiguous and find another fragment */
1604 ncl = scl; /* Start cluster */
1605 for (;;) {
1606 ncl++; /* Next cluster */
1607 if (ncl >= fs->n_fatent) { /* Check wrap-around */
1608 ncl = 2;
1609 if (ncl > scl) return 0; /* No free cluster found? */
1610 }
1611 cs = get_fat(obj, ncl); /* Get the cluster status */
1612 if (cs == 0) break; /* Found a free cluster? */
1613 if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */
1614 if (ncl == scl) return 0; /* No free cluster found? */
1615 }
1616 }
1617 res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */
1618 if (res == FR_OK && clst != 0) {
1619 res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */
1620 }
1621 }
1622
1623 if (res == FR_OK) { /* Update allocation information if the function succeeded */
1624 fs->last_clst = ncl;
1625 if (fs->free_clst > 0 && fs->free_clst <= fs->n_fatent - 2) {
1626 fs->free_clst--;
1627 fs->fsi_flag |= 1;
1628 }
1629 } else {
1630 ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */
1631 }
1632
1633 return ncl; /* Return new cluster number or error status */
1634}
1635
1636#endif /* !FF_FS_READONLY */
1637
1638
1639
1640
1641#if FF_USE_FASTSEEK
1642/*-----------------------------------------------------------------------*/
1643/* FAT handling - Convert offset into cluster with link map table */
1644/*-----------------------------------------------------------------------*/
1645
1646static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */
1647 FIL* fp, /* Pointer to the file object */
1648 FSIZE_t ofs /* File offset to be converted to cluster# */
1649)
1650{
1651 DWORD cl, ncl;
1652 DWORD *tbl;
1653 FATFS *fs = fp->obj.fs;
1654
1655
1656 tbl = fp->cltbl + 1; /* Top of CLMT */
1657 cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */
1658 for (;;) {
1659 ncl = *tbl++; /* Number of cluters in the fragment */
1660 if (ncl == 0) return 0; /* End of table? (error) */
1661 if (cl < ncl) break; /* In this fragment? */
1662 cl -= ncl; tbl++; /* Next fragment */
1663 }
1664 return cl + *tbl; /* Return the cluster number */
1665}
1666
1667#endif /* FF_USE_FASTSEEK */
1668
1669
1670
1671
1672/*-----------------------------------------------------------------------*/
1673/* Directory handling - Fill a cluster with zeros */
1674/*-----------------------------------------------------------------------*/
1675
1676#if !FF_FS_READONLY
1677static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
1678 FATFS *fs, /* Filesystem object */
1679 DWORD clst /* Directory table to clear */
1680)
1681{
1682 LBA_t sect;
1683 UINT n, szb;
1684 BYTE *ibuf;
1685
1686
1687 if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */
1688 sect = clst2sect(fs, clst); /* Top of the cluster */
1689 fs->winsect = sect; /* Set window to top of the cluster */
1690 memset(fs->win, 0, sizeof fs->win); /* Clear window buffer */
1691#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
1692 /* Allocate a temporary buffer */
1693 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) ;
1694 if (szb > SS(fs)) { /* Buffer allocated? */
1695 memset(ibuf, 0, szb);
1696 szb /= SS(fs); /* Bytes -> Sectors */
1697 for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */
1698 ff_memfree(ibuf);
1699 } else
1700#endif
1701 {
1702 ibuf = fs->win; szb = 1; /* Use window buffer (many single-sector writes may take a time) */
1703 for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */
1704 }
1705 return (n == fs->csize) ? FR_OK : FR_DISK_ERR;
1706}
1707#endif /* !FF_FS_READONLY */
1708
1709
1710
1711
1712/*-----------------------------------------------------------------------*/
1713/* Directory handling - Set directory index */
1714/*-----------------------------------------------------------------------*/
1715
1716static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
1717 DIR* dp, /* Pointer to directory object */
1718 DWORD ofs /* Offset of directory table */
1719)
1720{
1721 DWORD csz, clst;
1722 FATFS *fs = dp->obj.fs;
1723
1724
1725 if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */
1726 return FR_INT_ERR;
1727 }
1728 dp->dptr = ofs; /* Set current offset */
1729 clst = dp->obj.sclust; /* Table start cluster (0:root) */
1730 if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */
1731 clst = (DWORD)fs->dirbase;
1732 if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */
1733 }
1734
1735 if (clst == 0) { /* Static table (root-directory on the FAT volume) */
1736 if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */
1737 dp->sect = fs->dirbase;
1738
1739 } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
1740 csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */
1741 while (ofs >= csz) { /* Follow cluster chain */
1742 clst = get_fat(&dp->obj, clst); /* Get next cluster */
1743 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
1744 if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */
1745 ofs -= csz;
1746 }
1747 dp->sect = clst2sect(fs, clst);
1748 }
1749 dp->clust = clst; /* Current cluster# */
1750 if (dp->sect == 0) return FR_INT_ERR;
1751 dp->sect += ofs / SS(fs); /* Sector# of the directory entry */
1752 dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */
1753
1754 return FR_OK;
1755}
1756
1757
1758
1759
1760/*-----------------------------------------------------------------------*/
1761/* Directory handling - Move directory table index next */
1762/*-----------------------------------------------------------------------*/
1763
1764static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
1765 DIR* dp, /* Pointer to the directory object */
1766 int stretch /* 0: Do not stretch table, 1: Stretch table if needed */
1767)
1768{
1769 DWORD ofs, clst;
1770 FATFS *fs = dp->obj.fs;
1771
1772
1773 ofs = dp->dptr + SZDIRE; /* Next entry */
1774 if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset reached the max value */
1775 if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has been disabled */
1776
1777 if (ofs % SS(fs) == 0) { /* Sector changed? */
1778 dp->sect++; /* Next sector */
1779
1780 if (dp->clust == 0) { /* Static table */
1781 if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */
1782 dp->sect = 0; return FR_NO_FILE;
1783 }
1784 }
1785 else { /* Dynamic table */
1786 if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */
1787 clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */
1788 if (clst <= 1) return FR_INT_ERR; /* Internal error */
1789 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
1790 if (clst >= fs->n_fatent) { /* It reached end of dynamic table */
1791#if !FF_FS_READONLY
1792 if (!stretch) { /* If no stretch, report EOT */
1793 dp->sect = 0; return FR_NO_FILE;
1794 }
1795 clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */
1796 if (clst == 0) return FR_DENIED; /* No free cluster */
1797 if (clst == 1) return FR_INT_ERR; /* Internal error */
1798 if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
1799 if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR; /* Clean up the stretched table */
1800 if (FF_FS_EXFAT) dp->obj.stat |= 4; /* exFAT: The directory has been stretched */
1801#else
1802 if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */
1803 dp->sect = 0; return FR_NO_FILE; /* Report EOT */
1804#endif
1805 }
1806 dp->clust = clst; /* Initialize data for new cluster */
1807 dp->sect = clst2sect(fs, clst);
1808 }
1809 }
1810 }
1811 dp->dptr = ofs; /* Current entry */
1812 dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */
1813
1814 return FR_OK;
1815}
1816
1817
1818
1819
1820#if !FF_FS_READONLY
1821/*-----------------------------------------------------------------------*/
1822/* Directory handling - Reserve a block of directory entries */
1823/*-----------------------------------------------------------------------*/
1824
1825static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
1826 DIR* dp, /* Pointer to the directory object */
1827 UINT n_ent /* Number of contiguous entries to allocate */
1828)
1829{
1830 FRESULT res;
1831 UINT n;
1832 FATFS *fs = dp->obj.fs;
1833
1834
1835 res = dir_sdi(dp, 0);
1836 if (res == FR_OK) {
1837 n = 0;
1838 do {
1839 res = move_window(fs, dp->sect);
1840 if (res != FR_OK) break;
1841#if FF_FS_EXFAT
1842 if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */
1843#else
1844 if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */
1845#endif
1846 if (++n == n_ent) break; /* Is a block of contiguous free entries found? */
1847 } else {
1848 n = 0; /* Not a free entry, restart to search */
1849 }
1850 res = dir_next(dp, 1); /* Next entry with table stretch enabled */
1851 } while (res == FR_OK);
1852 }
1853
1854 if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */
1855 return res;
1856}
1857
1858#endif /* !FF_FS_READONLY */
1859
1860
1861
1862
1863/*-----------------------------------------------------------------------*/
1864/* FAT: Directory handling - Load/Store start cluster number */
1865/*-----------------------------------------------------------------------*/
1866
1867static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */
1868 FATFS* fs, /* Pointer to the fs object */
1869 const BYTE* dir /* Pointer to the key entry */
1870)
1871{
1872 DWORD cl;
1873
1874 cl = ld_16(dir + DIR_FstClusLO);
1875 if (fs->fs_type == FS_FAT32) {
1876 cl |= (DWORD)ld_16(dir + DIR_FstClusHI) << 16;
1877 }
1878
1879 return cl;
1880}
1881
1882
1883#if !FF_FS_READONLY
1884static void st_clust (
1885 FATFS* fs, /* Pointer to the fs object */
1886 BYTE* dir, /* Pointer to the key entry */
1887 DWORD cl /* Value to be set */
1888)
1889{
1890 st_16(dir + DIR_FstClusLO, (WORD)cl);
1891 if (fs->fs_type == FS_FAT32) {
1892 st_16(dir + DIR_FstClusHI, (WORD)(cl >> 16));
1893 }
1894}
1895#endif
1896
1897
1898
1899#if FF_USE_LFN
1900/*--------------------------------------------------------*/
1901/* FAT-LFN: Compare a part of file name with an LFN entry */
1902/*--------------------------------------------------------*/
1903
1904static int cmp_lfn ( /* 1:matched, 0:not matched */
1905 const WCHAR* lfnbuf, /* Pointer to the LFN to be compared */
1906 BYTE* dir /* Pointer to the LFN entry */
1907)
1908{
1909 UINT ni, di;
1910 WCHAR pchr, chr;
1911
1912
1913 if (ld_16(dir + LDIR_FstClusLO) != 0) return 0; /* Check if LDIR_FstClusLO is 0 */
1914
1915 ni = (UINT)((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the name to be compared */
1916
1917 for (pchr = 1, di = 0; di < 13; di++) { /* Process all characters in the entry */
1918 chr = ld_16(dir + LfnOfs[di]); /* Pick a character from the entry */
1919 if (pchr != 0) {
1920 if (ni >= FF_MAX_LFN + 1 || ff_wtoupper(chr) != ff_wtoupper(lfnbuf[ni++])) { /* Compare it with name */
1921 return 0; /* Not matched */
1922 }
1923 pchr = chr;
1924 } else {
1925 if (chr != 0xFFFF) return 0; /* Check filler */
1926 }
1927 }
1928
1929 if ((dir[LDIR_Ord] & LLEF) && pchr && lfnbuf[ni]) return 0; /* Last name segment matched but different length */
1930
1931 return 1; /* The part of LFN matched */
1932}
1933
1934
1935#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
1936/*-----------------------------------------------------*/
1937/* FAT-LFN: Pick a part of file name from an LFN entry */
1938/*-----------------------------------------------------*/
1939
1940static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
1941 WCHAR* lfnbuf, /* Pointer to the name buffer to be stored */
1942 const BYTE* dir /* Pointer to the LFN entry */
1943)
1944{
1945 UINT ni, di;
1946 WCHAR pchr, chr;
1947
1948
1949 if (ld_16(dir + LDIR_FstClusLO) != 0) return 0; /* Check if LDIR_FstClusLO is 0 */
1950
1951 ni = (UINT)((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the name buffer */
1952
1953 for (pchr = 1, di = 0; di < 13; di++) { /* Process all characters in the entry */
1954 chr = ld_16(dir + LfnOfs[di]); /* Pick a character from the entry */
1955 if (pchr != 0) {
1956 if (ni >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */
1957 lfnbuf[ni++] = pchr = chr; /* Store it */
1958 } else {
1959 if (chr != 0xFFFF) return 0; /* Check filler */
1960 }
1961 }
1962
1963 if (dir[LDIR_Ord] & LLEF && pchr != 0) { /* Put terminator if it is the last LFN part and not terminated */
1964 if (ni >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */
1965 lfnbuf[ni] = 0;
1966 }
1967
1968 return 1; /* The part of LFN is valid */
1969}
1970#endif
1971
1972
1973#if !FF_FS_READONLY
1974/*-----------------------------------------*/
1975/* FAT-LFN: Create an entry of LFN entries */
1976/*-----------------------------------------*/
1977
1978static void put_lfn (
1979 const WCHAR* lfn, /* Pointer to the LFN */
1980 BYTE* dir, /* Pointer to the LFN entry to be created */
1981 BYTE ord, /* LFN order (1-20) */
1982 BYTE sum /* Checksum of the corresponding SFN */
1983)
1984{
1985 UINT ni, di;
1986 WCHAR chr;
1987
1988
1989 dir[LDIR_Chksum] = sum; /* Set checksum */
1990 dir[LDIR_Attr] = AM_LFN; /* Set attribute */
1991 dir[LDIR_Type] = 0;
1992 st_16(dir + LDIR_FstClusLO, 0);
1993
1994 ni = (UINT)(ord - 1) * 13; /* Offset in the name */
1995 di = chr = 0;
1996 do { /* Fill the directory entry */
1997 if (chr != 0xFFFF) chr = lfn[ni++]; /* Get an effective character */
1998 st_16(dir + LfnOfs[di], chr); /* Set it */
1999 if (chr == 0) chr = 0xFFFF; /* Padding characters after the terminator */
2000 } while (++di < 13);
2001 if (chr == 0xFFFF || !lfn[ni]) ord |= LLEF; /* Last LFN part is the start of an enrty set */
2002 dir[LDIR_Ord] = ord; /* Set order in the entry set */
2003}
2004
2005#endif /* !FF_FS_READONLY */
2006#endif /* FF_USE_LFN */
2007
2008
2009
2010#if FF_USE_LFN && !FF_FS_READONLY
2011/*-----------------------------------------------------------------------*/
2012/* FAT-LFN: Create a Numbered SFN */
2013/*-----------------------------------------------------------------------*/
2014
2015static void gen_numname (
2016 BYTE* dst, /* Pointer to the buffer to store numbered SFN */
2017 const BYTE* src, /* Pointer to SFN in directory form */
2018 const WCHAR* lfn, /* Pointer to LFN */
2019 WORD seq /* Sequence number */
2020)
2021{
2022 BYTE ns[8], c;
2023 UINT i, j;
2024
2025
2026 memcpy(dst, src, 11); /* Prepare the SFN to be modified */
2027
2028 if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */
2029 WCHAR wc;
2030 DWORD crc_sreg = seq;
2031
2032 while (*lfn) { /* Create a CRC value as a hash of LFN */
2033 wc = *lfn++;
2034 for (i = 0; i < 16; i++) {
2035 crc_sreg = (crc_sreg << 1) + (wc & 1);
2036 wc >>= 1;
2037 if (crc_sreg & 0x10000) crc_sreg ^= 0x11021;
2038 }
2039 }
2040 seq = (WORD)crc_sreg;
2041 }
2042
2043 /* Make suffix (~ + 4-digit hexadecimal) */
2044 i = 7;
2045 do {
2046 c = (BYTE)((seq % 16) + '0'); seq /= 16;
2047 if (c > '9') c += 7;
2048 ns[i--] = c;
2049 } while (i && seq);
2050 ns[i] = '~';
2051
2052 /* Append the suffix to the SFN body */
2053 for (j = 0; j < i && dst[j] != ' '; j++) { /* Find the offset to append */
2054 if (dbc_1st(dst[j])) { /* To avoid DBC break up */
2055 if (j == i - 1) break;
2056 j++;
2057 }
2058 }
2059 do { /* Append the suffix */
2060 dst[j++] = (i < 8) ? ns[i++] : ' ';
2061 } while (j < 8);
2062}
2063#endif /* FF_USE_LFN && !FF_FS_READONLY */
2064
2065
2066
2067#if FF_USE_LFN
2068/*-----------------------------------------------------------------------*/
2069/* FAT-LFN: Calculate checksum of an SFN entry */
2070/*-----------------------------------------------------------------------*/
2071
2072static BYTE sum_sfn (
2073 const BYTE* dir /* Pointer to the SFN entry */
2074)
2075{
2076 BYTE sum = 0;
2077 UINT n = 11;
2078
2079 do {
2080 sum = (sum >> 1) + (sum << 7) + *dir++;
2081 } while (--n);
2082 return sum;
2083}
2084
2085#endif /* FF_USE_LFN */
2086
2087
2088
2089#if FF_FS_EXFAT
2090/*-----------------------------------------------------------------------*/
2091/* exFAT: Checksum */
2092/*-----------------------------------------------------------------------*/
2093
2094static WORD xdir_sum ( /* Get checksum of the directoly entry block */
2095 const BYTE* dir /* Directory entry block to be calculated */
2096)
2097{
2098 UINT i, szblk;
2099 WORD sum;
2100
2101
2102 szblk = ((UINT)dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes of the entry block */
2103 for (i = sum = 0; i < szblk; i++) {
2104 if (i == XDIR_SetSum) { /* Skip 2-byte sum field */
2105 i++;
2106 } else {
2107 sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i];
2108 }
2109 }
2110 return sum;
2111}
2112
2113
2114
2115static WORD xname_sum ( /* Get check sum (to be used as hash) of the file name */
2116 const WCHAR* name /* File name to be calculated */
2117)
2118{
2119 WCHAR chr;
2120 WORD sum = 0;
2121
2122
2123 while ((chr = *name++) != 0) {
2124 chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be up-case converted */
2125 sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF);
2126 sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8);
2127 }
2128 return sum;
2129}
2130
2131
2132#if !FF_FS_READONLY && FF_USE_MKFS
2133static DWORD xsum32 ( /* Returns 32-bit checksum */
2134 BYTE dat, /* Byte to be calculated (byte-by-byte processing) */
2135 DWORD sum /* Previous sum value */
2136)
2137{
2138 sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat;
2139 return sum;
2140}
2141#endif
2142
2143
2144
2145/*------------------------------------*/
2146/* exFAT: Get a directory entry block */
2147/*------------------------------------*/
2148
2149static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */
2150 DIR* dp /* Reading directory object pointing top of the entry block to load */
2151)
2152{
2153 FRESULT res;
2154 UINT i, sz_ent;
2155 BYTE *dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory directory entry block 85+C0+C1s */
2156
2157
2158 /* Load file-directory entry */
2159 res = move_window(dp->obj.fs, dp->sect);
2160 if (res != FR_OK) return res;
2161 if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* Invalid order? */
2162 memcpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE);
2163 sz_ent = ((UINT)dirb[XDIR_NumSec] + 1) * SZDIRE; /* Size of this entry block */
2164 if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; /* Invalid block size? */
2165
2166 /* Load stream extension entry */
2167 res = dir_next(dp, 0);
2168 if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */
2169 if (res != FR_OK) return res;
2170 res = move_window(dp->obj.fs, dp->sect);
2171 if (res != FR_OK) return res;
2172 if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* Invalid order? */
2173 memcpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE);
2174 if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; /* Invalid block size for the name? */
2175
2176 /* Load file name entries */
2177 i = 2 * SZDIRE; /* Name offset to load */
2178 do {
2179 res = dir_next(dp, 0);
2180 if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */
2181 if (res != FR_OK) return res;
2182 res = move_window(dp->obj.fs, dp->sect);
2183 if (res != FR_OK) return res;
2184 if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* Invalid order? */
2185 if (i < MAXDIRB(FF_MAX_LFN)) memcpy(dirb + i, dp->dir, SZDIRE); /* Load name entries only if the object is accessible */
2186 } while ((i += SZDIRE) < sz_ent);
2187
2188 /* Sanity check (do it for only accessible object) */
2189 if (i <= MAXDIRB(FF_MAX_LFN)) {
2190 if (xdir_sum(dirb) != ld_16(dirb + XDIR_SetSum)) return FR_INT_ERR;
2191 }
2192
2193 return FR_OK;
2194}
2195
2196
2197/*------------------------------------------------------------------*/
2198/* exFAT: Initialize object allocation info with loaded entry block */
2199/*------------------------------------------------------------------*/
2200
2201static void init_alloc_info (
2202 FFOBJID* dobj, /* Object allocation information to be initialized */
2203 DIR* sdir /* Additional source about containing direcotry */
2204)
2205{
2206 FATFS *fs = dobj->fs;
2207
2208
2209 if (sdir) { /* Initialize the containing directory. This block needs to precede the followings. */
2210 dobj->c_scl = sdir->obj.sclust;
2211 dobj->c_size = ((DWORD)sdir->obj.objsize & 0xFFFFFF00) | sdir->obj.stat;
2212 dobj->c_ofs = sdir->blk_ofs;
2213 }
2214 dobj->sclust = ld_32(fs->dirbuf + XDIR_FstClus); /* Start cluster */
2215 dobj->objsize = ld_64(fs->dirbuf + XDIR_FileSize); /* Size */
2216 dobj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */
2217 dobj->n_frag = 0; /* No last fragment info */
2218}
2219
2220
2221
2222#if !FF_FS_READONLY || FF_FS_RPATH
2223/*------------------------------------------------*/
2224/* exFAT: Load the object's directory entry block */
2225/*------------------------------------------------*/
2226
2227static FRESULT load_obj_xdir (
2228 DIR* dp, /* Blank directory object to be used to access containing directory */
2229 const FFOBJID* obj /* Object with its containing directory information */
2230)
2231{
2232 FRESULT res;
2233
2234 /* Open object containing directory */
2235 dp->obj.fs = obj->fs;
2236 dp->obj.sclust = obj->c_scl;
2237 dp->obj.stat = (BYTE)obj->c_size;
2238 dp->obj.objsize = obj->c_size & 0xFFFFFF00;
2239 dp->obj.n_frag = 0;
2240 dp->blk_ofs = obj->c_ofs;
2241
2242 res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */
2243 if (res == FR_OK) {
2244 res = load_xdir(dp); /* Load the object's entry block */
2245 }
2246 return res;
2247}
2248#endif
2249
2250
2251#if !FF_FS_READONLY
2252/*----------------------------------------*/
2253/* exFAT: Store the directory entry block */
2254/*----------------------------------------*/
2255
2256static FRESULT store_xdir (
2257 DIR* dp /* Pointer to the directory object */
2258)
2259{
2260 FRESULT res;
2261 UINT nent;
2262 BYTE *dirb = dp->obj.fs->dirbuf; /* Pointer to the entry set 85+C0+C1s */
2263
2264
2265 st_16(dirb + XDIR_SetSum, xdir_sum(dirb)); /* Create check sum */
2266
2267 /* Store the entry set to the directory */
2268 nent = dirb[XDIR_NumSec] + 1; /* Number of entries */
2269 res = dir_sdi(dp, dp->blk_ofs); /* Top of the entry set */
2270 while (res == FR_OK) {
2271 /* Set an entry to the directory */
2272 res = move_window(dp->obj.fs, dp->sect);
2273 if (res != FR_OK) break;
2274 memcpy(dp->dir, dirb, SZDIRE);
2275 dp->obj.fs->wflag = 1;
2276
2277 if (--nent == 0) break; /* All done? */
2278 dirb += SZDIRE;
2279 res = dir_next(dp, 0); /* Next entry */
2280 }
2281 return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR;
2282}
2283
2284
2285
2286/*-------------------------------------------*/
2287/* exFAT: Create a new directory entry block */
2288/*-------------------------------------------*/
2289
2290static void create_xdir (
2291 BYTE* dirb, /* Pointer to the directory entry block buffer */
2292 const WCHAR* lfn /* Pointer to the object name */
2293)
2294{
2295 UINT i;
2296 BYTE n_c1, nlen;
2297 WCHAR chr;
2298
2299
2300 /* Create file-directory and stream-extension entry (1st and 2nd entry) */
2301 memset(dirb, 0, 2 * SZDIRE);
2302 dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR;
2303 dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM;
2304
2305 /* Create file name entries (3rd enrty and follows) */
2306 i = SZDIRE * 2; /* Top of file name entries */
2307 nlen = n_c1 = 0; chr = 1;
2308 do {
2309 dirb[i++] = ET_FILENAME; dirb[i++] = 0;
2310 do { /* Fill name field */
2311 if (chr != 0 && (chr = lfn[nlen]) != 0) nlen++; /* Get a character if exist */
2312 st_16(dirb + i, chr); /* Store it */
2313 i += 2;
2314 } while (i % SZDIRE != 0);
2315 n_c1++;
2316 } while (lfn[nlen]); /* Fill next C1 entry if any char follows */
2317
2318 dirb[XDIR_NumName] = nlen; /* Set name length */
2319 dirb[XDIR_NumSec] = 1 + n_c1; /* Set secondary count (C0 + C1s) */
2320 st_16(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */
2321}
2322
2323#endif /* !FF_FS_READONLY */
2324#endif /* FF_FS_EXFAT */
2325
2326
2327
2328#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
2329/*-----------------------------------------------------------------------*/
2330/* Read an object from the directory */
2331/*-----------------------------------------------------------------------*/
2332
2333#define DIR_READ_FILE(dp) dir_read(dp, 0)
2334#define DIR_READ_LABEL(dp) dir_read(dp, 1)
2335
2336static FRESULT dir_read (
2337 DIR* dp, /* Pointer to the directory object */
2338 int vol /* Filtered by 0:file/directory or 1:volume label */
2339)
2340{
2341 FRESULT res = FR_NO_FILE;
2342 FATFS *fs = dp->obj.fs;
2343 BYTE attr, et;
2344#if FF_USE_LFN
2345 BYTE ord = 0xFF, sum = 0xFF;
2346#endif
2347
2348 while (dp->sect) {
2349 res = move_window(fs, dp->sect);
2350 if (res != FR_OK) break;
2351 et = dp->dir[DIR_Name]; /* Test for the entry type */
2352 if (et == 0) {
2353 res = FR_NO_FILE; break; /* Reached to end of the directory */
2354 }
2355#if FF_FS_EXFAT
2356 if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
2357 if (FF_USE_LABEL && vol) {
2358 if (et == ET_VLABEL) break; /* Volume label entry? */
2359 } else {
2360 if (et == ET_FILEDIR) { /* Start of the file entry block? */
2361 dp->blk_ofs = dp->dptr; /* Get location of the block */
2362 res = load_xdir(dp); /* Load the entry block */
2363 if (res == FR_OK) {
2364 dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */
2365 }
2366 break;
2367 }
2368 }
2369 } else
2370#endif
2371 { /* On the FAT/FAT32 volume */
2372 dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */
2373#if FF_USE_LFN /* LFN configuration */
2374 if (et == DDEM || et == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */
2375 ord = 0xFF;
2376 } else {
2377 if (attr == AM_LFN) { /* An LFN entry is found */
2378 if (et & LLEF) { /* Is it start of an LFN sequence? */
2379 sum = dp->dir[LDIR_Chksum];
2380 et &= (BYTE)~LLEF; ord = et;
2381 dp->blk_ofs = dp->dptr;
2382 }
2383 /* Check LFN validity and capture it */
2384 ord = (et == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
2385 } else { /* An SFN entry is found */
2386 if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */
2387 dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */
2388 }
2389 break;
2390 }
2391 }
2392#else /* Non LFN configuration */
2393 if (et != DDEM && et != '.' && attr != AM_LFN && (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */
2394 break;
2395 }
2396#endif
2397 }
2398 res = dir_next(dp, 0); /* Next entry */
2399 if (res != FR_OK) break;
2400 }
2401
2402 if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */
2403 return res;
2404}
2405
2406#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
2407
2408
2409
2410/*-----------------------------------------------------------------------*/
2411/* Directory handling - Find an object in the directory */
2412/*-----------------------------------------------------------------------*/
2413
2414static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
2415 DIR* dp /* Pointer to the directory object with the file name */
2416)
2417{
2418 FRESULT res;
2419 FATFS *fs = dp->obj.fs;
2420 BYTE et;
2421#if FF_USE_LFN
2422 BYTE attr, ord, sum;
2423#endif
2424
2425 res = dir_sdi(dp, 0); /* Rewind directory object */
2426 if (res != FR_OK) return res;
2427#if FF_FS_EXFAT
2428 if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
2429 BYTE nc;
2430 UINT di, ni;
2431 WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */
2432
2433 while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */
2434#if FF_MAX_LFN < 255
2435 if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */
2436#endif
2437 if (ld_16(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */
2438 for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */
2439 if ((di % SZDIRE) == 0) di += 2;
2440 if (ff_wtoupper(ld_16(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break;
2441 }
2442 if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */
2443 }
2444 return res;
2445 }
2446#endif
2447 /* On the FAT/FAT32 volume */
2448#if FF_USE_LFN
2449 ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
2450#endif
2451 do {
2452 res = move_window(fs, dp->sect);
2453 if (res != FR_OK) break;
2454 et = dp->dir[DIR_Name]; /* Entry type */
2455 if (et == 0) { res = FR_NO_FILE; break; } /* Reached end of directory table */
2456#if FF_USE_LFN /* LFN configuration */
2457 dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK;
2458 if (et == DDEM || ((attr & AM_VOL) && attr != AM_LFN)) { /* An entry without valid data */
2459 ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
2460 } else {
2461 if (attr == AM_LFN) { /* Is it an LFN entry? */
2462 if (!(dp->fn[NSFLAG] & NS_NOLFN)) {
2463 if (et & LLEF) { /* Is it start of an entry set? */
2464 et &= (BYTE)~LLEF;
2465 ord = et; /* Number of LFN entries */
2466 dp->blk_ofs = dp->dptr; /* Start offset of LFN */
2467 sum = dp->dir[LDIR_Chksum]; /* Sum of the SFN */
2468 }
2469 /* Check validity of the LFN entry and compare it with given name */
2470 ord = (et == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
2471 }
2472 } else { /* SFN entry */
2473 if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */
2474 if (!(dp->fn[NSFLAG] & NS_LOSS) && !memcmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */
2475 ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Not matched, reset LFN sequence */
2476 }
2477 }
2478#else /* Non LFN configuration */
2479 dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;
2480 if (!(dp->dir[DIR_Attr] & AM_VOL) && !memcmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */
2481#endif
2482 res = dir_next(dp, 0); /* Next entry */
2483 } while (res == FR_OK);
2484
2485 return res;
2486}
2487
2488
2489
2490
2491#if !FF_FS_READONLY
2492/*-----------------------------------------------------------------------*/
2493/* Register an object to the directory */
2494/*-----------------------------------------------------------------------*/
2495
2496static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
2497 DIR* dp /* Target directory with object name to be created */
2498)
2499{
2500 FRESULT res;
2501 FATFS *fs = dp->obj.fs;
2502#if FF_USE_LFN /* LFN configuration */
2503 UINT n, len, n_ent;
2504 BYTE sn[12];
2505
2506
2507 if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */
2508 for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */
2509
2510#if FF_FS_EXFAT
2511 if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
2512 n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
2513 res = dir_alloc(dp, n_ent); /* Allocate directory entries */
2514 if (res != FR_OK) return res;
2515 dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the allocated entry block offset */
2516
2517 if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */
2518 dp->obj.stat &= ~4;
2519 res = fill_first_frag(&dp->obj); /* Fill the first fragment on the FAT if needed */
2520 if (res != FR_OK) return res;
2521 res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill the last fragment on the FAT if needed */
2522 if (res != FR_OK) return res;
2523 if (dp->obj.sclust != 0) { /* Is it a sub-directory? */
2524 DIR dj;
2525
2526 res = load_obj_xdir(&dj, &dp->obj); /* Load the object status */
2527 if (res != FR_OK) return res;
2528 dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */
2529 st_64(fs->dirbuf + XDIR_FileSize, dp->obj.objsize);
2530 st_64(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);
2531 fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; /* Update the allocation status */
2532 res = store_xdir(&dj); /* Store the object status */
2533 if (res != FR_OK) return res;
2534#if FF_FS_RPATH /* Refrect changes to the current dir chain if the stretched dir is in it */
2535 for (n = 1; n <= fs->xcwds.depth && dp->obj.sclust != fs->xcwds.tbl[n].d_scl; n++) ; /* Check if the dir is in the current dir path */
2536 if (n <= fs->xcwds.depth) { /* If exist, update it */
2537 fs->xcwds.tbl[n].d_size = (DWORD)dp->obj.objsize | dp->obj.stat;
2538 }
2539#endif
2540 }
2541 }
2542
2543 create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */
2544 return FR_OK;
2545 }
2546#endif
2547 /* On the FAT/FAT32 volume */
2548 memcpy(sn, dp->fn, 12);
2549 if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */
2550 dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */
2551 for (n = 1; n < 100; n++) {
2552 gen_numname(dp->fn, sn, fs->lfnbuf, (WORD)n); /* Generate a numbered name */
2553 res = dir_find(dp); /* Check if the name collides with existing SFN */
2554 if (res != FR_OK) break;
2555 }
2556 if (n == 100) return FR_DENIED; /* Abort if too many collisions */
2557 if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */
2558 dp->fn[NSFLAG] = sn[NSFLAG];
2559 }
2560
2561 /* Create an SFN with/without LFNs. */
2562 n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */
2563 res = dir_alloc(dp, n_ent); /* Allocate entries */
2564 if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */
2565 res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE);
2566 if (res == FR_OK) {
2567 BYTE sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */
2568
2569 do { /* Store LFN entries in bottom first */
2570 res = move_window(fs, dp->sect);
2571 if (res != FR_OK) break;
2572 put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum);
2573 fs->wflag = 1;
2574 res = dir_next(dp, 0); /* Next entry */
2575 } while (res == FR_OK && --n_ent);
2576 }
2577 }
2578
2579#else /* Non LFN configuration */
2580 res = dir_alloc(dp, 1); /* Allocate an entry for SFN */
2581
2582#endif
2583
2584 /* Set SFN entry */
2585 if (res == FR_OK) {
2586 res = move_window(fs, dp->sect);
2587 if (res == FR_OK) {
2588 memset(dp->dir, 0, SZDIRE); /* Clean the entry */
2589 memcpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */
2590#if FF_USE_LFN
2591 dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put low-case flags */
2592#endif
2593 fs->wflag = 1;
2594 }
2595 }
2596
2597 return res;
2598}
2599
2600#endif /* !FF_FS_READONLY */
2601
2602
2603
2604#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
2605/*-----------------------------------------------------------------------*/
2606/* Remove an object from the directory */
2607/*-----------------------------------------------------------------------*/
2608
2609static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
2610 DIR* dp /* Directory object pointing the entry to be removed */
2611)
2612{
2613 FRESULT res;
2614 FATFS *fs = dp->obj.fs;
2615#if FF_USE_LFN /* LFN configuration */
2616 DWORD last = dp->dptr;
2617
2618 res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */
2619 if (res == FR_OK) {
2620 do {
2621 res = move_window(fs, dp->sect);
2622 if (res != FR_OK) break;
2623 if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
2624 dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */
2625 } else { /* On the FAT/FAT32 volume */
2626 dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */
2627 }
2628 fs->wflag = 1;
2629 if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */
2630 res = dir_next(dp, 0); /* Next entry */
2631 } while (res == FR_OK);
2632 if (res == FR_NO_FILE) res = FR_INT_ERR;
2633 }
2634#else /* Non LFN configuration */
2635
2636 res = move_window(fs, dp->sect);
2637 if (res == FR_OK) {
2638 dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/
2639 fs->wflag = 1;
2640 }
2641#endif
2642
2643 return res;
2644}
2645
2646#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
2647
2648
2649
2650#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
2651/*-----------------------------------------------------------------------*/
2652/* Get file information from directory entry */
2653/*-----------------------------------------------------------------------*/
2654
2655static void get_fileinfo (
2656 DIR* dp, /* Pointer to the directory object */
2657 FILINFO* fno /* Pointer to the file information to be filled */
2658)
2659{
2660 UINT si, di;
2661#if FF_USE_LFN
2662 WCHAR wc, hs;
2663 FATFS *fs = dp->obj.fs;
2664 UINT nw;
2665#else
2666 TCHAR c;
2667#endif
2668
2669
2670 fno->fname[0] = 0;
2671 if (dp->sect == 0) return; /* Exit if read pointer has reached end of directory */
2672
2673#if FF_USE_LFN /* LFN configuration */
2674#if FF_FS_EXFAT
2675 if (fs->fs_type == FS_EXFAT) { /* exFAT volume */
2676 UINT nc = 0;
2677
2678 si = SZDIRE * 2; di = 0; /* 1st C1 entry in the entry block */
2679 hs = 0;
2680 while (nc < fs->dirbuf[XDIR_NumName]) {
2681 if (si >= MAXDIRB(FF_MAX_LFN)) { /* Truncated directory block? */
2682 di = 0; break;
2683 }
2684 if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */
2685 wc = ld_16(fs->dirbuf + si); si += 2; nc++; /* Get a character */
2686 if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
2687 hs = wc; continue; /* Get low surrogate */
2688 }
2689 nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */
2690 if (nw == 0) { /* Buffer overflow or wrong char? */
2691 di = 0; break;
2692 }
2693 di += nw;
2694 hs = 0;
2695 }
2696 if (hs != 0) di = 0; /* Broken surrogate pair? */
2697 if (di == 0) fno->fname[di++] = '\?'; /* Inaccessible object name? */
2698 fno->fname[di] = 0; /* Terminate the name */
2699 fno->altname[0] = 0; /* exFAT does not support SFN */
2700
2701 fno->fattrib = fs->dirbuf[XDIR_Attr] & AM_MASKX; /* Attribute */
2702 fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_64(fs->dirbuf + XDIR_FileSize); /* Size */
2703 fno->ftime = ld_16(fs->dirbuf + XDIR_ModTime + 0); /* Last modified time */
2704 fno->fdate = ld_16(fs->dirbuf + XDIR_ModTime + 2); /* Last modified date */
2705#if FF_FS_CRTIME
2706 fno->crtime = ld_16(fs->dirbuf + XDIR_CrtTime + 0); /* Created time */
2707 fno->crdate = ld_16(fs->dirbuf + XDIR_CrtTime + 2); /* Created date */
2708#endif
2709 return;
2710 } else
2711#endif
2712 { /* FAT/FAT32 volume */
2713 if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */
2714 si = di = 0;
2715 hs = 0;
2716 while (fs->lfnbuf[si] != 0) {
2717 wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */
2718 if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
2719 hs = wc; continue; /* Get low surrogate */
2720 }
2721 nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */
2722 if (nw == 0) { /* Buffer overflow or wrong char? */
2723 di = 0; break;
2724 }
2725 di += nw;
2726 hs = 0;
2727 }
2728 if (hs != 0) di = 0; /* Broken surrogate pair? */
2729 fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */
2730 }
2731 }
2732
2733 si = di = 0;
2734 while (si < 11) { /* Get SFN from SFN entry */
2735 wc = dp->dir[si++]; /* Get a char */
2736 if (wc == ' ') continue; /* Skip padding spaces */
2737 if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */
2738 if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */
2739#if FF_LFN_UNICODE >= 1 /* Unicode output */
2740 if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */
2741 wc = wc << 8 | dp->dir[si++];
2742 }
2743 wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */
2744 if (wc == 0) { /* Wrong char in the current code page? */
2745 di = 0; break;
2746 }
2747 nw = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in API encoding */
2748 if (nw == 0) { /* Buffer overflow? */
2749 di = 0; break;
2750 }
2751 di += nw;
2752#else /* ANSI/OEM output */
2753 fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */
2754#endif
2755 }
2756 fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */
2757
2758 if (!fno->fname[0]) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
2759 if (di == 0) { /* If LFN and SFN both are invalid, */
2760 fno->fname[di++] = '\?'; /* This object is inaccessible due to wrong buffer or locale settings */
2761 } else {
2762 BYTE lcflg = NS_BODY;
2763
2764 for (si = di = 0; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */
2765 wc = (WCHAR)fno->altname[si];
2766 if (wc == '.') lcflg = NS_EXT;
2767 if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcflg)) wc += 0x20;
2768 fno->fname[di] = (TCHAR)wc;
2769 }
2770 }
2771 fno->fname[di] = 0; /* Terminate the LFN */
2772 if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */
2773 }
2774
2775#else /* Non-LFN configuration */
2776 si = di = 0;
2777 while (si < 11) { /* Copy name body and extension */
2778 c = (TCHAR)dp->dir[si++];
2779 if (c == ' ') continue; /* Skip padding spaces */
2780 if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */
2781 if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */
2782 fno->fname[di++] = c;
2783 }
2784 fno->fname[di] = 0; /* Terminate the SFN */
2785#endif
2786
2787 fno->fattrib = dp->dir[DIR_Attr] & AM_MASK; /* Attribute */
2788 fno->fsize = ld_32(dp->dir + DIR_FileSize); /* Size */
2789 fno->ftime = ld_16(dp->dir + DIR_ModTime + 0); /* Last modified time */
2790 fno->fdate = ld_16(dp->dir + DIR_ModTime + 2); /* Last Modified date */
2791#if FF_FS_CRTIME
2792 fno->crtime = ld_16(dp->dir + DIR_CrtTime + 0); /* Created time */
2793 fno->crdate = ld_16(dp->dir + DIR_CrtTime + 2); /* Created date */
2794#endif
2795}
2796
2797#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
2798
2799
2800
2801#if FF_USE_FIND && FF_FS_MINIMIZE <= 1
2802/*-----------------------------------------------------------------------*/
2803/* Pattern matching */
2804/*-----------------------------------------------------------------------*/
2805
2806#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */
2807
2808
2809static DWORD get_achar ( /* Get a character and advance ptr */
2810 const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
2811)
2812{
2813 DWORD chr;
2814
2815
2816#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
2817 chr = tchar2uni(ptr);
2818 if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */
2819 chr = ff_wtoupper(chr);
2820
2821#else /* ANSI/OEM input */
2822 chr = (BYTE)*(*ptr)++; /* Get a byte */
2823 if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */
2824#if FF_CODE_PAGE == 0
2825 if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper (SBCS extended char) */
2826#elif FF_CODE_PAGE < 900
2827 if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper (SBCS extended char) */
2828#endif
2829#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
2830 if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */
2831 chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0;
2832 }
2833#endif
2834
2835#endif
2836 return chr;
2837}
2838
2839
2840static int pattern_match ( /* 0:mismatched, 1:matched */
2841 const TCHAR* pat, /* Matching pattern */
2842 const TCHAR* nam, /* String to be tested */
2843 UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */
2844 UINT recur /* Recursion count */
2845)
2846{
2847 const TCHAR *pptr;
2848 const TCHAR *nptr;
2849 DWORD pchr, nchr;
2850 UINT sk;
2851
2852
2853 while ((skip & 0xFF) != 0) { /* Pre-skip name chars */
2854 if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
2855 skip--;
2856 }
2857 if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */
2858
2859 do {
2860 pptr = pat; nptr = nam; /* Top of pattern and name to match */
2861 for (;;) {
2862 if (*pptr == '\?' || *pptr == '*') { /* Wildcard term? */
2863 if (recur == 0) return 0; /* Too many wildcard terms? */
2864 sk = 0;
2865 do { /* Analyze the wildcard term */
2866 if (*pptr++ == '\?') {
2867 sk++;
2868 } else {
2869 sk |= 0x100;
2870 }
2871 } while (*pptr == '\?' || *pptr == '*');
2872 if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */
2873 nchr = *nptr; break; /* Branch mismatched */
2874 }
2875 pchr = get_achar(&pptr); /* Get a pattern char */
2876 nchr = get_achar(&nptr); /* Get a name char */
2877 if (pchr != nchr) break; /* Branch mismatched? */
2878 if (pchr == 0) return 1; /* Branch matched? (matched at end of both strings) */
2879 }
2880 get_achar(&nam); /* nam++ */
2881 } while (skip && nchr); /* Retry until end of name if infinite search is specified */
2882
2883 return 0;
2884}
2885
2886#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
2887
2888
2889
2890/*-----------------------------------------------------------------------*/
2891/* Pick a top segment and create the object name in directory form */
2892/*-----------------------------------------------------------------------*/
2893
2894static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
2895 DIR* dp, /* Pointer to the directory object */
2896 const TCHAR** path /* Pointer to pointer to the segment in the path string */
2897)
2898{
2899#if FF_USE_LFN /* LFN configuration */
2900 BYTE b, cf;
2901 WCHAR wc;
2902 WCHAR *lfn;
2903 const TCHAR* p;
2904 DWORD uc;
2905 UINT i, ni, si, di;
2906
2907
2908 /* Create an LFN into LFN working buffer */
2909 p = *path; lfn = dp->obj.fs->lfnbuf; di = 0;
2910 for (;;) {
2911 uc = tchar2uni(&p); /* Get a character */
2912 if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */
2913 if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */
2914 wc = (WCHAR)uc;
2915 if (wc < ' ' || IsSeparator(wc)) break; /* Break if end of the path or a separator is found */
2916 if (wc < 0x80 && strchr("*:<>|\"\?\x7F", (int)wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */
2917 if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */
2918 lfn[di++] = wc; /* Store the Unicode character */
2919 }
2920 if (wc < ' ') { /* Stopped at end of the path? */
2921 cf = NS_LAST; /* Last segment */
2922 } else { /* Stopped at a separator */
2923 while (IsSeparator(*p)) p++; /* Skip duplicated separators if exist */
2924 cf = 0; /* Next segment may follow */
2925 if (IsTerminator(*p)) cf = NS_LAST; /* Ignore terminating separator */
2926 }
2927 *path = p; /* Return pointer to the next segment */
2928
2929#if FF_FS_RPATH
2930 if ((di == 1 && lfn[di - 1] == '.') ||
2931 (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */
2932 lfn[di] = 0;
2933 for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */
2934 dp->fn[i] = (i < di) ? '.' : ' ';
2935 }
2936 dp->fn[i] = cf | NS_DOT; /* This is a dot entry */
2937 return FR_OK;
2938 }
2939#endif
2940 while (di) { /* Snip off trailing spaces and dots if exist */
2941 wc = lfn[di - 1];
2942 if (wc != ' ' && wc != '.') break;
2943 di--;
2944 }
2945 lfn[di] = 0; /* LFN is created into the working buffer */
2946 if (di == 0) return FR_INVALID_NAME; /* Reject null name */
2947
2948 /* Create SFN in directory form */
2949 for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */
2950 if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */
2951 while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */
2952
2953 memset(dp->fn, ' ', 11);
2954 i = b = 0; ni = 8;
2955 for (;;) {
2956 wc = lfn[si++]; /* Get an LFN character */
2957 if (wc == 0) break; /* Break on end of the LFN */
2958 if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */
2959 cf |= NS_LOSS | NS_LFN;
2960 continue;
2961 }
2962
2963 if (i >= ni || si == di) { /* End of field? */
2964 if (ni == 11) { /* Name extension overflow? */
2965 cf |= NS_LOSS | NS_LFN;
2966 break;
2967 }
2968 if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */
2969 if (si > di) break; /* No name extension? */
2970 si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */
2971 continue;
2972 }
2973
2974 if (wc >= 0x80) { /* Is this an extended character? */
2975 cf |= NS_LFN; /* LFN entry needs to be created */
2976#if FF_CODE_PAGE == 0
2977 if (ExCvt) { /* In SBCS cfg */
2978 wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
2979 if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
2980 } else { /* In DBCS cfg */
2981 wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-convert ==> ANSI/OEM code */
2982 }
2983#elif FF_CODE_PAGE < 900 /* In SBCS cfg */
2984 wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
2985 if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
2986#else /* In DBCS cfg */
2987 wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-convert ==> ANSI/OEM code */
2988#endif
2989 }
2990
2991 if (wc >= 0x100) { /* Is this a DBC? */
2992 if (i >= ni - 1) { /* Field overflow? */
2993 cf |= NS_LOSS | NS_LFN;
2994 i = ni; continue; /* Next field */
2995 }
2996 dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */
2997 } else { /* SBC */
2998 if (wc == 0 || strchr("+,;=[]", (int)wc)) { /* Replace illegal characters for SFN */
2999 wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
3000 } else {
3001 if (IsUpper(wc)) { /* ASCII upper case? */
3002 b |= 2;
3003 }
3004 if (IsLower(wc)) { /* ASCII lower case? */
3005 b |= 1; wc -= 0x20;
3006 }
3007 }
3008 }
3009 dp->fn[i++] = (BYTE)wc;
3010 }
3011
3012 if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */
3013
3014 if (ni == 8) b <<= 2; /* Shift capital flags if no extension */
3015 if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */
3016 if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
3017 if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */
3018 if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */
3019 }
3020
3021 dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */
3022
3023 return FR_OK;
3024
3025
3026#else /* FF_USE_LFN : Non-LFN configuration */
3027 BYTE c, d;
3028 BYTE *sfn;
3029 UINT ni, si, i;
3030 const char *p;
3031
3032 /* Create file name in directory form */
3033 p = *path; sfn = dp->fn;
3034 memset(sfn, ' ', 11);
3035 si = i = 0; ni = 8;
3036#if FF_FS_RPATH
3037 if (p[si] == '.') { /* Is this a dot entry? */
3038 for (;;) { /* Copy one or two dots */
3039 c = (BYTE)p[si++];
3040 if (c != '.' || si >= 3) break;
3041 sfn[i++] = c;
3042 }
3043 if (IsSeparator(c)) {
3044 while (IsSeparator(p[si])) si++; /* Skip duplicated separators */
3045 if ((BYTE)p[si] <= ' ') c = 0; /* Terminate if no segment follows */
3046 } else if (c > ' ') { /* Not in dot name */
3047 return FR_INVALID_NAME;
3048 }
3049 *path = p + si; /* Return pointer to the next segment */
3050 sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */
3051 return FR_OK;
3052 }
3053#endif
3054 for (;;) {
3055 c = (BYTE)p[si++]; /* Get a byte */
3056 if (c <= ' ') break; /* Break if end of the path name */
3057 if (IsSeparator(c)) { /* Break if a separator is found */
3058 while (IsSeparator(p[si])) si++; /* Skip duplicated separators */
3059 break;
3060 }
3061 if (c == '.' || i >= ni) { /* End of body or field overflow? */
3062 if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */
3063 i = 8; ni = 11; /* Enter file extension field */
3064 continue;
3065 }
3066#if FF_CODE_PAGE == 0
3067 if (ExCvt && c >= 0x80) { /* Is SBC extended character? */
3068 c = ExCvt[c & 0x7F]; /* To upper SBC extended character */
3069 }
3070#elif FF_CODE_PAGE < 900
3071 if (c >= 0x80) { /* Is SBC extended character? */
3072 c = ExCvt[c & 0x7F]; /* To upper SBC extended character */
3073 }
3074#endif
3075 if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */
3076 d = (BYTE)p[si++]; /* Get 2nd byte */
3077 if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */
3078 sfn[i++] = c;
3079 sfn[i++] = d;
3080 } else { /* SBC */
3081 if (strchr("*+,:;<=>[]|\"\?\x7F", (int)c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */
3082 if (IsLower(c)) c -= 0x20; /* To upper */
3083 sfn[i++] = c;
3084 }
3085 }
3086 *path = &p[si]; /* Return pointer to the next segment */
3087 if (i == 0) return FR_INVALID_NAME; /* Reject nul string */
3088
3089 if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */
3090 sfn[NSFLAG] = (c <= ' ' || p[si] <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */
3091
3092 return FR_OK;
3093#endif /* FF_USE_LFN */
3094}
3095
3096
3097
3098
3099/*-----------------------------------------------------------------------*/
3100/* Follow a file path */
3101/*-----------------------------------------------------------------------*/
3102
3103static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
3104 DIR* dp, /* Directory object to return last directory and found object */
3105 const TCHAR* path /* Full-path string to find a file or directory */
3106)
3107{
3108 FRESULT res;
3109 BYTE ns;
3110 FATFS *fs = dp->obj.fs;
3111
3112
3113 /* Determins the start directory (current directory or forced root directory) */
3114#if FF_FS_RPATH
3115 if (!IsSeparator(*path) && (FF_STR_VOLUME_ID != 2 || !IsTerminator(*path))) { /* Without heading separator */
3116 dp->obj.sclust = fs->cdir; /* Start at the current directory */
3117 } else
3118#endif
3119 { /* With heading separator */
3120 while (IsSeparator(*path)) path++; /* Strip heading separators */
3121 dp->obj.sclust = 0; /* Start at the root directory */
3122 }
3123
3124#if FF_FS_EXFAT
3125 dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */
3126#if FF_FS_RPATH
3127 if (fs->fs_type == FS_EXFAT) { /* exFAT: Retrieve the start-directory's status */
3128 if (dp->obj.sclust) { /* Start directory is a sub-directory */
3129 /* Load the current directory chain into working buffer and initialize directory object as current dir */
3130 memcpy(&fs->xcwds2, &fs->xcwds, sizeof fs->xcwds2);
3131 dp->obj.stat = (BYTE)fs->xcwds2.tbl[fs->xcwds2.depth].d_size;
3132 dp->obj.objsize = fs->xcwds2.tbl[fs->xcwds2.depth].d_size & 0xFFFFFF00;
3133 dp->obj.c_scl = fs->xcwds2.tbl[fs->xcwds2.depth - 1].d_scl;
3134 dp->obj.c_size = fs->xcwds2.tbl[fs->xcwds2.depth - 1].d_size & 0xFFFFFF00;
3135 dp->obj.c_ofs = fs->xcwds2.tbl[fs->xcwds2.depth - 1].nxt_ofs;
3136 } else { /* Start directory is the root directory */
3137 /* Clear the directory path working buffer as root directory */
3138 memset(&fs->xcwds2, 0, sizeof fs->xcwds2);
3139 }
3140 }
3141#endif
3142#endif
3143
3144 if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */
3145 dp->fn[NSFLAG] = NS_NONAME;
3146 res = dir_sdi(dp, 0);
3147
3148 } else { /* Follow path */
3149 for (;;) {
3150 res = create_name(dp, &path); /* Get a segment name of the path */
3151 if (res != FR_OK) break;
3152 ns = dp->fn[NSFLAG];
3153#if FF_FS_EXFAT && FF_FS_RPATH
3154 if (fs->fs_type == FS_EXFAT && (ns & NS_DOT)) { /* Is it a dot name? */
3155 /* There is no dot entry in exFAT volume, so it needs to follow the parent directory with recorded path */
3156 if (fs->lfnbuf[1] == '.' && fs->xcwds2.depth) { /* ".." in the sub-dir? */
3157 fs->xcwds2.depth--; /* Get into the parent directory and load the directory info */
3158 dp->obj.sclust = fs->xcwds2.tbl[fs->xcwds2.depth].d_scl;
3159 dp->obj.stat = (BYTE)fs->xcwds2.tbl[fs->xcwds2.depth].d_size;
3160 dp->obj.objsize = fs->xcwds2.tbl[fs->xcwds2.depth].d_size & 0xFFFFFF00;
3161 if (fs->xcwds2.depth) { /* Load containing dir info if needed */
3162 dp->obj.c_scl = fs->xcwds2.tbl[fs->xcwds2.depth - 1].d_scl;
3163 dp->obj.c_size = fs->xcwds2.tbl[fs->xcwds2.depth - 1].d_size;
3164 dp->obj.c_ofs = fs->xcwds2.tbl[fs->xcwds2.depth - 1].nxt_ofs;
3165 }
3166 }
3167 dp->obj.attr |= AM_DIR; /* This is a directory */
3168 dp->fn[NSFLAG] |= NS_NONAME; /* but dot names in exFAT volume are not directory entry */
3169 if (ns & NS_LAST) break; /* Last segment? */
3170 continue; /* Follow next segment */
3171 }
3172#endif
3173 res = dir_find(dp); /* Find an object with the segment name */
3174 if (res != FR_OK) { /* Failed to find the object */
3175 if (res == FR_NO_FILE) { /* Object is not found */
3176 if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there (may be root dir in FAT volume) */
3177 if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */
3178 dp->fn[NSFLAG] = NS_NONAME;
3179 res = FR_OK;
3180 } else { /* Could not find the object */
3181 if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */
3182 }
3183 }
3184 break;
3185 }
3186#if FF_FS_EXFAT && FF_FS_RPATH
3187 if (fs->fs_type == FS_EXFAT && (dp->obj.attr & AM_DIR)) { /* Record the path if it is a sub-directory */
3188 fs->xcwds2.tbl[fs->xcwds2.depth].nxt_ofs = dp->blk_ofs;
3189 if (++fs->xcwds2.depth >= sizeof fs->xcwds2.tbl / sizeof fs->xcwds2.tbl[0]) { /* Is it too deep path? */
3190 res = FR_NOT_ENOUGH_CORE; break;
3191 }
3192 fs->xcwds2.tbl[fs->xcwds2.depth].d_scl = ld_32(fs->dirbuf + XDIR_FstClus);
3193 fs->xcwds2.tbl[fs->xcwds2.depth].d_size = ld_32(fs->dirbuf + XDIR_FileSize) | (fs->dirbuf[XDIR_GenFlags] & 2);
3194 }
3195#endif
3196 if (ns & NS_LAST) break; /* If last segment matched, the function completed */
3197 /* Get into the sub-directory */
3198 if (!(dp->obj.attr & AM_DIR)) {
3199 res = FR_NO_PATH; break; /* It is not a sub-directory and cannot follow the path */
3200 }
3201#if FF_FS_EXFAT
3202 if (fs->fs_type == FS_EXFAT) {
3203 init_alloc_info(&dp->obj, dp); /* Open next directory */
3204 } else
3205#endif
3206 {
3207 dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */
3208 }
3209 }
3210 }
3211
3212 return res;
3213}
3214
3215
3216
3217
3218/*-----------------------------------------------------------------------*/
3219/* Get logical drive number from path name */
3220/*-----------------------------------------------------------------------*/
3221
3222static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
3223 const TCHAR** path /* Pointer to pointer to the path name */
3224)
3225{
3226 const TCHAR *tp;
3227 const TCHAR *tt;
3228 TCHAR chr;
3229 int i;
3230#if FF_STR_VOLUME_ID /* Find string volume ID */
3231 const char *vsp;
3232 char vchr;
3233#endif
3234
3235 tt = tp = *path;
3236 if (!tp) return -1; /* Invalid path name? */
3237 do { /* Find a colon in the path */
3238 chr = *tt++;
3239 } while (!IsTerminator(chr) && chr != ':');
3240
3241 if (chr == ':') { /* Is there a DOS/Windows style volume ID? */
3242 i = FF_VOLUMES;
3243 if (IsDigit(*tp) && tp + 2 == tt) { /* Is it a numeric volume ID + colon? */
3244 i = (int)*tp - '0'; /* Get the logical drive number */
3245 }
3246#if FF_STR_VOLUME_ID == 1 /* Arbitrary string volume ID is enabled */
3247 else {
3248 i = 0; /* Find volume ID string in the preconfigured table */
3249 do {
3250 vsp = VolumeStr[i]; tp = *path; /* Preconfigured string and path name to test */
3251 do { /* Compare the volume ID with path name in case-insensitive */
3252 vchr = *vsp++; chr = *tp++;
3253 if (IsLower(vchr)) vchr -= 0x20;
3254 if (IsLower(chr)) chr -= 0x20;
3255 } while (vchr && (TCHAR)vchr == chr);
3256 } while ((vchr || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */
3257 }
3258#endif
3259 if (i >= FF_VOLUMES) return -1; /* Not found or invalid volume ID */
3260 *path = tt; /* Snip the drive prefix off */
3261 return i; /* Return the found drive number */
3262 }
3263#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
3264 if (*tp == '/') { /* Is there a volume ID? */
3265 while (*(tp + 1) == '/') tp++; /* Skip duplicated separator */
3266 i = 0;
3267 do {
3268 vsp = VolumeStr[i]; tt = tp; /* Preconfigured string and path name to test */
3269 do { /* Compare the volume ID with path name in case-insensitive */
3270 vchr = *vsp++; chr = *(++tt);
3271 if (IsLower(vchr)) vchr -= 0x20;
3272 if (IsLower(chr)) chr -= 0x20;
3273 } while (vchr && (TCHAR)vchr == chr);
3274 } while ((vchr || (chr != '/' && !IsTerminator(chr))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */
3275 if (i >= FF_VOLUMES) return -1; /* Not found (invalid volume ID) */
3276 *path = tt; /* Snip the node name off */
3277 return i; /* Return the found drive number */
3278 }
3279#endif
3280 /* No drive prefix */
3281#if FF_FS_RPATH
3282 return (int)CurrVol; /* Default drive is current drive */
3283#else
3284 return 0; /* Default drive is 0 */
3285#endif
3286}
3287
3288
3289
3290
3291/*-----------------------------------------------------------------------*/
3292/* GPT support functions */
3293/*-----------------------------------------------------------------------*/
3294
3295#if FF_LBA64
3296
3297/* Calculate CRC32 in byte-by-byte */
3298
3299static DWORD crc32 ( /* Returns next CRC value */
3300 DWORD crc, /* Current CRC value */
3301 BYTE d /* A byte to be processed */
3302)
3303{
3304 BYTE b;
3305
3306
3307 for (b = 1; b; b <<= 1) {
3308 crc ^= (d & b) ? 1 : 0;
3309 crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1;
3310 }
3311 return crc;
3312}
3313
3314
3315/* Check validity of GPT header */
3316
3317static int test_gpt_header ( /* 0:Invalid, 1:Valid */
3318 const BYTE* gpth /* Pointer to the GPT header */
3319)
3320{
3321 UINT i;
3322 DWORD bcc, hlen;
3323
3324
3325 if (memcmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1", 12)) return 0; /* Check signature and version (1.0) */
3326 hlen = ld_32(gpth + GPTH_Size); /* Check header size */
3327 if (hlen < 92 || hlen > FF_MIN_SS) return 0;
3328 for (i = 0, bcc = 0xFFFFFFFF; i < hlen; i++) { /* Check header BCC */
3329 bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]);
3330 }
3331 if (~bcc != ld_32(gpth + GPTH_Bcc)) return 0;
3332 if (ld_32(gpth + GPTH_PteSize) != SZ_GPTE) return 0; /* Table entry size (must be SZ_GPTE bytes) */
3333 if (ld_32(gpth + GPTH_PtNum) > 128) return 0; /* Table size (must be 128 entries or less) */
3334
3335 return 1;
3336}
3337
3338#if !FF_FS_READONLY && FF_USE_MKFS
3339
3340/* Generate a random value */
3341static DWORD make_rand ( /* Returns a seed value for next */
3342 DWORD seed, /* Seed value */
3343 BYTE *buff, /* Output buffer */
3344 UINT n /* Data length */
3345)
3346{
3347 UINT r;
3348
3349
3350 if (seed == 0) seed = 1;
3351 do {
3352 for (r = 0; r < 8; r++) seed = seed & 1 ? seed >> 1 ^ 0xA3000000 : seed >> 1; /* Shift 8 bits the 32-bit LFSR */
3353 *buff++ = (BYTE)seed;
3354 } while (--n);
3355 return seed;
3356}
3357
3358#endif
3359#endif
3360
3361
3362
3363/*-----------------------------------------------------------------------*/
3364/* Load a sector and check if it is an FAT VBR */
3365/*-----------------------------------------------------------------------*/
3366
3367/* Check what the sector is */
3368
3369static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
3370 FATFS* fs, /* Filesystem object */
3371 LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */
3372)
3373{
3374 WORD w, sign;
3375 BYTE b;
3376
3377
3378 fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */
3379 if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */
3380 sign = ld_16(fs->win + BS_55AA);
3381#if FF_FS_EXFAT
3382 if (sign == 0xAA55 && !memcmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */
3383#endif
3384 b = fs->win[BS_JmpBoot];
3385 if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */
3386 if (sign == 0xAA55 && !memcmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) {
3387 return 0; /* It is an FAT32 VBR */
3388 }
3389 /* FAT volumes created in the early MS-DOS era lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */
3390 w = ld_16(fs->win + BPB_BytsPerSec);
3391 b = fs->win[BPB_SecPerClus];
3392 if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS /* Properness of sector size (512-4096 and 2^n) */
3393 && b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size (2^n) */
3394 && ld_16(fs->win + BPB_RsvdSecCnt) != 0 /* Properness of number of reserved sectors (MNBZ) */
3395 && (UINT)fs->win[BPB_NumFATs] - 1 <= 1 /* Properness of number of FATs (1 or 2) */
3396 && ld_16(fs->win + BPB_RootEntCnt) != 0 /* Properness of root dir size (MNBZ) */
3397 && (ld_16(fs->win + BPB_TotSec16) >= 128 || ld_32(fs->win + BPB_TotSec32) >= 0x10000) /* Properness of volume size (>=128) */
3398 && ld_16(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size (MNBZ) */
3399 return 0; /* It can be presumed an FAT VBR */
3400 }
3401 }
3402 return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (with valid or invalid BS) */
3403}
3404
3405
3406/* Find an FAT volume */
3407/* (It supports only generic partitioning rules, MBR, GPT and SFD) */
3408
3409static UINT find_volume ( /* Returns BS status found in the hosting drive */
3410 FATFS* fs, /* Filesystem object */
3411 UINT part /* Partition to fined = 0:find as SFD and partitions, >0:forced partition number */
3412)
3413{
3414 UINT fmt, i;
3415 DWORD mbr_pt[4];
3416
3417
3418 fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD format */
3419#if !FF_FS_MBR
3420 return fmt; /* No MBR available to check the partitioning */
3421#endif
3422 if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error */
3423
3424 /* Sector 0 is not an FAT VBR or forced partition number wants a partitioned drive */
3425
3426#if FF_LBA64
3427 if (fs->win[MBR_Table + PTE_System] == 0xEE) { /* GPT protective MBR? */
3428 DWORD n_ent, v_ent, ofs;
3429 QWORD pt_lba;
3430
3431 if (move_window(fs, 1) != FR_OK) return 4; /* Load GPT header sector (next to MBR) */
3432 if (!test_gpt_header(fs->win)) return 3; /* Check if GPT header is valid */
3433 n_ent = ld_32(fs->win + GPTH_PtNum); /* Number of entries */
3434 pt_lba = ld_64(fs->win + GPTH_PtOfs); /* Table location */
3435 for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT partition */
3436 if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != FR_OK) return 4; /* PT sector */
3437 ofs = i * SZ_GPTE % SS(fs); /* Offset in the sector */
3438 if (!memcmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */
3439 v_ent++; /* Order of MS BDP */
3440 fmt = check_fs(fs, ld_64(fs->win + ofs + GPTE_FstLba)); /* Load VBR and check status */
3441 if (part == 0 && fmt <= 1) return fmt; /* Auto search (valid FAT volume found first) */
3442 if (part != 0 && v_ent == part) return fmt; /* Forced partition order (regardless of it is valid or not) */
3443 }
3444 }
3445 return 3; /* Not found */
3446 }
3447#endif
3448 if (FF_MULTI_PARTITION && part > 4) return 3; /* MBR has four primary partitions max (FatFs does not support logical partition) */
3449 for (i = 0; i < 4; i++) { /* Load partition offset in the MBR */
3450 mbr_pt[i] = ld_32(fs->win + MBR_Table + i * SZ_PTE + PTE_StLba);
3451 }
3452 i = part ? part - 1 : 0; /* Table index to find first */
3453 do { /* Find an FAT volume */
3454 fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; /* Check if the partition is FAT */
3455 } while (part == 0 && fmt >= 2 && ++i < 4);
3456 return fmt;
3457}
3458
3459
3460
3461
3462/*-----------------------------------------------------------------------*/
3463/* Determine logical drive number and mount the volume if needed */
3464/*-----------------------------------------------------------------------*/
3465
3466static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
3467 const TCHAR** path, /* Pointer to pointer to the path name (drive number) */
3468 FATFS** rfs, /* Pointer to pointer to the found filesystem object */
3469 BYTE mode /* Desiered access mode to check write protection */
3470)
3471{
3472 int vol;
3473 FATFS *fs;
3474 DSTATUS stat;
3475 LBA_t bsect;
3476 UINT fmt;
3477
3478
3479 /* Get logical drive number */
3480 *rfs = 0;
3481 vol = get_ldnumber(path);
3482 if (vol < 0) return FR_INVALID_DRIVE;
3483
3484 /* Check if the filesystem object is valid or not */
3485 fs = FatFs[vol]; /* Get pointer to the filesystem object */
3486 if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */
3487#if FF_FS_REENTRANT
3488 if (!lock_volume(fs, 1)) return FR_TIMEOUT; /* Lock the volume, and system if needed */
3489#endif
3490 *rfs = fs; /* Return pointer to the filesystem object */
3491
3492 mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */
3493 if (fs->fs_type != 0) { /* If the volume has been mounted */
3494 stat = disk_status(fs->pdrv);
3495 if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
3496 if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */
3497 return FR_WRITE_PROTECTED;
3498 }
3499 return FR_OK; /* The filesystem object is already valid */
3500 }
3501 }
3502
3503 /* The filesystem object is not valid. */
3504 /* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */
3505
3506 fs->fs_type = 0; /* Invalidate the filesystem object */
3507 stat = disk_initialize(fs->pdrv); /* Initialize the volume hosting physical drive */
3508 if (stat & STA_NOINIT) { /* Check if the initialization succeeded */
3509 return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */
3510 }
3511 if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */
3512 return FR_WRITE_PROTECTED;
3513 }
3514#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
3515 if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;
3516 if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
3517#endif
3518
3519 /* Find an FAT volume on the hosting drive */
3520 fmt = find_volume(fs, LD2PT(vol));
3521 u64 winsect = fs->winsect;
3522 DEBUG_U64_2XU32(winsect);
3523 M_DEBUG("check_fs returned %d at LBA 0x%08x%08x\n", fmt, winsect_u32[1], winsect_u32[0]);
3524 if (fmt == 4) return FR_DISK_ERR; /* An error occurred in the disk I/O layer */
3525 if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */
3526 bsect = winsect; /* Volume offset in the hosting physical drive */
3527
3528 /* An FAT volume is found (bsect). Following code initializes the filesystem object */
3529
3530#if FF_FS_EXFAT
3531 if (fmt == 1) {
3532 QWORD maxlba;
3533 DWORD so, cv, bcl, ncl, i;
3534
3535 for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
3536 if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
3537
3538 if (ld_16(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */
3539
3540 if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
3541 return FR_NO_FILESYSTEM;
3542 }
3543
3544 maxlba = ld_64(fs->win + BPB_TotSecEx) + bsect; /* Last LBA of the volume + 1 */
3545 if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be accessed in 32-bit LBA) */
3546
3547 fs->fsize = ld_32(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */
3548
3549 fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */
3550 if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */
3551
3552 fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */
3553 if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768 sectors) */
3554
3555 ncl = ld_32(fs->win + BPB_NumClusEx); /* Number of clusters */
3556 if (ncl > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */
3557 fs->n_fatent = ncl + 2;
3558
3559 /* Boundaries and Limits */
3560 fs->volbase = bsect;
3561 fs->database = bsect + ld_32(fs->win + BPB_DataOfsEx);
3562 fs->fatbase = bsect + ld_32(fs->win + BPB_FatOfsEx);
3563 if (maxlba < (QWORD)fs->database + ncl * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size required) */
3564 fs->dirbase = ld_32(fs->win + BPB_RootClusEx);
3565
3566 /* Get bitmap location and check if it is contiguous (implementation assumption) */
3567 so = i = 0;
3568 for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */
3569 if (i == 0) {
3570 if (so >= fs->csize) return FR_NO_FILESYSTEM; /* Not found? */
3571 if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR;
3572 so++;
3573 }
3574 if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */
3575 i = (i + SZDIRE) % SS(fs); /* Next entry */
3576 }
3577 bcl = ld_32(fs->win + i + 20); /* Bitmap cluster */
3578 if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; /* (Wrong cluster#) */
3579 fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */
3580 for (;;) { /* Check if bitmap is contiguous */
3581 if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR;
3582 cv = ld_32(fs->win + bcl % (SS(fs) / 4) * 4);
3583 if (cv == 0xFFFFFFFF) break; /* Last link? */
3584 if (cv != ++bcl) return FR_NO_FILESYSTEM; /* Fragmented bitmap? */
3585 }
3586#if !FF_FS_READONLY
3587 fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Invalidate cluster allocation information */
3588 fs->fsi_flag = 0; /* Enable to sync PercInUse value in VBR */
3589#endif
3590 fmt = FS_EXFAT; /* FAT sub-type */
3591 } else
3592#endif /* FF_FS_EXFAT */
3593 {
3594 DWORD tsect, sysect, fasize, nclst, szbfat;
3595 WORD nrsv;
3596
3597 if (ld_16(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */
3598
3599 fasize = ld_16(fs->win + BPB_FATSz16); /* Number of sectors per FAT */
3600 if (fasize == 0) fasize = ld_32(fs->win + BPB_FATSz32);
3601 fs->fsize = fasize;
3602
3603 fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */
3604 if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
3605 fasize *= fs->n_fats; /* Number of sectors for FAT area */
3606
3607 fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */
3608 if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */
3609
3610 fs->n_rootdir = ld_16(fs->win + BPB_RootEntCnt); /* Number of root directory entries */
3611 if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */
3612
3613 tsect = ld_16(fs->win + BPB_TotSec16); /* Number of sectors on the volume */
3614 if (tsect == 0) tsect = ld_32(fs->win + BPB_TotSec32);
3615
3616 nrsv = ld_16(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */
3617 if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */
3618
3619 /* Determine the FAT sub type */
3620 sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */
3621 if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
3622 nclst = (tsect - sysect) / fs->csize; /* Number of clusters */
3623 if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
3624 fmt = 0;
3625 if (nclst <= MAX_FAT32) fmt = FS_FAT32;
3626 if (nclst <= MAX_FAT16) fmt = FS_FAT16;
3627 if (nclst <= MAX_FAT12) fmt = FS_FAT12;
3628 if (fmt == 0) return FR_NO_FILESYSTEM;
3629
3630 /* Boundaries and Limits */
3631 fs->n_fatent = nclst + 2; /* Number of FAT entries */
3632 fs->volbase = bsect; /* Volume start sector */
3633 fs->fatbase = bsect + nrsv; /* FAT start sector */
3634 fs->database = bsect + sysect; /* Data start sector */
3635 if (fmt == FS_FAT32) {
3636 if (ld_16(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */
3637 if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
3638 fs->dirbase = ld_32(fs->win + BPB_RootClus32); /* Root directory start cluster */
3639 szbfat = fs->n_fatent * 4; /* (Needed FAT size) */
3640 } else {
3641 if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
3642 fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
3643 szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */
3644 fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
3645 }
3646 if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */
3647
3648#if !FF_FS_READONLY
3649 /* Get FSInfo if available */
3650 fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Invalidate cluster allocation information */
3651 fs->fsi_flag = 0x80; /* Disable FSInfo by default */
3652 if (fmt == FS_FAT32
3653 && ld_16(fs->win + BPB_FSInfo32) == 1 /* FAT32: Enable FSInfo feature only if FSInfo sector is next to VBR */
3654 && move_window(fs, bsect + 1) == FR_OK)
3655 {
3656 fs->fsi_flag = 0;
3657 if ( ld_32(fs->win + FSI_LeadSig) == 0x41615252 /* Load FSInfo data if available */
3658 && ld_32(fs->win + FSI_StrucSig) == 0x61417272
3659 && ld_32(fs->win + FSI_TrailSig) == 0xAA550000)
3660 {
3661#if (FF_FS_NOFSINFO & 1) == 0 /* Get free cluster count if trust it */
3662 fs->free_clst = ld_32(fs->win + FSI_Free_Count);
3663#endif
3664#if (FF_FS_NOFSINFO & 2) == 0 /* Get next free cluster if rtust it */
3665 fs->last_clst = ld_32(fs->win + FSI_Nxt_Free);
3666#endif
3667 }
3668 }
3669#endif /* !FF_FS_READONLY */
3670 }
3671
3672 fs->fs_type = (BYTE)fmt;/* FAT sub-type (the filesystem object gets valid) */
3673 fs->id = ++Fsid; /* Volume mount ID */
3674
3675#if FF_USE_LFN == 1 /* Initilize pointers to the static working buffers */
3676 fs->lfnbuf = LfnBuf; /* LFN working buffer */
3677#if FF_FS_EXFAT
3678 fs->dirbuf = DirBuf; /* Directory block scratchpad buuffer */
3679#endif
3680#endif
3681
3682#if FF_FS_RPATH /* Set the current directory top layer (root) */
3683 fs->cdir = 0;
3684#if FF_FS_EXFAT
3685 memset(&fs->xcwds, 0, sizeof fs->xcwds);
3686#endif
3687#endif
3688
3689#if FF_FS_LOCK /* Clear file lock semaphores */
3690 clear_share(fs);
3691#endif
3692
3693 return FR_OK;
3694}
3695
3696
3697
3698
3699/*-----------------------------------------------------------------------*/
3700/* Check if the file/directory object is valid or not */
3701/*-----------------------------------------------------------------------*/
3702
3703static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
3704 FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR structure, to check validity */
3705 FATFS** rfs /* Pointer to pointer to the owner filesystem object to return */
3706)
3707{
3708 FRESULT res = FR_INVALID_OBJECT;
3709
3710
3711 if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */
3712#if FF_FS_REENTRANT
3713 if (lock_volume(obj->fs, 0)) { /* Take a grant to access the volume */
3714 if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the hosting physical drive is kept initialized */
3715 res = FR_OK;
3716 } else {
3717 unlock_volume(obj->fs, FR_OK); /* Invalidated volume, abort to access */
3718 }
3719 } else { /* Could not take */
3720 res = FR_TIMEOUT;
3721 }
3722#else
3723 if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the hosting physical drive is kept initialized */
3724 res = FR_OK;
3725 }
3726#endif
3727 }
3728 *rfs = (res == FR_OK) ? obj->fs : 0; /* Return corresponding filesystem object if it is valid */
3729 return res;
3730}
3731
3732
3733
3734
3735/*---------------------------------------------------------------------------
3736
3737 Public Functions (FatFs API)
3738
3739----------------------------------------------------------------------------*/
3740
3741
3742
3743/*-----------------------------------------------------------------------*/
3744/* API: Mount/Unmount a Logical Drive */
3745/*-----------------------------------------------------------------------*/
3746
3747FRESULT f_mount (
3748 FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/
3749 const TCHAR* path, /* Logical drive number to be mounted/unmounted */
3750 BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */
3751)
3752{
3753 FATFS *cfs;
3754 int vol;
3755 FRESULT res;
3756 const TCHAR *rp = path;
3757
3758
3759 /* Get volume ID (logical drive number) */
3760 vol = get_ldnumber(&rp);
3761 if (vol < 0) return FR_INVALID_DRIVE;
3762
3763 cfs = FatFs[vol]; /* Pointer to the filesystem object of the volume */
3764 if (cfs) { /* Unregister current filesystem object */
3765 FatFs[vol] = 0;
3766#if FF_FS_LOCK /* Clear file lock semaphores correspond to this volume */
3767 clear_share(cfs);
3768#endif
3769#if FF_FS_REENTRANT /* Discard mutex of the current volume */
3770 ff_mutex_delete(vol);
3771#endif
3772 cfs->fs_type = 0; /* Invalidate the filesystem object to be unregistered */
3773 }
3774
3775 if (fs) { /* Register new filesystem object */
3776 fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */
3777#if FF_FS_REENTRANT /* Create a volume mutex */
3778 fs->ldrv = (BYTE)vol; /* Owner volume ID */
3779 if (!ff_mutex_create(vol)) return FR_INT_ERR;
3780#if FF_FS_LOCK
3781 if (SysLock == 0) { /* Create a system mutex if needed */
3782 if (!ff_mutex_create(FF_VOLUMES)) {
3783 ff_mutex_delete(vol);
3784 return FR_INT_ERR;
3785 }
3786 SysLock = 1; /* System mutex is ready */
3787 }
3788#endif
3789#endif
3790 fs->fs_type = 0; /* Invalidate the new filesystem object */
3791 FatFs[vol] = fs; /* Register it */
3792 }
3793
3794 if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted in subsequent file functions */
3795
3796 res = mount_volume(&path, &fs, 0); /* Force mounted the volume in this function */
3797 LEAVE_FF(fs, res);
3798}
3799
3800
3801
3802
3803/*-----------------------------------------------------------------------*/
3804/* API: Open or Create a File */
3805/*-----------------------------------------------------------------------*/
3806
3807FRESULT f_open (
3808 FIL* fp, /* Pointer to the blank file object */
3809 const TCHAR* path, /* Pointer to the file name */
3810 BYTE mode /* Access mode and open mode flags */
3811)
3812{
3813 FRESULT res;
3814 DIR dj;
3815 FATFS *fs;
3816 DEF_NAMEBUFF
3817
3818
3819 if (!fp) return FR_INVALID_OBJECT; /* Reject null pointer */
3820
3821 /* Get logical drive number and mount the volume if needed */
3822 mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
3823 res = mount_volume(&path, &fs, mode);
3824
3825 if (res == FR_OK) {
3826 fp->obj.fs = fs;
3827 dj.obj.fs = fs;
3828 INIT_NAMEBUFF(fs);
3829 res = follow_path(&dj, path); /* Follow the file path */
3830#if !FF_FS_READONLY /* Read/Write configuration */
3831 if (res == FR_OK) {
3832 if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */
3833 res = FR_INVALID_NAME;
3834 }
3835#if FF_FS_LOCK
3836 else {
3837 res = chk_share(&dj, (mode & ~FA_READ) ? 1 : 0); /* Check if the file can be used */
3838 }
3839#endif
3840 }
3841 /* Create or Open a file */
3842 if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
3843 if (res != FR_OK) { /* No file, create new */
3844 if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */
3845#if FF_FS_LOCK
3846 res = enq_share() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
3847#else
3848 res = dir_register(&dj);
3849#endif
3850 }
3851 mode |= FA_CREATE_ALWAYS; /* File is created */
3852 }
3853 else { /* An object with the same name is already existing */
3854 if (mode & FA_CREATE_NEW) {
3855 res = FR_EXIST; /* Cannot create as new file */
3856 } else {
3857 if (dj.obj.attr & (AM_RDO | AM_DIR)) res = FR_DENIED; /* Cannot overwrite it (R/O or DIR) */
3858 }
3859 }
3860 if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate the file if overwrite mode */
3861 DWORD tm = GET_FATTIME();
3862#if FF_FS_EXFAT
3863 if (fs->fs_type == FS_EXFAT) {
3864 /* Get current allocation info */
3865 init_alloc_info(&fp->obj, 0);
3866 /* Set exFAT directory entry block initial state */
3867 memset(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */
3868 memset(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */
3869 fs->dirbuf[XDIR_Attr] = AM_ARC;
3870 st_32(fs->dirbuf + XDIR_CrtTime, tm); /* Set created time */
3871 st_32(fs->dirbuf + XDIR_ModTime, tm); /* Set modified time (tmp setting) */
3872 fs->dirbuf[XDIR_GenFlags] = 1;
3873 res = store_xdir(&dj);
3874 if (res == FR_OK && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */
3875 res = remove_chain(&fp->obj, fp->obj.sclust, 0);
3876 fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */
3877 }
3878 } else
3879#endif
3880 {
3881 DWORD cl;
3882 /* Set FAT directory entry initial state */
3883 st_32(dj.dir + DIR_CrtTime, tm); /* Set created time */
3884 st_32(dj.dir + DIR_ModTime, tm); /* Set modified time (tmp setting) */
3885 cl = ld_clust(fs, dj.dir); /* Get current cluster chain */
3886 dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */
3887 st_clust(fs, dj.dir, 0); /* Reset file allocation info */
3888 st_32(dj.dir + DIR_FileSize, 0);
3889 fs->wflag = 1;
3890 if (cl != 0) { /* Remove the cluster chain if exist */
3891 LBA_t sc = fs->winsect;
3892
3893 res = remove_chain(&dj.obj, cl, 0);
3894 if (res == FR_OK) {
3895 res = move_window(fs, sc);
3896 fs->last_clst = cl - 1; /* Reuse the cluster hole */
3897 }
3898 }
3899 }
3900 }
3901 }
3902 else { /* Open an existing file */
3903 if (res == FR_OK) { /* Is the object exsiting? */
3904 if (dj.obj.attr & AM_DIR) { /* File open against a directory */
3905 res = FR_NO_FILE;
3906 } else {
3907 if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */
3908 res = FR_DENIED;
3909 }
3910 }
3911 }
3912 }
3913 if (res == FR_OK) {
3914 if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* Set file change flag if created or overwritten */
3915 fp->dir_sect = fs->winsect; /* Pointer to the directory entry */
3916 fp->dir_ptr = dj.dir;
3917#if FF_FS_LOCK
3918 fp->obj.lockid = inc_share(&dj, (mode & ~FA_READ) ? 1 : 0); /* Lock the file for this session */
3919 if (fp->obj.lockid == 0) res = FR_INT_ERR;
3920#endif
3921 }
3922
3923#else /* R/O configuration */
3924 if (res == FR_OK) {
3925 if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin directory itself? */
3926 res = FR_INVALID_NAME;
3927 } else {
3928 if (dj.obj.attr & AM_DIR) { /* Is it a directory? */
3929 res = FR_NO_FILE;
3930 }
3931 }
3932 }
3933#endif
3934
3935 if (res == FR_OK) {
3936#if FF_FS_EXFAT
3937 if (fs->fs_type == FS_EXFAT) {
3938 init_alloc_info(&fp->obj, &dj);
3939 } else
3940#endif
3941 {
3942 fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */
3943 fp->obj.objsize = ld_32(dj.dir + DIR_FileSize);
3944 }
3945#if FF_USE_FASTSEEK
3946 fp->cltbl = 0; /* Disable fast seek mode */
3947#endif
3948 fp->obj.id = fs->id; /* Set current volume mount ID */
3949 fp->flag = mode; /* Set file access mode */
3950 fp->err = 0; /* Clear error flag */
3951 fp->sect = 0; /* Invalidate current data sector */
3952 fp->fptr = 0; /* Set file pointer top of the file */
3953#if !FF_FS_READONLY
3954#if !FF_FS_TINY
3955 memset(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */
3956#endif
3957 if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */
3958 DWORD bcs, clst;
3959 FSIZE_t ofs;
3960
3961 fp->fptr = fp->obj.objsize; /* Offset to seek */
3962 bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */
3963 clst = fp->obj.sclust; /* Follow the cluster chain */
3964 for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) {
3965 clst = get_fat(&fp->obj, clst);
3966 if (clst <= 1) res = FR_INT_ERR;
3967 if (clst == 0xFFFFFFFF) res = FR_DISK_ERR;
3968 }
3969 fp->clust = clst;
3970 if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */
3971 LBA_t sec = clst2sect(fs, clst);
3972
3973 if (sec == 0) {
3974 res = FR_INT_ERR;
3975 } else {
3976 fp->sect = sec + (DWORD)(ofs / SS(fs));
3977#if !FF_FS_TINY
3978 if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;
3979#endif
3980 }
3981 }
3982#if FF_FS_LOCK
3983 if (res != FR_OK) dec_share(fp->obj.lockid); /* Decrement file open counter if seek failed */
3984#endif
3985 }
3986#endif
3987 }
3988
3989 FREE_NAMEBUFF();
3990 }
3991
3992 if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */
3993
3994 LEAVE_FF(fs, res);
3995}
3996
3997
3998
3999
4000/*-----------------------------------------------------------------------*/
4001/* API: Read File */
4002/*-----------------------------------------------------------------------*/
4003
4004FRESULT f_read (
4005 FIL* fp, /* Open file to be read */
4006 void* buff, /* Data buffer to store the read data */
4007 UINT btr, /* Number of bytes to read */
4008 UINT* br /* Number of bytes read */
4009)
4010{
4011 FRESULT res;
4012 FATFS *fs;
4013 LBA_t sect;
4014 FSIZE_t remain;
4015 UINT rcnt, cc, csect;
4016 BYTE *rbuff = (BYTE*)buff;
4017
4018
4019 *br = 0; /* Clear read byte counter */
4020 res = validate(&fp->obj, &fs); /* Check validity of the file object */
4021 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
4022 if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
4023 remain = fp->obj.objsize - fp->fptr;
4024 if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
4025
4026 for ( ; btr > 0; btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { /* Repeat until btr bytes read */
4027 if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
4028 csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */
4029 if (csect == 0) { /* On the cluster boundary? */
4030 DWORD clst;
4031
4032 if (fp->fptr == 0) { /* On the top of the file? */
4033 clst = fp->obj.sclust; /* Follow cluster chain from the origin */
4034 } else { /* Middle or end of the file */
4035#if FF_USE_FASTSEEK
4036 if (fp->cltbl) {
4037 clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
4038 } else
4039#endif
4040 {
4041 clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */
4042 }
4043 }
4044 if (clst < 2) ABORT(fs, FR_INT_ERR);
4045 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
4046 fp->clust = clst; /* Update current cluster */
4047 }
4048 sect = clst2sect(fs, fp->clust); /* Get current sector */
4049 if (sect == 0) ABORT(fs, FR_INT_ERR);
4050 sect += csect;
4051 cc = btr / SS(fs); /* When remaining bytes >= sector size, */
4052 if (cc > 0) { /* Read maximum contiguous sectors directly */
4053 if (csect + cc > fs->csize) { /* Clip at cluster boundary */
4054 cc = fs->csize - csect;
4055 }
4056 if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
4057#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
4058#if FF_FS_TINY
4059 if (fs->wflag && fs->winsect - sect < cc) {
4060 memcpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));
4061 }
4062#else
4063 if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
4064 memcpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));
4065 }
4066#endif
4067#endif
4068 rcnt = SS(fs) * cc; /* Number of bytes transferred */
4069 continue;
4070 }
4071#if !FF_FS_TINY
4072 if (fp->sect != sect) { /* Load data sector if not in cache */
4073#if !FF_FS_READONLY
4074 if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
4075 if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
4076 fp->flag &= (BYTE)~FA_DIRTY;
4077 }
4078#endif
4079 if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
4080 }
4081#endif
4082 fp->sect = sect;
4083 }
4084 rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */
4085 if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */
4086#if FF_FS_TINY
4087 if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
4088 memcpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
4089#else
4090 memcpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */
4091#endif
4092 }
4093
4094 LEAVE_FF(fs, FR_OK);
4095}
4096
4097
4098
4099
4100#if !FF_FS_READONLY
4101/*-----------------------------------------------------------------------*/
4102/* API: Write File */
4103/*-----------------------------------------------------------------------*/
4104
4105FRESULT f_write (
4106 FIL* fp, /* Open file to be written */
4107 const void* buff, /* Data to be written */
4108 UINT btw, /* Number of bytes to write */
4109 UINT* bw /* Number of bytes written */
4110)
4111{
4112 FRESULT res;
4113 FATFS *fs;
4114 DWORD clst;
4115 LBA_t sect;
4116 UINT wcnt, cc, csect;
4117 const BYTE *wbuff = (const BYTE*)buff;
4118
4119
4120 *bw = 0; /* Clear write byte counter */
4121 res = validate(&fp->obj, &fs); /* Check validity of the file object */
4122 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
4123 if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
4124
4125 /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
4126 if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
4127 btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
4128 }
4129
4130 for ( ; btw > 0; btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { /* Repeat until all data written */
4131 if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
4132 csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */
4133 if (csect == 0) { /* On the cluster boundary? */
4134 if (fp->fptr == 0) { /* On the top of the file? */
4135 clst = fp->obj.sclust; /* Follow from the origin */
4136 if (clst == 0) { /* If no cluster is allocated, */
4137 clst = create_chain(&fp->obj, 0); /* create a new cluster chain */
4138 }
4139 } else { /* On the middle or end of the file */
4140#if FF_USE_FASTSEEK
4141 if (fp->cltbl) {
4142 clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
4143 } else
4144#endif
4145 {
4146 clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */
4147 }
4148 }
4149 if (clst == 0) break; /* Could not allocate a new cluster (disk full) */
4150 if (clst == 1) ABORT(fs, FR_INT_ERR);
4151 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
4152 fp->clust = clst; /* Update current cluster */
4153 if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */
4154 }
4155#if FF_FS_TINY
4156 if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */
4157#else
4158 if (fp->flag & FA_DIRTY) { /* Write-back sector cache */
4159 if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
4160 fp->flag &= (BYTE)~FA_DIRTY;
4161 }
4162#endif
4163 sect = clst2sect(fs, fp->clust); /* Get current sector */
4164 if (sect == 0) ABORT(fs, FR_INT_ERR);
4165 sect += csect;
4166 cc = btw / SS(fs); /* When remaining bytes >= sector size, */
4167 if (cc > 0) { /* Write maximum contiguous sectors directly */
4168 if (csect + cc > fs->csize) { /* Clip at cluster boundary */
4169 cc = fs->csize - csect;
4170 }
4171 if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
4172#if FF_FS_MINIMIZE <= 2
4173#if FF_FS_TINY
4174 if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
4175 memcpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));
4176 fs->wflag = 0;
4177 }
4178#else
4179 if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
4180 memcpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
4181 fp->flag &= (BYTE)~FA_DIRTY;
4182 }
4183#endif
4184#endif
4185 wcnt = SS(fs) * cc; /* Number of bytes transferred */
4186 continue;
4187 }
4188#if FF_FS_TINY
4189 if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */
4190 if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);
4191 fs->winsect = sect;
4192 }
4193#else
4194 if (fp->sect != sect && /* Fill sector cache with file data */
4195 fp->fptr < fp->obj.objsize &&
4196 disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
4197 ABORT(fs, FR_DISK_ERR);
4198 }
4199#endif
4200 fp->sect = sect;
4201 }
4202 wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */
4203 if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */
4204#if FF_FS_TINY
4205 if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
4206 memcpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
4207 fs->wflag = 1;
4208#else
4209 memcpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
4210 fp->flag |= FA_DIRTY;
4211#endif
4212 }
4213
4214 fp->flag |= FA_MODIFIED; /* Set file change flag */
4215
4216 LEAVE_FF(fs, FR_OK);
4217}
4218
4219
4220
4221
4222/*-----------------------------------------------------------------------*/
4223/* API: Synchronize the File */
4224/*-----------------------------------------------------------------------*/
4225
4226FRESULT f_sync (
4227 FIL* fp /* Open file to be synced */
4228)
4229{
4230 FRESULT res;
4231 FATFS *fs;
4232
4233
4234 res = validate(&fp->obj, &fs); /* Check validity of the file object */
4235 if (res == FR_OK) {
4236 if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */
4237#if !FF_FS_TINY
4238 if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */
4239 if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);
4240 fp->flag &= (BYTE)~FA_DIRTY;
4241 }
4242#endif
4243 /* Update the directory entry */
4244#if FF_FS_EXFAT
4245 if (fs->fs_type == FS_EXFAT) {
4246 res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */
4247 if (res == FR_OK) {
4248 res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */
4249 }
4250 if (res == FR_OK) {
4251 DIR dj;
4252 DEF_NAMEBUFF
4253
4254 INIT_NAMEBUFF(fs);
4255 res = load_obj_xdir(&dj, &fp->obj); /* Load directory entry block */
4256 if (res == FR_OK) {
4257 fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */
4258 fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation information */
4259 st_32(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); /* Update start cluster */
4260 st_64(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); /* Update file size */
4261 st_64(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); /* (FatFs does not support Valid File Size feature) */
4262 st_32(fs->dirbuf + XDIR_ModTime, GET_FATTIME()); /* Update modified time */
4263 fs->dirbuf[XDIR_ModTime10] = 0;
4264 fs->dirbuf[XDIR_ModTZ] = 0;
4265 st_32(fs->dirbuf + XDIR_AccTime, 0); /* Invalidate last access time */
4266 fs->dirbuf[XDIR_AccTZ] = 0;
4267 res = store_xdir(&dj); /* Restore it to the directory */
4268 if (res == FR_OK) {
4269 res = sync_fs(fs);
4270 fp->flag &= (BYTE)~FA_MODIFIED;
4271 }
4272 }
4273 FREE_NAMEBUFF();
4274 }
4275 } else
4276#endif
4277 {
4278 res = move_window(fs, fp->dir_sect);
4279 if (res == FR_OK) {
4280 BYTE *dir = fp->dir_ptr;
4281
4282 dir[DIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */
4283 st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation information */
4284 st_32(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */
4285 st_32(dir + DIR_ModTime, GET_FATTIME()); /* Update modified time */
4286 st_16(dir + DIR_LstAccDate, 0); /* Invalidate last access date */
4287 fs->wflag = 1;
4288 res = sync_fs(fs); /* Restore it to the directory */
4289 fp->flag &= (BYTE)~FA_MODIFIED;
4290 }
4291 }
4292 }
4293 }
4294
4295 LEAVE_FF(fs, res);
4296}
4297
4298#endif /* !FF_FS_READONLY */
4299
4300
4301
4302
4303/*-----------------------------------------------------------------------*/
4304/* API: Close File */
4305/*-----------------------------------------------------------------------*/
4306
4307FRESULT f_close (
4308 FIL* fp /* Open file to be closed */
4309)
4310{
4311 FRESULT res;
4312 FATFS *fs;
4313
4314#if !FF_FS_READONLY
4315 res = f_sync(fp); /* Flush cached data */
4316 if (res == FR_OK)
4317#endif
4318 {
4319 res = validate(&fp->obj, &fs); /* Lock volume */
4320 if (res == FR_OK) {
4321#if FF_FS_LOCK
4322 res = dec_share(fp->obj.lockid); /* Decrement file open counter */
4323 if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file object */
4324#else
4325 fp->obj.fs = 0; /* Invalidate file object */
4326#endif
4327#if FF_FS_REENTRANT
4328 unlock_volume(fs, FR_OK); /* Unlock volume */
4329#endif
4330 }
4331 }
4332 return res;
4333}
4334
4335
4336
4337
4338#if FF_FS_RPATH >= 1
4339/*-----------------------------------------------------------------------*/
4340/* API: Change Current Drive */
4341/*-----------------------------------------------------------------------*/
4342
4343FRESULT f_chdrive (
4344 const TCHAR* path /* Drive number to set */
4345)
4346{
4347 int vol;
4348
4349
4350 /* Get logical drive number */
4351 vol = get_ldnumber(&path);
4352 if (vol < 0) return FR_INVALID_DRIVE;
4353 CurrVol = (BYTE)vol; /* Set it as current volume */
4354
4355 return FR_OK;
4356}
4357
4358
4359
4360
4361/*-----------------------------------------------------------------------*/
4362/* API: Change Current Directory */
4363/*-----------------------------------------------------------------------*/
4364
4365FRESULT f_chdir (
4366 const TCHAR* path /* Pointer to the directory path */
4367)
4368{
4369 FRESULT res;
4370 DIR dj;
4371 FATFS *fs;
4372 DEF_NAMEBUFF
4373
4374
4375 res = mount_volume(&path, &fs, 0); /* Get logical drive and mount the volume if needed */
4376 if (res == FR_OK) {
4377 dj.obj.fs = fs;
4378 INIT_NAMEBUFF(fs);
4379 res = follow_path(&dj, path); /* Follow the path */
4380 if (res == FR_OK) { /* Follow completed */
4381 if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start directory itself? */
4382#if FF_FS_EXFAT
4383 if (fs->fs_type == FS_EXFAT) {
4384 memcpy(&fs->xcwds, &fs->xcwds2, sizeof fs->xcwds);
4385 }
4386#endif
4387 fs->cdir = dj.obj.sclust;
4388 } else {
4389 if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */
4390#if FF_FS_EXFAT
4391 if (fs->fs_type == FS_EXFAT) {
4392 memcpy(&fs->xcwds, &fs->xcwds2, sizeof fs->xcwds);
4393 fs->cdir = fs->xcwds.tbl[fs->xcwds.depth].d_scl; /* Sub-directory cluster */
4394 } else
4395#endif
4396 {
4397 fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */
4398 }
4399 } else {
4400 res = FR_NO_PATH; /* Reached but a file */
4401 }
4402 }
4403 }
4404 FREE_NAMEBUFF();
4405 if (res == FR_NO_FILE) res = FR_NO_PATH;
4406#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed if in Unix style volume ID */
4407 if (res == FR_OK) {
4408 UINT i;
4409
4410 for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */
4411 CurrVol = (BYTE)i;
4412 }
4413#endif
4414 }
4415
4416 LEAVE_FF(fs, res);
4417}
4418
4419
4420
4421
4422#if FF_FS_RPATH >= 2
4423/*-----------------------------------------------------------------------*/
4424/* API: Get Curent Directory */
4425/*-----------------------------------------------------------------------*/
4426
4427FRESULT f_getcwd (
4428 TCHAR* buff, /* Pointer to the buffer to store the current direcotry path */
4429 UINT len /* Size of buff in unit of TCHAR */
4430)
4431{
4432 FRESULT res;
4433 DIR dj;
4434 FATFS *fs;
4435#if FF_VOLUMES >= 2
4436 UINT vl;
4437#if FF_STR_VOLUME_ID
4438 const char *vid;
4439#endif
4440#endif
4441 FILINFO fno;
4442 DEF_NAMEBUFF
4443
4444
4445 buff[0] = 0; /* A null str to get current drive */
4446 res = mount_volume((const TCHAR**)&buff, &fs, 0);
4447 if (res == FR_OK) {
4448 dj.obj.fs = fs;
4449 INIT_NAMEBUFF(fs);
4450#if FF_FS_EXFAT
4451 if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
4452 UINT wi = 0;
4453 UINT di, ni;
4454
4455#if FF_VOLUMES >= 2 /* Add drive prefix if needed */
4456#if FF_STR_VOLUME_ID == 0 /* Numeric volume ID */
4457 if (wi < len) buff[wi++] = '0' + CurrVol;
4458#else /* String volume ID */
4459 if (FF_STR_VOLUME_ID == 2 && wi < len) buff[wi++] = '/';
4460 for (vid = (const char*)VolumeStr[CurrVol]; *vid && wi < len; buff[wi++] = *vid++) ;
4461#endif
4462 if (FF_STR_VOLUME_ID <= 1 && wi < len) buff[wi++] = ':';
4463#endif
4464 if (wi < len) buff[wi++] = '/';
4465 for (di = 0; wi < len && di < fs->xcwds.depth; di++) { /* Follow current directory path with cwd structure */
4466 dj.obj.sclust = fs->xcwds.tbl[di].d_scl; /* Open this directory */
4467 dj.obj.stat = (BYTE)fs->xcwds.tbl[di].d_size;
4468 dj.obj.objsize = fs->xcwds.tbl[di].d_size & 0xFFFFFF00;
4469 res = dir_sdi(&dj, fs->xcwds.tbl[di].nxt_ofs); /* Find next directory */
4470 if (res != FR_OK) break;
4471 res = DIR_READ_FILE(&dj); /* Get the directory name */
4472 if (res != FR_OK) break;
4473 get_fileinfo(&dj, &fno);
4474 if (di > 0 && wi < len) buff[wi++] = '/'; /* Add the directory name with a directory separator */
4475 for (ni = 0; fno.fname[ni] && wi < len; buff[wi++] = fno.fname[ni++]) ;
4476 }
4477 if (wi == len) { /* Buffer overflow? */
4478 res = FR_NOT_ENOUGH_CORE;
4479 } else {
4480 buff[wi] = 0; /* Terminate the string */
4481 }
4482 }
4483 else
4484#endif
4485 { /* On the FAT/FAT32 volume */
4486 TCHAR *tp = buff;
4487 UINT i, nl;
4488 DWORD ccl;
4489
4490 /* Follow parent directories toward the root directory and create the cwd path */
4491 i = len; /* Bottom of buffer (directory stack base) */
4492 dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */
4493 while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */
4494 res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */
4495 if (res != FR_OK) break;
4496 res = move_window(fs, dj.sect);
4497 if (res != FR_OK) break;
4498 dj.obj.sclust = ld_clust(fs, dj.dir); /* Go to parent directory */
4499 res = dir_sdi(&dj, 0);
4500 if (res != FR_OK) break;
4501 do { /* Find the entry links to this sub-directory */
4502 res = DIR_READ_FILE(&dj);
4503 if (res != FR_OK) break;
4504 if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */
4505 res = dir_next(&dj, 0);
4506 } while (res == FR_OK);
4507 if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be 'not found'. */
4508 if (res != FR_OK) break;
4509 get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */
4510 for (nl = 0; fno.fname[nl]; nl++) ; /* Name length */
4511 if (i < nl + 1) { /* Insufficient space to store the path name? */
4512 res = FR_NOT_ENOUGH_CORE; break;
4513 }
4514 while (nl) buff[--i] = fno.fname[--nl]; /* Stack the name */
4515 buff[--i] = '/';
4516 }
4517 if (res == FR_OK) {
4518 if (i == len) buff[--i] = '/'; /* Is it the root-directory? */
4519#if FF_VOLUMES >= 2 /* Put drive prefix */
4520 vl = 0;
4521#if FF_STR_VOLUME_ID >= 1 /* String volume ID */
4522 for (nl = 0, vid = (const char*)VolumeStr[CurrVol]; vid[nl]; nl++) ; /* Volume ID length */
4523 if (i >= nl + 2) {
4524 if (FF_STR_VOLUME_ID == 2) *tp++ = '/'; /* Unix style */
4525 for (vl = 0; vl < nl; *tp++ = vid[vl], vl++) ;
4526 if (FF_STR_VOLUME_ID == 1) *tp++ = ':'; /* DOS/Windows style */
4527 vl++;
4528 }
4529#else /* Numeric volume ID */
4530 if (i >= 3) {
4531 *tp++ = '0' + CurrVol;
4532 *tp++ = ':';
4533 vl = 2;
4534 }
4535#endif
4536 if (vl == 0) res = FR_NOT_ENOUGH_CORE;
4537#endif
4538 /* Add current directory path */
4539 if (res == FR_OK) {
4540 do { /* Copy stacked path string */
4541 *tp++ = buff[i++];
4542 } while (i < len);
4543 }
4544 }
4545 *tp = 0;
4546 }
4547 FREE_NAMEBUFF();
4548 }
4549
4550 LEAVE_FF(fs, res);
4551}
4552
4553#endif /* FF_FS_RPATH >= 2 */
4554#endif /* FF_FS_RPATH >= 1 */
4555
4556
4557
4558#if FF_FS_MINIMIZE <= 2
4559/*-----------------------------------------------------------------------*/
4560/* API: Seek File Read/Write Pointer */
4561/*-----------------------------------------------------------------------*/
4562
4563FRESULT f_lseek (
4564 FIL* fp, /* Pointer to the file object */
4565 FSIZE_t ofs /* File pointer from top of file */
4566)
4567{
4568 FRESULT res;
4569 FATFS *fs;
4570 DWORD clst, bcs;
4571 LBA_t nsect;
4572 FSIZE_t ifptr;
4573
4574
4575 res = validate(&fp->obj, &fs); /* Check validity of the file object */
4576 if (res == FR_OK) res = (FRESULT)fp->err;
4577#if FF_FS_EXFAT && !FF_FS_READONLY
4578 if (res == FR_OK && fs->fs_type == FS_EXFAT) {
4579 res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */
4580 }
4581#endif
4582 if (res != FR_OK) LEAVE_FF(fs, res);
4583
4584#if FF_USE_FASTSEEK
4585 if (fp->cltbl) { /* Fast seek */
4586 DWORD cl, pcl, ncl, tcl, tlen, ulen;
4587 DWORD *tbl;
4588 LBA_t dsc;
4589
4590 if (ofs == CREATE_LINKMAP) { /* Create CLMT */
4591 tbl = fp->cltbl;
4592 tlen = *tbl++; ulen = 2; /* Given table size and required table size */
4593 cl = fp->obj.sclust; /* Origin of the chain */
4594 if (cl != 0) {
4595 do {
4596 /* Get a fragment */
4597 tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */
4598 do {
4599 pcl = cl; ncl++;
4600 cl = get_fat(&fp->obj, cl);
4601 if (cl <= 1) ABORT(fs, FR_INT_ERR);
4602 if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
4603 } while (cl == pcl + 1);
4604 if (ulen <= tlen) { /* Store the length and top of the fragment */
4605 *tbl++ = ncl; *tbl++ = tcl;
4606 }
4607 } while (cl < fs->n_fatent); /* Repeat until end of chain */
4608 }
4609 *fp->cltbl = ulen; /* Number of items used */
4610 if (ulen <= tlen) {
4611 *tbl = 0; /* Terminate table */
4612 } else {
4613 res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */
4614 }
4615 } else { /* Fast seek */
4616 if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */
4617 fp->fptr = ofs; /* Set file pointer */
4618 if (ofs > 0) {
4619 fp->clust = clmt_clust(fp, ofs - 1);
4620 dsc = clst2sect(fs, fp->clust);
4621 if (dsc == 0) ABORT(fs, FR_INT_ERR);
4622 dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1);
4623 if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */
4624#if !FF_FS_TINY
4625#if !FF_FS_READONLY
4626 if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
4627 if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
4628 fp->flag &= (BYTE)~FA_DIRTY;
4629 }
4630#endif
4631 if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */
4632#endif
4633 fp->sect = dsc;
4634 }
4635 }
4636 }
4637 } else
4638#endif
4639
4640 /* Normal Seek */
4641 {
4642#if FF_FS_EXFAT
4643 if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */
4644#endif
4645 if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */
4646 ofs = fp->obj.objsize;
4647 }
4648 ifptr = fp->fptr;
4649 fp->fptr = nsect = 0;
4650 if (ofs > 0) {
4651 bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */
4652 if (ifptr > 0 &&
4653 (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
4654 fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */
4655 ofs -= fp->fptr;
4656 clst = fp->clust;
4657 } else { /* When seek to back cluster, */
4658 clst = fp->obj.sclust; /* start from the first cluster */
4659#if !FF_FS_READONLY
4660 if (clst == 0) { /* If no cluster chain, create a new chain */
4661 clst = create_chain(&fp->obj, 0);
4662 if (clst == 1) ABORT(fs, FR_INT_ERR);
4663 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
4664 fp->obj.sclust = clst;
4665 }
4666#endif
4667 fp->clust = clst;
4668 }
4669 if (clst != 0) {
4670 while (ofs > bcs) { /* Cluster following loop */
4671 ofs -= bcs; fp->fptr += bcs;
4672#if !FF_FS_READONLY
4673 if (fp->flag & FA_WRITE) { /* Check if in write mode or not */
4674 if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */
4675 fp->obj.objsize = fp->fptr;
4676 fp->flag |= FA_MODIFIED;
4677 }
4678 clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */
4679 if (clst == 0) { /* Clip file size in case of disk full */
4680 ofs = 0; break;
4681 }
4682 } else
4683#endif
4684 {
4685 clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */
4686 }
4687 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
4688 if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
4689 fp->clust = clst;
4690 }
4691 fp->fptr += ofs;
4692 if (ofs % SS(fs)) {
4693 nsect = clst2sect(fs, clst); /* Current sector */
4694 if (nsect == 0) ABORT(fs, FR_INT_ERR);
4695 nsect += (DWORD)(ofs / SS(fs));
4696 }
4697 }
4698 }
4699 if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */
4700 fp->obj.objsize = fp->fptr;
4701 fp->flag |= FA_MODIFIED;
4702 }
4703 if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */
4704#if !FF_FS_TINY
4705#if !FF_FS_READONLY
4706 if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
4707 if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
4708 fp->flag &= (BYTE)~FA_DIRTY;
4709 }
4710#endif
4711 if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
4712#endif
4713 fp->sect = nsect;
4714 }
4715 }
4716
4717 LEAVE_FF(fs, res);
4718}
4719
4720
4721
4722#if FF_FS_MINIMIZE <= 1
4723/*-----------------------------------------------------------------------*/
4724/* API: Create a Directory Object */
4725/*-----------------------------------------------------------------------*/
4726
4727FRESULT f_opendir (
4728 DIR* dp, /* Pointer to directory object to create */
4729 const TCHAR* path /* Pointer to the directory path */
4730)
4731{
4732 FRESULT res;
4733 FATFS *fs;
4734 DEF_NAMEBUFF
4735
4736
4737 if (!dp) return FR_INVALID_OBJECT; /* Reject null pointer */
4738
4739 res = mount_volume(&path, &fs, 0); /* Get logical drive and mount the volume if needed */
4740 if (res == FR_OK) {
4741 dp->obj.fs = fs;
4742 INIT_NAMEBUFF(fs);
4743 res = follow_path(dp, path); /* Follow the path to the directory */
4744 if (res == FR_OK) { /* Follow completed */
4745 if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is neither the origin directory itself nor dot name in exFAT */
4746 if (dp->obj.attr & AM_DIR) { /* This object is a sub-directory */
4747#if FF_FS_EXFAT
4748 if (fs->fs_type == FS_EXFAT) {
4749 init_alloc_info(&dp->obj, dp); /* Get object allocation info */
4750 } else
4751#endif
4752 {
4753 dp->obj.sclust = ld_clust(fs, dp->dir); /* Get object allocation info */
4754 }
4755 } else { /* This object is a file */
4756 res = FR_NO_PATH;
4757 }
4758 }
4759 if (res == FR_OK) {
4760 dp->obj.id = fs->id; /* Set current volume mount ID */
4761 res = dir_sdi(dp, 0); /* Rewind directory */
4762#if FF_FS_LOCK
4763 if (res == FR_OK) {
4764 if (dp->obj.sclust) { /* Is this a sub-directory? */
4765 dp->obj.lockid = inc_share(dp, 0); /* Lock the sub-directory */
4766 if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES;
4767 } else {
4768 dp->obj.lockid = 0; /* Root directory does not need to be locked */
4769 }
4770 }
4771#endif
4772 }
4773 }
4774 FREE_NAMEBUFF();
4775 if (res == FR_NO_FILE) res = FR_NO_PATH;
4776 }
4777 if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the directory object if function failed */
4778
4779 LEAVE_FF(fs, res);
4780}
4781
4782
4783
4784
4785/*-----------------------------------------------------------------------*/
4786/* API: Close Directory */
4787/*-----------------------------------------------------------------------*/
4788
4789FRESULT f_closedir (
4790 DIR *dp /* Pointer to the directory object to be closed */
4791)
4792{
4793 FRESULT res;
4794 FATFS *fs;
4795
4796
4797 res = validate(&dp->obj, &fs); /* Check validity of the file object */
4798 if (res == FR_OK) {
4799#if FF_FS_LOCK
4800 if (dp->obj.lockid) res = dec_share(dp->obj.lockid); /* Decrement sub-directory open counter */
4801 if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory object */
4802#else
4803 dp->obj.fs = 0; /* Invalidate directory object */
4804#endif
4805#if FF_FS_REENTRANT
4806 unlock_volume(fs, FR_OK); /* Unlock volume */
4807#endif
4808 }
4809 return res;
4810}
4811
4812
4813
4814
4815/*-----------------------------------------------------------------------*/
4816/* API: Read Directory Entries in Sequence */
4817/*-----------------------------------------------------------------------*/
4818
4819FRESULT f_readdir (
4820 DIR* dp, /* Pointer to the open directory object */
4821 FILINFO* fno /* Pointer to file information to return */
4822)
4823{
4824 FRESULT res;
4825 FATFS *fs;
4826 DEF_NAMEBUFF
4827
4828
4829 res = validate(&dp->obj, &fs); /* Check validity of the directory object */
4830 if (res == FR_OK) {
4831 if (!fno) {
4832 res = dir_sdi(dp, 0); /* Rewind the directory object */
4833 } else {
4834 INIT_NAMEBUFF(fs);
4835 fno->fname[0] = 0; /* Clear file information */
4836 res = DIR_READ_FILE(dp); /* Read an item */
4837 if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */
4838 if (res == FR_OK) { /* A valid entry is found */
4839 get_fileinfo(dp, fno); /* Get the object information */
4840 res = dir_next(dp, 0); /* Increment index for next */
4841 if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */
4842 }
4843 FREE_NAMEBUFF();
4844 }
4845 }
4846
4847 if (fno && res != FR_OK) fno->fname[0] = 0; /* Clear the file information if any error occured */
4848 LEAVE_FF(fs, res);
4849}
4850
4851
4852
4853#if FF_USE_FIND
4854/*-----------------------------------------------------------------------*/
4855/* API: Find Next File */
4856/*-----------------------------------------------------------------------*/
4857
4858FRESULT f_findnext (
4859 DIR* dp, /* Pointer to the open directory object */
4860 FILINFO* fno /* Pointer to the file information structure */
4861)
4862{
4863 FRESULT res;
4864
4865
4866 for (;;) {
4867 res = f_readdir(dp, fno); /* Get a directory item */
4868 if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */
4869 if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */
4870#if FF_USE_LFN && FF_USE_FIND == 2
4871 if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) break; /* Test for alternative name if exist */
4872#endif
4873 }
4874 return res;
4875}
4876
4877
4878
4879/*-----------------------------------------------------------------------*/
4880/* API: Find First File */
4881/*-----------------------------------------------------------------------*/
4882
4883FRESULT f_findfirst (
4884 DIR* dp, /* Pointer to the blank directory object */
4885 FILINFO* fno, /* Pointer to the file information structure */
4886 const TCHAR* path, /* Pointer to the directory to open */
4887 const TCHAR* pattern /* Pointer to the matching pattern */
4888)
4889{
4890 FRESULT res;
4891
4892
4893 dp->pat = pattern; /* Save pointer to pattern string */
4894 res = f_opendir(dp, path); /* Open the target directory */
4895 if (res == FR_OK) {
4896 res = f_findnext(dp, fno); /* Find the first item */
4897 }
4898 return res;
4899}
4900
4901#endif /* FF_USE_FIND */
4902
4903
4904
4905#if FF_FS_MINIMIZE == 0
4906/*-----------------------------------------------------------------------*/
4907/* API: Get File Status */
4908/*-----------------------------------------------------------------------*/
4909
4910FRESULT f_stat (
4911 const TCHAR* path, /* Pointer to the file path */
4912 FILINFO* fno /* Pointer to file information to return */
4913)
4914{
4915 FRESULT res;
4916 DIR dj;
4917 DEF_NAMEBUFF
4918
4919
4920 /* Get logical drive and mount the volume if needed */
4921 res = mount_volume(&path, &dj.obj.fs, 0);
4922
4923 if (res == FR_OK) {
4924 INIT_NAMEBUFF(dj.obj.fs);
4925 res = follow_path(&dj, path); /* Follow the file path */
4926 if (res == FR_OK) { /* Follow completed */
4927 if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */
4928 res = FR_INVALID_NAME;
4929 } else { /* Found an object */
4930 if (fno) get_fileinfo(&dj, fno);
4931 }
4932 }
4933 FREE_NAMEBUFF();
4934 }
4935
4936 if (fno && res != FR_OK) fno->fname[0] = 0; /* Invalidate the file information if an error occured */
4937 LEAVE_FF(dj.obj.fs, res);
4938}
4939
4940
4941
4942#if !FF_FS_READONLY
4943/*-----------------------------------------------------------------------*/
4944/* API: Get Number of Free Clusters */
4945/*-----------------------------------------------------------------------*/
4946
4947FRESULT f_getfree (
4948 const TCHAR* path, /* Logical drive number */
4949 DWORD* nclst, /* Pointer to a variable to return number of free clusters */
4950 FATFS** fatfs /* Pointer to a pointer to return corresponding filesystem object */
4951)
4952{
4953 FRESULT res;
4954 FATFS *fs;
4955 DWORD nfree, clst, stat;
4956 LBA_t sect;
4957 UINT i;
4958 FFOBJID obj;
4959
4960
4961 /* Get logical drive and mount the volume if needed */
4962 res = mount_volume(&path, &fs, 0);
4963
4964 if (res == FR_OK) {
4965 *fatfs = fs; /* Return ptr to the fs object */
4966 /* If free_clst is valid, return it without full FAT scan */
4967 if (fs->free_clst <= fs->n_fatent - 2) {
4968 *nclst = fs->free_clst;
4969 } else {
4970 /* Scan FAT to obtain the correct free cluster count */
4971 nfree = 0;
4972 if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit field FAT entries */
4973 clst = 2; obj.fs = fs;
4974 do {
4975 stat = get_fat(&obj, clst);
4976 if (stat == 0xFFFFFFFF) {
4977 res = FR_DISK_ERR; break;
4978 }
4979 if (stat == 1) {
4980 res = FR_INT_ERR; break;
4981 }
4982 if (stat == 0) nfree++;
4983 } while (++clst < fs->n_fatent);
4984 } else {
4985#if FF_FS_EXFAT
4986 if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan allocation bitmap */
4987 BYTE bm;
4988 UINT b;
4989
4990 clst = fs->n_fatent - 2; /* Number of clusters */
4991 sect = fs->bitbase; /* Bitmap sector */
4992 i = 0; /* Offset in the sector */
4993 do { /* Counts numbuer of clear bits (free clusters) in the bitmap */
4994 if (i == 0) { /* New sector? */
4995 res = move_window(fs, sect++);
4996 if (res != FR_OK) break;
4997 }
4998 for (b = 8, bm = ~fs->win[i]; b && clst; b--, clst--) { /* Count clear bits in a byte */
4999 nfree += bm & 1;
5000 bm >>= 1;
5001 }
5002 i = (i + 1) % SS(fs); /* Next byte */
5003 } while (clst);
5004 } else
5005#endif
5006 { /* FAT16/32: Scan WORD/DWORD FAT entries */
5007 clst = fs->n_fatent; /* Number of entries */
5008 sect = fs->fatbase; /* Top of the FAT */
5009 i = 0; /* Offset in the sector */
5010 do { /* Counts numbuer of entries with zero in the FAT */
5011 if (i == 0) { /* New sector? */
5012 res = move_window(fs, sect++);
5013 if (res != FR_OK) break;
5014 }
5015 if (fs->fs_type == FS_FAT16) {
5016 if (ld_16(fs->win + i) == 0) nfree++; /* FAT16: Is this cluster free? */
5017 i += 2; /* Next entry */
5018 } else {
5019 if ((ld_32(fs->win + i) & 0x0FFFFFFF) == 0) nfree++; /* FAT32: Is this cluster free? */
5020 i += 4; /* Next entry */
5021 }
5022 i %= SS(fs);
5023 } while (--clst);
5024 }
5025 }
5026 if (res == FR_OK) { /* Update parameters if succeeded */
5027 *nclst = nfree; /* Return the free clusters */
5028 fs->free_clst = nfree; /* Now free cluster count is valid */
5029 fs->fsi_flag |= 1; /* FAT32/exfAT : Allocation information is to be updated */
5030 }
5031 }
5032 }
5033
5034 LEAVE_FF(fs, res);
5035}
5036
5037
5038
5039
5040/*-----------------------------------------------------------------------*/
5041/* API: Truncate File */
5042/*-----------------------------------------------------------------------*/
5043
5044FRESULT f_truncate (
5045 FIL* fp /* Pointer to the file object */
5046)
5047{
5048 FRESULT res;
5049 FATFS *fs;
5050 DWORD ncl;
5051
5052
5053 /* Check validity of the file object */
5054 res = validate(&fp->obj, &fs);
5055 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
5056 if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
5057
5058 if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */
5059 if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */
5060 res = remove_chain(&fp->obj, fp->obj.sclust, 0);
5061 fp->obj.sclust = 0;
5062 } else { /* When truncate a part of the file, remove remaining clusters */
5063 ncl = get_fat(&fp->obj, fp->clust);
5064 res = FR_OK;
5065 if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
5066 if (ncl == 1) res = FR_INT_ERR;
5067 if (res == FR_OK && ncl < fs->n_fatent) {
5068 res = remove_chain(&fp->obj, ncl, fp->clust);
5069 }
5070 }
5071 fp->obj.objsize = fp->fptr; /* Set file size to current read/write point */
5072 fp->flag |= FA_MODIFIED;
5073#if !FF_FS_TINY
5074 if (res == FR_OK && (fp->flag & FA_DIRTY)) {
5075 if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
5076 res = FR_DISK_ERR;
5077 } else {
5078 fp->flag &= (BYTE)~FA_DIRTY;
5079 }
5080 }
5081#endif
5082 if (res != FR_OK) ABORT(fs, res);
5083 }
5084
5085 LEAVE_FF(fs, res);
5086}
5087
5088
5089
5090
5091/*-----------------------------------------------------------------------*/
5092/* API: Delete a File/Directory */
5093/*-----------------------------------------------------------------------*/
5094
5095FRESULT f_unlink (
5096 const TCHAR* path /* Pointer to the file or directory path */
5097)
5098{
5099 FRESULT res;
5100 FATFS *fs;
5101 DIR dj, sdj;
5102 DWORD dclst = 0;
5103#if FF_FS_EXFAT
5104 FFOBJID obj;
5105#endif
5106 DEF_NAMEBUFF
5107
5108 /* Get logical drive and mount the volume if needed */
5109 res = mount_volume(&path, &fs, FA_WRITE);
5110 if (res == FR_OK) {
5111 dj.obj.fs = fs;
5112 INIT_NAMEBUFF(fs);
5113 res = follow_path(&dj, path); /* Follow the path to the object */
5114 if (res == FR_OK) {
5115 if (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME)) {
5116 res = FR_INVALID_NAME; /* It must be a real object */
5117 } else if (dj.obj.attr & AM_RDO) {
5118 res = FR_DENIED; /* The object must not be read-only */
5119#if FF_FS_LOCK
5120 } else {
5121 res = chk_share(&dj, 2); /* Check if the object is in use */
5122#endif
5123 }
5124 }
5125 if (res == FR_OK) { /* The object is accessible */
5126#if FF_FS_EXFAT
5127 obj.fs = fs;
5128 if (fs->fs_type == FS_EXFAT) {
5129 init_alloc_info(&obj, 0);
5130 dclst = obj.sclust;
5131 } else
5132#endif
5133 {
5134 dclst = ld_clust(fs, dj.dir);
5135 }
5136 if (dj.obj.attr & AM_DIR) { /* Is the object a sub-directory? */
5137#if FF_FS_RPATH
5138 if (dclst == fs->cdir) {
5139 res = FR_DENIED; /* Current directory cannot be removed */
5140 } else
5141#endif
5142 {
5143 sdj.obj.fs = fs; /* Open the sub-directory */
5144 sdj.obj.sclust = dclst;
5145#if FF_FS_EXFAT
5146 if (fs->fs_type == FS_EXFAT) {
5147 sdj.obj.objsize = obj.objsize;
5148 sdj.obj.stat = obj.stat;
5149 }
5150#endif
5151 res = dir_sdi(&sdj, 0);
5152 if (res == FR_OK) {
5153 res = DIR_READ_FILE(&sdj); /* Check if the sub-directory is empty */
5154 if (res == FR_OK) res = FR_DENIED; /* Not empty? */
5155 if (res == FR_NO_FILE) res = FR_OK; /* Empty? */
5156 }
5157 }
5158 }
5159 }
5160 if (res == FR_OK) { /* It is ready to remove the object */
5161 res = dir_remove(&dj); /* Remove the directory entry */
5162 if (res == FR_OK && dclst != 0) { /* Remove the cluster chain if exist */
5163#if FF_FS_EXFAT
5164 res = remove_chain(&obj, dclst, 0);
5165#else
5166 res = remove_chain(&dj.obj, dclst, 0);
5167#endif
5168 }
5169 if (res == FR_OK) res = sync_fs(fs);
5170 }
5171 FREE_NAMEBUFF();
5172 }
5173
5174 LEAVE_FF(fs, res);
5175}
5176
5177
5178
5179
5180/*-----------------------------------------------------------------------*/
5181/* API: Create a Directory */
5182/*-----------------------------------------------------------------------*/
5183
5184FRESULT f_mkdir (
5185 const TCHAR* path /* Pointer to the directory path */
5186)
5187{
5188 FRESULT res;
5189 FATFS *fs;
5190 DIR dj;
5191 FFOBJID sobj;
5192 DWORD dcl, pcl, tm;
5193 DEF_NAMEBUFF
5194
5195
5196 res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive and mount the volume if needed */
5197 if (res == FR_OK) {
5198 dj.obj.fs = fs;
5199 INIT_NAMEBUFF(fs);
5200 res = follow_path(&dj, path); /* Follow the file path */
5201 if (res == FR_OK) { /* Invalid name or name collision */
5202 res = (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME)) ? FR_INVALID_NAME : FR_EXIST;
5203 }
5204 if (res == FR_NO_FILE) { /* It is clear to create a new directory */
5205 sobj.fs = fs; /* New object ID to create a new chain */
5206 dcl = create_chain(&sobj, 0); /* Allocate a cluster for the new directory */
5207 res = FR_OK;
5208 if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster? */
5209 if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */
5210 if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk error? */
5211 tm = GET_FATTIME();
5212 if (res == FR_OK) {
5213 res = dir_clear(fs, dcl); /* Clear the allocated cluster as new direcotry table */
5214 if (res == FR_OK) {
5215 if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */
5216 memset(fs->win + DIR_Name, ' ', 11); /* Create "." entry */
5217 fs->win[DIR_Name] = '.';
5218 fs->win[DIR_Attr] = AM_DIR;
5219 st_32(fs->win + DIR_ModTime, tm);
5220 st_clust(fs, fs->win, dcl);
5221 memcpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */
5222 fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;
5223 st_clust(fs, fs->win + SZDIRE, pcl);
5224 fs->wflag = 1;
5225 }
5226 res = dir_register(&dj); /* Register the object to the parent directory */
5227 }
5228 }
5229 if (res == FR_OK) {
5230#if FF_FS_EXFAT
5231 if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */
5232 st_32(fs->dirbuf + XDIR_CrtTime, tm); /* Created time */
5233 st_32(fs->dirbuf + XDIR_ModTime, tm);
5234 st_32(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */
5235 st_32(fs->dirbuf + XDIR_FileSize, (DWORD)fs->csize * SS(fs)); /* Directory size needs to be valid */
5236 st_32(fs->dirbuf + XDIR_ValidFileSize, (DWORD)fs->csize * SS(fs));
5237 fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag */
5238 fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */
5239 res = store_xdir(&dj);
5240 } else
5241#endif
5242 {
5243 st_32(dj.dir + DIR_CrtTime, tm); /* Created time */
5244 st_32(dj.dir + DIR_ModTime, tm);
5245 st_clust(fs, dj.dir, dcl); /* Table start cluster */
5246 dj.dir[DIR_Attr] = AM_DIR; /* Attribute */
5247 fs->wflag = 1;
5248 }
5249 if (res == FR_OK) {
5250 res = sync_fs(fs);
5251 }
5252 } else {
5253 remove_chain(&sobj, dcl, 0); /* Could not register, remove the allocated cluster */
5254 }
5255 }
5256 FREE_NAMEBUFF();
5257 }
5258
5259 LEAVE_FF(fs, res);
5260}
5261
5262
5263
5264
5265/*-----------------------------------------------------------------------*/
5266/* API: Rename a File/Directory */
5267/*-----------------------------------------------------------------------*/
5268
5269FRESULT f_rename (
5270 const TCHAR* path_old, /* Pointer to the object name to be renamed */
5271 const TCHAR* path_new /* Pointer to the new name */
5272)
5273{
5274 FRESULT res;
5275 FATFS *fs;
5276 DIR djo, djn;
5277 BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir;
5278 DEF_NAMEBUFF
5279
5280
5281 get_ldnumber(&path_new); /* Snip the drive number of new name off */
5282 res = mount_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */
5283 if (res == FR_OK) {
5284 djo.obj.fs = fs;
5285 INIT_NAMEBUFF(fs);
5286 res = follow_path(&djo, path_old); /* Check old object */
5287 if (res == FR_OK) {
5288 if (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME)) {
5289 res = FR_INVALID_NAME; /* Object must not be a dot name or blank name */
5290#if FF_FS_LOCK
5291 } else {
5292 res = chk_share(&djo, 2); /* Check if the object is in use */
5293#endif
5294 }
5295 }
5296 if (res == FR_OK) { /* It is ready to rename the object */
5297#if FF_FS_EXFAT
5298 if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */
5299#if FF_FS_RPATH
5300 UINT i;
5301 DWORD dscl = ld_32(fs->dirbuf + XDIR_FstClus);
5302
5303 for (i = 1; i <= fs->xcwds.depth && dscl != fs->xcwds.tbl[i].d_scl; i++) ; /* Check if the object is a sub-dir in the current dir path */
5304 if (i <= fs->xcwds.depth) {
5305 res = FR_DENIED; /* Reject to rename a sub-dir in the current dir path */
5306 } else
5307#endif
5308 {
5309 memcpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */
5310 memcpy(&djn, &djo, sizeof djn);
5311 res = follow_path(&djn, path_new); /* Check if new object name collides with an existing one */
5312 }
5313 if (res == FR_OK) { /* Is new name already in use by another object? */
5314 res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
5315 }
5316 if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
5317 res = dir_register(&djn); /* Register the new entry */
5318 if (res == FR_OK) {
5319 BYTE nf, nn;
5320 WORD nh;
5321
5322 nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; /* Save name length and hash */
5323 nh = ld_16(fs->dirbuf + XDIR_NameHash);
5324 memcpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */
5325 fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; /* Restore name length and hash */
5326 st_16(fs->dirbuf + XDIR_NameHash, nh);
5327 if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
5328/* Start of critical section where an interruption can cause a cross-link */
5329 res = store_xdir(&djn);
5330 }
5331 }
5332 } else
5333#endif
5334 { /* At FAT/FAT32 volume */
5335 memcpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */
5336 memcpy(&djn, &djo, sizeof djn); /* Duplicate the directory object */
5337 res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
5338 if (res == FR_OK) { /* Is new name already in use by another object? */
5339 res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
5340 }
5341 if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
5342 res = dir_register(&djn); /* Register the new entry */
5343 if (res == FR_OK) {
5344 dir = djn.dir; /* Copy directory entry of the object except name */
5345 memcpy(dir + 13, buf + 13, SZDIRE - 13);
5346 dir[DIR_Attr] = buf[DIR_Attr];
5347 if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
5348 fs->wflag = 1;
5349 if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory being moved if needed */
5350 LBA_t sect = clst2sect(fs, ld_clust(fs, dir));
5351
5352 if (sect == 0) {
5353 res = FR_INT_ERR;
5354 } else {
5355/* Start of critical section where an interruption can cause a cross-link */
5356 res = move_window(fs, sect);
5357 dir = fs->win + SZDIRE * 1; /* Pointer to .. entry */
5358 if (res == FR_OK && dir[1] == '.') {
5359 st_clust(fs, dir, djn.obj.sclust);
5360 fs->wflag = 1;
5361 }
5362 }
5363 }
5364 }
5365 }
5366 }
5367 if (res == FR_OK) { /* New entry has been created */
5368 res = dir_remove(&djo); /* Remove old entry */
5369 if (res == FR_OK) {
5370 res = sync_fs(fs);
5371 }
5372 }
5373/* End of the critical section */
5374 }
5375 FREE_NAMEBUFF();
5376 }
5377
5378 LEAVE_FF(fs, res);
5379}
5380
5381#endif /* !FF_FS_READONLY */
5382#endif /* FF_FS_MINIMIZE == 0 */
5383#endif /* FF_FS_MINIMIZE <= 1 */
5384#endif /* FF_FS_MINIMIZE <= 2 */
5385
5386
5387
5388#if FF_USE_CHMOD && !FF_FS_READONLY
5389/*-----------------------------------------------------------------------*/
5390/* API: Change Attribute */
5391/*-----------------------------------------------------------------------*/
5392
5393FRESULT f_chmod (
5394 const TCHAR* path, /* Pointer to the file path */
5395 BYTE attr, /* Attribute bits to set/clear */
5396 BYTE mask /* Attribute mask to change */
5397)
5398{
5399 FRESULT res;
5400 FATFS *fs;
5401 DIR dj;
5402 DEF_NAMEBUFF
5403
5404
5405 /* Get logical drive and mount the volume if needed */
5406 res = mount_volume(&path, &fs, FA_WRITE);
5407
5408 if (res == FR_OK) {
5409 dj.obj.fs = fs;
5410 INIT_NAMEBUFF(fs);
5411 res = follow_path(&dj, path); /* Follow the file path */
5412 if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */
5413 if (res == FR_OK) {
5414 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */
5415#if FF_FS_EXFAT
5416 if (fs->fs_type == FS_EXFAT) {
5417 fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */
5418 res = store_xdir(&dj);
5419 } else
5420#endif
5421 {
5422 dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
5423 fs->wflag = 1;
5424 }
5425 if (res == FR_OK) {
5426 res = sync_fs(fs);
5427 }
5428 }
5429 FREE_NAMEBUFF();
5430 }
5431
5432 LEAVE_FF(fs, res);
5433}
5434
5435
5436
5437
5438/*-----------------------------------------------------------------------*/
5439/* API: Change Timestamp */
5440/*-----------------------------------------------------------------------*/
5441
5442FRESULT f_utime (
5443 const TCHAR* path, /* Pointer to the file/directory name */
5444 const FILINFO* fno /* Timestamp to be set */
5445)
5446{
5447 FRESULT res;
5448 FATFS *fs;
5449 DIR dj;
5450 DEF_NAMEBUFF
5451
5452
5453 /* Get logical drive and mount the volume if needed */
5454 res = mount_volume(&path, &fs, FA_WRITE);
5455
5456 if (res == FR_OK) {
5457 dj.obj.fs = fs;
5458 INIT_NAMEBUFF(fs);
5459 res = follow_path(&dj, path); /* Follow the file path */
5460 if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */
5461 if (res == FR_OK) {
5462#if FF_FS_EXFAT
5463 if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
5464 if (fno->fdate) { /* Change last modified time if needed */
5465 st_32(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
5466 fs->dirbuf[XDIR_ModTime10] = 0;
5467 fs->dirbuf[XDIR_ModTZ] = 0;
5468 }
5469#if FF_FS_CRTIME
5470 if (fno->crdate) { /* Change created time if needed */
5471 st_32(fs->dirbuf + XDIR_CrtTime, (DWORD)fno->crdate << 16 | fno->crtime);
5472 fs->dirbuf[XDIR_CrtTime10] = 0;
5473 fs->dirbuf[XDIR_CrtTZ] = 0;
5474 }
5475#endif
5476 res = store_xdir(&dj);
5477 } else
5478#endif
5479 { /* On the FAT volume */
5480 if (fno->fdate) { /* Change last modified time if needed */
5481 st_32(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
5482 }
5483#if FF_FS_CRTIME
5484 if (fno->crdate) { /* Change created time if needed */
5485 st_32(dj.dir + DIR_CrtTime, (DWORD)fno->crdate << 16 | fno->crtime);
5486 dj.dir[DIR_CrtTime10] = 0;
5487 }
5488#endif
5489 fs->wflag = 1;
5490 }
5491 if (res == FR_OK) {
5492 res = sync_fs(fs);
5493 }
5494 }
5495 FREE_NAMEBUFF();
5496 }
5497
5498 LEAVE_FF(fs, res);
5499}
5500
5501#endif /* FF_USE_CHMOD && !FF_FS_READONLY */
5502
5503
5504
5505#if FF_USE_LABEL
5506/*-----------------------------------------------------------------------*/
5507/* API: Get Volume Label */
5508/*-----------------------------------------------------------------------*/
5509
5510FRESULT f_getlabel (
5511 const TCHAR* path, /* Logical drive number */
5512 TCHAR* label, /* Buffer to store the volume label */
5513 DWORD* vsn /* Variable to store the volume serial number */
5514)
5515{
5516 FRESULT res;
5517 FATFS *fs;
5518 DIR dj;
5519 UINT si, di;
5520 WCHAR wc;
5521
5522
5523 res = mount_volume(&path, &fs, 0); /* Get logical drive and mount the volume if needed */
5524
5525 /* Get volume label */
5526 if (res == FR_OK && label) {
5527 dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */
5528 res = dir_sdi(&dj, 0);
5529 if (res == FR_OK) {
5530 res = DIR_READ_LABEL(&dj); /* Find a volume label entry */
5531 if (res == FR_OK) {
5532#if FF_FS_EXFAT
5533 if (fs->fs_type == FS_EXFAT) {
5534 WCHAR hs;
5535 UINT nw;
5536
5537 for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */
5538 wc = ld_16(dj.dir + XDIR_Label + si * 2);
5539 if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */
5540 hs = wc; continue;
5541 }
5542 nw = put_utf((DWORD)hs << 16 | wc, &label[di], 4); /* Store it in API encoding */
5543 if (nw == 0) { /* Encode error? */
5544 di = 0; break;
5545 }
5546 di += nw;
5547 hs = 0;
5548 }
5549 if (hs != 0) di = 0; /* Broken surrogate pair? */
5550 label[di] = 0;
5551 } else
5552#endif
5553 {
5554 si = di = 0; /* Extract volume label from AM_VOL entry */
5555 while (si < 11) {
5556 wc = dj.dir[si++];
5557#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
5558 if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */
5559 wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */
5560 if (wc == 0) { /* Invalid char in current code page? */
5561 di = 0; break;
5562 }
5563 di += put_utf(wc, &label[di], 4); /* Store it in Unicode */
5564#else /* ANSI/OEM output */
5565 label[di++] = (TCHAR)wc;
5566#endif
5567 }
5568 do { /* Truncate trailing spaces */
5569 label[di] = 0;
5570 if (di == 0) break;
5571 } while (label[--di] == ' ');
5572 }
5573 }
5574 }
5575 if (res == FR_NO_FILE) { /* No label entry and return nul string */
5576 label[0] = 0;
5577 res = FR_OK;
5578 }
5579 }
5580
5581 /* Get volume serial number */
5582 if (res == FR_OK && vsn) {
5583 res = move_window(fs, fs->volbase); /* Load VBR */
5584 if (res == FR_OK) {
5585 switch (fs->fs_type) {
5586 case FS_EXFAT:
5587 di = BPB_VolIDEx;
5588 break;
5589
5590 case FS_FAT32:
5591 di = BS_VolID32;
5592 break;
5593
5594 default: /* FAT12/16 */
5595 di = fs->win[BS_BootSig] == 0x29 ? BS_VolID : 0;
5596 }
5597 *vsn = di ? ld_32(fs->win + di) : 0; /* Get VSN in the VBR */
5598 }
5599 }
5600
5601 LEAVE_FF(fs, res);
5602}
5603
5604
5605
5606#if !FF_FS_READONLY
5607/*-----------------------------------------------------------------------*/
5608/* API: Set Volume Label */
5609/*-----------------------------------------------------------------------*/
5610
5611FRESULT f_setlabel (
5612 const TCHAR* label /* Volume label to set with heading logical drive number */
5613)
5614{
5615 FRESULT res;
5616 FATFS *fs;
5617 DIR dj;
5618 BYTE dirvn[22];
5619 UINT di;
5620 WCHAR wc;
5621 static const char badchr[18] = "+.,;=[]" "/*:<>|\\\"\?\x7F"; /* [0..16] for FAT, [7..16] for exFAT */
5622#if FF_USE_LFN
5623 DWORD dc;
5624#endif
5625
5626 /* Get logical drive and mount the volume if needed */
5627 res = mount_volume(&label, &fs, FA_WRITE);
5628 if (res != FR_OK) LEAVE_FF(fs, res);
5629#if FF_STR_VOLUME_ID == 2
5630 for ( ; *label == '/'; label++) ; /* Snip the separators off */
5631#endif
5632
5633#if FF_FS_EXFAT
5634 if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
5635 memset(dirvn, 0, 22);
5636 di = 0;
5637 while ((UINT)*label >= ' ') { /* Create volume label */
5638 dc = tchar2uni(&label); /* Get a Unicode character */
5639 if (dc >= 0x10000) {
5640 if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */
5641 dc = 0;
5642 } else {
5643 st_16(dirvn + di * 2, (WCHAR)(dc >> 16)); di++;
5644 }
5645 }
5646 if (dc == 0 || strchr(&badchr[7], (int)dc) || di >= 11) { /* Check validity of the volume label */
5647 LEAVE_FF(fs, FR_INVALID_NAME);
5648 }
5649 st_16(dirvn + di * 2, (WCHAR)dc); di++;
5650 }
5651 } else
5652#endif
5653 { /* On the FAT/FAT32 volume */
5654 memset(dirvn, ' ', 11);
5655 di = 0;
5656 while ((UINT)*label >= ' ') { /* Create volume label */
5657#if FF_USE_LFN
5658 dc = tchar2uni(&label);
5659 wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0;
5660#else /* ANSI/OEM input */
5661 wc = (BYTE)*label++;
5662 if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0;
5663 if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */
5664#if FF_CODE_PAGE == 0
5665 if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
5666#elif FF_CODE_PAGE < 900
5667 if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
5668#endif
5669#endif
5670 if (wc == 0 || strchr(&badchr[0], (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */
5671 LEAVE_FF(fs, FR_INVALID_NAME);
5672 }
5673 if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8);
5674 dirvn[di++] = (BYTE)wc;
5675 }
5676 if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */
5677 while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */
5678 }
5679
5680 /* Set volume label */
5681 dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */
5682 res = dir_sdi(&dj, 0);
5683 if (res == FR_OK) {
5684 res = DIR_READ_LABEL(&dj); /* Get volume label entry */
5685 if (res == FR_OK) {
5686 if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
5687 dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */
5688 memcpy(dj.dir + XDIR_Label, dirvn, 22);
5689 } else {
5690 if (di != 0) {
5691 memcpy(dj.dir, dirvn, 11); /* Change the volume label */
5692 } else {
5693 dj.dir[DIR_Name] = DDEM; /* Remove the volume label */
5694 }
5695 }
5696 fs->wflag = 1;
5697 res = sync_fs(fs);
5698 } else { /* No volume label entry or an error */
5699 if (res == FR_NO_FILE) {
5700 res = FR_OK;
5701 if (di != 0) { /* Create a volume label entry */
5702 res = dir_alloc(&dj, 1); /* Allocate an entry */
5703 if (res == FR_OK) {
5704 memset(dj.dir, 0, SZDIRE); /* Clean the entry */
5705 if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
5706 dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */
5707 dj.dir[XDIR_NumLabel] = (BYTE)di;
5708 memcpy(dj.dir + XDIR_Label, dirvn, 22);
5709 } else {
5710 dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */
5711 memcpy(dj.dir, dirvn, 11);
5712 }
5713 fs->wflag = 1;
5714 res = sync_fs(fs);
5715 }
5716 }
5717 }
5718 }
5719 }
5720
5721 LEAVE_FF(fs, res);
5722}
5723
5724#endif /* !FF_FS_READONLY */
5725#endif /* FF_USE_LABEL */
5726
5727
5728
5729#if FF_USE_EXPAND && !FF_FS_READONLY
5730/*-----------------------------------------------------------------------*/
5731/* API: Allocate a Contiguous Blocks to the File */
5732/*-----------------------------------------------------------------------*/
5733
5734FRESULT f_expand (
5735 FIL* fp, /* Pointer to the file object */
5736 FSIZE_t fsz, /* File size to be expanded to */
5737 BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */
5738)
5739{
5740 FRESULT res;
5741 FATFS *fs;
5742 DWORD n, clst, stcl, scl, ncl, tcl, lclst;
5743
5744
5745 res = validate(&fp->obj, &fs); /* Check validity of the file object */
5746 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
5747 if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);
5748#if FF_FS_EXFAT
5749 if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */
5750#endif
5751 n = (DWORD)fs->csize * SS(fs); /* Cluster size */
5752 tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */
5753 stcl = fs->last_clst; lclst = 0;
5754 if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2;
5755
5756#if FF_FS_EXFAT
5757 if (fs->fs_type == FS_EXFAT) {
5758 scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */
5759 if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */
5760 if (scl == 0xFFFFFFFF) res = FR_DISK_ERR;
5761 if (res == FR_OK) { /* A contiguous free area is found */
5762 if (opt) { /* Allocate it now */
5763 res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */
5764 lclst = scl + tcl - 1;
5765 } else { /* Set it as suggested point for next allocation */
5766 lclst = scl - 1;
5767 }
5768 }
5769 } else
5770#endif
5771 {
5772 scl = clst = stcl; ncl = 0;
5773 for (;;) { /* Find a contiguous cluster block */
5774 n = get_fat(&fp->obj, clst);
5775 if (++clst >= fs->n_fatent) clst = 2;
5776 if (n == 1) {
5777 res = FR_INT_ERR; break;
5778 }
5779 if (n == 0xFFFFFFFF) {
5780 res = FR_DISK_ERR; break;
5781 }
5782 if (n == 0) { /* Is it a free cluster? */
5783 if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */
5784 } else {
5785 scl = clst; ncl = 0; /* Not a free cluster */
5786 }
5787 if (clst == stcl) { /* No contiguous cluster? */
5788 res = FR_DENIED; break;
5789 }
5790 }
5791 if (res == FR_OK) { /* A contiguous free area is found */
5792 if (opt) { /* Allocate it now */
5793 for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */
5794 res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1);
5795 if (res != FR_OK) break;
5796 lclst = clst;
5797 }
5798 } else { /* Set it as suggested point for next allocation */
5799 lclst = scl - 1;
5800 }
5801 }
5802 }
5803
5804 if (res == FR_OK) {
5805 fs->last_clst = lclst; /* Set suggested start cluster to start next */
5806 if (opt) { /* Is it allocated now? */
5807 fp->obj.sclust = scl; /* Update object allocation information */
5808 fp->obj.objsize = fsz;
5809 if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */
5810 fp->flag |= FA_MODIFIED;
5811 if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */
5812 fs->free_clst -= tcl;
5813 fs->fsi_flag |= 1;
5814 }
5815 }
5816 }
5817
5818 LEAVE_FF(fs, res);
5819}
5820
5821#endif /* FF_USE_EXPAND && !FF_FS_READONLY */
5822
5823
5824
5825#if FF_USE_FORWARD
5826/*-----------------------------------------------------------------------*/
5827/* API: Forward Data to the Stream Directly */
5828/*-----------------------------------------------------------------------*/
5829
5830FRESULT f_forward (
5831 FIL* fp, /* Pointer to the file object */
5832 UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
5833 UINT btf, /* Number of bytes to forward */
5834 UINT* bf /* Pointer to number of bytes forwarded */
5835)
5836{
5837 FRESULT res;
5838 FATFS *fs;
5839 DWORD clst;
5840 LBA_t sect;
5841 FSIZE_t remain;
5842 UINT rcnt, csect;
5843 BYTE *dbuf;
5844
5845
5846 *bf = 0; /* Clear transfer byte counter */
5847 res = validate(&fp->obj, &fs); /* Check validity of the file object */
5848 if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
5849 if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
5850
5851 remain = fp->obj.objsize - fp->fptr;
5852 if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */
5853
5854 for ( ; btf > 0 && (*func)(0, 0); fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { /* Repeat until all data transferred or stream goes busy */
5855 csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */
5856 if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
5857 if (csect == 0) { /* On the cluster boundary? */
5858 clst = (fp->fptr == 0) ? /* On the top of the file? */
5859 fp->obj.sclust : get_fat(&fp->obj, fp->clust);
5860 if (clst <= 1) ABORT(fs, FR_INT_ERR);
5861 if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
5862 fp->clust = clst; /* Update current cluster */
5863 }
5864 }
5865 sect = clst2sect(fs, fp->clust); /* Get current data sector */
5866 if (sect == 0) ABORT(fs, FR_INT_ERR);
5867 sect += csect;
5868#if FF_FS_TINY
5869 if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */
5870 dbuf = fs->win;
5871#else
5872 if (fp->sect != sect) { /* Fill sector cache with file data */
5873#if !FF_FS_READONLY
5874 if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
5875 if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
5876 fp->flag &= (BYTE)~FA_DIRTY;
5877 }
5878#endif
5879 if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
5880 }
5881 dbuf = fp->buf;
5882#endif
5883 fp->sect = sect;
5884 rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */
5885 if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */
5886 rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */
5887 if (rcnt == 0) ABORT(fs, FR_INT_ERR);
5888 }
5889
5890 LEAVE_FF(fs, FR_OK);
5891}
5892#endif /* FF_USE_FORWARD */
5893
5894
5895
5896#if !FF_FS_READONLY && FF_USE_MKFS
5897/*-----------------------------------------------------------------------*/
5898/* API: Create FAT/exFAT volume (with a sub-function) */
5899/*-----------------------------------------------------------------------*/
5900
5901#define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
5902#define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT [byte] (>=128KB) */
5903#define GPT_ITEMS 128 /* Number of GPT table items (>=128, sector aligned) */
5904
5905
5906/* Create partitions on the physical drive in format of MBR or GPT */
5907
5908static FRESULT create_partition (
5909 BYTE drv, /* Physical drive number */
5910 const LBA_t plst[], /* Partition list */
5911 BYTE sys, /* System ID for each partition (for only MBR) */
5912 BYTE *buf /* Working buffer for a sector */
5913)
5914{
5915 UINT i, cy;
5916 LBA_t sz_drv;
5917 DWORD sz_drv32, nxt_alloc32, sz_part32;
5918 BYTE *pte;
5919 BYTE hd, n_hd, sc, n_sc;
5920
5921 /* Get physical drive size */
5922 if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR;
5923
5924#if FF_LBA64
5925 if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT format */
5926 WORD ss;
5927 UINT sz_ptbl, pi, si, ofs;
5928 DWORD bcc, rnd, align;
5929 QWORD nxt_alloc, sz_part, sz_pool, top_bpt;
5930 static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
5931
5932#if FF_MAX_SS != FF_MIN_SS
5933 if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; /* Get sector size */
5934 if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
5935#else
5936 ss = FF_MAX_SS;
5937#endif
5938 rnd = (DWORD)sz_drv + GET_FATTIME(); /* Random seed */
5939 align = GPT_ALIGN / ss; /* Partition alignment for GPT [sector] */
5940 sz_ptbl = GPT_ITEMS * SZ_GPTE / ss; /* Size of partition table [sector] */
5941 top_bpt = sz_drv - sz_ptbl - 1; /* Backup partition table start LBA */
5942 nxt_alloc = 2 + sz_ptbl; /* First allocatable LBA */
5943 sz_pool = top_bpt - nxt_alloc; /* Size of allocatable area [sector] */
5944 bcc = 0xFFFFFFFF; sz_part = 1;
5945 pi = si = 0; /* partition table index, map index */
5946 do {
5947 if (pi * SZ_GPTE % ss == 0) memset(buf, 0, ss); /* Clean the buffer if needed */
5948 if (sz_part != 0) { /* Is the size table not termintated? */
5949 nxt_alloc = (nxt_alloc + align - 1) & ((QWORD)0 - align); /* Align partition start LBA */
5950 sz_part = plst[si++]; /* Get a partition size */
5951 if (sz_part <= 100) { /* Is the size in percentage? */
5952 sz_part = sz_pool * sz_part / 100; /* Sectors in percentage */
5953 sz_part = (sz_part + align - 1) & ((QWORD)0 - align); /* Align partition end LBA (only if in percentage) */
5954 }
5955 if (nxt_alloc + sz_part > top_bpt) { /* Clip the size at end of the pool */
5956 sz_part = (nxt_alloc < top_bpt) ? top_bpt - nxt_alloc : 0;
5957 }
5958 }
5959 if (sz_part != 0) { /* Add a partition? */
5960 ofs = pi * SZ_GPTE % ss;
5961 memcpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Set partition GUID (Microsoft Basic Data) */
5962 rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Set unique partition GUID */
5963 st_64(buf + ofs + GPTE_FstLba, nxt_alloc); /* Set partition start LBA */
5964 st_64(buf + ofs + GPTE_LstLba, nxt_alloc + sz_part - 1); /* Set partition end LBA */
5965 nxt_alloc += sz_part; /* Next allocatable LBA */
5966 }
5967 if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the sector buffer if it is filled up */
5968 for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; /* Calculate table check sum */
5969 if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to primary table */
5970 if (disk_write(drv, buf, top_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to secondary table */
5971 }
5972 } while (++pi < GPT_ITEMS);
5973
5974 /* Create primary GPT header */
5975 memset(buf, 0, ss);
5976 memcpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */
5977 st_32(buf + GPTH_PtBcc, ~bcc); /* Table check sum */
5978 st_64(buf + GPTH_CurLba, 1); /* LBA of this header */
5979 st_64(buf + GPTH_BakLba, sz_drv - 1); /* LBA of secondary header */
5980 st_64(buf + GPTH_FstLba, 2 + sz_ptbl); /* LBA of first allocatable sector */
5981 st_64(buf + GPTH_LstLba, top_bpt - 1); /* LBA of last allocatable sector */
5982 st_32(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */
5983 st_32(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */
5984 st_32(buf + GPTH_PtOfs, 2); /* LBA of this table */
5985 rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk GUID */
5986 for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */
5987 st_32(buf + GPTH_Bcc, ~bcc); /* Header check sum */
5988 if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR;
5989
5990 /* Create secondary GPT header */
5991 st_64(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */
5992 st_64(buf + GPTH_BakLba, 1); /* LBA of primary header */
5993 st_64(buf + GPTH_PtOfs, top_bpt); /* LBA of this table */
5994 st_32(buf + GPTH_Bcc, 0);
5995 for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */
5996 st_32(buf + GPTH_Bcc, ~bcc); /* Header check sum */
5997 if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return FR_DISK_ERR;
5998
5999 /* Create protective MBR */
6000 memset(buf, 0, ss);
6001 memcpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */
6002 st_16(buf + BS_55AA, 0xAA55);
6003 if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;
6004
6005 } else
6006#endif
6007 { /* Create partitions in MBR format */
6008 sz_drv32 = (DWORD)sz_drv;
6009 n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */
6010 for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ;
6011 if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */
6012
6013 memset(buf, 0, FF_MAX_SS); /* Clear MBR */
6014 pte = buf + MBR_Table; /* Partition table in the MBR */
6015 for (i = 0, nxt_alloc32 = n_sc; i < 4 && nxt_alloc32 != 0 && nxt_alloc32 < sz_drv32; i++, nxt_alloc32 += sz_part32) {
6016 sz_part32 = (DWORD)plst[i]; /* Get partition size */
6017 if (sz_part32 <= 100) sz_part32 = (sz_part32 == 100) ? sz_drv32 : sz_drv32 / 100 * sz_part32; /* Size in percentage? */
6018 if (nxt_alloc32 + sz_part32 > sz_drv32 || nxt_alloc32 + sz_part32 < nxt_alloc32) sz_part32 = sz_drv32 - nxt_alloc32; /* Clip at drive size */
6019 if (sz_part32 == 0) break; /* End of table or no sector to allocate? */
6020
6021 st_32(pte + PTE_StLba, nxt_alloc32); /* Partition start LBA sector */
6022 st_32(pte + PTE_SizLba, sz_part32); /* Size of partition [sector] */
6023 pte[PTE_System] = sys; /* System type */
6024
6025 cy = (UINT)(nxt_alloc32 / n_sc / n_hd); /* Partitio start CHS cylinder */
6026 hd = (BYTE)(nxt_alloc32 / n_sc % n_hd); /* Partition start CHS head */
6027 sc = (BYTE)(nxt_alloc32 % n_sc + 1); /* Partition start CHS sector */
6028 pte[PTE_StHead] = hd;
6029 pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
6030 pte[PTE_StCyl] = (BYTE)cy;
6031
6032 cy = (UINT)((nxt_alloc32 + sz_part32 - 1) / n_sc / n_hd); /* Partition end CHS cylinder */
6033 hd = (BYTE)((nxt_alloc32 + sz_part32 - 1) / n_sc % n_hd); /* Partition end CHS head */
6034 sc = (BYTE)((nxt_alloc32 + sz_part32 - 1) % n_sc + 1); /* Partition end CHS sector */
6035 pte[PTE_EdHead] = hd;
6036 pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
6037 pte[PTE_EdCyl] = (BYTE)cy;
6038
6039 pte += SZ_PTE; /* Next entry */
6040 }
6041
6042 st_16(buf + BS_55AA, 0xAA55); /* MBR signature */
6043 if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */
6044 }
6045
6046 return FR_OK;
6047}
6048
6049
6050
6051FRESULT f_mkfs (
6052 const TCHAR* path, /* Logical drive number */
6053 const MKFS_PARM* opt, /* Format options */
6054 void* work, /* Pointer to working buffer (null: use len bytes of heap memory) */
6055 UINT len /* Size of working buffer [byte] */
6056)
6057{
6058 static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4K sector unit) */
6059 static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128K sector unit) */
6060 static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
6061 BYTE fsopt, fsty, sys, pdrv, ipart;
6062 BYTE *buf;
6063 BYTE *pte;
6064 WORD ss; /* Sector size */
6065 DWORD sz_buf, sz_blk, n_clst, pau, nsect, n, vsn;
6066 LBA_t sz_vol, b_vol, b_fat, b_data; /* Volume size, base LBA of volume, base LBA of FAT and base LBA of data */
6067 LBA_t sect, lba[2];
6068 DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved area, FAT area, directry area, data area and cluster */
6069 UINT n_fat, n_root, i; /* Number of FATs, number of roor directory entries and some index */
6070 int vol;
6071 DSTATUS ds;
6072 FRESULT res;
6073
6074
6075 /* Check mounted drive and clear work area */
6076 vol = get_ldnumber(&path); /* Get logical drive number to be formatted */
6077 if (vol < 0) return FR_INVALID_DRIVE;
6078 if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the fs object if mounted */
6079 pdrv = LD2PD(vol); /* Hosting physical drive */
6080 ipart = LD2PT(vol); /* Hosting partition (0:create as new, 1..:existing partition) */
6081
6082 /* Initialize the hosting physical drive */
6083 ds = disk_initialize(pdrv);
6084 if (ds & STA_NOINIT) return FR_NOT_READY;
6085 if (ds & STA_PROTECT) return FR_WRITE_PROTECTED;
6086
6087 /* Get physical drive parameters (sz_drv, sz_blk and ss) */
6088 if (!opt) opt = &defopt; /* Use default parameter if it is not given */
6089 sz_blk = opt->align;
6090 if (sz_blk == 0) disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk); /* Block size from the parameter or lower layer */
6091 if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Use default if the block size is invalid */
6092#if FF_MAX_SS != FF_MIN_SS
6093 if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;
6094 if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
6095#else
6096 ss = FF_MAX_SS;
6097#endif
6098
6099 /* Options for FAT sub-type and FAT parameters */
6100 fsopt = opt->fmt & (FM_ANY | FM_SFD);
6101 n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1;
6102 n_root = (opt->n_root >= 1 && opt->n_root <= 32768 && (opt->n_root % (ss / SZDIRE)) == 0) ? opt->n_root : 512;
6103 sz_au = (opt->au_size <= 0x1000000 && (opt->au_size & (opt->au_size - 1)) == 0) ? opt->au_size : 0;
6104 sz_au /= ss; /* Byte --> Sector */
6105
6106 /* Get working buffer */
6107 sz_buf = len / ss; /* Size of working buffer [sector] */
6108 if (sz_buf == 0) return FR_NOT_ENOUGH_CORE;
6109 buf = (BYTE*)work; /* Working buffer */
6110#if FF_USE_LFN == 3
6111 if (!buf) buf = ff_memalloc(sz_buf * ss); /* Use heap memory for working buffer */
6112#endif
6113 if (!buf) return FR_NOT_ENOUGH_CORE;
6114
6115 /* Determine where the volume to be located (b_vol, sz_vol) */
6116 b_vol = sz_vol = 0;
6117 if (FF_MULTI_PARTITION && ipart != 0) { /* Is the volume associated with any specific partition? */
6118 /* Get partition location from the existing partition table */
6119 if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */
6120 if (ld_16(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */
6121#if FF_LBA64
6122 if (buf[MBR_Table + PTE_System] == 0xEE) { /* GPT protective MBR? */
6123 DWORD n_ent, ofs;
6124 QWORD pt_lba;
6125
6126 /* Get the partition location from GPT */
6127 if (disk_read(pdrv, buf, 1, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load GPT header sector (next to MBR) */
6128 if (!test_gpt_header(buf)) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if GPT header is valid */
6129 n_ent = ld_32(buf + GPTH_PtNum); /* Number of entries */
6130 pt_lba = ld_64(buf + GPTH_PtOfs); /* Table start sector */
6131 ofs = i = 0;
6132 while (n_ent) { /* Find MS Basic partition with order of ipart */
6133 if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */
6134 if (!memcmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */
6135 b_vol = ld_64(buf + ofs + GPTE_FstLba);
6136 sz_vol = ld_64(buf + ofs + GPTE_LstLba) - b_vol + 1;
6137 break;
6138 }
6139 n_ent--; ofs = (ofs + SZ_GPTE) % ss; /* Next entry */
6140 }
6141 if (n_ent == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* Partition not found */
6142 fsopt |= 0x80; /* Partitioning is in GPT */
6143 } else
6144#endif
6145 { /* Get the partition location from MBR partition table */
6146 pte = buf + (MBR_Table + (ipart - 1) * SZ_PTE);
6147 if (ipart > 4 || pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */
6148 b_vol = ld_32(pte + PTE_StLba); /* Get volume start sector */
6149 sz_vol = ld_32(pte + PTE_SizLba); /* Get volume size */
6150 }
6151 } else { /* The volume is associated with a physical drive */
6152 if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6153 if (!(fsopt & FM_SFD)) { /* To be partitioned? */
6154 /* Create a single-partition on the drive in this function */
6155#if FF_LBA64
6156 if (sz_vol >= FF_MIN_GPT) { /* Which partition type to create, MBR or GPT? */
6157 fsopt |= 0x80; /* Partitioning is in GPT */
6158 b_vol = GPT_ALIGN / ss; sz_vol -= b_vol + GPT_ITEMS * SZ_GPTE / ss + 1; /* Estimated partition offset and size */
6159 } else
6160#endif
6161 { /* Partitioning is in MBR */
6162 if (sz_vol > N_SEC_TRACK) {
6163 b_vol = N_SEC_TRACK; sz_vol -= b_vol; /* Estimated partition offset and size */
6164 }
6165 }
6166 }
6167 }
6168 if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128 sectors */
6169
6170 /* Now start to create an FAT volume at b_vol and sz_vol */
6171
6172 do { /* Pre-determine the FAT type */
6173 if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT possible? */
6174 if ((fsopt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || sz_au > 128) { /* exFAT only, vol >= 64M sectors or sz_au > 128 sectors? */
6175 fsty = FS_EXFAT; break;
6176 }
6177 }
6178#if FF_LBA64
6179 if (sz_vol >= 0x100000000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too large volume for FAT/FAT32 */
6180#endif
6181 if (sz_au > 128) sz_au = 128; /* Invalid AU for FAT/FAT32? */
6182 if (fsopt & FM_FAT32) { /* FAT32 possible? */
6183 if (!(fsopt & FM_FAT)) { /* no-FAT? */
6184 fsty = FS_FAT32; break;
6185 }
6186 }
6187 if (!(fsopt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */
6188 fsty = FS_FAT16;
6189 } while (0);
6190
6191 vsn = (DWORD)sz_vol + GET_FATTIME(); /* VSN generated from current time and partition size */
6192
6193#if FF_FS_EXFAT
6194 if (fsty == FS_EXFAT) { /* Create an exFAT volume */
6195 DWORD szb_bit, szb_case, sum, nbit, clu, clen[3];
6196 WCHAR ch, si;
6197 UINT j, st;
6198
6199 if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */
6200#if FF_USE_TRIM
6201 lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
6202 disk_ioctl(pdrv, CTRL_TRIM, lba);
6203#endif
6204 /* Determine FAT location, data location and number of clusters */
6205 if (sz_au == 0) { /* AU auto-selection */
6206 sz_au = 8;
6207 if (sz_vol >= 0x80000) sz_au = 64; /* >= 512Ks */
6208 if (sz_vol >= 0x4000000) sz_au = 256; /* >= 64Ms */
6209 }
6210 b_fat = b_vol + 32; /* FAT start at offset 32 */
6211 sz_fat = (DWORD)((sz_vol / sz_au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */
6212 b_data = (b_fat + sz_fat + sz_blk - 1) & ~((LBA_t)sz_blk - 1); /* Align data area to the erase block boundary */
6213 if (b_data - b_vol >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */
6214 n_clst = (DWORD)((sz_vol - (b_data - b_vol)) / sz_au); /* Number of clusters */
6215 if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */
6216 if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */
6217
6218 szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
6219 clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
6220
6221 /* Create a compressed up-case table */
6222 sect = b_data + sz_au * clen[0]; /* Table start sector */
6223 sum = 0; /* Table checksum to be stored in the 82 entry */
6224 st = 0; si = 0; i = 0; j = 0; szb_case = 0;
6225 do {
6226 switch (st) {
6227 case 0:
6228 ch = (WCHAR)ff_wtoupper(si); /* Get an up-case char */
6229 if (ch != si) {
6230 si++; break; /* Store the up-case char if exist */
6231 }
6232 for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */
6233 if (j >= 128) {
6234 ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 chars */
6235 }
6236 st = 1; /* Do not compress short run */
6237 /* FALLTHROUGH */
6238 case 1:
6239 ch = si++; /* Fill the short run */
6240 if (--j == 0) st = 0;
6241 break;
6242
6243 default:
6244 ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars to skip */
6245 st = 0;
6246 }
6247 sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */
6248 sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum);
6249 i += 2; szb_case += 2;
6250 if (si == 0 || i == sz_buf * ss) { /* Write buffered data when buffer full or end of process */
6251 n = (i + ss - 1) / ss;
6252 if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6253 sect += n; i = 0;
6254 }
6255 } while (si);
6256 clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
6257 clen[2] = 1; /* Number of root directory clusters */
6258
6259 /* Initialize the allocation bitmap */
6260 sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of bitmap sectors */
6261 nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */
6262 do {
6263 memset(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */
6264 for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */
6265 n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
6266 if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6267 sect += n; nsect -= n;
6268 } while (nsect);
6269
6270 /* Initialize the FAT */
6271 sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */
6272 j = nbit = clu = 0;
6273 do {
6274 memset(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */
6275 if (clu == 0) { /* Initialize FAT [0] and FAT[1] */
6276 st_32(buf + i, 0xFFFFFFF8); i += 4; clu++;
6277 st_32(buf + i, 0xFFFFFFFF); i += 4; clu++;
6278 }
6279
6280 do { /* Create chains of bitmap, up-case and root directory */
6281 while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */
6282 st_32(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF);
6283 i += 4; clu++; nbit--;
6284 }
6285 if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */
6286 } while (nbit != 0 && i < sz_buf * ss);
6287 n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
6288 if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6289 sect += n; nsect -= n;
6290 } while (nsect);
6291
6292 /* Initialize the root directory */
6293 memset(buf, 0, sz_buf * ss);
6294 buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry (no label) */
6295 buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */
6296 st_32(buf + SZDIRE * 1 + 20, 2); /* cluster */
6297 st_32(buf + SZDIRE * 1 + 24, szb_bit); /* size */
6298 buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */
6299 st_32(buf + SZDIRE * 2 + 4, sum); /* sum */
6300 st_32(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* cluster */
6301 st_32(buf + SZDIRE * 2 + 24, szb_case); /* size */
6302 sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_au; /* Start of the root directory and number of sectors */
6303 do { /* Fill root directory sectors */
6304 n = (nsect > sz_buf) ? sz_buf : nsect;
6305 if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6306 memset(buf, 0, ss); /* Rest of entries are filled with zero */
6307 sect += n; nsect -= n;
6308 } while (nsect);
6309
6310 /* Create two set of the exFAT VBR blocks */
6311 sect = b_vol;
6312 for (n = 0; n < 2; n++) {
6313 /* Main record (+0) */
6314 memset(buf, 0, ss);
6315 memcpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */
6316 st_64(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */
6317 st_64(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */
6318 st_32(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* FAT offset [sector] */
6319 st_32(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
6320 st_32(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
6321 st_32(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
6322 st_32(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root directory cluster number */
6323 st_32(buf + BPB_VolIDEx, vsn); /* VSN */
6324 st_16(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
6325 for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
6326 for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */
6327 buf[BPB_NumFATsEx] = 1; /* Number of FATs */
6328 buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */
6329 st_16(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */
6330 st_16(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */
6331 for (i = sum = 0; i < ss; i++) { /* VBR checksum */
6332 if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum);
6333 }
6334 if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6335 /* Extended bootstrap record (+1..+8) */
6336 memset(buf, 0, ss);
6337 st_16(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */
6338 for (j = 1; j < 9; j++) {
6339 for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */
6340 if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6341 }
6342 /* OEM/Reserved record (+9..+10) */
6343 memset(buf, 0, ss);
6344 for ( ; j < 11; j++) {
6345 for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */
6346 if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6347 }
6348 /* Sum record (+11) */
6349 for (i = 0; i < ss; i += 4) st_32(buf + i, sum); /* Fill with checksum value */
6350 if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6351 }
6352
6353 } else
6354#endif /* FF_FS_EXFAT */
6355 { /* Create an FAT/FAT32 volume */
6356 do {
6357 pau = sz_au;
6358 /* Pre-determine number of clusters and FAT sub-type */
6359 if (fsty == FS_FAT32) { /* FAT32 volume */
6360 if (pau == 0) { /* AU auto-selection */
6361 n = (DWORD)sz_vol / 0x20000; /* Volume size in unit of 128KS */
6362 for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */
6363 }
6364 n_clst = (DWORD)sz_vol / pau; /* Number of clusters */
6365 sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */
6366 sz_rsv = 32; /* Number of reserved sectors */
6367 sz_dir = 0; /* No static directory */
6368 if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED);
6369 } else { /* FAT volume */
6370 if (pau == 0) { /* au auto-selection */
6371 n = (DWORD)sz_vol / 0x1000; /* Volume size in unit of 4KS */
6372 for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */
6373 }
6374 n_clst = (DWORD)sz_vol / pau;
6375 if (n_clst > MAX_FAT12) {
6376 n = n_clst * 2 + 4; /* FAT size [byte] */
6377 } else {
6378 fsty = FS_FAT12;
6379 n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */
6380 }
6381 sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */
6382 sz_rsv = 1; /* Number of reserved sectors */
6383 sz_dir = (DWORD)n_root * SZDIRE / ss; /* Root directory size [sector] */
6384 }
6385 b_fat = b_vol + sz_rsv; /* FAT base */
6386 b_data = b_fat + sz_fat * n_fat + sz_dir; /* Data base */
6387
6388 /* Align data area to erase block boundary (for flash memory media) */
6389 n = (DWORD)(((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data); /* Sectors to next nearest from current data base */
6390 if (fsty == FS_FAT32) { /* FAT32: Move FAT */
6391 sz_rsv += n; b_fat += n;
6392 } else { /* FAT: Expand FAT */
6393 if (n % n_fat) { /* Adjust fractional error if needed */
6394 n--; sz_rsv++; b_fat++;
6395 }
6396 sz_fat += n / n_fat;
6397 }
6398
6399 /* Determine number of clusters and final check of validity of the FAT sub-type */
6400 if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */
6401 n_clst = ((DWORD)sz_vol - sz_rsv - sz_fat * n_fat - sz_dir) / pau;
6402 if (fsty == FS_FAT32) {
6403 if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32? */
6404 if (sz_au == 0 && (sz_au = pau / 2) != 0) continue; /* Adjust cluster size and retry */
6405 LEAVE_MKFS(FR_MKFS_ABORTED);
6406 }
6407 }
6408 if (fsty == FS_FAT16) {
6409 if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */
6410 if (sz_au == 0 && (pau * 2) <= 64) {
6411 sz_au = pau * 2; continue; /* Adjust cluster size and retry */
6412 }
6413 if ((fsopt & FM_FAT32)) {
6414 fsty = FS_FAT32; continue; /* Switch type to FAT32 and retry */
6415 }
6416 if (sz_au == 0 && (sz_au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
6417 LEAVE_MKFS(FR_MKFS_ABORTED);
6418 }
6419 if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */
6420 if (sz_au == 0 && (sz_au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
6421 LEAVE_MKFS(FR_MKFS_ABORTED);
6422 }
6423 }
6424 if (fsty == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */
6425
6426 /* Ok, it is the valid cluster configuration */
6427 break;
6428 } while (1);
6429
6430#if FF_USE_TRIM
6431 lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
6432 disk_ioctl(pdrv, CTRL_TRIM, lba);
6433#endif
6434 /* Create FAT VBR */
6435 memset(buf, 0, ss);
6436 memcpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11); /* Boot jump code (x86), OEM name */
6437 st_16(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */
6438 buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */
6439 st_16(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */
6440 buf[BPB_NumFATs] = (BYTE)n_fat; /* Number of FATs */
6441 st_16(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : n_root)); /* Number of root directory entries */
6442 if (sz_vol < 0x10000) {
6443 st_16(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */
6444 } else {
6445 st_32(buf + BPB_TotSec32, (DWORD)sz_vol); /* Volume size in 32-bit LBA */
6446 }
6447 buf[BPB_Media] = 0xF8; /* Media descriptor byte */
6448 st_16(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */
6449 st_16(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */
6450 st_32(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */
6451 if (fsty == FS_FAT32) {
6452 st_32(buf + BS_VolID32, vsn); /* VSN */
6453 st_32(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */
6454 st_32(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */
6455 st_16(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */
6456 st_16(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */
6457 buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */
6458 buf[BS_BootSig32] = 0x29; /* Extended boot signature */
6459 memcpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */
6460 } else {
6461 st_32(buf + BS_VolID, vsn); /* VSN */
6462 st_16(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */
6463 buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */
6464 buf[BS_BootSig] = 0x29; /* Extended boot signature */
6465 memcpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */
6466 }
6467 st_16(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */
6468 if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */
6469
6470 /* Create FSINFO record if needed */
6471 if (fsty == FS_FAT32) {
6472 disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */
6473 memset(buf, 0, ss);
6474 st_32(buf + FSI_LeadSig, 0x41615252);
6475 st_32(buf + FSI_StrucSig, 0x61417272);
6476 st_32(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */
6477 st_32(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */
6478 st_16(buf + BS_55AA, 0xAA55);
6479 disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */
6480 disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */
6481 }
6482
6483 /* Initialize FAT area */
6484 memset(buf, 0, sz_buf * ss);
6485 sect = b_fat; /* FAT start sector */
6486 for (i = 0; i < n_fat; i++) { /* Initialize FATs each */
6487 if (fsty == FS_FAT32) {
6488 st_32(buf + 0, 0xFFFFFFF8); /* FAT[0] */
6489 st_32(buf + 4, 0xFFFFFFFF); /* FAT[1] */
6490 st_32(buf + 8, 0x0FFFFFFF); /* FAT[2] (root directory at cluster# 2) */
6491 } else {
6492 st_32(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* FAT[0] and FAT[1] */
6493 }
6494 nsect = sz_fat; /* Number of FAT sectors */
6495 do { /* Fill FAT sectors */
6496 n = (nsect > sz_buf) ? sz_buf : nsect;
6497 if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6498 memset(buf, 0, ss); /* Rest of FAT area is initially zero */
6499 sect += n; nsect -= n;
6500 } while (nsect);
6501 }
6502
6503 /* Initialize root directory (fill with zero) */
6504 nsect = (fsty == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */
6505 do {
6506 n = (nsect > sz_buf) ? sz_buf : nsect;
6507 if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6508 sect += n; nsect -= n;
6509 } while (nsect);
6510 }
6511
6512 /* A FAT volume has been created here */
6513
6514 /* Determine system ID in the MBR partition table */
6515 if (FF_FS_EXFAT && fsty == FS_EXFAT) {
6516 sys = 0x07; /* exFAT */
6517 } else if (fsty == FS_FAT32) {
6518 sys = 0x0C; /* FAT32X */
6519 } else if (sz_vol >= 0x10000) {
6520 sys = 0x06; /* FAT12/16 (large) */
6521 } else if (fsty == FS_FAT16) {
6522 sys = 0x04; /* FAT16 */
6523 } else {
6524 sys = 0x01; /* FAT12 */
6525 }
6526
6527 /* Update partition information */
6528 if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the existing partition */
6529 if (!FF_LBA64 || !(fsopt & 0x80)) { /* Is the partition in MBR? */
6530 /* Update system ID in the partition table */
6531 if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */
6532 buf[MBR_Table + (ipart - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */
6533 if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */
6534 }
6535 } else { /* Volume as a new single partition */
6536 if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD format */
6537 lba[0] = sz_vol; lba[1] = 0;
6538 res = create_partition(pdrv, lba, sys, buf);
6539 if (res != FR_OK) LEAVE_MKFS(res);
6540 }
6541 }
6542
6543 if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
6544
6545 LEAVE_MKFS(FR_OK);
6546}
6547
6548
6549
6550
6551#if FF_MULTI_PARTITION
6552/*-----------------------------------------------------------------------*/
6553/* API: Create Partition Table on the Physical Drive */
6554/*-----------------------------------------------------------------------*/
6555
6556FRESULT f_fdisk (
6557 BYTE pdrv, /* Physical drive number */
6558 const LBA_t ptbl[], /* Pointer to the size table for each partitions */
6559 void* work /* Pointer to the working buffer (null: use heap memory) */
6560)
6561{
6562 BYTE *buf = (BYTE*)work;
6563 DSTATUS stat;
6564 FRESULT res;
6565
6566
6567 /* Initialize the physical drive */
6568 stat = disk_initialize(pdrv);
6569 if (stat & STA_NOINIT) return FR_NOT_READY;
6570 if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
6571
6572#if FF_USE_LFN == 3
6573 if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */
6574#endif
6575 if (!buf) return FR_NOT_ENOUGH_CORE;
6576
6577 res = create_partition(pdrv, ptbl, 0x07, buf); /* Create partitions (system ID is temporary setting and determined by f_mkfs) */
6578
6579 LEAVE_MKFS(res);
6580}
6581
6582#endif /* FF_MULTI_PARTITION */
6583#endif /* !FF_FS_READONLY && FF_USE_MKFS */
6584
6585
6586
6587
6588#if FF_USE_STRFUNC
6589#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
6590#error Wrong FF_STRF_ENCODE setting
6591#endif
6592/*-----------------------------------------------------------------------*/
6593/* API: Get a String from the File */
6594/*-----------------------------------------------------------------------*/
6595
6596TCHAR* f_gets (
6597 TCHAR* buff, /* Pointer to the buffer to store read string */
6598 int len, /* Size of string buffer (items) */
6599 FIL* fp /* Pointer to the file object */
6600)
6601{
6602 int nc = 0;
6603 TCHAR *p = buff;
6604 BYTE s[4];
6605 UINT rc;
6606 DWORD dc;
6607#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
6608 WCHAR wc;
6609#endif
6610#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
6611 UINT ct;
6612#endif
6613
6614#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
6615 /* Make a room for the character and terminator */
6616 if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2;
6617 if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4;
6618 if (FF_LFN_UNICODE == 3) len -= 1;
6619 while (nc < len) {
6620#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
6621 f_read(fp, s, 1, &rc); /* Get a code unit */
6622 if (rc != 1) break; /* EOF? */
6623 wc = s[0];
6624 if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */
6625 f_read(fp, s, 1, &rc); /* Get 2nd byte */
6626 if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong code? */
6627 wc = wc << 8 | s[0];
6628 }
6629 dc = ff_oem2uni(wc, CODEPAGE); /* Convert ANSI/OEM into Unicode */
6630 if (dc == 0) continue; /* Conversion error? */
6631#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
6632 f_read(fp, s, 2, &rc); /* Get a code unit */
6633 if (rc != 2) break; /* EOF? */
6634 dc = (FF_STRF_ENCODE == 1) ? ld_16(s) : s[0] << 8 | s[1];
6635 if (IsSurrogateL(dc)) continue; /* Broken surrogate pair? */
6636 if (IsSurrogateH(dc)) { /* High surrogate? */
6637 f_read(fp, s, 2, &rc); /* Get low surrogate */
6638 if (rc != 2) break; /* EOF? */
6639 wc = (FF_STRF_ENCODE == 1) ? ld_16(s) : s[0] << 8 | s[1];
6640 if (!IsSurrogateL(wc)) continue; /* Broken surrogate pair? */
6641 dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); /* Merge surrogate pair */
6642 }
6643#else /* Read a character in UTF-8 */
6644 f_read(fp, s, 1, &rc); /* Get a code unit */
6645 if (rc != 1) break; /* EOF? */
6646 dc = s[0];
6647 if (dc >= 0x80) { /* Multi-byte sequence? */
6648 ct = 0;
6649 if ((dc & 0xE0) == 0xC0) { /* 2-byte sequence? */
6650 dc &= 0x1F; ct = 1;
6651 }
6652 if ((dc & 0xF0) == 0xE0) { /* 3-byte sequence? */
6653 dc &= 0x0F; ct = 2;
6654 }
6655 if ((dc & 0xF8) == 0xF0) { /* 4-byte sequence? */
6656 dc &= 0x07; ct = 3;
6657 }
6658 if (ct == 0) continue;
6659 f_read(fp, s, ct, &rc); /* Get trailing bytes */
6660 if (rc != ct) break;
6661 rc = 0;
6662 do { /* Merge the byte sequence */
6663 if ((s[rc] & 0xC0) != 0x80) break;
6664 dc = dc << 6 | (s[rc] & 0x3F);
6665 } while (++rc < ct);
6666 if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= 0x110000) continue; /* Wrong encoding? */
6667 }
6668#endif
6669 /* A code point is available in dc to be output */
6670
6671 if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */
6672#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
6673 if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */
6674 *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */
6675 dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */
6676 }
6677 *p++ = (TCHAR)dc; nc++;
6678 if (dc == '\n') break; /* End of line? */
6679#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
6680 if (dc < 0x80) { /* Single byte? */
6681 *p++ = (TCHAR)dc;
6682 nc++;
6683 if (dc == '\n') break; /* End of line? */
6684 } else if (dc < 0x800) { /* 2-byte sequence? */
6685 *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F));
6686 *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
6687 nc += 2;
6688 } else if (dc < 0x10000) { /* 3-byte sequence? */
6689 *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F));
6690 *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
6691 *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
6692 nc += 3;
6693 } else { /* 4-byte sequence */
6694 *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07));
6695 *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F));
6696 *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
6697 *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
6698 nc += 4;
6699 }
6700#endif
6701 }
6702
6703#else /* Byte-by-byte read without any conversion (ANSI/OEM API) */
6704 len -= 1; /* Make a room for the terminator */
6705 while (nc < len) {
6706 f_read(fp, s, 1, &rc); /* Get a byte */
6707 if (rc != 1) break; /* EOF? */
6708 dc = s[0];
6709 if (FF_USE_STRFUNC == 2 && dc == '\r') continue;
6710 *p++ = (TCHAR)dc; nc++;
6711 if (dc == '\n') break;
6712 }
6713#endif
6714
6715 *p = 0; /* Terminate the string */
6716 return nc ? buff : 0; /* When no data read due to EOF or error, return with error. */
6717}
6718
6719
6720
6721
6722#if !FF_FS_READONLY
6723#include <stdarg.h>
6724#define SZ_PUTC_BUF 64
6725#define SZ_NUM_BUF 32
6726
6727/*-----------------------------------------------------------------------*/
6728/* API: Put a Character to the File (with sub-functions) */
6729/*-----------------------------------------------------------------------*/
6730
6731/* Output buffer and work area */
6732
6733typedef struct {
6734 FIL *fp; /* Pointer to the writing file */
6735 int idx, nchr; /* Write index of buf[] (-1:error), number of written encoding units */
6736#if FF_USE_LFN && FF_LFN_UNICODE == 1
6737 WCHAR hs;
6738#elif FF_USE_LFN && FF_LFN_UNICODE == 2
6739 BYTE bs[4];
6740 UINT wi, ct;
6741#endif
6742 BYTE buf[SZ_PUTC_BUF]; /* Write buffer */
6743} putbuff;
6744
6745
6746/* Buffered file write with code conversion */
6747
6748static void putc_bfd (putbuff* pb, TCHAR c)
6749{
6750 UINT n;
6751 int i, nc;
6752#if FF_USE_LFN && FF_LFN_UNICODE
6753 WCHAR hs, wc;
6754#if FF_LFN_UNICODE == 2
6755 DWORD dc;
6756 const TCHAR* tp;
6757#endif
6758#endif
6759
6760 if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */
6761 putc_bfd(pb, '\r');
6762 }
6763
6764 i = pb->idx; /* Write index of pb->buf[] */
6765 if (i < 0) return; /* In write error? */
6766 nc = pb->nchr; /* Write unit counter */
6767
6768#if FF_USE_LFN && FF_LFN_UNICODE
6769#if FF_LFN_UNICODE == 1 /* UTF-16 input */
6770 if (IsSurrogateH(c)) { /* Is this a high-surrogate? */
6771 pb->hs = c; return; /* Save it for next */
6772 }
6773 hs = pb->hs; pb->hs = 0;
6774 if (hs != 0) { /* Is there a leading high-surrogate? */
6775 if (!IsSurrogateL(c)) hs = 0; /* Discard high-surrogate if a stray high-surrogate */
6776 } else {
6777 if (IsSurrogateL(c)) return; /* Discard stray low-surrogate */
6778 }
6779 wc = c;
6780#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
6781 for (;;) {
6782 if (pb->ct == 0) { /* Not in the multi-byte sequence? */
6783 pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */
6784 if ((BYTE)c < 0x80) break; /* Single byte code? */
6785 if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte sequence? */
6786 if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte sequence? */
6787 if (((BYTE)c & 0xF8) == 0xF0) pb->ct = 3; /* 4-byte sequence? */
6788 return; /* Invalid leading byte (discard it) */
6789 } else { /* In the multi-byte sequence */
6790 if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */
6791 pb->ct = 0; continue; /* Discard the sequence */
6792 }
6793 pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */
6794 if (--pb->ct == 0) break; /* End of the sequence? */
6795 return;
6796 }
6797 }
6798 tp = (const TCHAR*)pb->bs;
6799 dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
6800 if (dc == 0xFFFFFFFF) return; /* Wrong code? */
6801 hs = (WCHAR)(dc >> 16);
6802 wc = (WCHAR)dc;
6803#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
6804 if (IsSurrogate(c) || c >= 0x110000) return; /* Discard invalid code */
6805 if (c >= 0x10000) { /* Out of BMP? */
6806 hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high surrogate */
6807 wc = 0xDC00 | (c & 0x3FF); /* Make low surrogate */
6808 } else {
6809 hs = 0;
6810 wc = (WCHAR)c;
6811 }
6812#endif
6813 /* A code point in UTF-16 is available in hs and wc */
6814
6815#if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */
6816 if (hs != 0) { /* Surrogate pair? */
6817 st_16(&pb->buf[i], hs);
6818 i += 2;
6819 nc++;
6820 }
6821 st_16(&pb->buf[i], wc);
6822 i += 2;
6823#elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */
6824 if (hs != 0) { /* Surrogate pair? */
6825 pb->buf[i++] = (BYTE)(hs >> 8);
6826 pb->buf[i++] = (BYTE)hs;
6827 nc++;
6828 }
6829 pb->buf[i++] = (BYTE)(wc >> 8);
6830 pb->buf[i++] = (BYTE)wc;
6831#elif FF_STRF_ENCODE == 3 /* Write a code point in UTF-8 */
6832 if (hs != 0) { /* 4-byte sequence? */
6833 nc += 3;
6834 hs = (hs & 0x3FF) + 0x40;
6835 pb->buf[i++] = (BYTE)(0xF0 | hs >> 8);
6836 pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F));
6837 pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F));
6838 pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
6839 } else {
6840 if (wc < 0x80) { /* Single byte? */
6841 pb->buf[i++] = (BYTE)wc;
6842 } else {
6843 if (wc < 0x800) { /* 2-byte sequence? */
6844 nc += 1;
6845 pb->buf[i++] = (BYTE)(0xC0 | wc >> 6);
6846 } else { /* 3-byte sequence */
6847 nc += 2;
6848 pb->buf[i++] = (BYTE)(0xE0 | wc >> 12);
6849 pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F));
6850 }
6851 pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
6852 }
6853 }
6854#else /* Write a code point in ANSI/OEM */
6855 if (hs != 0) return;
6856 wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */
6857 if (wc == 0) return;
6858 if (wc >= 0x100) {
6859 pb->buf[i++] = (BYTE)(wc >> 8); nc++;
6860 }
6861 pb->buf[i++] = (BYTE)wc;
6862#endif
6863
6864#else /* ANSI/OEM input (without re-encoding) */
6865 pb->buf[i++] = (BYTE)c;
6866#endif
6867
6868 if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */
6869 f_write(pb->fp, pb->buf, (UINT)i, &n);
6870 i = (n == (UINT)i) ? 0 : -1;
6871 }
6872 pb->idx = i;
6873 pb->nchr = nc + 1;
6874}
6875
6876
6877/* Flush characters left in the buffer and return number of characters written */
6878
6879static int putc_flush (putbuff* pb)
6880{
6881 UINT nw;
6882
6883 if ( pb->idx >= 0 /* Flush buffered characters to the file */
6884 && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
6885 && (UINT)pb->idx == nw) {
6886 return pb->nchr;
6887 }
6888 return -1;
6889}
6890
6891
6892/* Initialize write buffer */
6893
6894static void putc_init (putbuff* pb, FIL* fp)
6895{
6896 memset(pb, 0, sizeof (putbuff));
6897 pb->fp = fp;
6898}
6899
6900
6901
6902int f_putc (
6903 TCHAR c, /* A character to be output */
6904 FIL* fp /* Pointer to the file object */
6905)
6906{
6907 putbuff pb;
6908
6909
6910 putc_init(&pb, fp);
6911 putc_bfd(&pb, c); /* Put the character */
6912 return putc_flush(&pb);
6913}
6914
6915
6916
6917
6918/*-----------------------------------------------------------------------*/
6919/* API: Put a String to the File */
6920/*-----------------------------------------------------------------------*/
6921
6922int f_puts (
6923 const TCHAR* str, /* Pointer to the string to be output */
6924 FIL* fp /* Pointer to the file object */
6925)
6926{
6927 putbuff pb;
6928
6929
6930 putc_init(&pb, fp);
6931 while (*str) putc_bfd(&pb, *str++); /* Put the string */
6932 return putc_flush(&pb);
6933}
6934
6935
6936
6937
6938/*-----------------------------------------------------------------------*/
6939/* API: Put a Formatted String to the File (with sub-functions) */
6940/*-----------------------------------------------------------------------*/
6941#if FF_PRINT_FLOAT && FF_INTDEF == 2
6942#include <math.h>
6943
6944static int ilog10 (double n) /* Calculate log10(n) in integer output */
6945{
6946 int rv = 0;
6947
6948 while (n >= 10) { /* Decimate digit in right shift */
6949 if (n >= 100000) {
6950 n /= 100000; rv += 5;
6951 } else {
6952 n /= 10; rv++;
6953 }
6954 }
6955 while (n < 1) { /* Decimate digit in left shift */
6956 if (n < 0.00001) {
6957 n *= 100000; rv -= 5;
6958 } else {
6959 n *= 10; rv--;
6960 }
6961 }
6962 return rv;
6963}
6964
6965
6966static double i10x (int n) /* Calculate 10^n in integer input */
6967{
6968 double rv = 1;
6969
6970 while (n > 0) { /* Left shift */
6971 if (n >= 5) {
6972 rv *= 100000; n -= 5;
6973 } else {
6974 rv *= 10; n--;
6975 }
6976 }
6977 while (n < 0) { /* Right shift */
6978 if (n <= -5) {
6979 rv /= 100000; n += 5;
6980 } else {
6981 rv /= 10; n++;
6982 }
6983 }
6984 return rv;
6985}
6986
6987
6988static void ftoa (
6989 char* buf, /* Buffer to output the floating point string */
6990 double val, /* Value to output */
6991 int prec, /* Number of fractional digits */
6992 TCHAR fmt /* Notation */
6993)
6994{
6995 int digit;
6996 int exp = 0, mag = 0;
6997 char sign = 0;
6998 double w;
6999 const char *er = 0;
7000 const char ds = FF_PRINT_FLOAT == 2 ? ',' : '.';
7001
7002
7003 if (isnan(val)) { /* Not a number? */
7004 er = "NaN";
7005 } else {
7006 if (prec < 0) prec = 6; /* Default precision? (6 fractional digits) */
7007 if (val < 0) { /* Negative? */
7008 val = 0 - val; sign = '-';
7009 } else {
7010 sign = '+';
7011 }
7012 if (isinf(val)) { /* Infinite? */
7013 er = "INF";
7014 } else {
7015 if (fmt == 'f') { /* Decimal notation? */
7016 val += i10x(0 - prec) / 2; /* Round (nearest) */
7017 mag = ilog10(val);
7018 if (mag < 0) mag = 0;
7019 if (mag + prec + 3 >= SZ_NUM_BUF) er = "OV"; /* Buffer overflow? */
7020 } else { /* E notation */
7021 if (val != 0) { /* Not a true zero? */
7022 val += i10x(ilog10(val) - prec) / 2; /* Round (nearest) */
7023 exp = ilog10(val);
7024 if (exp > 99 || prec + 7 >= SZ_NUM_BUF) { /* Buffer overflow or E > +99? */
7025 er = "OV";
7026 } else {
7027 if (exp < -99) exp = -99;
7028 val /= i10x(exp); /* Normalize */
7029 }
7030 }
7031 }
7032 }
7033 if (!er) { /* Not error condition */
7034 if (sign == '-') *buf++ = sign; /* Add a - if negative value */
7035 do { /* Put decimal number */
7036 if (mag == -1) *buf++ = ds; /* Insert a decimal separator when get into fractional part */
7037 w = i10x(mag); /* Snip the highest digit d */
7038 digit = (int)(val / w); val -= digit * w;
7039 *buf++ = (char)('0' + digit); /* Put the digit */
7040 } while (--mag >= -prec); /* Output all digits specified by prec */
7041 if (fmt != 'f') { /* Put exponent if needed */
7042 *buf++ = (char)fmt;
7043 if (exp < 0) {
7044 exp = 0 - exp; *buf++ = '-';
7045 } else {
7046 *buf++ = '+';
7047 }
7048 *buf++ = (char)('0' + exp / 10);
7049 *buf++ = (char)('0' + exp % 10);
7050 }
7051 }
7052 }
7053 if (er) { /* Error condition */
7054 if (sign) *buf++ = sign; /* Add sign if needed */
7055 do { /* Put error symbol */
7056 *buf++ = *er++;
7057 } while (*er);
7058 }
7059 *buf = 0; /* Term */
7060}
7061#endif /* FF_PRINT_FLOAT && FF_INTDEF == 2 */
7062
7063
7064
7065int f_printf (
7066 FIL* fp, /* Pointer to the file object */
7067 const TCHAR* fmt, /* Pointer to the format string */
7068 ... /* Optional arguments... */
7069)
7070{
7071 va_list arp;
7072 putbuff pb;
7073 UINT i, j, width, flag, radix;
7074 int prec;
7075#if FF_PRINT_LLI && FF_INTDEF == 2
7076 QWORD val;
7077#else
7078 DWORD val;
7079#endif
7080 TCHAR *tp;
7081 TCHAR chr, pad;
7082 TCHAR nul = 0;
7083 char digit, str[SZ_NUM_BUF];
7084
7085
7086 putc_init(&pb, fp);
7087
7088 va_start(arp, fmt);
7089
7090 for (;;) {
7091 chr = *fmt++;
7092 if (chr == 0) break; /* End of format string */
7093 if (chr != '%') { /* Not an escape character (pass-through) */
7094 putc_bfd(&pb, chr);
7095 continue;
7096 }
7097 flag = width = 0; pad = ' '; prec = -1; /* Initialize the parameters */
7098 chr = *fmt++;
7099 if (chr == '0') { /* Flag: '0' padded */
7100 pad = '0'; chr = *fmt++;
7101 } else if (chr == '-') { /* Flag: Left aligned */
7102 flag = 2; chr = *fmt++;
7103 }
7104 if (chr == '*') { /* Minimum width from an argument */
7105 width = (UINT)va_arg(arp, int);
7106 chr = *fmt++;
7107 } else {
7108 while (IsDigit(chr)) { /* Minimum width */
7109 width = width * 10 + chr - '0';
7110 chr = *fmt++;
7111 }
7112 }
7113 if (chr == '.') { /* Precision */
7114 chr = *fmt++;
7115 if (chr == '*') { /* Precision from an argument */
7116 prec = va_arg(arp, int);
7117 chr = *fmt++;
7118 } else {
7119 prec = 0;
7120 while (IsDigit(chr)) { /* Precision */
7121 prec = prec * 10 + chr - '0';
7122 chr = *fmt++;
7123 }
7124 }
7125 }
7126 if (chr == 'l') { /* Size: long int */
7127 flag |= 4; chr = *fmt++;
7128#if FF_PRINT_LLI && FF_INTDEF == 2
7129 if (chr == 'l') { /* Size: long long int */
7130 flag |= 8; chr = *fmt++;
7131 }
7132#endif
7133 }
7134 if (chr == 0) break; /* End of format string */
7135 switch (chr) { /* Atgument type is... */
7136 case 'b': /* Unsigned binary */
7137 radix = 2; break;
7138
7139 case 'o': /* Unsigned octal */
7140 radix = 8; break;
7141
7142 case 'd': /* Signed decimal */
7143 case 'u': /* Unsigned decimal */
7144 radix = 10; break;
7145
7146 case 'x': /* Unsigned hexadecimal (lower case) */
7147 case 'X': /* Unsigned hexadecimal (upper case) */
7148 radix = 16; break;
7149
7150 case 'c': /* Character */
7151 putc_bfd(&pb, (TCHAR)va_arg(arp, int));
7152 continue;
7153
7154 case 's': /* String */
7155 tp = va_arg(arp, TCHAR*); /* Get a pointer argument */
7156 if (!tp) tp = &nul; /* Null pointer generates a null string */
7157 for (j = 0; tp[j]; j++) ; /* j = tcslen(tp) */
7158 if (prec >= 0 && j > (UINT)prec) j = (UINT)prec; /* Limited length of string body */
7159 for ( ; !(flag & 2) && j < width; j++) putc_bfd(&pb, pad); /* Left padding */
7160 while (*tp && prec--) putc_bfd(&pb, *tp++); /* Body */
7161 while (j++ < width) putc_bfd(&pb, ' '); /* Right padding */
7162 continue;
7163#if FF_PRINT_FLOAT && FF_INTDEF == 2
7164 case 'f': /* Floating point (decimal) */
7165 case 'e': /* Floating point (e) */
7166 case 'E': /* Floating point (E) */
7167 ftoa(str, va_arg(arp, double), prec, chr); /* Make a floating point string */
7168 for (j = strlen(str); !(flag & 2) && j < width; j++) putc_bfd(&pb, pad); /* Leading pads */
7169 for (i = 0; str[i]; putc_bfd(&pb, str[i++])) ; /* Body */
7170 while (j++ < width) putc_bfd(&pb, ' '); /* Trailing pads */
7171 continue;
7172#endif
7173 default: /* Unknown type (pass-through) */
7174 putc_bfd(&pb, chr);
7175 continue;
7176 }
7177
7178 /* Get an integer argument and put it in numeral */
7179#if FF_PRINT_LLI && FF_INTDEF == 2
7180 if (flag & 8) { /* long long argument? */
7181 val = (QWORD)va_arg(arp, long long);
7182 } else if (flag & 4) { /* long argument? */
7183 val = (chr == 'd') ? (QWORD)(long long)va_arg(arp, long) : (QWORD)va_arg(arp, unsigned long);
7184 } else { /* int/short/char argument */
7185 val = (chr == 'd') ? (QWORD)(long long)va_arg(arp, int) : (QWORD)va_arg(arp, unsigned int);
7186 }
7187 if (chr == 'd' && (val & 0x8000000000000000)) { /* Negative value? */
7188 val = 0 - val; flag |= 1;
7189 }
7190#else
7191 if (flag & 4) { /* long argument? */
7192 val = (DWORD)va_arg(arp, long);
7193 } else { /* int/short/char argument */
7194 val = (chr == 'd') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int);
7195 }
7196 if (chr == 'd' && (val & 0x80000000)) { /* Negative value? */
7197 val = 0 - val; flag |= 1;
7198 }
7199#endif
7200 i = 0;
7201 do { /* Make an integer number string */
7202 digit = (char)(val % radix) + '0'; val /= radix;
7203 if (digit > '9') digit += (chr == 'x') ? 0x27 : 0x07;
7204 str[i++] = digit;
7205 } while (val && i < SZ_NUM_BUF);
7206 if (flag & 1) str[i++] = '-'; /* Sign */
7207 /* Write it */
7208 for (j = i; !(flag & 2) && j < width; j++) { /* Leading pads */
7209 putc_bfd(&pb, pad);
7210 }
7211 do { /* Body */
7212 putc_bfd(&pb, (TCHAR)str[--i]);
7213 } while (i);
7214 while (j++ < width) { /* Trailing pads */
7215 putc_bfd(&pb, ' ');
7216 }
7217 }
7218
7219 va_end(arp);
7220
7221 return putc_flush(&pb);
7222}
7223
7224#endif /* !FF_FS_READONLY */
7225#endif /* FF_USE_STRFUNC */
7226
7227
7228
7229#if FF_CODE_PAGE == 0
7230/*-----------------------------------------------------------------------*/
7231/* API: Set Active Codepage for the Path Name */
7232/*-----------------------------------------------------------------------*/
7233
7234FRESULT f_setcp (
7235 WORD cp /* Value to be set as active code page */
7236)
7237{
7238 static const WORD validcp[22] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0};
7239 static const BYTE *const tables[22] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct855, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0};
7240 UINT i;
7241
7242
7243 for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */
7244 if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not found? */
7245
7246 CodePage = cp;
7247 if (cp >= 900) { /* DBCS */
7248 ExCvt = 0;
7249 DbcTbl = tables[i];
7250 } else { /* SBCS */
7251 ExCvt = tables[i];
7252 DbcTbl = 0;
7253 }
7254 return FR_OK;
7255}
7256#endif /* FF_CODE_PAGE == 0 */
7257
Definition ff.h:245
Definition ff.h:154
Definition ff.h:199
Definition ff.h:264
Definition ff.h:222
Definition libgs.h:334