1 /*----------------------------------------------------------------------------/
2 / FatFs - FAT file system module R0.07a (C)ChaN, 2009
3 /-----------------------------------------------------------------------------/
4 / FatFs module is an open source software to implement FAT file system to
5 / small embedded systems. This is a free software and is opened for education,
6 / research and commercial developments under license policy of following trems.
8 / Copyright (C) 2009, ChaN, all right reserved.
10 / * The FatFs module is a free software and there is NO WARRANTY.
11 / * No restriction on use. You can use, modify and redistribute it for
12 / personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
13 / * Redistributions of source code must retain the above copyright notice.
14 //-----------------------------------------------------------------------------/
15 / Feb 26,'06 R0.00 Prototype.
17 / Apr 29,'06 R0.01 First stable version.
19 / Jun 01,'06 R0.02 Added FAT12 support.
20 / Removed unbuffered mode.
21 / Fixed a problem on small (<32M) patition.
22 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
24 / Sep 22,'06 R0.03 Added f_rename().
25 / Changed option _FS_MINIMUM to _FS_MINIMIZE.
26 / Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast.
27 / Fixed f_mkdir() creates incorrect directory on FAT32.
29 / Feb 04,'07 R0.04 Supported multiple drive system.
30 / Changed some interfaces for multiple drive system.
31 / Changed f_mountdrv() to f_mount().
33 / Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive.
34 / Added a capability of extending file size to f_lseek().
35 / Added minimization level 3.
36 / Fixed an endian sensitive code in f_mkfs().
37 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
38 / Added FSInfo support.
39 / Fixed DBCS name can result FR_INVALID_NAME.
40 / Fixed short seek (<= csize) collapses the file object.
42 / Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs().
43 / Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
44 / Fixed f_mkdir() on FAT32 creates incorrect directory.
45 / Feb 03,'08 R0.05a Added f_truncate() and f_utime().
46 / Fixed off by one error at FAT sub-type determination.
47 / Fixed btr in f_read() can be mistruncated.
48 / Fixed cached sector is not flushed when create and close
51 / Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets().
52 / Improved performance of f_lseek() on moving to the same
53 / or following cluster.
55 / Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option.
56 / Added long file name support.
57 / Added multiple code page support.
58 / Added re-entrancy for multitask operation.
59 / Added auto cluster size selection to f_mkfs().
60 / Added rewind option to f_readdir().
61 / Changed result code of critical errors.
62 / Renamed string functions to avoid name collision.
63 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
64 / Added multiple sector size support.
65 /---------------------------------------------------------------------------*/
67 #include "ff.h" /* FatFs configurations and declarations */
68 #include "diskio.h" /* Declarations of low level disk I/O functions */
71 /*--------------------------------------------------------------------------
73 Module Private Definitions
75 ---------------------------------------------------------------------------*/
79 #error Static LFN work area must not be used in re-entrant configuration.
81 #define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; }
82 #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
86 #define LEAVE_FF(fs, res) return res
90 #define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
97 /*--------------------------------------------------------------------------
101 ---------------------------------------------------------------------------*/
104 FATFS
*FatFs
[_DRIVES
]; /* Pointer to the file system objects (logical drives) */
106 WORD Fsid
; /* File system mount ID */
109 #if _USE_LFN == 1 /* LFN with static LFN working buffer */
111 WORD LfnBuf
[_MAX_LFN
+ 1];
112 #define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR *lp = LfnBuf
113 #define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp
115 #elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */
116 #define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
117 #define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp
120 #define NAMEBUF(sp,lp) BYTE sp[12]
121 #define INITBUF(dj,sp,lp) dj.fn = sp
128 /*--------------------------------------------------------------------------
132 ---------------------------------------------------------------------------*/
135 /*-----------------------------------------------------------------------*/
136 /* String functions */
137 /*-----------------------------------------------------------------------*/
139 /* Copy memory to memory */
141 void mem_cpy (void* dst
, const void* src
, int cnt
) {
142 char *d
= (char*)dst
;
143 const char *s
= (const char *)src
;
144 while (cnt
--) *d
++ = *s
++;
149 void mem_set (void* dst
, int val
, int cnt
) {
150 char *d
= (char*)dst
;
151 while (cnt
--) *d
++ = (char)val
;
154 /* Compare memory to memory */
156 int mem_cmp (const void* dst
, const void* src
, int cnt
) {
157 const char *d
= (const char *)dst
, *s
= (const char *)src
;
159 while (cnt
-- && (r
= *d
++ - *s
++) == 0) ;
163 /* Check if chr is contained in the string */
165 int chk_chr (const char* str
, int chr
) {
166 while (*str
&& *str
!= chr
) str
++;
172 /*-----------------------------------------------------------------------*/
173 /* Request/Release grant to access the volume */
174 /*-----------------------------------------------------------------------*/
179 FATFS
*fs
/* File system object */
182 return ff_req_grant(fs
->sobj
);
188 FATFS
*fs
, /* File system object */
189 FRESULT res
/* Result code to be returned */
192 if (res
!= FR_NOT_ENABLED
&&
193 res
!= FR_INVALID_DRIVE
&&
194 res
!= FR_INVALID_OBJECT
&&
196 ff_rel_grant(fs
->sobj
);
203 /*-----------------------------------------------------------------------*/
204 /* Change window offset */
205 /*-----------------------------------------------------------------------*/
208 FRESULT
move_window (
209 FATFS
*fs
, /* File system object */
210 DWORD sector
/* Sector number to make apperance in the fs->win[] */
211 ) /* Move to zero only writes back dirty window */
217 if (wsect
!= sector
) { /* Changed current window */
219 if (fs
->wflag
) { /* Write back dirty window if needed */
220 if (disk_write(fs
->drive
, fs
->win
, wsect
, 1) != RES_OK
)
223 if (wsect
< (fs
->fatbase
+ fs
->sects_fat
)) { /* In FAT area */
225 for (nf
= fs
->n_fats
; nf
>= 2; nf
--) { /* Refrect the change to FAT copy */
226 wsect
+= fs
->sects_fat
;
227 disk_write(fs
->drive
, fs
->win
, wsect
, 1);
233 if (disk_read(fs
->drive
, fs
->win
, sector
, 1) != RES_OK
)
235 fs
->winsect
= sector
;
245 /*-----------------------------------------------------------------------*/
246 /* Clean-up cached data */
247 /*-----------------------------------------------------------------------*/
250 FRESULT
sync ( /* FR_OK: successful, FR_DISK_ERR: failed */
251 FATFS
*fs
/* File system object */
257 res
= move_window(fs
, 0);
259 /* Update FSInfo sector if needed */
260 if (fs
->fs_type
== FS_FAT32
&& fs
->fsi_flag
) {
262 mem_set(fs
->win
, 0, 512);
263 ST_WORD(fs
->win
+BS_55AA
, 0xAA55);
264 ST_DWORD(fs
->win
+FSI_LeadSig
, 0x41615252);
265 ST_DWORD(fs
->win
+FSI_StrucSig
, 0x61417272);
266 ST_DWORD(fs
->win
+FSI_Free_Count
, fs
->free_clust
);
267 ST_DWORD(fs
->win
+FSI_Nxt_Free
, fs
->last_clust
);
268 disk_write(fs
->drive
, fs
->win
, fs
->fsi_sector
, 1);
271 /* Make sure that no pending write process in the physical drive */
272 if (disk_ioctl(fs
->drive
, CTRL_SYNC
, (void*)NULL
) != RES_OK
)
283 /*-----------------------------------------------------------------------*/
284 /* Get a cluster status */
285 /*-----------------------------------------------------------------------*/
288 DWORD
get_cluster ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */
289 FATFS
*fs
, /* File system object */
290 DWORD clst
/* Cluster# to get the link information */
297 if (clst
< 2 || clst
>= fs
->max_clust
) /* Check cluster address range */
301 switch (fs
->fs_type
) {
303 bc
= (WORD
)clst
* 3 / 2;
304 if (move_window(fs
, fsect
+ (bc
/ SS(fs
)))) break;
305 wc
= fs
->win
[bc
& (SS(fs
) - 1)]; bc
++;
306 if (move_window(fs
, fsect
+ (bc
/ SS(fs
)))) break;
307 wc
|= (WORD
)fs
->win
[bc
& (SS(fs
) - 1)] << 8;
308 return (clst
& 1) ? (wc
>> 4) : (wc
& 0xFFF);
311 if (move_window(fs
, fsect
+ (clst
/ (SS(fs
) / 2)))) break;
312 return LD_WORD(&fs
->win
[((WORD
)clst
* 2) & (SS(fs
) - 1)]);
315 if (move_window(fs
, fsect
+ (clst
/ (SS(fs
) / 4)))) break;
316 return LD_DWORD(&fs
->win
[((WORD
)clst
* 4) & (SS(fs
) - 1)]) & 0x0FFFFFFF;
319 return 0xFFFFFFFF; /* An error occured at the disk I/O layer */
325 /*-----------------------------------------------------------------------*/
326 /* Change a cluster status */
327 /*-----------------------------------------------------------------------*/
330 FRESULT
put_cluster (
331 FATFS
*fs
, /* File system object */
332 DWORD clst
, /* Cluster# to be changed (must be 2 to fs->max_clust-1) */
333 DWORD val
/* New value to mark the cluster */
342 if (clst
< 2 || clst
>= fs
->max_clust
) { /* Check cluster address range */
347 switch (fs
->fs_type
) {
349 bc
= (WORD
)clst
* 3 / 2;
350 res
= move_window(fs
, fsect
+ (bc
/ SS(fs
)));
351 if (res
!= FR_OK
) break;
352 p
= &fs
->win
[bc
& (SS(fs
) - 1)];
353 *p
= (clst
& 1) ? ((*p
& 0x0F) | ((BYTE
)val
<< 4)) : (BYTE
)val
;
356 res
= move_window(fs
, fsect
+ (bc
/ SS(fs
)));
357 if (res
!= FR_OK
) break;
358 p
= &fs
->win
[bc
& (SS(fs
) - 1)];
359 *p
= (clst
& 1) ? (BYTE
)(val
>> 4) : ((*p
& 0xF0) | ((BYTE
)(val
>> 8) & 0x0F));
363 res
= move_window(fs
, fsect
+ (clst
/ (SS(fs
) / 2)));
364 if (res
!= FR_OK
) break;
365 ST_WORD(&fs
->win
[((WORD
)clst
* 2) & (SS(fs
) - 1)], (WORD
)val
);
369 res
= move_window(fs
, fsect
+ (clst
/ (SS(fs
) / 4)));
370 if (res
!= FR_OK
) break;
371 ST_DWORD(&fs
->win
[((WORD
)clst
* 4) & (SS(fs
) - 1)], val
);
382 #endif /* !_FS_READONLY */
387 /*-----------------------------------------------------------------------*/
388 /* Remove a cluster chain */
389 /*-----------------------------------------------------------------------*/
392 FRESULT
remove_chain (
393 FATFS
*fs
, /* File system object */
394 DWORD clst
/* Cluster# to remove chain from */
401 if (clst
< 2 || clst
>= fs
->max_clust
) { /* Check cluster address range */
406 while (clst
< fs
->max_clust
) { /* Not a last link? */
407 nxt
= get_cluster(fs
, clst
); /* Get cluster status */
408 if (nxt
== 0) break; /* Empty cluster? */
409 if (nxt
== 1) { res
= FR_INT_ERR
; break; } /* Internal error? */
410 if (nxt
== 0xFFFFFFFF) { res
= FR_DISK_ERR
; break; } /* Disk error? */
411 res
= put_cluster(fs
, clst
, 0); /* Mark the cluster "empty" */
412 if (res
!= FR_OK
) break;
413 if (fs
->free_clust
!= 0xFFFFFFFF) { /* Update FSInfo */
417 clst
= nxt
; /* Next cluster */
428 /*-----------------------------------------------------------------------*/
429 /* Stretch or create a cluster chain */
430 /*-----------------------------------------------------------------------*/
433 DWORD
create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
434 FATFS
*fs
, /* File system object */
435 DWORD clst
/* Cluster# to stretch. 0 means create a new chain. */
438 DWORD cs
, ncl
, scl
, mcl
;
442 if (clst
== 0) { /* Create new chain */
443 scl
= fs
->last_clust
; /* Get suggested start point */
444 if (scl
== 0 || scl
>= mcl
) scl
= 1;
446 else { /* Stretch existing chain */
447 cs
= get_cluster(fs
, clst
); /* Check the cluster status */
448 if (cs
< 2) return 1; /* It is an invalid cluster */
449 if (cs
< mcl
) return cs
; /* It is already followed by next cluster */
453 ncl
= scl
; /* Start cluster */
455 ncl
++; /* Next cluster */
456 if (ncl
>= mcl
) { /* Wrap around */
458 if (ncl
> scl
) return 0; /* No free custer */
460 cs
= get_cluster(fs
, ncl
); /* Get the cluster status */
461 if (cs
== 0) break; /* Found a free cluster */
462 if (cs
== 0xFFFFFFFF || cs
== 1)/* An error occured */
464 if (ncl
== scl
) return 0; /* No free custer */
467 if (put_cluster(fs
, ncl
, 0x0FFFFFFF)) /* Mark the new cluster "in use" */
469 if (clst
!= 0) { /* Link it to previous one if needed */
470 if (put_cluster(fs
, clst
, ncl
))
474 fs
->last_clust
= ncl
; /* Update FSINFO */
475 if (fs
->free_clust
!= 0xFFFFFFFF) {
480 return ncl
; /* Return new cluster number */
482 #endif /* !_FS_READONLY */
487 /*-----------------------------------------------------------------------*/
488 /* Get sector# from cluster# */
489 /*-----------------------------------------------------------------------*/
492 DWORD
clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */
493 FATFS
*fs
, /* File system object */
494 DWORD clst
/* Cluster# to be converted */
498 if (clst
>= (fs
->max_clust
- 2)) return 0; /* Invalid cluster# */
499 return clst
* fs
->csize
+ fs
->database
;
505 /*-----------------------------------------------------------------------*/
506 /* Seek directory index */
507 /*-----------------------------------------------------------------------*/
511 DIR *dj
, /* Pointer to directory object */
512 WORD idx
/* Directory index number */
521 if (clst
== 1 || clst
>= dj
->fs
->max_clust
) /* Check start cluster range */
524 if (clst
== 0) { /* Static table */
525 if (idx
>= dj
->fs
->n_rootdir
) /* Index is out of range */
527 dj
->sect
= dj
->fs
->dirbase
+ idx
/ (SS(dj
->fs
) / 32);
529 else { /* Dynamic table */
530 ic
= SS(dj
->fs
) / 32 * dj
->fs
->csize
; /* Indexes per cluster */
531 while (idx
>= ic
) { /* Follow cluster chain */
532 clst
= get_cluster(dj
->fs
, clst
); /* Get next cluster */
533 if (clst
== 0xFFFFFFFF) return FR_DISK_ERR
; /* Disk error */
534 if (clst
< 2 || clst
>= dj
->fs
->max_clust
) /* Reached to end of table or int error */
539 dj
->sect
= clust2sect(dj
->fs
, clst
) + idx
/ (SS(dj
->fs
) / 32);
541 dj
->dir
= dj
->fs
->win
+ (idx
% (SS(dj
->fs
) / 32)) * 32;
543 return FR_OK
; /* Seek succeeded */
549 /*-----------------------------------------------------------------------*/
550 /* Move directory index next */
551 /*-----------------------------------------------------------------------*/
554 FRESULT
dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
555 DIR *dj
, /* Pointer to directory object */
556 BOOL streach
/* FALSE: Do not streach table, TRUE: Streach table if needed */
564 if (!i
|| !dj
->sect
) /* Report EOT when index has reached 65535 */
567 if (!(i
% (SS(dj
->fs
) / 32))) { /* Sector changed? */
568 dj
->sect
++; /* Next sector */
570 if (dj
->sclust
== 0) { /* Static table */
571 if (i
>= dj
->fs
->n_rootdir
) /* Report EOT when end of table */
574 else { /* Dynamic table */
575 if (((i
/ (SS(dj
->fs
) / 32)) & (dj
->fs
->csize
- 1)) == 0) { /* Cluster changed? */
576 clst
= get_cluster(dj
->fs
, dj
->clust
); /* Get next cluster */
577 if (clst
<= 1) return FR_INT_ERR
;
578 if (clst
== 0xFFFFFFFF) return FR_DISK_ERR
;
579 if (clst
>= dj
->fs
->max_clust
) { /* When it reached end of dinamic table */
582 if (!streach
) return FR_NO_FILE
; /* When do not streach, report EOT */
583 clst
= create_chain(dj
->fs
, dj
->clust
); /* Streach cluster chain */
584 if (clst
== 0) return FR_DENIED
; /* No free cluster */
585 if (clst
== 1) return FR_INT_ERR
;
586 if (clst
== 0xFFFFFFFF) return FR_DISK_ERR
;
587 /* Clean-up streached table */
588 if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; /* Flush active window */
589 mem_set(dj
->fs
->win
, 0, SS(dj
->fs
)); /* Clear window buffer */
590 dj
->fs
->winsect
= clust2sect(dj
->fs
, clst
); /* Cluster start sector */
591 for (c
= 0; c
< dj
->fs
->csize
; c
++) { /* Fill the new cluster with 0 */
593 if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
;
596 dj
->fs
->winsect
-= c
; /* Rewind window address */
598 return FR_NO_FILE
; /* Report EOT */
601 dj
->clust
= clst
; /* Initialize data for new cluster */
602 dj
->sect
= clust2sect(dj
->fs
, clst
);
608 dj
->dir
= dj
->fs
->win
+ (i
% (SS(dj
->fs
) / 32)) * 32;
616 /*-----------------------------------------------------------------------*/
617 /* Test/Pick/Fit an LFN segment from/to directory entry */
618 /*-----------------------------------------------------------------------*/
621 const BYTE LfnOfs
[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */
625 BOOL
test_lfn ( /* TRUE:Matched, FALSE:Not matched */
626 WCHAR
*lfnbuf
, /* Pointer to the LFN to be compared */
627 BYTE
*dir
/* Pointer to the directory entry containing a part of LFN */
634 i
= ((dir
[LDIR_Ord
] & 0xBF) - 1) * 13; /* Offset in the LFN buffer */
637 if (i
>= _MAX_LFN
) return FALSE
; /* Out of buffer range? */
638 wc1
= LD_WORD(dir
+LfnOfs
[s
]); /* Get both characters to compare */
640 if (IsLower(wc1
)) wc1
-= 0x20; /* Compare it (ignore case) */
641 if (IsLower(wc2
)) wc2
-= 0x20;
642 if (wc1
!= wc2
) return FALSE
;
643 } while (++s
< 13 && wc1
); /* Repeat until last char or a NUL char is processed */
645 return TRUE
; /* The LFN entry matched */
651 BOOL
pick_lfn ( /* TRUE:Succeeded, FALSE:Buffer overflow */
652 WCHAR
*lfnbuf
, /* Pointer to the Unicode-LFN buffer */
653 BYTE
*dir
/* Pointer to the directory entry */
660 i
= ((dir
[LDIR_Ord
] & 0xBF) - 1) * 13; /* Offset in the LFN buffer */
663 wchr
= LD_WORD(dir
+LfnOfs
[s
]); /* Get an LFN char */
664 if (!wchr
) break; /* End of LFN? */
665 if (i
>= _MAX_LFN
) return FALSE
; /* Buffer overflow */
666 lfnbuf
[i
++] = wchr
; /* Store it */
667 } while (++s
< 13); /* Repeat until last char is copied */
668 if (dir
[LDIR_Ord
] & 0x40) lfnbuf
[i
] = 0; /* Put terminator if last LFN entry */
677 const WCHAR
*lfnbuf
, /* Pointer to the LFN buffer */
678 BYTE
*dir
, /* Pointer to the directory entry */
679 BYTE ord
, /* LFN order (1-20) */
680 BYTE sum
/* SFN sum */
687 dir
[LDIR_Chksum
] = sum
; /* Set check sum */
688 dir
[LDIR_Attr
] = AM_LFN
; /* Set attribute. LFN entry */
690 ST_WORD(dir
+LDIR_FstClusLO
, 0);
692 i
= (ord
- 1) * 13; /* Offset in the LFN buffer */
695 if (wchr
!= 0xFFFF) wchr
= lfnbuf
[i
++]; /* Get an effective char */
696 ST_WORD(dir
+LfnOfs
[s
], wchr
); /* Put it */
697 if (!wchr
) wchr
= 0xFFFF; /* Padding chars following last char */
699 if (wchr
== 0xFFFF || !lfnbuf
[i
]) ord
|= 0x40;/* Bottom LFN part is the start of LFN sequence */
700 dir
[LDIR_Ord
] = ord
; /* Set the LFN order */
708 /*-----------------------------------------------------------------------*/
709 /* Create numbered name */
710 /*-----------------------------------------------------------------------*/
713 BYTE
*dst
, /* Pointer to genartated SFN */
714 const BYTE
*src
, /* Pointer to source SFN to be modified */
715 const WCHAR
*lfn
, /* Pointer to LFN */
716 WORD num
/* Sequense number */
723 mem_cpy(dst
, src
, 11);
725 if (num
> 5) { /* On many collisions, generate a hash number instead of sequencial number */
726 do num
= (num
>> 1) + (num
<< 15) + (WORD
)*lfn
++; while (*lfn
);
732 ns
[i
--] = (num
% 10) + '0';
737 /* Append the number */
738 for (j
= 0; j
< i
&& dst
[j
] != ' '; j
++) {
739 if (IsDBCS1(dst
[j
])) {
740 if (j
== i
- 1) break;
745 dst
[j
++] = (i
< 8) ? ns
[i
++] : ' ';
753 /*-----------------------------------------------------------------------*/
754 /* Calculate sum of an SFN */
755 /*-----------------------------------------------------------------------*/
759 const BYTE
*dir
/* Ptr to directory entry */
765 do sum
= (sum
>> 1) + (sum
<< 7) + *dir
++; while (--n
);
773 /*-----------------------------------------------------------------------*/
774 /* Find an object in the directory */
775 /*-----------------------------------------------------------------------*/
779 DIR *dj
/* Pointer to the directory object linked to the file name */
783 BYTE a
, c
, lfen
, ord
, sum
, *dir
;
786 res
= dir_seek(dj
, 0); /* Rewind directory object */
787 if (res
!= FR_OK
) return res
;
789 ord
= sum
= 0xFF; lfen
= *(dj
->fn
+11) & 1;
791 res
= move_window(dj
->fs
, dj
->sect
);
792 if (res
!= FR_OK
) break;
793 dir
= dj
->dir
; /* Ptr to the directory entry of current index */
795 if (c
== 0) { res
= FR_NO_FILE
; break; } /* Reached to end of table */
796 a
= dir
[DIR_Attr
] & AM_MASK
;
797 #if _USE_LFN /* LFN configuration */
798 if (c
== 0xE5 || c
== '.' || ((a
& AM_VOL
) && a
!= AM_LFN
)) { /* An entry without valid data */
801 if (a
== AM_LFN
) { /* An LFN entry is found */
803 if (c
& 0x40) { /* Is it start of LFN sequence? */
804 sum
= dir
[LDIR_Chksum
];
805 c
&= 0xBF; ord
= c
; /* LFN start order */
806 dj
->lfn_idx
= dj
->index
;
808 /* Check LFN validity. Compare LFN if it is out of 8.3 format */
809 ord
= (c
== ord
&& sum
== dir
[LDIR_Chksum
] && (!lfen
|| test_lfn(dj
->lfn
, dir
))) ? ord
- 1 : 0xFF;
811 } else { /* An SFN entry is found */
812 if (ord
|| sum
!= sum_sfn(dir
)) { /* Did not LFN match? */
813 dj
->lfn_idx
= 0xFFFF;
816 if (lfen
) { /* Match LFN if it is out of 8.3 format */
818 } else { /* Match SFN if LFN is in 8.3 format */
819 if (!mem_cmp(dir
, dj
->fn
, 11)) break;
823 #else /* Non LFN configuration */
824 if (c
!= 0xE5 && c
!= '.' && !(a
& AM_VOL
) && !mem_cmp(dir
, dj
->fn
, 11)) /* Is it a valid entry? */
827 res
= dir_next(dj
, FALSE
); /* Next entry */
828 } while (res
== FR_OK
);
836 /*-----------------------------------------------------------------------*/
837 /* Read an object from the directory */
838 /*-----------------------------------------------------------------------*/
839 #if _FS_MINIMIZE <= 2
842 DIR *dj
/* Pointer to the directory object to store read object name */
846 BYTE a
, c
, ord
, sum
, *dir
;
852 res
= move_window(dj
->fs
, dj
->sect
);
853 if (res
!= FR_OK
) break;
854 dir
= dj
->dir
; /* Ptr to the directory entry of current index */
856 if (c
== 0) { res
= FR_NO_FILE
; break; } /* Reached to end of table */
857 a
= dir
[DIR_Attr
] & AM_MASK
;
858 #if _USE_LFN /* LFN configuration */
859 if (c
== 0xE5 || c
== '.' || ((a
& AM_VOL
) && a
!= AM_LFN
)) { /* An entry without valid data */
862 if (a
== AM_LFN
) { /* An LFN entry is found */
863 if (c
& 0x40) { /* Is it start of LFN sequence? */
864 sum
= dir
[LDIR_Chksum
];
866 dj
->lfn_idx
= dj
->index
;
868 /* Check LFN validity and capture it */
869 ord
= (c
== ord
&& sum
== dir
[LDIR_Chksum
] && pick_lfn(dj
->lfn
, dir
)) ? ord
- 1 : 0xFF;
870 } else { /* An SFN entry is found */
871 if (ord
|| sum
!= sum_sfn(dir
)) /* Is there a valid LFN entry? */
872 dj
->lfn_idx
= 0xFFFF; /* No LFN. */
876 #else /* Non LFN configuration */
877 if (c
!= 0xE5 && c
!= '.' && !(a
& AM_VOL
)) /* Is it a valid entry? */
880 res
= dir_next(dj
, FALSE
); /* Next entry */
881 if (res
!= FR_OK
) break;
884 if (res
!= FR_OK
) dj
->sect
= 0;
892 /*-----------------------------------------------------------------------*/
893 /* Register an object to the directory */
894 /*-----------------------------------------------------------------------*/
897 FRESULT
dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
898 DIR *dj
/* Target directory with object name to be created */
904 #if _USE_LFN /* LFN configuration */
906 BYTE sn
[12], *fn
, sum
;
909 fn
= dj
->fn
; lfn
= dj
->lfn
;
911 if (sn
[11] & 1) { /* When LFN is out of 8.3 format, generate a numbered name */
912 fn
[11] = 0; dj
->lfn
= NULL
; /* Find only SFN */
913 for (n
= 1; n
< 100; n
++) {
914 gen_numname(fn
, sn
, lfn
, n
); /* Generate a numbered name */
915 res
= dir_find(dj
); /* Check if the name collides with existing SFN */
916 if (res
!= FR_OK
) break;
918 if (n
== 100) return FR_DENIED
; /* Abort if too many collisions */
919 if (res
!= FR_NO_FILE
) return res
; /* Abort if the result is other than 'not collided' */
920 fn
[11] = sn
[11]; dj
->lfn
= lfn
;
922 if (sn
[11] & 2) { /* When eliminate LFN, reserve only an SFN entry. */
924 } else { /* Otherwise reserve an SFN + LFN entries. */
925 for (ne
= 0; lfn
[ne
]; ne
++) ;
929 /* Reserve contiguous entries */
930 res
= dir_seek(dj
, 0);
931 if (res
!= FR_OK
) return res
;
934 res
= move_window(dj
->fs
, dj
->sect
);
935 if (res
!= FR_OK
) break;
936 c
= *dj
->dir
; /* Check the entry status */
937 if (c
== 0xE5 || c
== 0) { /* Is it a blank entry? */
938 if (n
== 0) is
= dj
->index
; /* First index of the contigulus entry */
939 if (++n
== ne
) break; /* A contiguous entry that requiered count is found */
941 n
= 0; /* Not a blank entry. Restart to search */
943 res
= dir_next(dj
, TRUE
); /* Next entry with table streach */
944 } while (res
== FR_OK
);
946 if (res
== FR_OK
&& ne
> 1) { /* Initialize LFN entry if needed */
947 res
= dir_seek(dj
, is
);
949 sum
= sum_sfn(dj
->fn
); /* Sum of the SFN tied to the LFN */
951 do { /* Store LFN entries in bottom first */
952 res
= move_window(dj
->fs
, dj
->sect
);
953 if (res
!= FR_OK
) break;
954 fit_lfn(dj
->lfn
, dj
->dir
, (BYTE
)ne
, sum
);
956 res
= dir_next(dj
, FALSE
); /* Next entry */
957 } while (res
== FR_OK
&& --ne
);
961 #else /* Non LFN configuration */
962 res
= dir_seek(dj
, 0);
964 do { /* Find a blank entry for the SFN */
965 res
= move_window(dj
->fs
, dj
->sect
);
966 if (res
!= FR_OK
) break;
968 if (c
== 0xE5 || c
== 0) break; /* Is it a blank entry? */
969 res
= dir_next(dj
, TRUE
); /* Next entry with table streach */
970 } while (res
== FR_OK
);
974 if (res
== FR_OK
) { /* Initialize the SFN entry */
975 res
= move_window(dj
->fs
, dj
->sect
);
978 mem_set(dir
, 0, 32); /* Clean the entry */
979 mem_cpy(dir
, dj
->fn
, 11); /* Put SFN */
980 dir
[DIR_NTres
] = *(dj
->fn
+11) & 0x18; /* Put NT flag */
987 #endif /* !_FS_READONLY */
992 /*-----------------------------------------------------------------------*/
993 /* Remove an object from the directory */
994 /*-----------------------------------------------------------------------*/
995 #if !_FS_READONLY && !_FS_MINIMIZE
997 FRESULT
dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */
998 DIR *dj
/* Directory object pointing the entry to be removed */
1003 #if _USE_LFN /* LFN configuration */
1006 i
= dj
->index
; /* SFN index */
1007 res
= dir_seek(dj
, (WORD
)((dj
->lfn_idx
== 0xFFFF) ? i
: dj
->lfn_idx
)); /* Goto the SFN or top of the LFN entries */
1010 res
= move_window(dj
->fs
, dj
->sect
);
1011 if (res
!= FR_OK
) break;
1012 *dj
->dir
= 0xE5; /* Mark the entry "deleted" */
1014 if (dj
->index
>= i
) break; /* When SFN is deleted, all entries of the object is deleted. */
1015 res
= dir_next(dj
, FALSE
); /* Next entry */
1016 } while (res
== FR_OK
);
1017 if (res
== FR_NO_FILE
) res
= FR_INT_ERR
;
1020 #else /* Non LFN configuration */
1021 res
= dir_seek(dj
, dj
->index
);
1023 res
= move_window(dj
->fs
, dj
->sect
);
1025 *dj
->dir
= 0xE5; /* Mark the entry "deleted" */
1033 #endif /* !_FS_READONLY */
1038 /*-----------------------------------------------------------------------*/
1039 /* Pick a segment and create the object name in directory form */
1040 /*-----------------------------------------------------------------------*/
1043 FRESULT
create_name (
1044 DIR *dj
, /* Pointer to the directory object */
1045 const char **path
/* Pointer to pointer to the segment in the path string */
1049 BYTE c
, b
, cf
, *sfn
;
1054 /* Create LFN in Unicode */
1059 w
= (BYTE
)p
[si
++]; /* Get a character */
1060 if (w
< ' ' || w
== '/' || w
== '\\') break; /* Break on end of segment */
1061 if (IsDBCS1(w
)) { /* If it is DBC 1st byte */
1062 c
= p
[si
++]; /* Get 2nd byte */
1063 if (!IsDBCS2(c
)) /* Reject invalid DBC */
1064 return FR_INVALID_NAME
;
1067 if (chk_chr("\"*:<>\?|\x7F", w
)) /* Reject unallowable chars for LFN */
1068 return FR_INVALID_NAME
;
1070 w
= ff_convert(w
, 1); /* Convert OEM to Unicode, store it */
1071 if (!w
|| di
>= _MAX_LFN
) /* Reject invalid code or too long name */
1072 return FR_INVALID_NAME
;
1075 *path
= &p
[si
]; /* Rerurn pointer to the next segment */
1076 cf
= (w
< ' ') ? 4 : 0; /* Set last segment flag if end of path */
1078 while (di
) { /* Strip trailing spaces and dots */
1080 if (w
!= ' ' && w
!= '.') break;
1083 if (!di
) return FR_INVALID_NAME
; /* Reject null string */
1085 lfn
[di
] = 0; /* LFN is created */
1087 /* Create SFN in directory form */
1089 mem_set(sfn
, ' ', 11);
1090 for (si
= 0; lfn
[si
] == ' ' || lfn
[si
] == '.'; si
++) ; /* Strip leading spaces and dots */
1092 while (di
&& lfn
[di
- 1] != '.') di
--; /* Find extension (di<=si: no extension) */
1096 w
= lfn
[si
++]; /* Get an LFN char */
1097 if (w
== 0) break; /* Break when enf of the LFN */
1098 if (w
== ' ' || (w
== '.' && si
!= di
)) { /* Remove spaces and dots */
1101 if (i
>= ni
|| si
== di
) { /* Here is extension or end of SFN */
1102 if (ni
== 11) { /* Extension is longer than 3 bytes */
1105 if (si
!= di
) cf
|= 1; /* File name is longer than 8 bytes */
1106 if (si
> di
) break; /* No extension */
1107 si
= di
; i
= 8; ni
= 11; /* Enter extension section */
1110 w
= ff_convert(w
, 0); /* Unicode -> OEM code */
1111 if (w
>= 0x80) cf
|= 0x20; /* If there is any extended char, force create an LFN */
1112 if (w
>= 0x100) { /* Double byte char */
1114 cf
|= 1; i
= ni
; continue;
1116 sfn
[i
++] = (BYTE
)(w
>> 8);
1117 } else { /* Single byte char */
1118 if (chk_chr("+,;[=]", w
)) { /* Replace unallowable chars for SFN */
1121 if (IsUpper(w
)) { /* Large capital */
1124 if (IsLower(w
)) { /* Small capital */
1132 if (sfn
[0] == 0xE5) sfn
[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */
1134 if (ni
== 8) b
<<= 2;
1135 if ((cf
& 0x21) == 0) { /* When LFN is in 8.3 format without extended char, NT flags are created */
1136 if ((b
& 0x03) == 0x01) cf
|= 0x10; /* NT flag (Extension has only small capital) */
1137 if ((b
& 0x0C) == 0x04) cf
|= 0x08; /* NT flag (Filename has only small capital) */
1138 if ((b
& 0x0C) != 0x0C && (b
& 0x03) != 0x03) cf
|= 2; /* Eliminate LFN when non composite capitals */
1141 sfn
[11] = cf
; /* SFN is created */
1148 /* Create file name in directory form */
1150 mem_set(sfn
, ' ', 11);
1151 si
= i
= b
= 0; ni
= 8;
1155 if (c
< ' ' || c
== '/' || c
== '\\') break; /* Break on end of segment */
1156 if (c
== '.' || i
>= ni
) {
1157 if (ni
!= 8 || c
!= '.') return FR_INVALID_NAME
;
1161 if (c
>= 0x80) b
|= 3; /* If there is any extended char, eliminate NT flag */
1162 if (IsDBCS1(c
)) { /* If it is DBC 1st byte */
1163 d
= p
[si
++]; /* Get 2nd byte */
1164 if (!IsDBCS2(d
) || i
>= ni
- 1) /* Reject invalid DBC */
1165 return FR_INVALID_NAME
;
1169 if (chk_chr(" +,;[=]\"*:<>\?|\x7F", c
)) /* Reject unallowable chrs for SFN */
1170 return FR_INVALID_NAME
;
1181 *path
= &p
[si
]; /* Rerurn pointer to the next segment */
1182 c
= (c
< ' ') ? 4 : 0; /* Set last segment flag if end of path */
1184 if (!i
) return FR_INVALID_NAME
; /* Reject null string */
1185 if (sfn
[0] == 0xE5) sfn
[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */
1187 if (ni
== 8) b
<<= 2;
1188 if ((b
& 0x03) == 0x01) c
|= 0x10; /* NT flag (Extension has only small capital) */
1189 if ((b
& 0x0C) == 0x04) c
|= 0x08; /* NT flag (Filename has only small capital) */
1191 sfn
[11] = c
; /* Store NT flag, File name is created */
1200 /*-----------------------------------------------------------------------*/
1201 /* Get file information from directory entry */
1202 /*-----------------------------------------------------------------------*/
1203 #if _FS_MINIMIZE <= 1
1205 void get_fileinfo ( /* No return code */
1206 DIR *dj
, /* Pointer to the directory object */
1207 FILINFO
*fno
/* Pointer to store the file information */
1218 nt
= dir
[DIR_NTres
]; /* NT flag */
1219 for (i
= 0; i
< 8; i
++) { /* Copy file name body */
1221 if (c
== ' ') break;
1222 if (c
== 0x05) c
= 0xE5;
1223 if ((nt
& 0x08) && IsUpper(c
)) c
+= 0x20;
1226 if (dir
[8] != ' ') { /* Copy file name extension */
1228 for (i
= 8; i
< 11; i
++) {
1230 if (c
== ' ') break;
1231 if ((nt
& 0x10) && IsUpper(c
)) c
+= 0x20;
1235 fno
->fattrib
= dir
[DIR_Attr
]; /* Attribute */
1236 fno
->fsize
= LD_DWORD(dir
+DIR_FileSize
); /* Size */
1237 fno
->fdate
= LD_WORD(dir
+DIR_WrtDate
); /* Date */
1238 fno
->ftime
= LD_WORD(dir
+DIR_WrtTime
); /* Time */
1248 if (dj
->sect
&& dj
->lfn_idx
!= 0xFFFF) {/* Get LFN if available */
1250 while ((wchr
= *lfn
++) != 0) { /* Get an LFN char */
1251 wchr
= ff_convert(wchr
, 0); /* Unicode -> OEM code */
1252 if (!wchr
) { i
= 0; break; } /* Conversion error, no LFN */
1253 if (_DF1S
&& wchr
>= 0x100) /* Put 1st byte if it is a DBC */
1254 p
[i
++] = (char)(wchr
>> 8);
1255 p
[i
++] = (char)wchr
;
1256 if (i
>= fno
->lfsize
) { i
= 0; break; } /* Buffer overrun, no LFN */
1259 p
[i
] = 0; /* Terminator */
1263 #endif /* _FS_MINIMIZE <= 1 */
1268 /*-----------------------------------------------------------------------*/
1269 /* Follow a file path */
1270 /*-----------------------------------------------------------------------*/
1273 FRESULT
follow_path ( /* FR_OK(0): successful, !=0: error code */
1274 DIR *dj
, /* Directory object to return last directory and found object */
1275 const char *path
/* Full-path string to find a file or directory */
1282 if (*path
== '/' || *path
== '\\' ) path
++; /* Strip heading separator */
1284 dj
->sclust
= /* Set start directory (root dir) */
1285 (dj
->fs
->fs_type
== FS_FAT32
) ? dj
->fs
->dirbase
: 0;
1287 if ((BYTE
)*path
< ' ') { /* Null path means the root directory */
1288 res
= dir_seek(dj
, 0);
1291 } else { /* Follow path */
1293 res
= create_name(dj
, &path
); /* Get a segment */
1294 if (res
!= FR_OK
) break;
1295 res
= dir_find(dj
); /* Find it */
1296 last
= *(dj
->fn
+11) & 4;
1297 if (res
!= FR_OK
) { /* Could not find the object */
1298 if (res
== FR_NO_FILE
&& !last
)
1302 if (last
) break; /* Last segment match. Function completed. */
1303 dir
= dj
->dir
; /* There is next segment. Follow the sub directory */
1304 if (!(dir
[DIR_Attr
] & AM_DIR
)) { /* Cannot follow because it is a file */
1305 res
= FR_NO_PATH
; break;
1307 dj
->sclust
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
);
1317 /*-----------------------------------------------------------------------*/
1318 /* Load boot record and check if it is an FAT boot record */
1319 /*-----------------------------------------------------------------------*/
1322 BYTE
check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
1323 FATFS
*fs
, /* File system object */
1324 DWORD sect
/* Sector# (lba) to check if it is an FAT boot record or not */
1327 if (disk_read(fs
->drive
, fs
->win
, sect
, 1) != RES_OK
) /* Load boot record */
1329 if (LD_WORD(&fs
->win
[BS_55AA
]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */
1332 if (!mem_cmp(&fs
->win
[BS_FilSysType
], "FAT", 3)) /* Check FAT signature */
1334 if (!mem_cmp(&fs
->win
[BS_FilSysType32
], "FAT32", 5) && !(fs
->win
[BPB_ExtFlags
] & 0x80))
1343 /*-----------------------------------------------------------------------*/
1344 /* Make sure that the file system is valid */
1345 /*-----------------------------------------------------------------------*/
1348 FRESULT
auto_mount ( /* FR_OK(0): successful, !=0: any error occured */
1349 const char **path
, /* Pointer to pointer to the path name (drive number) */
1350 FATFS
**rfs
, /* Pointer to pointer to the found file system object */
1351 BYTE chk_wp
/* !=0: Check media write protection for write access */
1355 BYTE vol
, fmt
, *tbl
;
1357 DWORD bsect
, fsize
, tsect
, mclst
;
1358 const char *p
= *path
;
1362 /* Get logical drive number from the path name */
1363 vol
= p
[0] - '0'; /* Is there a drive number? */
1364 if (vol
<= 9 && p
[1] == ':') {
1365 p
+= 2; /* Found a drive number, get and strip it */
1366 *path
= p
; /* Return pointer to the path name */
1368 vol
= 0; /* No drive number is given, use drive number 0 as default */
1371 /* Check if the logical drive number is valid or not */
1372 if (vol
>= _DRIVES
) return FR_INVALID_DRIVE
; /* Is the drive number valid? */
1373 *rfs
= fs
= FatFs
[vol
]; /* Returen pointer to the corresponding file system object */
1374 if (!fs
) return FR_NOT_ENABLED
; /* Is the file system object registered? */
1376 ENTER_FF(fs
); /* Lock file system */
1378 if (fs
->fs_type
) { /* If the logical drive has been mounted */
1379 stat
= disk_status(fs
->drive
);
1380 if (!(stat
& STA_NOINIT
)) { /* and physical drive is kept initialized (has not been changed), */
1382 if (chk_wp
&& (stat
& STA_PROTECT
)) /* Check write protection if needed */
1383 return FR_WRITE_PROTECTED
;
1385 return FR_OK
; /* The file system object is valid */
1389 /* The logical drive must be re-mounted. Following code attempts to mount the volume */
1391 fs
->fs_type
= 0; /* Clear the file system object */
1392 fs
->drive
= LD2PD(vol
); /* Bind the logical drive and a physical drive */
1393 stat
= disk_initialize(fs
->drive
); /* Initialize low level disk I/O layer */
1394 if (stat
& STA_NOINIT
) /* Check if the drive is ready */
1395 return FR_NOT_READY
;
1396 #if _MAX_SS != 512 /* Get disk sector size if needed */
1397 if (disk_ioctl(fs
->drive
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK
|| SS(fs
) > _MAX_SS
)
1398 return FR_NO_FILESYSTEM
;
1401 if (chk_wp
&& (stat
& STA_PROTECT
)) /* Check write protection if needed */
1402 return FR_WRITE_PROTECTED
;
1404 /* Search FAT partition on the drive */
1405 fmt
= check_fs(fs
, bsect
= 0); /* Check sector 0 as an SFD format */
1406 if (fmt
== 1) { /* Not an FAT boot record, it may be patitioned */
1407 /* Check a partition listed in top of the partition table */
1408 tbl
= &fs
->win
[MBR_Table
+ LD2PT(vol
) * 16]; /* Partition table */
1409 if (tbl
[4]) { /* Is the partition existing? */
1410 bsect
= LD_DWORD(&tbl
[8]); /* Partition offset in LBA */
1411 fmt
= check_fs(fs
, bsect
); /* Check the partition */
1414 if (fmt
== 3) return FR_DISK_ERR
;
1415 if (fmt
|| LD_WORD(fs
->win
+BPB_BytsPerSec
) != SS(fs
)) /* No valid FAT patition is found */
1416 return FR_NO_FILESYSTEM
;
1418 /* Initialize the file system object */
1419 fsize
= LD_WORD(fs
->win
+BPB_FATSz16
); /* Number of sectors per FAT */
1420 if (!fsize
) fsize
= LD_DWORD(fs
->win
+BPB_FATSz32
);
1421 fs
->sects_fat
= fsize
;
1422 fs
->n_fats
= fs
->win
[BPB_NumFATs
]; /* Number of FAT copies */
1423 fsize
*= fs
->n_fats
; /* (Number of sectors in FAT area) */
1424 fs
->fatbase
= bsect
+ LD_WORD(fs
->win
+BPB_RsvdSecCnt
); /* FAT start sector (lba) */
1425 fs
->csize
= fs
->win
[BPB_SecPerClus
]; /* Number of sectors per cluster */
1426 fs
->n_rootdir
= LD_WORD(fs
->win
+BPB_RootEntCnt
); /* Nmuber of root directory entries */
1427 tsect
= LD_WORD(fs
->win
+BPB_TotSec16
); /* Number of sectors on the file system */
1428 if (!tsect
) tsect
= LD_DWORD(fs
->win
+BPB_TotSec32
);
1429 fs
->max_clust
= mclst
= (tsect
/* Last cluster# + 1 */
1430 - LD_WORD(fs
->win
+BPB_RsvdSecCnt
) - fsize
- fs
->n_rootdir
/ (SS(fs
)/32)
1433 fmt
= FS_FAT12
; /* Determine the FAT sub type */
1434 if (mclst
>= 0xFF7) fmt
= FS_FAT16
; /* Number of clusters >= 0xFF5 */
1435 if (mclst
>= 0xFFF7) fmt
= FS_FAT32
; /* Number of clusters >= 0xFFF5 */
1437 if (fmt
== FS_FAT32
)
1438 fs
->dirbase
= LD_DWORD(fs
->win
+BPB_RootClus
); /* Root directory start cluster */
1440 fs
->dirbase
= fs
->fatbase
+ fsize
; /* Root directory start sector (lba) */
1441 fs
->database
= fs
->fatbase
+ fsize
+ fs
->n_rootdir
/ (SS(fs
)/32); /* Data start sector (lba) */
1444 /* Initialize allocation information */
1445 fs
->free_clust
= 0xFFFFFFFF;
1447 /* Get fsinfo if needed */
1448 if (fmt
== FS_FAT32
) {
1449 fs
->fsi_sector
= bsect
+ LD_WORD(fs
->win
+BPB_FSInfo
);
1451 if (disk_read(fs
->drive
, fs
->win
, fs
->fsi_sector
, 1) == RES_OK
&&
1452 LD_WORD(fs
->win
+BS_55AA
) == 0xAA55 &&
1453 LD_DWORD(fs
->win
+FSI_LeadSig
) == 0x41615252 &&
1454 LD_DWORD(fs
->win
+FSI_StrucSig
) == 0x61417272) {
1455 fs
->last_clust
= LD_DWORD(fs
->win
+FSI_Nxt_Free
);
1456 fs
->free_clust
= LD_DWORD(fs
->win
+FSI_Free_Count
);
1461 fs
->fs_type
= fmt
; /* FAT syb-type */
1462 fs
->id
= ++Fsid
; /* File system mount ID */
1471 /*-----------------------------------------------------------------------*/
1472 /* Check if the file/dir object is valid or not */
1473 /*-----------------------------------------------------------------------*/
1476 FRESULT
validate ( /* FR_OK(0): The object is valid, !=0: Invalid */
1477 FATFS
*fs
, /* Pointer to the file system object */
1478 WORD id
/* Member id of the target object to be checked */
1481 if (!fs
|| !fs
->fs_type
|| fs
->id
!= id
)
1482 return FR_INVALID_OBJECT
;
1484 ENTER_FF(fs
); /* Lock file system */
1486 if (disk_status(fs
->drive
) & STA_NOINIT
)
1487 return FR_NOT_READY
;
1495 /*--------------------------------------------------------------------------
1499 --------------------------------------------------------------------------*/
1503 /*-----------------------------------------------------------------------*/
1504 /* Mount/Unmount a Locical Drive */
1505 /*-----------------------------------------------------------------------*/
1508 BYTE vol
, /* Logical drive number to be mounted/unmounted */
1509 FATFS
*fs
/* Pointer to new file system object (NULL for unmount)*/
1515 if (vol
>= _DRIVES
) /* Check if the drive number is valid */
1516 return FR_INVALID_DRIVE
;
1517 rfs
= FatFs
[vol
]; /* Get current state */
1520 #if _FS_REENTRANT /* Discard sync object of the current volume */
1521 if (!ff_del_syncobj(fs
->sobj
)) return FR_INT_ERR
;
1523 rfs
->fs_type
= 0; /* Clear old fs object */
1527 fs
->fs_type
= 0; /* Clear new fs object */
1528 #if _FS_REENTRANT /* Create sync object for the new volume */
1529 if (!ff_cre_syncobj(vol
, &fs
->sobj
)) return FR_INT_ERR
;
1532 FatFs
[vol
] = fs
; /* Register new fs object */
1540 /*-----------------------------------------------------------------------*/
1541 /* Open or Create a File */
1542 /*-----------------------------------------------------------------------*/
1545 FIL
*fp
, /* Pointer to the blank file object */
1546 const char *path
, /* Pointer to the file name */
1547 BYTE mode
/* Access mode and file open mode flags */
1556 fp
->fs
= NULL
; /* Clear file object */
1558 mode
&= (FA_READ
| FA_WRITE
| FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
);
1559 res
= auto_mount(&path
, &dj
.fs
, (BYTE
)(mode
& (FA_WRITE
| FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
)));
1562 res
= auto_mount(&path
, &dj
.fs
, 0);
1564 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
);
1565 INITBUF(dj
, sfn
, lfn
);
1566 res
= follow_path(&dj
, path
); /* Follow the file path */
1569 /* Create or Open a file */
1570 if (mode
& (FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
)) {
1573 if (res
!= FR_OK
) { /* No file, create new */
1574 if (res
== FR_NO_FILE
)
1575 res
= dir_register(&dj
);
1576 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
);
1577 mode
|= FA_CREATE_ALWAYS
;
1580 else { /* Any object is already existing */
1581 if (mode
& FA_CREATE_NEW
) /* Cannot create new */
1582 LEAVE_FF(dj
.fs
, FR_EXIST
);
1584 if (!dir
|| (dir
[DIR_Attr
] & (AM_RDO
| AM_DIR
))) /* Cannot overwrite it (R/O or DIR) */
1585 LEAVE_FF(dj
.fs
, FR_DENIED
);
1586 if (mode
& FA_CREATE_ALWAYS
) { /* Resize it to zero if needed */
1587 cl
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); /* Get start cluster */
1588 ST_WORD(dir
+DIR_FstClusHI
, 0); /* cluster = 0 */
1589 ST_WORD(dir
+DIR_FstClusLO
, 0);
1590 ST_DWORD(dir
+DIR_FileSize
, 0); /* size = 0 */
1592 ps
= dj
.fs
->winsect
; /* Remove the cluster chain */
1594 res
= remove_chain(dj
.fs
, cl
);
1595 if (res
) LEAVE_FF(dj
.fs
, res
);
1596 dj
.fs
->last_clust
= cl
- 1; /* Reuse the cluster hole */
1598 res
= move_window(dj
.fs
, ps
);
1599 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
);
1602 if (mode
& FA_CREATE_ALWAYS
) {
1603 dir
[DIR_Attr
] = 0; /* Reset attribute */
1605 ST_DWORD(dir
+DIR_CrtTime
, ps
); /* Created time */
1607 mode
|= FA__WRITTEN
; /* Set file changed flag */
1610 /* Open an existing file */
1612 #endif /* !_FS_READONLY */
1613 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); /* Follow failed */
1615 if (!dir
|| (dir
[DIR_Attr
] & AM_DIR
)) /* It is a directory */
1616 LEAVE_FF(dj
.fs
, FR_NO_FILE
);
1618 if ((mode
& FA_WRITE
) && (dir
[DIR_Attr
] & AM_RDO
)) /* R/O violation */
1619 LEAVE_FF(dj
.fs
, FR_DENIED
);
1621 fp
->dir_sect
= dj
.fs
->winsect
; /* Pointer to the directory entry */
1622 fp
->dir_ptr
= dj
.dir
;
1624 fp
->flag
= mode
; /* File access mode */
1625 fp
->org_clust
= /* File start cluster */
1626 ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
);
1627 fp
->fsize
= LD_DWORD(dir
+DIR_FileSize
); /* File size */
1628 fp
->fptr
= 0; fp
->csect
= 255; /* File pointer */
1630 fp
->fs
= dj
.fs
; fp
->id
= dj
.fs
->id
; /* Owner file system object of the file */
1632 LEAVE_FF(dj
.fs
, FR_OK
);
1638 /*-----------------------------------------------------------------------*/
1640 /*-----------------------------------------------------------------------*/
1643 FIL
*fp
, /* Pointer to the file object */
1644 void *buff
, /* Pointer to data buffer */
1645 UINT btr
, /* Number of bytes to read */
1646 UINT
*br
/* Pointer to number of bytes read */
1650 DWORD clst
, sect
, remain
;
1657 res
= validate(fp
->fs
, fp
->id
); /* Check validity of the object */
1658 if (res
!= FR_OK
) LEAVE_FF(fp
->fs
, res
);
1659 if (fp
->flag
& FA__ERROR
) /* Check abort flag */
1660 LEAVE_FF(fp
->fs
, FR_INT_ERR
);
1661 if (!(fp
->flag
& FA_READ
)) /* Check access mode */
1662 LEAVE_FF(fp
->fs
, FR_DENIED
);
1663 remain
= fp
->fsize
- fp
->fptr
;
1664 if (btr
> remain
) btr
= (UINT
)remain
; /* Truncate btr by remaining bytes */
1666 for ( ; btr
; /* Repeat until all data transferred */
1667 rbuff
+= rcnt
, fp
->fptr
+= rcnt
, *br
+= rcnt
, btr
-= rcnt
) {
1668 if ((fp
->fptr
% SS(fp
->fs
)) == 0) { /* On the sector boundary? */
1669 if (fp
->csect
>= fp
->fs
->csize
) { /* On the cluster boundary? */
1670 clst
= (fp
->fptr
== 0) ? /* On the top of the file? */
1671 fp
->org_clust
: get_cluster(fp
->fs
, fp
->curr_clust
);
1672 if (clst
<= 1) ABORT(fp
->fs
, FR_INT_ERR
);
1673 if (clst
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
);
1674 fp
->curr_clust
= clst
; /* Update current cluster */
1675 fp
->csect
= 0; /* Reset sector offset in the cluster */
1677 sect
= clust2sect(fp
->fs
, fp
->curr_clust
); /* Get current sector */
1678 if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
);
1680 cc
= btr
/ SS(fp
->fs
); /* When remaining bytes >= sector size, */
1681 if (cc
) { /* Read maximum contiguous sectors directly */
1682 if (fp
->csect
+ cc
> fp
->fs
->csize
) /* Clip at cluster boundary */
1683 cc
= fp
->fs
->csize
- fp
->csect
;
1684 if (disk_read(fp
->fs
->drive
, rbuff
, sect
, (BYTE
)cc
) != RES_OK
)
1685 ABORT(fp
->fs
, FR_DISK_ERR
);
1686 fp
->csect
+= (BYTE
)cc
; /* Next sector address in the cluster */
1687 rcnt
= SS(fp
->fs
) * cc
; /* Number of bytes transferred */
1692 if (fp
->flag
& FA__DIRTY
) { /* Write sector I/O buffer if needed */
1693 if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
1694 ABORT(fp
->fs
, FR_DISK_ERR
);
1695 fp
->flag
&= (BYTE
)~FA__DIRTY
;
1698 if (fp
->dsect
!= sect
) { /* Fill sector buffer with file data */
1699 if (disk_read(fp
->fs
->drive
, fp
->buf
, sect
, 1) != RES_OK
)
1700 ABORT(fp
->fs
, FR_DISK_ERR
);
1704 fp
->csect
++; /* Next sector address in the cluster */
1706 rcnt
= SS(fp
->fs
) - (fp
->fptr
% SS(fp
->fs
)); /* Get partial sector data from sector buffer */
1707 if (rcnt
> btr
) rcnt
= btr
;
1709 if (move_window(fp
->fs
, fp
->dsect
)) /* Move sector window */
1710 ABORT(fp
->fs
, FR_DISK_ERR
);
1711 mem_cpy(rbuff
, &fp
->fs
->win
[fp
->fptr
% SS(fp
->fs
)], rcnt
); /* Pick partial sector */
1713 mem_cpy(rbuff
, &fp
->buf
[fp
->fptr
% SS(fp
->fs
)], rcnt
); /* Pick partial sector */
1718 LEAVE_FF(fp
->fs
, FR_OK
);
1725 /*-----------------------------------------------------------------------*/
1727 /*-----------------------------------------------------------------------*/
1730 FIL
*fp
, /* Pointer to the file object */
1731 const void *buff
, /* Pointer to the data to be written */
1732 UINT btw
, /* Number of bytes to write */
1733 UINT
*bw
/* Pointer to number of bytes written */
1739 const BYTE
*wbuff
= buff
;
1744 res
= validate(fp
->fs
, fp
->id
); /* Check validity of the object */
1745 if (res
!= FR_OK
) LEAVE_FF(fp
->fs
, res
);
1746 if (fp
->flag
& FA__ERROR
) /* Check abort flag */
1747 LEAVE_FF(fp
->fs
, FR_INT_ERR
);
1748 if (!(fp
->flag
& FA_WRITE
)) /* Check access mode */
1749 LEAVE_FF(fp
->fs
, FR_DENIED
);
1750 if (fp
->fsize
+ btw
< fp
->fsize
) btw
= 0; /* File size cannot reach 4GB */
1752 for ( ; btw
; /* Repeat until all data transferred */
1753 wbuff
+= wcnt
, fp
->fptr
+= wcnt
, *bw
+= wcnt
, btw
-= wcnt
) {
1754 if ((fp
->fptr
% SS(fp
->fs
)) == 0) { /* On the sector boundary? */
1755 if (fp
->csect
>= fp
->fs
->csize
) { /* On the cluster boundary? */
1756 if (fp
->fptr
== 0) { /* On the top of the file? */
1757 clst
= fp
->org_clust
; /* Follow from the origin */
1758 if (clst
== 0) /* When there is no cluster chain, */
1759 fp
->org_clust
= clst
= create_chain(fp
->fs
, 0); /* Create a new cluster chain */
1760 } else { /* Middle or end of the file */
1761 clst
= create_chain(fp
->fs
, fp
->curr_clust
); /* Follow or streach cluster chain */
1763 if (clst
== 0) break; /* Could not allocate a new cluster (disk full) */
1764 if (clst
== 1) ABORT(fp
->fs
, FR_INT_ERR
);
1765 if (clst
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
);
1766 fp
->curr_clust
= clst
; /* Update current cluster */
1767 fp
->csect
= 0; /* Reset sector address in the cluster */
1770 if (fp
->fs
->winsect
== fp
->dsect
&& move_window(fp
->fs
, 0)) /* Write back data buffer prior to following direct transfer */
1771 ABORT(fp
->fs
, FR_DISK_ERR
);
1773 if (fp
->flag
& FA__DIRTY
) { /* Write back data buffer prior to following direct transfer */
1774 if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
1775 ABORT(fp
->fs
, FR_DISK_ERR
);
1776 fp
->flag
&= (BYTE
)~FA__DIRTY
;
1779 sect
= clust2sect(fp
->fs
, fp
->curr_clust
); /* Get current sector */
1780 if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
);
1782 cc
= btw
/ SS(fp
->fs
); /* When remaining bytes >= sector size, */
1783 if (cc
) { /* Write maximum contiguous sectors directly */
1784 if (fp
->csect
+ cc
> fp
->fs
->csize
) /* Clip at cluster boundary */
1785 cc
= fp
->fs
->csize
- fp
->csect
;
1786 if (disk_write(fp
->fs
->drive
, wbuff
, sect
, (BYTE
)cc
) != RES_OK
)
1787 ABORT(fp
->fs
, FR_DISK_ERR
);
1789 if (fp
->fs
->winsect
- sect
< cc
) { /* Refill sector cache if it gets dirty by the direct write */
1790 mem_cpy(fp
->fs
->win
, wbuff
+ ((fp
->fs
->winsect
- sect
) * SS(fp
->fs
)), SS(fp
->fs
));
1794 if (fp
->dsect
- sect
< cc
) { /* Refill sector cache if it gets dirty by the direct write */
1795 mem_cpy(fp
->buf
, wbuff
+ ((fp
->dsect
- sect
) * SS(fp
->fs
)), SS(fp
->fs
));
1796 fp
->flag
&= ~FA__DIRTY
;
1799 fp
->csect
+= (BYTE
)cc
; /* Next sector address in the cluster */
1800 wcnt
= SS(fp
->fs
) * cc
; /* Number of bytes transferred */
1804 if (fp
->fptr
>= fp
->fsize
) { /* Avoid silly buffer filling at growing edge */
1805 if (move_window(fp
->fs
, 0)) ABORT(fp
->fs
, FR_DISK_ERR
);
1806 fp
->fs
->winsect
= sect
;
1809 if (fp
->dsect
!= sect
) { /* Fill sector buffer with file data */
1810 if (fp
->fptr
< fp
->fsize
&&
1811 disk_read(fp
->fs
->drive
, fp
->buf
, sect
, 1) != RES_OK
)
1812 ABORT(fp
->fs
, FR_DISK_ERR
);
1816 fp
->csect
++; /* Next sector address in the cluster */
1818 wcnt
= SS(fp
->fs
) - (fp
->fptr
% SS(fp
->fs
)); /* Put partial sector into file I/O buffer */
1819 if (wcnt
> btw
) wcnt
= btw
;
1821 if (move_window(fp
->fs
, fp
->dsect
)) /* Move sector window */
1822 ABORT(fp
->fs
, FR_DISK_ERR
);
1823 mem_cpy(&fp
->fs
->win
[fp
->fptr
% SS(fp
->fs
)], wbuff
, wcnt
); /* Fit partial sector */
1826 mem_cpy(&fp
->buf
[fp
->fptr
% SS(fp
->fs
)], wbuff
, wcnt
); /* Fit partial sector */
1827 fp
->flag
|= FA__DIRTY
;
1831 if (fp
->fptr
> fp
->fsize
) fp
->fsize
= fp
->fptr
; /* Update file size if needed */
1832 fp
->flag
|= FA__WRITTEN
; /* Set file changed flag */
1834 LEAVE_FF(fp
->fs
, FR_OK
);
1840 /*-----------------------------------------------------------------------*/
1841 /* Synchronize the File Object */
1842 /*-----------------------------------------------------------------------*/
1845 FIL
*fp
/* Pointer to the file object */
1853 res
= validate(fp
->fs
, fp
->id
); /* Check validity of the object */
1855 if (fp
->flag
& FA__WRITTEN
) { /* Has the file been written? */
1856 #if !_FS_TINY /* Write-back dirty buffer */
1857 if (fp
->flag
& FA__DIRTY
) {
1858 if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
1859 LEAVE_FF(fp
->fs
, FR_DISK_ERR
);
1860 fp
->flag
&= (BYTE
)~FA__DIRTY
;
1863 /* Update the directory entry */
1864 res
= move_window(fp
->fs
, fp
->dir_sect
);
1867 dir
[DIR_Attr
] |= AM_ARC
; /* Set archive bit */
1868 ST_DWORD(dir
+DIR_FileSize
, fp
->fsize
); /* Update file size */
1869 ST_WORD(dir
+DIR_FstClusLO
, fp
->org_clust
); /* Update start cluster */
1870 ST_WORD(dir
+DIR_FstClusHI
, fp
->org_clust
>> 16);
1871 tim
= get_fattime(); /* Updated time */
1872 ST_DWORD(dir
+DIR_WrtTime
, tim
);
1873 fp
->flag
&= (BYTE
)~FA__WRITTEN
;
1880 LEAVE_FF(fp
->fs
, res
);
1883 #endif /* !_FS_READONLY */
1888 /*-----------------------------------------------------------------------*/
1890 /*-----------------------------------------------------------------------*/
1893 FIL
*fp
/* Pointer to the file object to be closed */
1900 res
= validate(fp
->fs
, fp
->id
);
1901 if (res
== FR_OK
) fp
->fs
= NULL
;
1902 LEAVE_FF(fp
->fs
, res
);
1905 if (res
== FR_OK
) fp
->fs
= NULL
;
1913 #if _FS_MINIMIZE <= 2
1914 /*-----------------------------------------------------------------------*/
1915 /* Seek File R/W Pointer */
1916 /*-----------------------------------------------------------------------*/
1919 FIL
*fp
, /* Pointer to the file object */
1920 DWORD ofs
/* File pointer from top of file */
1924 DWORD clst
, bcs
, nsect
, ifptr
;
1927 res
= validate(fp
->fs
, fp
->id
); /* Check validity of the object */
1928 if (res
!= FR_OK
) LEAVE_FF(fp
->fs
, res
);
1929 if (fp
->flag
& FA__ERROR
) /* Check abort flag */
1930 LEAVE_FF(fp
->fs
, FR_INT_ERR
);
1931 if (ofs
> fp
->fsize
/* In read-only mode, clip offset with the file size */
1933 && !(fp
->flag
& FA_WRITE
)
1938 fp
->fptr
= 0; fp
->csect
= 255;
1941 bcs
= (DWORD
)fp
->fs
->csize
* SS(fp
->fs
); /* Cluster size (byte) */
1943 (ofs
- 1) / bcs
>= (ifptr
- 1) / bcs
) { /* When seek to same or following cluster, */
1944 fp
->fptr
= (ifptr
- 1) & ~(bcs
- 1); /* start from the current cluster */
1946 clst
= fp
->curr_clust
;
1947 } else { /* When seek to back cluster, */
1948 clst
= fp
->org_clust
; /* start from the first cluster */
1950 if (clst
== 0) { /* If no cluster chain, create a new chain */
1951 clst
= create_chain(fp
->fs
, 0);
1952 if (clst
== 1) ABORT(fp
->fs
, FR_INT_ERR
);
1953 if (clst
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
);
1954 fp
->org_clust
= clst
;
1957 fp
->curr_clust
= clst
;
1960 while (ofs
> bcs
) { /* Cluster following loop */
1962 if (fp
->flag
& FA_WRITE
) { /* Check if in write mode or not */
1963 clst
= create_chain(fp
->fs
, clst
); /* Force streached if in write mode */
1964 if (clst
== 0) { /* When disk gets full, clip file size */
1969 clst
= get_cluster(fp
->fs
, clst
); /* Follow cluster chain if not in write mode */
1970 if (clst
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
);
1971 if (clst
<= 1 || clst
>= fp
->fs
->max_clust
) ABORT(fp
->fs
, FR_INT_ERR
);
1972 fp
->curr_clust
= clst
;
1977 fp
->csect
= (BYTE
)(ofs
/ SS(fp
->fs
)); /* Sector offset in the cluster */
1978 if (ofs
% SS(fp
->fs
)) {
1979 nsect
= clust2sect(fp
->fs
, clst
); /* Current sector */
1980 if (!nsect
) ABORT(fp
->fs
, FR_INT_ERR
);
1986 if (nsect
&& nsect
!= fp
->dsect
&& fp
->fptr
% SS(fp
->fs
)) {
1989 if (fp
->flag
& FA__DIRTY
) { /* Write-back dirty buffer if needed */
1990 if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
1991 ABORT(fp
->fs
, FR_DISK_ERR
);
1992 fp
->flag
&= (BYTE
)~FA__DIRTY
;
1995 if (disk_read(fp
->fs
->drive
, fp
->buf
, nsect
, 1) != RES_OK
)
1996 ABORT(fp
->fs
, FR_DISK_ERR
);
2001 if (fp
->fptr
> fp
->fsize
) { /* Set changed flag if the file size is extended */
2002 fp
->fsize
= fp
->fptr
;
2003 fp
->flag
|= FA__WRITTEN
;
2007 LEAVE_FF(fp
->fs
, res
);
2013 #if _FS_MINIMIZE <= 1
2014 /*-----------------------------------------------------------------------*/
2015 /* Create a Directroy Object */
2016 /*-----------------------------------------------------------------------*/
2019 DIR *dj
, /* Pointer to directory object to create */
2020 const char *path
/* Pointer to the directory path */
2028 res
= auto_mount(&path
, &dj
->fs
, 0);
2030 INITBUF((*dj
), sfn
, lfn
);
2031 res
= follow_path(dj
, path
); /* Follow the path to the directory */
2032 if (res
== FR_OK
) { /* Follow completed */
2034 if (dir
) { /* It is not the root dir */
2035 if (dir
[DIR_Attr
] & AM_DIR
) { /* The object is a directory */
2036 dj
->sclust
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
);
2037 } else { /* The object is not a directory */
2040 } else { /* It is the root dir */
2041 dj
->sclust
= (dj
->fs
->fs_type
== FS_FAT32
) ? dj
->fs
->dirbase
: 0;
2043 if (res
== FR_OK
) res
= dir_seek(dj
, 0);
2044 dj
->id
= dj
->fs
->id
;
2046 if (res
== FR_NO_FILE
) res
= FR_NO_PATH
;
2050 LEAVE_FF(dj
->fs
, res
);
2056 /*-----------------------------------------------------------------------*/
2057 /* Read Directory Entry in Sequense */
2058 /*-----------------------------------------------------------------------*/
2061 DIR *dj
, /* Pointer to the open directory object */
2062 FILINFO
*fno
/* Pointer to file information to return */
2069 res
= validate(dj
->fs
, dj
->id
); /* Check validity of the object */
2071 INITBUF((*dj
), sfn
, lfn
);
2073 res
= dir_seek(dj
, 0);
2076 if (res
== FR_NO_FILE
) {
2080 if (res
== FR_OK
) { /* A valid entry is found */
2081 get_fileinfo(dj
, fno
); /* Get the object information */
2082 res
= dir_next(dj
, FALSE
); /* Increment index for next */
2083 if (res
== FR_NO_FILE
) {
2091 LEAVE_FF(dj
->fs
, res
);
2096 #if _FS_MINIMIZE == 0
2097 /*-----------------------------------------------------------------------*/
2098 /* Get File Status */
2099 /*-----------------------------------------------------------------------*/
2102 const char *path
, /* Pointer to the file path */
2103 FILINFO
*fno
/* Pointer to file information to return */
2111 res
= auto_mount(&path
, &dj
.fs
, 0);
2113 INITBUF(dj
, sfn
, lfn
);
2114 res
= follow_path(&dj
, path
); /* Follow the file path */
2115 if (res
== FR_OK
) { /* Follwo completed */
2116 if (dj
.dir
) /* Found an object */
2117 get_fileinfo(&dj
, fno
);
2118 else /* It is root dir */
2119 res
= FR_INVALID_NAME
;
2123 LEAVE_FF(dj
.fs
, res
);
2129 /*-----------------------------------------------------------------------*/
2131 /*-----------------------------------------------------------------------*/
2133 FRESULT
f_truncate (
2134 FIL
*fp
/* Pointer to the file object */
2141 res
= validate(fp
->fs
, fp
->id
); /* Check validity of the object */
2142 if (res
!= FR_OK
) LEAVE_FF(fp
->fs
, res
);
2143 if (fp
->flag
& FA__ERROR
) /* Check abort flag */
2144 LEAVE_FF(fp
->fs
, FR_INT_ERR
);
2145 if (!(fp
->flag
& FA_WRITE
)) /* Check access mode */
2146 LEAVE_FF(fp
->fs
, FR_DENIED
);
2148 if (fp
->fsize
> fp
->fptr
) {
2149 fp
->fsize
= fp
->fptr
; /* Set file size to current R/W point */
2150 fp
->flag
|= FA__WRITTEN
;
2151 if (fp
->fptr
== 0) { /* When set file size to zero, remove entire cluster chain */
2152 res
= remove_chain(fp
->fs
, fp
->org_clust
);
2154 } else { /* When truncate a part of the file, remove remaining clusters */
2155 ncl
= get_cluster(fp
->fs
, fp
->curr_clust
);
2157 if (ncl
== 0xFFFFFFFF) res
= FR_DISK_ERR
;
2158 if (ncl
== 1) res
= FR_INT_ERR
;
2159 if (res
== FR_OK
&& ncl
< fp
->fs
->max_clust
) {
2160 res
= put_cluster(fp
->fs
, fp
->curr_clust
, 0x0FFFFFFF);
2161 if (res
== FR_OK
) res
= remove_chain(fp
->fs
, ncl
);
2165 if (res
!= FR_OK
) fp
->flag
|= FA__ERROR
;
2167 LEAVE_FF(fp
->fs
, res
);
2173 /*-----------------------------------------------------------------------*/
2174 /* Get Number of Free Clusters */
2175 /*-----------------------------------------------------------------------*/
2178 const char *path
, /* Pointer to the logical drive number (root dir) */
2179 DWORD
*nclst
, /* Pointer to the variable to return number of free clusters */
2180 FATFS
**fatfs
/* Pointer to pointer to corresponding file system object to return */
2184 DWORD n
, clst
, sect
;
2188 /* Get drive number */
2189 res
= auto_mount(&path
, fatfs
, 0);
2190 if (res
!= FR_OK
) LEAVE_FF(*fatfs
, res
);
2192 /* If number of free cluster is valid, return it without cluster scan. */
2193 if ((*fatfs
)->free_clust
<= (*fatfs
)->max_clust
- 2) {
2194 *nclst
= (*fatfs
)->free_clust
;
2195 LEAVE_FF(*fatfs
, FR_OK
);
2198 /* Get number of free clusters */
2199 fat
= (*fatfs
)->fs_type
;
2201 if (fat
== FS_FAT12
) {
2204 if ((WORD
)get_cluster(*fatfs
, clst
) == 0) n
++;
2205 } while (++clst
< (*fatfs
)->max_clust
);
2207 clst
= (*fatfs
)->max_clust
;
2208 sect
= (*fatfs
)->fatbase
;
2212 res
= move_window(*fatfs
, sect
++);
2214 LEAVE_FF(*fatfs
, res
);
2217 if (fat
== FS_FAT16
) {
2218 if (LD_WORD(p
) == 0) n
++;
2221 if (LD_DWORD(p
) == 0) n
++;
2226 (*fatfs
)->free_clust
= n
;
2227 if (fat
== FS_FAT32
) (*fatfs
)->fsi_flag
= 1;
2230 LEAVE_FF(*fatfs
, FR_OK
);
2236 /*-----------------------------------------------------------------------*/
2237 /* Delete a File or Directory */
2238 /*-----------------------------------------------------------------------*/
2241 const char *path
/* Pointer to the file or directory path */
2251 res
= auto_mount(&path
, &dj
.fs
, 1);
2252 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
);
2254 INITBUF(dj
, sfn
, lfn
);
2255 res
= follow_path(&dj
, path
); /* Follow the file path */
2256 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); /* Follow failed */
2259 if (!dir
) /* Is it the root directory? */
2260 LEAVE_FF(dj
.fs
, FR_INVALID_NAME
);
2261 if (dir
[DIR_Attr
] & AM_RDO
) /* Is it a R/O object? */
2262 LEAVE_FF(dj
.fs
, FR_DENIED
);
2263 dclst
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
);
2265 if (dir
[DIR_Attr
] & AM_DIR
) { /* It is a sub-directory */
2266 if (dclst
< 2) LEAVE_FF(dj
.fs
, FR_INT_ERR
);
2267 mem_cpy(&sdj
, &dj
, sizeof(DIR)); /* Check if the sub-dir is empty or not */
2269 res
= dir_seek(&sdj
, 0);
2270 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
);
2271 res
= dir_read(&sdj
);
2272 if (res
== FR_OK
) res
= FR_DENIED
; /* Not empty sub-dir */
2273 if (res
!= FR_NO_FILE
) LEAVE_FF(dj
.fs
, res
);
2276 res
= dir_remove(&dj
); /* Remove directory entry */
2279 res
= remove_chain(dj
.fs
, dclst
); /* Remove the cluster chain */
2280 if (res
== FR_OK
) res
= sync(dj
.fs
);
2283 LEAVE_FF(dj
.fs
, res
);
2289 /*-----------------------------------------------------------------------*/
2290 /* Create a Directory */
2291 /*-----------------------------------------------------------------------*/
2294 const char *path
/* Pointer to the directory path */
2301 DWORD dsect
, dclst
, pclst
, tim
;
2304 res
= auto_mount(&path
, &dj
.fs
, 1);
2305 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
);
2307 INITBUF(dj
, sfn
, lfn
);
2308 res
= follow_path(&dj
, path
); /* Follow the file path */
2309 if (res
== FR_OK
) res
= FR_EXIST
; /* Any file or directory is already existing */
2310 if (res
!= FR_NO_FILE
) /* Any error occured */
2311 LEAVE_FF(dj
.fs
, res
);
2313 dclst
= create_chain(dj
.fs
, 0); /* Allocate a new cluster for new directory table */
2315 if (dclst
== 0) res
= FR_DENIED
;
2316 if (dclst
== 1) res
= FR_INT_ERR
;
2317 if (dclst
== 0xFFFFFFFF) res
= FR_DISK_ERR
;
2319 res
= move_window(dj
.fs
, 0);
2320 if (res
!= FR_OK
) LEAVE_FF(dj
.fs
, res
);
2321 dsect
= clust2sect(dj
.fs
, dclst
);
2323 dir
= dj
.fs
->win
; /* Initialize the new directory table */
2324 mem_set(dir
, 0, SS(dj
.fs
));
2325 mem_set(dir
+DIR_Name
, ' ', 8+3); /* Create "." entry */
2326 dir
[DIR_Name
] = '.';
2327 dir
[DIR_Attr
] = AM_DIR
;
2328 tim
= get_fattime();
2329 ST_DWORD(dir
+DIR_WrtTime
, tim
);
2330 ST_WORD(dir
+DIR_FstClusLO
, dclst
);
2331 ST_WORD(dir
+DIR_FstClusHI
, dclst
>> 16);
2332 mem_cpy(dir
+32, dir
, 32); /* Create ".." entry */
2335 if (dj
.fs
->fs_type
== FS_FAT32
&& pclst
== dj
.fs
->dirbase
)
2337 ST_WORD(dir
+32+DIR_FstClusLO
, pclst
);
2338 ST_WORD(dir
+32+DIR_FstClusHI
, pclst
>> 16);
2339 for (n
= 0; n
< dj
.fs
->csize
; n
++) { /* Write dot entries and clear left sectors */
2340 dj
.fs
->winsect
= dsect
++;
2342 res
= move_window(dj
.fs
, 0);
2343 if (res
) LEAVE_FF(dj
.fs
, res
);
2344 mem_set(dir
, 0, SS(dj
.fs
));
2347 res
= dir_register(&dj
);
2349 remove_chain(dj
.fs
, dclst
);
2352 dir
[DIR_Attr
] = AM_DIR
; /* Attribute */
2353 ST_DWORD(dir
+DIR_WrtTime
, tim
); /* Crated time */
2354 ST_WORD(dir
+DIR_FstClusLO
, dclst
); /* Table start cluster */
2355 ST_WORD(dir
+DIR_FstClusHI
, dclst
>> 16);
2360 LEAVE_FF(dj
.fs
, res
);
2366 /*-----------------------------------------------------------------------*/
2367 /* Change File Attribute */
2368 /*-----------------------------------------------------------------------*/
2371 const char *path
, /* Pointer to the file path */
2372 BYTE value
, /* Attribute bits */
2373 BYTE mask
/* Attribute mask to change */
2382 res
= auto_mount(&path
, &dj
.fs
, 1);
2384 INITBUF(dj
, sfn
, lfn
);
2385 res
= follow_path(&dj
, path
); /* Follow the file path */
2388 if (!dir
) { /* Is it a root directory? */
2389 res
= FR_INVALID_NAME
;
2390 } else { /* File or sub directory */
2391 mask
&= AM_RDO
|AM_HID
|AM_SYS
|AM_ARC
; /* Valid attribute mask */
2392 dir
[DIR_Attr
] = (value
& mask
) | (dir
[DIR_Attr
] & (BYTE
)~mask
); /* Apply attribute change */
2399 LEAVE_FF(dj
.fs
, res
);
2405 /*-----------------------------------------------------------------------*/
2406 /* Change Timestamp */
2407 /*-----------------------------------------------------------------------*/
2410 const char *path
, /* Pointer to the file/directory name */
2411 const FILINFO
*fno
/* Pointer to the timestamp to be set */
2420 res
= auto_mount(&path
, &dj
.fs
, 1);
2422 INITBUF(dj
, sfn
, lfn
);
2423 res
= follow_path(&dj
, path
); /* Follow the file path */
2426 if (!dir
) { /* Root directory */
2427 res
= FR_INVALID_NAME
;
2428 } else { /* File or sub-directory */
2429 ST_WORD(dir
+DIR_WrtTime
, fno
->ftime
);
2430 ST_WORD(dir
+DIR_WrtDate
, fno
->fdate
);
2437 LEAVE_FF(dj
.fs
, res
);
2443 /*-----------------------------------------------------------------------*/
2444 /* Rename File/Directory */
2445 /*-----------------------------------------------------------------------*/
2448 const char *path_old
, /* Pointer to the old name */
2449 const char *path_new
/* Pointer to the new name */
2459 INITBUF(dj_old
, sfn
, lfn
);
2460 res
= auto_mount(&path_old
, &dj_old
.fs
, 1);
2462 dj_new
.fs
= dj_old
.fs
;
2463 res
= follow_path(&dj_old
, path_old
); /* Check old object */
2465 if (res
!= FR_OK
) LEAVE_FF(dj_old
.fs
, res
); /* The old object is not found */
2467 if (!dj_old
.dir
) LEAVE_FF(dj_old
.fs
, FR_NO_FILE
); /* Is root dir? */
2468 mem_cpy(buf
, dj_old
.dir
+DIR_Attr
, 21); /* Save the object information */
2470 mem_cpy(&dj_new
, &dj_old
, sizeof(DIR));
2471 res
= follow_path(&dj_new
, path_new
); /* Check new object */
2472 if (res
== FR_OK
) res
= FR_EXIST
; /* The new object name is already existing */
2473 if (res
== FR_NO_FILE
) { /* Is it a valid path and no name collision? */
2474 res
= dir_register(&dj_new
); /* Register the new object */
2476 dir
= dj_new
.dir
; /* Copy object information into new entry */
2477 mem_cpy(dir
+13, buf
+2, 19);
2478 dir
[DIR_Attr
] = buf
[0];
2479 dj_old
.fs
->wflag
= 1;
2480 if (dir
[DIR_Attr
] & AM_DIR
) { /* Update .. entry in the directory if needed */
2481 dw
= clust2sect(dj_new
.fs
, (DWORD
)LD_WORD(dir
+DIR_FstClusHI
) | LD_WORD(dir
+DIR_FstClusLO
));
2485 res
= move_window(dj_new
.fs
, dw
);
2486 dir
= dj_new
.fs
->win
+32;
2487 if (res
== FR_OK
&& dir
[1] == '.') {
2488 dw
= (dj_new
.fs
->fs_type
== FS_FAT32
&& dj_new
.sclust
== dj_new
.fs
->dirbase
) ? 0 : dj_new
.sclust
;
2489 ST_WORD(dir
+DIR_FstClusLO
, dw
);
2490 ST_WORD(dir
+DIR_FstClusHI
, dw
>> 16);
2491 dj_new
.fs
->wflag
= 1;
2496 res
= dir_remove(&dj_old
); /* Remove old entry */
2498 res
= sync(dj_old
.fs
);
2503 LEAVE_FF(dj_old
.fs
, res
);
2506 #endif /* !_FS_READONLY */
2507 #endif /* _FS_MINIMIZE == 0 */
2508 #endif /* _FS_MINIMIZE <= 1 */
2509 #endif /* _FS_MINIMIZE <= 2 */
2513 /*-----------------------------------------------------------------------*/
2514 /* Forward data to the stream directly (Available on only _FS_TINY cfg) */
2515 /*-----------------------------------------------------------------------*/
2516 #if _USE_FORWARD && _FS_TINY
2519 FIL
*fp
, /* Pointer to the file object */
2520 UINT (*func
)(const BYTE
*,UINT
), /* Pointer to the streaming function */
2521 UINT btr
, /* Number of bytes to forward */
2522 UINT
*bf
/* Pointer to number of bytes forwarded */
2526 DWORD remain
, clst
, sect
;
2532 res
= validate(fp
->fs
, fp
->id
); /* Check validity of the object */
2533 if (res
!= FR_OK
) LEAVE_FF(fp
->fs
, res
);
2534 if (fp
->flag
& FA__ERROR
) /* Check error flag */
2535 LEAVE_FF(fp
->fs
, FR_INT_ERR
);
2536 if (!(fp
->flag
& FA_READ
)) /* Check access mode */
2537 LEAVE_FF(fp
->fs
, FR_DENIED
);
2539 remain
= fp
->fsize
- fp
->fptr
;
2540 if (btr
> remain
) btr
= (UINT
)remain
; /* Truncate btr by remaining bytes */
2542 for ( ; btr
&& (*func
)(NULL
, 0); /* Repeat until all data transferred or stream becomes busy */
2543 fp
->fptr
+= rcnt
, *bf
+= rcnt
, btr
-= rcnt
) {
2544 if ((fp
->fptr
% SS(fp
->fs
)) == 0) { /* On the sector boundary? */
2545 if (fp
->csect
>= fp
->fs
->csize
) { /* On the cluster boundary? */
2546 clst
= (fp
->fptr
== 0) ? /* On the top of the file? */
2547 fp
->org_clust
: get_cluster(fp
->fs
, fp
->curr_clust
);
2548 if (clst
<= 1) ABORT(fp
->fs
, FR_INT_ERR
);
2549 if (clst
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
);
2550 fp
->curr_clust
= clst
; /* Update current cluster */
2551 fp
->csect
= 0; /* Reset sector address in the cluster */
2553 fp
->csect
++; /* Next sector address in the cluster */
2555 sect
= clust2sect(fp
->fs
, fp
->curr_clust
); /* Get current data sector */
2556 if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
);
2557 sect
+= fp
->csect
- 1;
2558 if (move_window(fp
->fs
, sect
)) /* Move sector window */
2559 ABORT(fp
->fs
, FR_DISK_ERR
);
2561 rcnt
= SS(fp
->fs
) - (WORD
)(fp
->fptr
% SS(fp
->fs
)); /* Forward data from sector window */
2562 if (rcnt
> btr
) rcnt
= btr
;
2563 rcnt
= (*func
)(&fp
->fs
->win
[(WORD
)fp
->fptr
% SS(fp
->fs
)], rcnt
);
2564 if (!rcnt
) ABORT(fp
->fs
, FR_INT_ERR
);
2567 LEAVE_FF(fp
->fs
, FR_OK
);
2569 #endif /* _USE_FORWARD */
2573 #if _USE_MKFS && !_FS_READONLY
2574 /*-----------------------------------------------------------------------*/
2575 /* Create File System on the Drive */
2576 /*-----------------------------------------------------------------------*/
2577 #define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */
2578 #define N_FATS 1 /* 1 or 2 */
2579 #define MAX_SECTOR 131072000UL /* Maximum partition size */
2580 #define MIN_SECTOR 2000UL /* Minimum partition size */
2584 BYTE drv
, /* Logical drive number */
2585 BYTE partition
, /* Partitioning rule 0:FDISK, 1:SFD */
2586 WORD allocsize
/* Allocation unit size [bytes] */
2589 static const DWORD sstbl
[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 };
2590 static const WORD cstbl
[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 };
2592 DWORD b_part
, b_fat
, b_dir
, b_data
; /* Area offset (LBA) */
2593 DWORD n_part
, n_rsv
, n_fat
, n_dir
; /* Area size */
2600 /* Check validity of the parameters */
2601 if (drv
>= _DRIVES
) return FR_INVALID_DRIVE
;
2602 if (partition
>= 2) return FR_MKFS_ABORTED
;
2604 /* Check mounted drive and clear work area */
2606 if (!fs
) return FR_NOT_ENABLED
;
2610 /* Get disk statics */
2611 stat
= disk_initialize(drv
);
2612 if (stat
& STA_NOINIT
) return FR_NOT_READY
;
2613 if (stat
& STA_PROTECT
) return FR_WRITE_PROTECTED
;
2614 if (disk_ioctl(drv
, GET_SECTOR_COUNT
, &n_part
) != RES_OK
|| n_part
< MIN_SECTOR
)
2615 return FR_MKFS_ABORTED
;
2616 if (n_part
> MAX_SECTOR
) n_part
= MAX_SECTOR
;
2617 b_part
= (!partition
) ? 63 : 0; /* Boot sector */
2620 if (!allocsize
) { /* Auto selection of cluster size */
2621 for (n
= 0; n_part
< sstbl
[n
]; n
++) ;
2622 allocsize
= cstbl
[n
];
2625 for (as
= 512; as
<= 32768U && as
!= allocsize
; as
<<= 1);
2626 if (as
!= allocsize
) return FR_MKFS_ABORTED
;
2627 #if _MAX_SS != 512 /* Check disk sector size */
2628 if (disk_ioctl(drv
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK
2630 || SS(fs
) > allocsize
)
2631 return FR_MKFS_ABORTED
;
2633 allocsize
/= SS(fs
); /* Number of sectors per cluster */
2635 /* Pre-compute number of clusters and FAT type */
2636 n_clst
= n_part
/ allocsize
;
2638 if (n_clst
>= 0xFF5) fmt
= FS_FAT16
;
2639 if (n_clst
>= 0xFFF5) fmt
= FS_FAT32
;
2641 /* Determine offset and size of FAT structure */
2644 n_fat
= ((n_clst
* 3 + 1) / 2 + 3 + SS(fs
) - 1) / SS(fs
);
2645 n_rsv
= 1 + partition
;
2646 n_dir
= N_ROOTDIR
* 32 / SS(fs
);
2649 n_fat
= ((n_clst
* 2) + 4 + SS(fs
) - 1) / SS(fs
);
2650 n_rsv
= 1 + partition
;
2651 n_dir
= N_ROOTDIR
* 32 / SS(fs
);
2654 n_fat
= ((n_clst
* 4) + 8 + SS(fs
) - 1) / SS(fs
);
2655 n_rsv
= 33 - partition
;
2658 b_fat
= b_part
+ n_rsv
; /* FATs start sector */
2659 b_dir
= b_fat
+ n_fat
* N_FATS
; /* Directory start sector */
2660 b_data
= b_dir
+ n_dir
; /* Data start sector */
2662 /* Align data start sector to erase block boundary (for flash memory media) */
2663 if (disk_ioctl(drv
, GET_BLOCK_SIZE
, &n
) != RES_OK
) return FR_MKFS_ABORTED
;
2664 n
= (b_data
+ n
- 1) & ~(n
- 1);
2665 n_fat
+= (n
- b_data
) / N_FATS
;
2666 /* b_dir and b_data are no longer used below */
2668 /* Determine number of cluster and final check of validity of the FAT type */
2669 n_clst
= (n_part
- n_rsv
- n_fat
* N_FATS
- n_dir
) / allocsize
;
2670 if ( (fmt
== FS_FAT16
&& n_clst
< 0xFF5)
2671 || (fmt
== FS_FAT32
&& n_clst
< 0xFFF5))
2672 return FR_MKFS_ABORTED
;
2674 /* Create partition table if needed */
2676 DWORD n_disk
= b_part
+ n_part
;
2678 tbl
= fs
->win
+MBR_Table
;
2679 ST_DWORD(tbl
, 0x00010180); /* Partition start in CHS */
2680 if (n_disk
< 63UL * 255 * 1024) { /* Partition end in CHS */
2681 n_disk
= n_disk
/ 63 / 255;
2682 tbl
[7] = (BYTE
)n_disk
;
2683 tbl
[6] = (BYTE
)((n_disk
>> 2) | 63);
2685 ST_WORD(&tbl
[6], 0xFFFF);
2688 if (fmt
!= FS_FAT32
) /* System ID */
2689 tbl
[4] = (n_part
< 0x10000) ? 0x04 : 0x06;
2692 ST_DWORD(tbl
+8, 63); /* Partition start in LBA */
2693 ST_DWORD(tbl
+12, n_part
); /* Partition size in LBA */
2694 ST_WORD(tbl
+64, 0xAA55); /* Signature */
2695 if (disk_write(drv
, fs
->win
, 0, 1) != RES_OK
)
2699 /* Create boot record */
2700 tbl
= fs
->win
; /* Clear buffer */
2701 mem_set(tbl
, 0, SS(fs
));
2702 ST_DWORD(tbl
+BS_jmpBoot
, 0x90FEEB); /* Boot code (jmp $, nop) */
2703 ST_WORD(tbl
+BPB_BytsPerSec
, SS(fs
)); /* Sector size */
2704 tbl
[BPB_SecPerClus
] = (BYTE
)allocsize
; /* Sectors per cluster */
2705 ST_WORD(tbl
+BPB_RsvdSecCnt
, n_rsv
); /* Reserved sectors */
2706 tbl
[BPB_NumFATs
] = N_FATS
; /* Number of FATs */
2707 ST_WORD(tbl
+BPB_RootEntCnt
, SS(fs
) / 32 * n_dir
); /* Number of rootdir entries */
2708 if (n_part
< 0x10000) { /* Number of total sectors */
2709 ST_WORD(tbl
+BPB_TotSec16
, n_part
);
2711 ST_DWORD(tbl
+BPB_TotSec32
, n_part
);
2713 tbl
[BPB_Media
] = 0xF8; /* Media descripter */
2714 ST_WORD(tbl
+BPB_SecPerTrk
, 63); /* Number of sectors per track */
2715 ST_WORD(tbl
+BPB_NumHeads
, 255); /* Number of heads */
2716 ST_DWORD(tbl
+BPB_HiddSec
, b_part
); /* Hidden sectors */
2717 n
= get_fattime(); /* Use current time as a VSN */
2718 if (fmt
!= FS_FAT32
) {
2719 ST_DWORD(tbl
+BS_VolID
, n
); /* Volume serial number */
2720 ST_WORD(tbl
+BPB_FATSz16
, n_fat
); /* Number of secters per FAT */
2721 tbl
[BS_DrvNum
] = 0x80; /* Drive number */
2722 tbl
[BS_BootSig
] = 0x29; /* Extended boot signature */
2723 mem_cpy(tbl
+BS_VolLab
, "NO NAME FAT ", 19); /* Volume lavel, FAT signature */
2725 ST_DWORD(tbl
+BS_VolID32
, n
); /* Volume serial number */
2726 ST_DWORD(tbl
+BPB_FATSz32
, n_fat
); /* Number of secters per FAT */
2727 ST_DWORD(tbl
+BPB_RootClus
, 2); /* Root directory cluster (2) */
2728 ST_WORD(tbl
+BPB_FSInfo
, 1); /* FSInfo record offset (bs+1) */
2729 ST_WORD(tbl
+BPB_BkBootSec
, 6); /* Backup boot record offset (bs+6) */
2730 tbl
[BS_DrvNum32
] = 0x80; /* Drive number */
2731 tbl
[BS_BootSig32
] = 0x29; /* Extended boot signature */
2732 mem_cpy(tbl
+BS_VolLab32
, "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */
2734 ST_WORD(tbl
+BS_55AA
, 0xAA55); /* Signature */
2735 if (disk_write(drv
, tbl
, b_part
+0, 1) != RES_OK
)
2737 if (fmt
== FS_FAT32
)
2738 disk_write(drv
, tbl
, b_part
+6, 1);
2740 /* Initialize FAT area */
2741 for (m
= 0; m
< N_FATS
; m
++) {
2742 mem_set(tbl
, 0, SS(fs
)); /* 1st sector of the FAT */
2743 if (fmt
!= FS_FAT32
) {
2744 n
= (fmt
== FS_FAT12
) ? 0x00FFFFF8 : 0xFFFFFFF8;
2745 ST_DWORD(tbl
, n
); /* Reserve cluster #0-1 (FAT12/16) */
2747 ST_DWORD(tbl
+0, 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */
2748 ST_DWORD(tbl
+4, 0xFFFFFFFF);
2749 ST_DWORD(tbl
+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */
2751 if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
)
2753 mem_set(tbl
, 0, SS(fs
)); /* Following FAT entries are filled by zero */
2754 for (n
= 1; n
< n_fat
; n
++) {
2755 if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
)
2760 /* Initialize Root directory */
2761 m
= (BYTE
)((fmt
== FS_FAT32
) ? allocsize
: n_dir
);
2763 if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
)
2767 /* Create FSInfo record if needed */
2768 if (fmt
== FS_FAT32
) {
2769 ST_WORD(tbl
+BS_55AA
, 0xAA55);
2770 ST_DWORD(tbl
+FSI_LeadSig
, 0x41615252);
2771 ST_DWORD(tbl
+FSI_StrucSig
, 0x61417272);
2772 ST_DWORD(tbl
+FSI_Free_Count
, n_clst
- 1);
2773 ST_DWORD(tbl
+FSI_Nxt_Free
, 0xFFFFFFFF);
2774 disk_write(drv
, tbl
, b_part
+1, 1);
2775 disk_write(drv
, tbl
, b_part
+7, 1);
2778 return (disk_ioctl(drv
, CTRL_SYNC
, (void*)NULL
) == RES_OK
) ? FR_OK
: FR_DISK_ERR
;
2781 #endif /* _USE_MKFS && !_FS_READONLY */
2787 /*-----------------------------------------------------------------------*/
2788 /* Get a string from the file */
2789 /*-----------------------------------------------------------------------*/
2791 char* buff
, /* Pointer to the string buffer to read */
2792 int len
, /* Size of string buffer */
2793 FIL
* fil
/* Pointer to the file object */
2801 while (i
< len
- 1) { /* Read bytes until buffer gets filled */
2802 f_read(fil
, p
, 1, &rc
);
2803 if (rc
!= 1) break; /* Break when no data to read */
2804 #if _USE_STRFUNC >= 2
2805 if (*p
== '\r') continue; /* Strip '\r' */
2808 if (*p
++ == '\n') break; /* Break when reached end of line */
2811 return i
? buff
: NULL
; /* When no data read (eof or error), return with error. */
2818 /*-----------------------------------------------------------------------*/
2819 /* Put a character to the file */
2820 /*-----------------------------------------------------------------------*/
2822 int chr
, /* A character to be output */
2823 FIL
* fil
/* Ponter to the file object */
2830 #if _USE_STRFUNC >= 2
2831 if (chr
== '\n') f_putc ('\r', fil
); /* LF -> CRLF conversion */
2833 if (!fil
) { /* Special value may be used to switch the destination to any other device */
2834 /* put_console(chr); */
2838 f_write(fil
, &c
, 1, &bw
); /* Write a byte to the file */
2839 return bw
? chr
: EOF
; /* Return the result */
2845 /*-----------------------------------------------------------------------*/
2846 /* Put a string to the file */
2847 /*-----------------------------------------------------------------------*/
2849 const char* str
, /* Pointer to the string to be output */
2850 FIL
* fil
/* Pointer to the file object */
2856 for (n
= 0; *str
; str
++, n
++) {
2857 if (f_putc(*str
, fil
) == EOF
) return EOF
;
2865 /*-----------------------------------------------------------------------*/
2866 /* Put a formatted string to the file */
2867 /*-----------------------------------------------------------------------*/
2869 FIL
* fil
, /* Pointer to the file object */
2870 const char* str
, /* Pointer to the format string */
2871 ... /* Optional arguments... */
2883 for (cc
= res
= 0; cc
!= EOF
; res
+= cc
) {
2885 if (c
== 0) break; /* End of string */
2886 if (c
!= '%') { /* Non escape cahracter */
2887 cc
= f_putc(c
, fil
);
2888 if (cc
!= EOF
) cc
= 1;
2893 if (c
== '0') { /* Flag: '0' padding */
2896 while (c
>= '0' && c
<= '9') { /* Precision */
2897 w
= w
* 10 + (c
- '0');
2900 if (c
== 'l') { /* Prefix: Size is long int */
2903 if (c
== 's') { /* Type is string */
2904 cc
= f_puts(va_arg(arp
, char*), fil
);
2907 if (c
== 'c') { /* Type is character */
2908 cc
= f_putc(va_arg(arp
, int), fil
);
2909 if (cc
!= EOF
) cc
= 1;
2913 if (c
== 'd') r
= 10; /* Type is signed decimal */
2914 if (c
== 'u') r
= 10; /* Type is unsigned decimal */
2915 if (c
== 'X') r
= 16; /* Type is unsigned hexdecimal */
2916 if (r
== 0) break; /* Unknown type */
2917 if (f
& 2) { /* Get the value */
2918 val
= (ULONG
)va_arg(arp
, long);
2920 val
= (c
== 'd') ? (ULONG
)(long)va_arg(arp
, int) : (ULONG
)va_arg(arp
, unsigned int);
2922 /* Put numeral string */
2924 if (val
& 0x80000000) {
2929 i
= sizeof(s
) - 1; s
[i
] = 0;
2931 c
= (UCHAR
)(val
% r
+ '0');
2932 if (c
> '9') c
+= 7;
2936 if (i
&& (f
& 4)) s
[--i
] = '-';
2937 w
= sizeof(s
) - 1 - w
;
2938 while (i
&& i
> w
) s
[--i
] = (f
& 1) ? '0' : ' ';
2939 cc
= f_puts(&s
[i
], fil
);
2943 return (cc
== EOF
) ? cc
: res
;
2946 #endif /* !_FS_READONLY */
2947 #endif /* _USE_STRFUNC */