1 /*----------------------------------------------------------------------------/
2 / FatFs - FAT file system module R0.07e (C)ChaN, 2009
3 /-----------------------------------------------------------------------------/
4 / FatFs module is a generic FAT file system module for small embedded systems.
5 / This is a free software that opened for education, research and commercial
6 / 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 products UNDER YOUR RESPONSIBILITY.
13 / * Redistributions of source code must retain the above copyright notice.
15 /-----------------------------------------------------------------------------/
16 / Feb 26,'06 R0.00 Prototype.
18 / Apr 29,'06 R0.01 First stable version.
20 / Jun 01,'06 R0.02 Added FAT12 support.
21 / Removed unbuffered mode.
22 / Fixed a problem on small (<32M) patition.
23 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
25 / Sep 22,'06 R0.03 Added f_rename().
26 / Changed option _FS_MINIMUM to _FS_MINIMIZE.
27 / Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast.
28 / Fixed f_mkdir() creates incorrect directory on FAT32.
30 / Feb 04,'07 R0.04 Supported multiple drive system.
31 / Changed some interfaces for multiple drive system.
32 / Changed f_mountdrv() to f_mount().
34 / Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive.
35 / Added a capability of extending file size to f_lseek().
36 / Added minimization level 3.
37 / Fixed an endian sensitive code in f_mkfs().
38 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
39 / Added FSInfo support.
40 / Fixed DBCS name can result FR_INVALID_NAME.
41 / Fixed short seek (<= csize) collapses the file object.
43 / Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs().
44 / Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
45 / Fixed f_mkdir() on FAT32 creates incorrect directory.
46 / Feb 03,'08 R0.05a Added f_truncate() and f_utime().
47 / Fixed off by one error at FAT sub-type determination.
48 / Fixed btr in f_read() can be mistruncated.
49 / Fixed cached sector is not flushed when create and close
52 / Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets().
53 / Improved performance of f_lseek() on moving to the same
54 / or following cluster.
56 / Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option.
57 / Added long file name support.
58 / Added multiple code page support.
59 / Added re-entrancy for multitask operation.
60 / Added auto cluster size selection to f_mkfs().
61 / Added rewind option to f_readdir().
62 / Changed result code of critical errors.
63 / Renamed string functions to avoid name collision.
64 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
65 / Added multiple sector size support.
66 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
67 / Fixed wrong cache control in f_lseek().
68 / Added relative path feature.
69 / Added f_chdir() and f_chdrive().
70 / Added proper case conversion to extended char.
71 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
72 / Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
73 / Fixed name matching error on the 13 char boundary.
74 / Added a configuration option, _LFN_UNICODE.
75 / Changed f_readdir() to return the SFN with always upper
76 / case on non-LFN cfg.
77 /---------------------------------------------------------------------------*/
78 #include "ff.h" /* FatFs configurations and declarations */
79 #include "diskio.h" /* Declarations of low level disk I/O functions */
81 /*--------------------------------------------------------------------------
83 Module Private Definitions
85 ---------------------------------------------------------------------------*/
87 #error Wrong include file (ff.h).
91 #error Static LFN work area must not be used in re-entrant configuration.
93 #define ENTER_FF( fs ) \
101 #define LEAVE_FF( fs, res ) \
103 unlock_fs( fs, res ); \
108 #define ENTER_FF( fs )
109 #define LEAVE_FF( fs, res ) return res
111 #define ABORT( fs, res ) \
113 fp->flag |= FA__ERROR; \
114 LEAVE_FF( fs, res ); \
121 /* Name status flags */
122 #define NS 11 /* Offset of name status byte */
123 #define NS_LOSS 0x01 /* Out of 8.3 format */
124 #define NS_LFN 0x02 /* Force to create LFN entry */
125 #define NS_LAST 0x04 /* Last segment */
126 #define NS_BODY 0x08 /* Lower case flag (body) */
127 #define NS_EXT 0x10 /* Lower case flag (ext) */
128 #define NS_DOT 0x20 /* Dot entry */
130 /*--------------------------------------------------------------------------
134 ---------------------------------------------------------------------------*/
135 #if _DRIVES < 1 || _DRIVES > 9
136 #error Number of drives must be 1-9.
138 static FATFS
*FatFs
[_DRIVES
]; /* Pointer to the file system objects (logical drives) */
140 static WORD Fsid
; /* File system mount ID */
143 static BYTE Drive
; /* Current drive */
145 #if _USE_LFN == 1 /* LFN with static LFN working buffer */
146 static WCHAR LfnBuf
[_MAX_LFN
+ 1];
147 #define NAMEBUF( sp, lp ) \
150 #define INITBUF( dj, sp, lp ) \
154 #elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */
155 #define NAMEBUF( sp, lp ) \
157 WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
158 #define INITBUF( dj, sp, lp ) \
163 #define NAMEBUF( sp, lp ) BYTE sp[12]
164 #define INITBUF( dj, sp, lp ) dj.fn = sp
167 /*--------------------------------------------------------------------------
169 Module Private Functions
171 ---------------------------------------------------------------------------*/
173 /*-----------------------------------------------------------------------*/
175 /* String functions */
177 /*-----------------------------------------------------------------------*/
179 /* Copy memory to memory */
180 static void mem_cpy ( void *dst
, const void *src
, int cnt
)
182 char *d
= ( char * ) dst
;
183 const char *s
= ( const char * ) src
;
191 static void mem_set ( void *dst
, int val
, int cnt
)
193 char *d
= ( char * ) dst
;
200 /* Compare memory to memory */
201 static int mem_cmp ( const void *dst
, const void *src
, int cnt
)
203 const char *d
= ( const char * ) dst
, *s
= ( const char * ) src
;
205 while( cnt
-- && (r
= *d
++ -*s
++) == 0 );
209 /* Check if chr is contained in the string */
210 static int chk_chr ( const char *str
, int chr
)
212 while( *str
&& *str
!= chr
)
220 /*-----------------------------------------------------------------------*/
222 /* Request/Release grant to access the volume */
224 /*-----------------------------------------------------------------------*/
226 static BOOL
lock_fs ( FATFS
* fs
/* File system object */ )
228 return ff_req_grant( fs
->sobj
);
231 static void unlock_fs ( FATFS
* fs
, /* File system object */ FRESULT res
/* Result code to be returned */ )
233 if( res
!= FR_NOT_ENABLED
&& res
!= FR_INVALID_DRIVE
&& res
!= FR_INVALID_OBJECT
&& res
!= FR_TIMEOUT
)
235 ff_rel_grant( fs
->sobj
);
241 /*-----------------------------------------------------------------------*/
243 /* Change window offset */
245 /*-----------------------------------------------------------------------*/
246 static FRESULT
move_window ( FATFS
* fs
, /* File system object */ DWORD sector
/* Sector number to make apperance in the fs->win[] */ ) /* Move to zero only writes back dirty window */
251 if( wsect
!= sector
)
252 { /* Changed current window */
255 { /* Write back dirty window if needed */
256 if( disk_write(fs
->drive
, fs
->win
, wsect
, 1) != RES_OK
)
262 if( wsect
< (fs
->fatbase
+ fs
->sects_fat
) )
265 for( nf
= fs
->n_fats
; nf
> 1; nf
-- )
266 { /* Refrect the change to all FAT copies */
267 wsect
+= fs
->sects_fat
;
268 disk_write( fs
->drive
, fs
->win
, wsect
, 1 );
276 if( disk_read(fs
->drive
, fs
->win
, sector
, 1) != RES_OK
)
281 fs
->winsect
= sector
;
288 /*-----------------------------------------------------------------------*/
290 /* Clean-up cached data */
292 /*-----------------------------------------------------------------------*/
294 static FRESULT
sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ FATFS
* fs
/* File system object */ )
298 res
= move_window( fs
, 0 );
301 /* Update FSInfo sector if needed */
302 if( fs
->fs_type
== FS_FAT32
&& fs
->fsi_flag
)
305 mem_set( fs
->win
, 0, 512 );
306 ST_WORD( fs
->win
+ BS_55AA
, 0xAA55 );
307 ST_DWORD( fs
->win
+ FSI_LeadSig
, 0x41615252 );
308 ST_DWORD( fs
->win
+ FSI_StrucSig
, 0x61417272 );
309 ST_DWORD( fs
->win
+ FSI_Free_Count
, fs
->free_clust
);
310 ST_DWORD( fs
->win
+ FSI_Nxt_Free
, fs
->last_clust
);
311 disk_write( fs
->drive
, fs
->win
, fs
->fsi_sector
, 1 );
315 /* Make sure that no pending write process in the physical drive */
316 if( disk_ioctl(fs
->drive
, CTRL_SYNC
, ( void * ) NULL
) != RES_OK
)
327 /*-----------------------------------------------------------------------*/
329 /* FAT access - Read value of a FAT entry */
331 /*-----------------------------------------------------------------------*/
333 ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */
334 FATFS
* fs
, /* File system object */ DWORD clst
/* Cluster# to get the link information */
340 if( clst
< 2 || clst
>= fs
->max_clust
)
346 switch( fs
->fs_type
)
351 if( move_window(fs
, fsect
+ (bc
/ SS(fs
))) )
356 wc
= fs
->win
[bc
& ( SS(fs
) - 1 )];
358 if( move_window(fs
, fsect
+ (bc
/ SS(fs
))) )
363 wc
|= ( WORD
) fs
->win
[bc
& ( SS(fs
) - 1 )] << 8;
364 return( clst
& 1 ) ? ( wc
>> 4 ) : ( wc
& 0xFFF );
367 if( move_window(fs
, fsect
+ (clst
/ (SS(fs
) / 2))) )
372 return LD_WORD( &fs
->win
[((WORD
) clst
* 2) & (SS(fs
) - 1)] );
375 if( move_window(fs
, fsect
+ (clst
/ (SS(fs
) / 4))) )
381 LD_DWORD( &fs
->win
[((WORD
) clst
* 4) & (SS(fs
) - 1)] )
385 return 0xFFFFFFFF; /* An error occured at the disk I/O layer */
388 /*-----------------------------------------------------------------------*/
390 /* FAT access - Change value of a FAT entry */
392 /*-----------------------------------------------------------------------*/
395 ( FATFS
* fs
, /* File system object */ DWORD clst
, /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */ DWORD val
/* New value to mark the cluster */ )
402 if( clst
< 2 || clst
>= fs
->max_clust
)
409 switch( fs
->fs_type
)
414 res
= move_window( fs
, fsect
+ (bc
/ SS(fs
)) );
420 p
= &fs
->win
[bc
& ( SS(fs
) - 1 )];
421 *p
= ( clst
& 1 ) ? ( (*p
& 0x0F) | ((BYTE
) val
<< 4) ) : ( BYTE
) val
;
424 res
= move_window( fs
, fsect
+ (bc
/ SS(fs
)) );
430 p
= &fs
->win
[bc
& ( SS(fs
) - 1 )];
431 *p
= ( clst
& 1 ) ? ( BYTE
) ( val
>> 4 ) : ( (*p
& 0xF0) | ((BYTE
) (val
>> 8) & 0x0F) );
435 res
= move_window( fs
, fsect
+ (clst
/ (SS(fs
) / 2)) );
441 ST_WORD( &fs
->win
[((WORD
) clst
* 2) & (SS(fs
) - 1)], (WORD
) val
);
445 res
= move_window( fs
, fsect
+ (clst
/ (SS(fs
) / 4)) );
451 ST_DWORD( &fs
->win
[((WORD
) clst
* 4) & (SS(fs
) - 1)], val
);
464 #endif /* !_FS_READONLY */
466 /*-----------------------------------------------------------------------*/
468 /* FAT handling - Remove a cluster chain */
470 /*-----------------------------------------------------------------------*/
472 static FRESULT
remove_chain ( FATFS
* fs
, /* File system object */ DWORD clst
/* Cluster# to remove a chain from */ )
477 if( clst
< 2 || clst
>= fs
->max_clust
)
478 { /* Check the range of cluster# */
484 while( clst
< fs
->max_clust
)
485 { /* Not a last link? */
486 nxt
= get_fat( fs
, clst
); /* Get cluster status */
489 break; /* Empty cluster? */
496 } /* Internal error? */
497 if( nxt
== 0xFFFFFFFF )
502 res
= put_fat( fs
, clst
, 0 ); /* Mark the cluster "empty" */
508 if( fs
->free_clust
!= 0xFFFFFFFF )
509 { /* Update FSInfo */
514 clst
= nxt
; /* Next cluster */
523 /*-----------------------------------------------------------------------*/
525 /* FAT handling - Stretch or Create a cluster chain */
527 /*-----------------------------------------------------------------------*/
529 static DWORD create_chain
530 ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
531 FATFS
* fs
, /* File system object */ DWORD clst
/* Cluster# to stretch. 0 means create a new chain. */
534 DWORD cs
, ncl
, scl
, mcl
;
538 { /* Create new chain */
539 scl
= fs
->last_clust
; /* Get suggested start point */
540 if( scl
== 0 || scl
>= mcl
)
546 { /* Stretch existing chain */
547 cs
= get_fat( fs
, clst
); /* Check the cluster status */
550 return 1; /* It is an invalid cluster */
555 return cs
; /* It is already followed by next cluster */
561 ncl
= scl
; /* Start cluster */
564 ncl
++; /* Next cluster */
570 return 0; /* No free custer */
574 cs
= get_fat( fs
, ncl
); /* Get the cluster status */
577 break; /* Found a free cluster */
580 if( cs
== 0xFFFFFFFF || cs
== 1 )
581 { /* An error occured */
587 return 0; /* No free custer */
591 if( put_fat(fs
, ncl
, 0x0FFFFFFF) )
592 { /* Mark the new cluster "in use" */
597 { /* Link it to the previous one if needed */
598 if( put_fat(fs
, clst
, ncl
) )
604 fs
->last_clust
= ncl
; /* Update FSINFO */
605 if( fs
->free_clust
!= 0xFFFFFFFF )
611 return ncl
; /* Return new cluster number */
614 #endif /* !_FS_READONLY */
616 /*-----------------------------------------------------------------------*/
618 /* Get sector# from cluster# */
620 /*-----------------------------------------------------------------------*/
622 ( /* !=0: Sector number, 0: Failed - invalid cluster# */ FATFS
* fs
, /* File system object */ DWORD clst
/* Cluster# to be converted */ )
625 if( clst
>= (fs
->max_clust
- 2) )
627 return 0; /* Invalid cluster# */
630 return clst
* fs
->csize
+ fs
->database
;
633 /*-----------------------------------------------------------------------*/
635 /* Directory handling - Seek directory index */
637 /*-----------------------------------------------------------------------*/
638 static FRESULT
dir_seek ( DIR * dj
, /* Pointer to directory object */ WORD idx
/* Directory index number */ )
645 if( clst
== 1 || clst
>= dj
->fs
->max_clust
)
646 { /* Check start cluster range */
650 if( !clst
&& dj
->fs
->fs_type
== FS_FAT32
)
651 { /* Replace cluster# 0 with root cluster# if in FAT32 */
652 clst
= dj
->fs
->dirbase
;
658 if( idx
>= dj
->fs
->n_rootdir
)
659 { /* Index is out of range */
663 dj
->sect
= dj
->fs
->dirbase
+ idx
/ ( SS(dj
->fs
) / 32 ); /* Sector# */
666 { /* Dynamic table */
669 / 32 * dj
->fs
->csize
; /* Entries per cluster */
671 { /* Follow cluster chain */
672 clst
= get_fat( dj
->fs
, clst
); /* Get next cluster */
673 if( clst
== 0xFFFFFFFF )
675 return FR_DISK_ERR
; /* Disk error */
678 if( clst
< 2 || clst
>= dj
->fs
->max_clust
)
679 { /* Reached to end of table or int error */
688 clust2sect( dj
->fs
, clst
)
689 + idx
/ ( SS(dj
->fs
) / 32 ); /* Sector# */
692 dj
->dir
= dj
->fs
->win
+ ( idx
% (SS(dj
->fs
) / 32) ) * 32; /* Ptr to the entry in the sector */
694 return FR_OK
; /* Seek succeeded */
697 /*-----------------------------------------------------------------------*/
699 /* Directory handling - Move directory index next */
701 /*-----------------------------------------------------------------------*/
702 static FRESULT dir_next
703 ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
705 dj
, /* Pointer to directory object */ BOOL streach
/* FALSE: Do not streach table, TRUE: Streach table if needed */
712 if( !i
|| !dj
->sect
)
713 { /* Report EOT when index has reached 65535 */
717 if( !(i
% (SS(dj
->fs
) / 32)) )
718 { /* Sector changed? */
719 dj
->sect
++; /* Next sector */
723 if( i
>= dj
->fs
->n_rootdir
)
724 { /* Report EOT when end of table */
729 { /* Dynamic table */
730 if( ((i
/ (SS(dj
->fs
) / 32)) & (dj
->fs
->csize
- 1)) == 0 )
731 { /* Cluster changed? */
732 clst
= get_fat( dj
->fs
, dj
->clust
); /* Get next cluster */
738 if( clst
== 0xFFFFFFFF )
743 if( clst
>= dj
->fs
->max_clust
)
744 { /* When it reached end of dynamic table */
749 return FR_NO_FILE
; /* When do not streach, report EOT */
752 clst
= create_chain( dj
->fs
, dj
->clust
); /* Streach cluster chain */
755 return FR_DENIED
; /* No free cluster */
763 if( clst
== 0xFFFFFFFF )
768 /* Clean-up streached table */
769 if( move_window(dj
->fs
, 0) )
771 return FR_DISK_ERR
; /* Flush active window */
774 mem_set( dj
->fs
->win
, 0, SS(dj
->fs
) ); /* Clear window buffer */
775 dj
->fs
->winsect
= clust2sect( dj
->fs
, clst
); /* Cluster start sector */
776 for( c
= 0; c
< dj
->fs
->csize
; c
++ )
777 { /* Fill the new cluster with 0 */
779 if( move_window(dj
->fs
, 0) )
787 dj
->fs
->winsect
-= c
; /* Rewind window address */
789 return FR_NO_FILE
; /* Report EOT */
793 dj
->clust
= clst
; /* Initialize data for new cluster */
794 dj
->sect
= clust2sect( dj
->fs
, clst
);
800 dj
->dir
= dj
->fs
->win
+ ( i
% (SS(dj
->fs
) / 32) ) * 32;
805 /*-----------------------------------------------------------------------*/
807 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */
809 /*-----------------------------------------------------------------------*/
811 static const BYTE LfnOfs
[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 };
813 /* Offset of LFN chars in the directory entry */
816 ( /* TRUE:Matched, FALSE:Not matched */
817 WCHAR
*lfnbuf
, /* Pointer to the LFN to be compared */
818 BYTE
*dir
/* Pointer to the directory entry containing a part of LFN */
824 i
= ( (dir
[LDIR_Ord
] & 0xBF) - 1 ) * 13; /* Get offset in the LFN buffer */
829 uc
= LD_WORD( dir
+ LfnOfs
[s
] ); /* Pick an LFN character from the entry */
831 { /* Last char has not been processed */
832 wc
= ff_wtoupper( uc
); /* Convert it to upper case */
833 if( i
>= _MAX_LFN
|| wc
!= ff_wtoupper(lfnbuf
[i
++]) )
835 return FALSE
; /* Not matched */
842 return FALSE
; /* Check filler */
847 /* Repeat until all chars in the entry are checked */
849 if( (dir
[LDIR_Ord
] & 0x40) && wc
&& lfnbuf
[i
] )
850 { /* Last segment matched but different length */
854 return TRUE
; /* The part of LFN matched */
858 ( /* TRUE:Succeeded, FALSE:Buffer overflow */
859 WCHAR
*lfnbuf
, /* Pointer to the Unicode-LFN buffer */
860 BYTE
*dir
/* Pointer to the directory entry */
866 i
= ( (dir
[LDIR_Ord
] & 0x3F) - 1 ) * 13; /* Offset in the LFN buffer */
872 uc
= LD_WORD( dir
+ LfnOfs
[s
] ); /* Pick an LFN character from the entry */
874 { /* Last char has not been processed */
877 return FALSE
; /* Buffer overflow? */
880 lfnbuf
[i
++] = wc
= uc
; /* Store it */
886 return FALSE
; /* Check filler */
891 /* Read all character in the entry */
893 if( dir
[LDIR_Ord
] & 0x40 )
894 { /* Put terminator if it is the last LFN part */
897 return FALSE
; /* Buffer overflow? */
909 const WCHAR
*lfnbuf
, /* Pointer to the LFN buffer */
910 BYTE
*dir
, /* Pointer to the directory entry */
911 BYTE ord
, /* LFN order (1-20) */
912 BYTE sum
/* SFN sum */
918 dir
[LDIR_Chksum
] = sum
; /* Set check sum */
919 dir
[LDIR_Attr
] = AM_LFN
; /* Set attribute. LFN entry */
921 ST_WORD( dir
+ LDIR_FstClusLO
, 0 );
923 i
= ( ord
- 1 ) * 13; /* Get offset in the LFN buffer */
929 wc
= lfnbuf
[i
++]; /* Get an effective char */
932 ST_WORD( dir
+ LfnOfs
[s
], wc
); /* Put it */
935 wc
= 0xFFFF; /* Padding chars following last char */
938 if( wc
== 0xFFFF || !lfnbuf
[i
] )
940 ord
|= 0x40; /* Bottom LFN part is the start of LFN sequence */
943 dir
[LDIR_Ord
] = ord
; /* Set the LFN order */
949 /*-----------------------------------------------------------------------*/
951 /* Create numbered name */
953 /*-----------------------------------------------------------------------*/
957 BYTE
*dst
, /* Pointer to genartated SFN */
958 const BYTE
*src
, /* Pointer to source SFN to be modified */
959 const WCHAR
*lfn
, /* Pointer to LFN */
960 WORD num
/* Sequense number */
966 mem_cpy( dst
, src
, 11 );
969 { /* On many collisions, generate a hash number instead of sequencial number */
972 num
= ( num
>> 1 ) + ( num
<< 15 ) + ( WORD
) * lfn
++;
980 ns
[i
--] = ( num
% 10 ) + '0';
985 /* Append the number */
986 for( j
= 0; j
< i
&& dst
[j
] != ' '; j
++ )
988 if( IsDBCS1(dst
[j
]) )
1001 dst
[j
++] = ( i
< 8 ) ? ns
[i
++] : ' ';
1007 /*-----------------------------------------------------------------------*/
1009 /* Calculate sum of an SFN */
1011 /*-----------------------------------------------------------------------*/
1013 static BYTE
sum_sfn( const BYTE
*dir
/* Ptr to directory entry */ )
1020 sum
= ( sum
>> 1 ) + ( sum
<< 7 ) +*dir
++;
1027 /*-----------------------------------------------------------------------*/
1029 /* Directory handling - Find an object in the directory */
1031 /*-----------------------------------------------------------------------*/
1032 static FRESULT
dir_find( DIR *dj
/* Pointer to the directory object linked to the file name */ )
1039 res
= dir_seek( dj
, 0 ); /* Rewind directory object */
1050 res
= move_window( dj
->fs
, dj
->sect
);
1056 dir
= dj
->dir
; /* Ptr to the directory entry of current index */
1062 } /* Reached to end of table */
1064 #if _USE_LFN /* LFN configuration */
1065 a
= dir
[DIR_Attr
] & AM_MASK
;
1066 if( c
== 0xE5 || ((a
& AM_VOL
) && a
!= AM_LFN
) )
1067 { /* An entry without valid data */
1073 { /* An LFN entry is found */
1077 { /* Is it start of LFN sequence? */
1078 sum
= dir
[LDIR_Chksum
];
1080 ord
= c
; /* LFN start order */
1081 dj
->lfn_idx
= dj
->index
;
1084 /* Check validity of the LFN entry and compare it with given name */
1085 ord
= ( c
== ord
&& sum
== dir
[LDIR_Chksum
] && cmp_lfn(dj
->lfn
, dir
) ) ? ord
- 1 : 0xFF;
1089 { /* An SFN entry is found */
1090 if( !ord
&& sum
== sum_sfn(dir
) )
1092 break; /* LFN matched? */
1096 dj
->lfn_idx
= 0xFFFF; /* Reset LFN sequence */
1097 if( !(dj
->fn
[NS
] & NS_LOSS
) && !mem_cmp(dir
, dj
->fn
, 11) )
1099 break; /* SFN matched? */
1104 #else /* Non LFN configuration */
1105 if( !(dir
[DIR_Attr
] & AM_VOL
) && !mem_cmp(dir
, dj
->fn
, 11) )
1106 { /* Is it a valid entry? */
1111 res
= dir_next( dj
, FALSE
); /* Next entry */
1112 } while( res
== FR_OK
);
1117 /*-----------------------------------------------------------------------*/
1119 /* Read an object from the directory */
1121 /*-----------------------------------------------------------------------*/
1122 #if _FS_MINIMIZE <= 1
1123 static FRESULT
dir_read( DIR *dj
/* Pointer to the directory object that pointing the entry to be read */ )
1128 BYTE a
, ord
= 0xFF, sum
= 0xFF;
1133 res
= move_window( dj
->fs
, dj
->sect
);
1139 dir
= dj
->dir
; /* Ptr to the directory entry of current index */
1145 } /* Reached to end of table */
1147 #if _USE_LFN /* LFN configuration */
1148 a
= dir
[DIR_Attr
] & AM_MASK
;
1149 if( c
== 0xE5 || (!_FS_RPATH
&& c
== '.') || ((a
& AM_VOL
) && a
!= AM_LFN
) )
1150 { /* An entry without valid data */
1156 { /* An LFN entry is found */
1158 { /* Is it start of LFN sequence? */
1159 sum
= dir
[LDIR_Chksum
];
1162 dj
->lfn_idx
= dj
->index
;
1165 /* Check LFN validity and capture it */
1166 ord
= ( c
== ord
&& sum
== dir
[LDIR_Chksum
] && pick_lfn(dj
->lfn
, dir
) ) ? ord
- 1 : 0xFF;
1169 { /* An SFN entry is found */
1170 if( ord
|| sum
!= sum_sfn(dir
) )
1171 { /* Is there a valid LFN? */
1172 dj
->lfn_idx
= 0xFFFF; /* It has no LFN. */
1179 #else /* Non LFN configuration */
1180 if( c
!= 0xE5 && (_FS_RPATH
|| c
!= '.') && !(dir
[DIR_Attr
] & AM_VOL
) )
1181 { /* Is it a valid entry? */
1186 res
= dir_next( dj
, FALSE
); /* Next entry */
1203 /*-----------------------------------------------------------------------*/
1205 /* Register an object to the directory */
1207 /*-----------------------------------------------------------------------*/
1209 static FRESULT dir_register
1210 ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
1211 DIR *dj
/* Target directory with object name to be created */
1216 #if _USE_LFN /* LFN configuration */
1218 BYTE sn
[12], *fn
, sum
;
1223 mem_cpy( sn
, fn
, 12 );
1225 if( _FS_RPATH
&& (sn
[NS
] & NS_DOT
) )
1227 return FR_INVALID_NAME
; /* Cannot create dot entry */
1230 if( sn
[NS
] & NS_LOSS
)
1231 { /* When LFN is out of 8.3 format, generate a numbered name */
1233 dj
->lfn
= NULL
; /* Find only SFN */
1234 for( n
= 1; n
< 100; n
++ )
1236 gen_numname( fn
, sn
, lfn
, n
); /* Generate a numbered name */
1237 res
= dir_find( dj
); /* Check if the name collides with existing SFN */
1246 return FR_DENIED
; /* Abort if too many collisions */
1249 if( res
!= FR_NO_FILE
)
1251 return res
; /* Abort if the result is other than 'not collided' */
1258 if( sn
[NS
] & NS_LFN
)
1259 { /* When LFN is to be created, reserve reserve an SFN + LFN entries. */
1260 for( ne
= 0; lfn
[ne
]; ne
++ );
1261 ne
= ( ne
+ 25 ) / 13;
1264 { /* Otherwise reserve only an SFN entry. */
1268 /* Reserve contiguous entries */
1269 res
= dir_seek( dj
, 0 );
1278 res
= move_window( dj
->fs
, dj
->sect
);
1284 c
= *dj
->dir
; /* Check the entry status */
1285 if( c
== 0xE5 || c
== 0 )
1286 { /* Is it a blank entry? */
1289 is
= dj
->index
; /* First index of the contigulus entry */
1294 break; /* A contiguous entry that requiered count is found */
1299 n
= 0; /* Not a blank entry. Restart to search */
1302 res
= dir_next( dj
, TRUE
); /* Next entry with table streach */
1303 } while( res
== FR_OK
);
1305 if( res
== FR_OK
&& ne
> 1 )
1306 { /* Initialize LFN entry if needed */
1307 res
= dir_seek( dj
, is
);
1310 sum
= sum_sfn( dj
->fn
); /* Sum of the SFN tied to the LFN */
1313 { /* Store LFN entries in bottom first */
1314 res
= move_window( dj
->fs
, dj
->sect
);
1320 fit_lfn( dj
->lfn
, dj
->dir
, (BYTE
) ne
, sum
);
1322 res
= dir_next( dj
, FALSE
); /* Next entry */
1323 } while( res
== FR_OK
&& --ne
);
1327 #else /* Non LFN configuration */
1328 res
= dir_seek( dj
, 0 );
1332 { /* Find a blank entry for the SFN */
1333 res
= move_window( dj
->fs
, dj
->sect
);
1340 if( c
== 0xE5 || c
== 0 )
1342 break; /* Is it a blank entry? */
1345 res
= dir_next( dj
, TRUE
); /* Next entry with table streach */
1346 } while( res
== FR_OK
);
1351 { /* Initialize the SFN entry */
1352 res
= move_window( dj
->fs
, dj
->sect
);
1356 mem_set( dir
, 0, 32 ); /* Clean the entry */
1357 mem_cpy( dir
, dj
->fn
, 11 ); /* Put SFN */
1358 dir
[DIR_NTres
] = *( dj
->fn
+ NS
) & ( NS_BODY
| NS_EXT
); /* Put NT flag */
1366 #endif /* !_FS_READONLY */
1368 /*-----------------------------------------------------------------------*/
1370 /* Remove an object from the directory */
1372 /*-----------------------------------------------------------------------*/
1373 #if !_FS_READONLY && !_FS_MINIMIZE
1374 static FRESULT dir_remove
1375 ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */
1376 DIR *dj
/* Directory object pointing the entry to be removed */
1380 #if _USE_LFN /* LFN configuration */
1383 i
= dj
->index
; /* SFN index */
1384 res
= dir_seek( dj
, (WORD
) ((dj
->lfn_idx
== 0xFFFF) ? i
: dj
->lfn_idx
) ); /* Goto the SFN or top of the LFN entries */
1389 res
= move_window( dj
->fs
, dj
->sect
);
1395 *dj
->dir
= 0xE5; /* Mark the entry "deleted" */
1397 if( dj
->index
>= i
)
1399 break; /* When reached SFN, all entries of the object has been deleted. */
1402 res
= dir_next( dj
, FALSE
); /* Next entry */
1403 } while( res
== FR_OK
);
1404 if( res
== FR_NO_FILE
)
1410 #else /* Non LFN configuration */
1411 res
= dir_seek( dj
, dj
->index
);
1414 res
= move_window( dj
->fs
, dj
->sect
);
1417 *dj
->dir
= 0xE5; /* Mark the entry "deleted" */
1426 #endif /* !_FS_READONLY */
1428 /*-----------------------------------------------------------------------*/
1430 /* Pick a segment and create the object name in directory form */
1432 /*-----------------------------------------------------------------------*/
1433 static FRESULT create_name
1435 DIR *dj
, /* Pointer to the directory object */
1436 const XCHAR
**path
/* Pointer to pointer to the segment in the path string */
1441 static const BYTE cvt
[] = _EXCVT
;
1443 #if _USE_LFN /* LFN configuration */
1449 /* Create LFN in Unicode */
1455 w
= p
[si
++]; /* Get a character */
1456 if( w
< ' ' || w
== '/' || w
== '\\' )
1458 break; /* Break on end of segment */
1461 if( di
>= _MAX_LFN
)
1462 { /* Reject too long name */
1463 return FR_INVALID_NAME
;
1469 { /* If it is a DBC 1st byte */
1470 b
= p
[si
++]; /* Get 2nd byte */
1472 { /* Reject invalid code for DBC */
1473 return FR_INVALID_NAME
;
1479 w
= ff_convert( w
, 1 ); /* Convert OEM to Unicode */
1482 return FR_INVALID_NAME
; /* Reject invalid code */
1486 if( w
< 0x80 && chk_chr("\"*:<>\?|\x7F", w
) )
1487 { /* Reject illegal chars for LFN */
1488 return FR_INVALID_NAME
;
1491 lfn
[di
++] = w
; /* Store the Unicode char */
1494 *path
= &p
[si
]; /* Rerurn pointer to the next segment */
1495 cf
= ( w
< ' ' ) ? NS_LAST
: 0; /* Set last segment flag if end of path */
1497 if( (di
== 1 && lfn
[di
- 1] == '.') || /* Is this a dot entry? */ (di
== 2 && lfn
[di
- 1] == '.' && lfn
[di
- 2] == '.') )
1500 for( i
= 0; i
< 11; i
++ )
1502 dj
->fn
[i
] = ( i
< di
) ? '.' : ' ';
1505 dj
->fn
[i
] = cf
| NS_DOT
; /* This is a dot entry */
1511 { /* Strip trailing spaces and dots */
1513 if( w
!= ' ' && w
!= '.' )
1523 return FR_INVALID_NAME
; /* Reject null string */
1526 lfn
[di
] = 0; /* LFN is created */
1528 /* Create SFN in directory form */
1529 mem_set( dj
->fn
, ' ', 11 );
1530 for( si
= 0; lfn
[si
] == ' ' || lfn
[si
] == '.'; si
++ );
1532 /* Strip leading spaces and dots */
1535 cf
|= NS_LOSS
| NS_LFN
;
1538 while( di
&& lfn
[di
- 1] != '.' )
1540 di
--; /* Find extension (di<=si: no extension) */
1547 w
= lfn
[si
++]; /* Get an LFN char */
1550 break; /* Break on enf of the LFN */
1553 if( w
== ' ' || (w
== '.' && si
!= di
) )
1554 { /* Remove spaces and dots */
1555 cf
|= NS_LOSS
| NS_LFN
;
1559 if( i
>= ni
|| si
== di
)
1560 { /* Extension or end of SFN */
1562 { /* Long extension */
1563 cf
|= NS_LOSS
| NS_LFN
;
1569 cf
|= NS_LOSS
| NS_LFN
; /* Out of 8.3 format */
1574 break; /* No extension */
1579 ni
= 11; /* Enter extension section */
1585 { /* Non ASCII char */
1587 w
= ff_convert( w
, 0 ); /* Unicode -> OEM code */
1590 w
= cvt
[w
- 0x80]; /* Convert extended char to upper (SBCS) */
1594 w
= ff_convert( ff_wtoupper(w
), 0 ); /* Upper converted Unicode -> OEM code */
1596 cf
|= NS_LFN
; /* Force create LFN entry */
1599 if( _DF1S
&& w
>= 0x100 )
1600 { /* Double byte char */
1603 cf
|= NS_LOSS
| NS_LFN
;
1608 dj
->fn
[i
++] = ( BYTE
) ( w
>> 8 );
1611 { /* Single byte char */
1612 if( !w
|| chk_chr("+,;[=]", w
) )
1613 { /* Replace illegal chars for SFN */
1615 cf
|= NS_LOSS
| NS_LFN
; /* Lossy conversion */
1620 { /* ASCII large capital */
1626 { /* ASCII small capital */
1634 dj
->fn
[i
++] = ( BYTE
) w
;
1637 if( dj
->fn
[0] == 0xE5 )
1639 dj
->fn
[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */
1647 if( (b
& 0x0C) == 0x0C || (b
& 0x03) == 0x03 )
1648 { /* Create LFN entry when there are composite capitals */
1652 if( !(cf
& NS_LFN
) )
1653 { /* When LFN is in 8.3 format without extended char, NT flags are created */
1654 if( (b
& 0x03) == 0x01 )
1656 cf
|= NS_EXT
; /* NT flag (Extension has only small capital) */
1659 if( (b
& 0x0C) == 0x04 )
1661 cf
|= NS_BODY
; /* NT flag (Filename has only small capital) */
1665 dj
->fn
[NS
] = cf
; /* SFN is created */
1669 #else /* Non-LFN configuration */
1674 /* Create file name in directory form */
1676 mem_set( sfn
, ' ', 11 );
1682 { /* Is this a dot entry? */
1686 if( c
!= '.' || si
>= 3 )
1694 if( c
!= '/' && c
!= '\\' && c
> ' ' )
1696 return FR_INVALID_NAME
;
1699 *path
= &p
[si
]; /* Rerurn pointer to the next segment */
1700 sfn
[NS
] = ( c
<= ' ' ) ? NS_LAST
| NS_DOT
: NS_DOT
; /* Set last segment flag if end of path */
1708 if( c
<= ' ' || c
== '/' || c
== '\\' )
1710 break; /* Break on end of segment */
1713 if( c
== '.' || i
>= ni
)
1715 if( ni
!= 8 || c
!= '.' )
1717 return FR_INVALID_NAME
;
1727 { /* Extended char */
1729 c
= cvt
[c
- 0x80]; /* Convert extend char (SBCS) */
1731 b
|= 3; /* Eliminate NT flag if ext char is exist */
1732 #if !_DF1S /* ASCII only cfg */
1733 return FR_INVALID_NAME
;
1739 { /* DBC 1st byte? */
1740 d
= p
[si
++]; /* Get 2nd byte */
1741 if( !IsDBCS2(d
) || i
>= ni
- 1 )
1742 { /* Reject invalid DBC */
1743 return FR_INVALID_NAME
;
1750 { /* Single byte code */
1751 if( chk_chr(" \"*+,[=]|\x7F", c
) )
1752 { /* Reject illegal chrs for SFN */
1753 return FR_INVALID_NAME
;
1757 { /* ASCII large capital? */
1763 { /* ASCII small capital? */
1773 *path
= &p
[si
]; /* Return pointer to the next segment */
1774 c
= ( c
<= ' ' ) ? NS_LAST
: 0; /* Set last segment flag if end of path */
1778 return FR_INVALID_NAME
; /* Reject null string */
1781 if( sfn
[0] == 0xE5 )
1783 sfn
[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */
1791 if( (b
& 0x03) == 0x01 )
1793 c
|= NS_EXT
; /* NT flag (Extension has only small capital) */
1796 if( (b
& 0x0C) == 0x04 )
1798 c
|= NS_BODY
; /* NT flag (Filename has only small capital) */
1801 sfn
[NS
] = c
; /* Store NT flag, File name is created */
1807 /*-----------------------------------------------------------------------*/
1809 /* Get file information from directory entry */
1811 /*-----------------------------------------------------------------------*/
1812 #if _FS_MINIMIZE <= 1
1813 static void get_fileinfo
1814 ( /* No return code */
1815 DIR *dj
, /* Pointer to the directory object */
1816 FILINFO
*fno
/* Pointer to the file information to be filled */
1827 nt
= dir
[DIR_NTres
]; /* NT flag */
1828 for( i
= 0; i
< 8; i
++ )
1829 { /* Copy name body */
1841 if( _USE_LFN
&& (nt
& NS_BODY
) && IsUpper(c
) )
1850 { /* Copy name extension */
1852 for( i
= 8; i
< 11; i
++ )
1860 if( _USE_LFN
&& (nt
& NS_EXT
) && IsUpper(c
) )
1869 fno
->fattrib
= dir
[DIR_Attr
]; /* Attribute */
1870 fno
->fsize
= LD_DWORD( dir
+ DIR_FileSize
); /* Size */
1871 fno
->fdate
= LD_WORD( dir
+ DIR_WrtDate
); /* Date */
1872 fno
->ftime
= LD_WORD( dir
+ DIR_WrtTime
); /* Time */
1880 XCHAR
*tp
= fno
->lfname
;
1884 if( dj
->sect
&& dj
->lfn_idx
!= 0xFFFF )
1885 { /* Get LFN if available */
1887 while( (w
= *lfn
++) != 0 )
1888 { /* Get an LFN char */
1890 w
= ff_convert( w
, 0 ); /* Unicode -> OEM conversion */
1895 } /* Could not convert, no LFN */
1897 if( _DF1S
&& w
>= 0x100 )
1898 { /* Put 1st byte if it is a DBC */
1899 tp
[i
++] = ( XCHAR
) ( w
>> 8 );
1903 if( i
>= fno
->lfsize
- 1 )
1907 } /* Buffer overrun, no LFN */
1909 tp
[i
++] = ( XCHAR
) w
;
1913 tp
[i
] = 0; /* Terminator */
1919 #endif /* _FS_MINIMIZE <= 1 */
1921 /*-----------------------------------------------------------------------*/
1923 /* Follow a file path */
1925 /*-----------------------------------------------------------------------*/
1926 static FRESULT follow_path
1927 ( /* FR_OK(0): successful, !=0: error code */
1928 DIR *dj
, /* Directory object to return last directory and found object */
1929 const XCHAR
*path
/* Full-path string to find a file or directory */
1935 while( !_USE_LFN
&& *path
== ' ' )
1937 path
++; /* Skip leading spaces */
1941 if( *path
== '/' || *path
== '\\' )
1942 { /* There is a heading separator */
1944 dj
->sclust
= 0; /* Strip it and start from the root dir */
1947 { /* No heading saparator */
1948 dj
->sclust
= dj
->fs
->cdir
; /* Start from the current dir */
1952 if( *path
== '/' || *path
== '\\' )
1953 { /* Strip heading separator if exist */
1957 dj
->sclust
= 0; /* Start from the root dir */
1959 if( (UINT
) * path
< ' ' )
1960 { /* Null path means the start directory itself */
1961 res
= dir_seek( dj
, 0 );
1968 res
= create_name( dj
, &path
); /* Get a segment */
1974 res
= dir_find( dj
); /* Find it */
1975 last
= *( dj
->fn
+ NS
) & NS_LAST
;
1977 { /* Could not find the object */
1978 if( res
== FR_NO_FILE
&& !last
)
1988 break; /* Last segment match. Function completed. */
1991 dir
= dj
->dir
; /* There is next segment. Follow the sub directory */
1992 if( !(dir
[DIR_Attr
] & AM_DIR
) )
1993 { /* Cannot follow because it is a file */
1998 dj
->sclust
= ( (DWORD
) LD_WORD(dir
+ DIR_FstClusHI
) << 16 ) | LD_WORD( dir
+ DIR_FstClusLO
);
2005 /*-----------------------------------------------------------------------*/
2007 /* Load boot record and check if it is an FAT boot record */
2009 /*-----------------------------------------------------------------------*/
2010 static BYTE check_fs
2011 ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
2012 FATFS
*fs
, /* File system object */
2013 DWORD sect
/* Sector# (lba) to check if it is an FAT boot record or not */
2016 if( disk_read(fs
->drive
, fs
->win
, sect
, 1) != RES_OK
)
2017 { /* Load boot record */
2021 if( LD_WORD(&fs
->win
[BS_55AA
]) != 0xAA55 )
2022 { /* Check record signature (always placed at offset 510 even if the sector size is >512) */
2026 if( (LD_DWORD(&fs
->win
[BS_FilSysType
]) & 0xFFFFFF) == 0x544146 )
2027 { /* Check "FAT" string */
2031 if( (LD_DWORD(&fs
->win
[BS_FilSysType32
]) & 0xFFFFFF) == 0x544146 )
2039 /*-----------------------------------------------------------------------*/
2041 /* Make sure that the file system is valid */
2043 /*-----------------------------------------------------------------------*/
2045 ( /* FR_OK(0): successful, !=0: any error occured */
2046 const XCHAR
**path
, /* Pointer to pointer to the path name (drive number) */
2047 FATFS
**rfs
, /* Pointer to pointer to the found file system object */
2048 BYTE chk_wp
/* !=0: Check media write protection for write access */
2054 DWORD bsect
, fsize
, tsect
, mclst
;
2055 const XCHAR
*p
= *path
;
2058 /* Get logical drive number from the path name */
2059 vol
= p
[0] - '0'; /* Is there a drive number? */
2060 if( vol
<= 9 && p
[1] == ':' )
2061 { /* Found a drive number, get and strip it */
2063 *path
= p
; /* Return pointer to the path name */
2066 { /* No drive number is given */
2068 vol
= Drive
; /* Use current drive */
2070 vol
= 0; /* Use drive 0 */
2074 /* Check if the logical drive is valid or not */
2075 if( vol
>= _DRIVES
)
2076 { /* Is the drive number valid? */
2077 return FR_INVALID_DRIVE
;
2080 *rfs
= fs
= FatFs
[vol
]; /* Returen pointer to the corresponding file system object */
2083 return FR_NOT_ENABLED
; /* Is the file system object available? */
2086 ENTER_FF( fs
); /* Lock file system */
2089 { /* If the logical drive has been mounted */
2090 stat
= disk_status( fs
->drive
);
2091 if( !(stat
& STA_NOINIT
) )
2092 { /* and the physical drive is kept initialized (has not been changed), */
2094 if( chk_wp
&& (stat
& STA_PROTECT
) )
2095 { /* Check write protection if needed */
2096 return FR_WRITE_PROTECTED
;
2100 return FR_OK
; /* The file system object is valid */
2104 /* The logical drive must be mounted. Following code attempts to mount the volume */
2105 fs
->fs_type
= 0; /* Clear the file system object */
2106 fs
->drive
= ( BYTE
) LD2PD( vol
); /* Bind the logical drive and a physical drive */
2107 stat
= disk_initialize( fs
->drive
); /* Initialize low level disk I/O layer */
2108 if( stat
& STA_NOINIT
)
2109 { /* Check if the drive is ready */
2110 return FR_NOT_READY
;
2113 #if _MAX_SS != 512 /* Get disk sector size if needed */
2114 if( disk_ioctl(fs
->drive
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK
|| SS(fs
) > _MAX_SS
)
2116 return FR_NO_FILESYSTEM
;
2121 if( chk_wp
&& (stat
& STA_PROTECT
) )
2122 { /* Check disk write protection if needed */
2123 return FR_WRITE_PROTECTED
;
2128 /* Search FAT partition on the drive */
2129 fmt
= check_fs( fs
, bsect
= 0 ); /* Check sector 0 as an SFD format */
2131 { /* Not an FAT boot record, it may be patitioned */
2132 /* Check a partition listed in top of the partition table */
2133 tbl
= &fs
->win
[MBR_Table
+ LD2PT( vol
) * 16]; /* Partition table */
2135 { /* Is the partition existing? */
2136 bsect
= LD_DWORD( &tbl
[8] ); /* Partition offset in LBA */
2137 fmt
= check_fs( fs
, bsect
); /* Check the partition */
2146 if( fmt
|| LD_WORD(fs
->win
+ BPB_BytsPerSec
) != SS(fs
) )
2147 { /* No valid FAT patition is found */
2148 return FR_NO_FILESYSTEM
;
2151 /* Initialize the file system object */
2152 fsize
= LD_WORD( fs
->win
+ BPB_FATSz16
); /* Number of sectors per FAT */
2155 fsize
= LD_DWORD( fs
->win
+ BPB_FATSz32
);
2158 fs
->sects_fat
= fsize
;
2159 fs
->n_fats
= fs
->win
[BPB_NumFATs
]; /* Number of FAT copies */
2160 fsize
*= fs
->n_fats
; /* (Number of sectors in FAT area) */
2161 fs
->fatbase
= bsect
+ LD_WORD( fs
->win
+ BPB_RsvdSecCnt
); /* FAT start sector (lba) */
2162 fs
->csize
= fs
->win
[BPB_SecPerClus
]; /* Number of sectors per cluster */
2163 fs
->n_rootdir
= LD_WORD( fs
->win
+ BPB_RootEntCnt
); /* Nmuber of root directory entries */
2164 tsect
= LD_WORD( fs
->win
+ BPB_TotSec16
); /* Number of sectors on the volume */
2167 tsect
= LD_DWORD( fs
->win
+ BPB_TotSec32
);
2170 fs
->max_clust
= mclst
=
2172 tsect
/* Last cluster# + 1 (Number of clusters + 2) */ -
2173 LD_WORD(fs
->win
+ BPB_RsvdSecCnt
) -
2181 fmt
= FS_FAT12
; /* Determine the FAT sub type */
2182 if( mclst
>= 0xFF7 )
2184 fmt
= FS_FAT16
; /* Number of clusters >= 0xFF5 */
2187 if( mclst
>= 0xFFF7 )
2189 fmt
= FS_FAT32
; /* Number of clusters >= 0xFFF5 */
2192 if( fmt
== FS_FAT32
)
2194 fs
->dirbase
= LD_DWORD( fs
->win
+ BPB_RootClus
); /* Root directory start cluster */
2198 fs
->dirbase
= fs
->fatbase
+ fsize
; /* Root directory start sector (lba) */
2201 fs
->database
= fs
->fatbase
+ fsize
+ fs
->n_rootdir
/ ( SS(fs
) / 32 ); /* Data start sector (lba) */
2205 /* Initialize allocation information */
2206 fs
->free_clust
= 0xFFFFFFFF;
2209 /* Get fsinfo if needed */
2210 if( fmt
== FS_FAT32
)
2213 fs
->fsi_sector
= bsect
+ LD_WORD( fs
->win
+ BPB_FSInfo
);
2216 disk_read(fs
->drive
, fs
->win
, fs
->fsi_sector
, 1) == RES_OK
&&
2217 LD_WORD(fs
->win
+ BS_55AA
) == 0xAA55 &&
2218 LD_DWORD(fs
->win
+ FSI_LeadSig
) == 0x41615252 &&
2219 LD_DWORD(fs
->win
+ FSI_StrucSig
) == 0x61417272
2222 fs
->last_clust
= LD_DWORD( fs
->win
+ FSI_Nxt_Free
);
2223 fs
->free_clust
= LD_DWORD( fs
->win
+ FSI_Free_Count
);
2228 fs
->fs_type
= fmt
; /* FAT sub-type */
2229 fs
->winsect
= 0; /* Invalidate sector cache */
2231 fs
->cdir
= 0; /* Current directory (root dir) */
2233 fs
->id
= ++Fsid
; /* File system mount ID */
2238 /*-----------------------------------------------------------------------*/
2240 /* Check if the file/dir object is valid or not */
2242 /*-----------------------------------------------------------------------*/
2243 static FRESULT validate
2244 ( /* FR_OK(0): The object is valid, !=0: Invalid */
2245 FATFS
*fs
, /* Pointer to the file system object */
2246 WORD id
/* Member id of the target object to be checked */
2249 if( !fs
|| !fs
->fs_type
|| fs
->id
!= id
)
2251 return FR_INVALID_OBJECT
;
2254 ENTER_FF( fs
); /* Lock file system */
2256 if( disk_status(fs
->drive
) & STA_NOINIT
)
2258 return FR_NOT_READY
;
2264 /*--------------------------------------------------------------------------
2268 --------------------------------------------------------------------------*/
2270 /*-----------------------------------------------------------------------*/
2272 /* Mount/Unmount a Locical Drive */
2274 /*-----------------------------------------------------------------------*/
2277 BYTE vol
, /* Logical drive number to be mounted/unmounted */
2278 FATFS
*fs
/* Pointer to new file system object (NULL for unmount)*/
2283 if( vol
>= _DRIVES
)
2284 { /* Check if the drive number is valid */
2285 return FR_INVALID_DRIVE
;
2288 rfs
= FatFs
[vol
]; /* Get current fs object */
2292 #if _FS_REENTRANT /* Discard sync object of the current volume */
2293 if( !ff_del_syncobj(rfs
->sobj
) )
2299 rfs
->fs_type
= 0; /* Clear old fs object */
2304 fs
->fs_type
= 0; /* Clear new fs object */
2305 #if _FS_REENTRANT /* Create sync object for the new volume */
2306 if( !ff_cre_syncobj(vol
, &fs
->sobj
) )
2314 FatFs
[vol
] = fs
; /* Register new fs object */
2319 /*-----------------------------------------------------------------------*/
2321 /* Open or Create a File */
2323 /*-----------------------------------------------------------------------*/
2326 FIL
*fp
, /* Pointer to the blank file object */
2327 const XCHAR
*path
, /* Pointer to the file name */
2328 BYTE mode
/* Access mode and file open mode flags */
2333 NAMEBUF( sfn
, lfn
);
2337 fp
->fs
= NULL
; /* Clear file object */
2339 mode
&= ( FA_READ
| FA_WRITE
| FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
);
2340 res
= chk_mounted( &path
, &dj
.fs
, (BYTE
) (mode
& (FA_WRITE
| FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
)) );
2343 res
= chk_mounted( &path
, &dj
.fs
, 0 );
2347 LEAVE_FF( dj
.fs
, res
);
2350 INITBUF( dj
, sfn
, lfn
);
2351 res
= follow_path( &dj
, path
); /* Follow the file path */
2355 /* Create or Open a file */
2356 if( mode
& (FA_CREATE_ALWAYS
| FA_OPEN_ALWAYS
| FA_CREATE_NEW
) )
2361 { /* No file, create new */
2362 if( res
== FR_NO_FILE
)
2363 { /* There is no file to open, create a new entry */
2364 res
= dir_register( &dj
);
2369 LEAVE_FF( dj
.fs
, res
);
2372 mode
|= FA_CREATE_ALWAYS
;
2373 dir
= dj
.dir
; /* Created entry (SFN entry) */
2376 { /* Any object is already existing */
2377 if( mode
& FA_CREATE_NEW
)
2378 { /* Cannot create new */
2379 LEAVE_FF( dj
.fs
, FR_EXIST
);
2383 if( !dir
|| (dir
[DIR_Attr
] & (AM_RDO
| AM_DIR
)) )
2384 { /* Cannot overwrite it (R/O or DIR) */
2385 LEAVE_FF( dj
.fs
, FR_DENIED
);
2388 if( mode
& FA_CREATE_ALWAYS
)
2389 { /* Resize it to zero on over write mode */
2390 cl
= ( (DWORD
) LD_WORD(dir
+ DIR_FstClusHI
) << 16 ) | LD_WORD( dir
+ DIR_FstClusLO
); /* Get start cluster */
2391 ST_WORD( dir
+ DIR_FstClusHI
, 0 ); /* cluster = 0 */
2392 ST_WORD( dir
+ DIR_FstClusLO
, 0 );
2393 ST_DWORD( dir
+ DIR_FileSize
, 0 ); /* size = 0 */
2395 ps
= dj
.fs
->winsect
; /* Remove the cluster chain */
2398 res
= remove_chain( dj
.fs
, cl
);
2401 LEAVE_FF( dj
.fs
, res
);
2404 dj
.fs
->last_clust
= cl
- 1; /* Reuse the cluster hole */
2407 res
= move_window( dj
.fs
, ps
);
2410 LEAVE_FF( dj
.fs
, res
);
2415 if( mode
& FA_CREATE_ALWAYS
)
2417 dir
[DIR_Attr
] = 0; /* Reset attribute */
2419 ST_DWORD( dir
+ DIR_CrtTime
, ps
); /* Created time */
2421 mode
|= FA__WRITTEN
; /* Set file changed flag */
2425 /* Open an existing file */
2428 #endif /* !_FS_READONLY */
2431 LEAVE_FF( dj
.fs
, res
); /* Follow failed */
2435 if( !dir
|| (dir
[DIR_Attr
] & AM_DIR
) )
2436 { /* It is a directory */
2437 LEAVE_FF( dj
.fs
, FR_NO_FILE
);
2441 if( (mode
& FA_WRITE
) && (dir
[DIR_Attr
] & AM_RDO
) )
2442 { /* R/O violation */
2443 LEAVE_FF( dj
.fs
, FR_DENIED
);
2447 fp
->dir_sect
= dj
.fs
->winsect
; /* Pointer to the directory entry */
2448 fp
->dir_ptr
= dj
.dir
;
2450 fp
->flag
= mode
; /* File access mode */
2451 fp
->org_clust
= /* File start cluster */
2452 ( (DWORD
) LD_WORD(dir
+ DIR_FstClusHI
) << 16 ) | LD_WORD( dir
+ DIR_FstClusLO
);
2453 fp
->fsize
= LD_DWORD( dir
+ DIR_FileSize
); /* File size */
2455 fp
->csect
= 255; /* File pointer */
2458 fp
->id
= dj
.fs
->id
; /* Owner file system object of the file */
2460 LEAVE_FF( dj
.fs
, FR_OK
);
2463 /*-----------------------------------------------------------------------*/
2467 /*-----------------------------------------------------------------------*/
2470 FIL
*fp
, /* Pointer to the file object */
2471 void *buff
, /* Pointer to data buffer */
2472 UINT btr
, /* Number of bytes to read */
2473 UINT
*br
/* Pointer to number of bytes read */
2477 DWORD clst
, sect
, remain
;
2481 *br
= 0; /* Initialize bytes read */
2483 res
= validate( fp
->fs
, fp
->id
); /* Check validity of the object */
2486 LEAVE_FF( fp
->fs
, res
);
2489 if( fp
->flag
& FA__ERROR
)
2490 { /* Check abort flag */
2491 LEAVE_FF( fp
->fs
, FR_INT_ERR
);
2494 if( !(fp
->flag
& FA_READ
) )
2495 { /* Check access mode */
2496 LEAVE_FF( fp
->fs
, FR_DENIED
);
2499 remain
= fp
->fsize
- fp
->fptr
;
2502 btr
= ( UINT
) remain
; /* Truncate btr by remaining bytes */
2505 for( ; btr
; /* Repeat until all data transferred */ rbuff
+= rcnt
, fp
->fptr
+= rcnt
, *br
+= rcnt
, btr
-= rcnt
)
2507 if( (fp
->fptr
% SS(fp
->fs
)) == 0 )
2508 { /* On the sector boundary? */
2509 if( fp
->csect
>= fp
->fs
->csize
)
2510 { /* On the cluster boundary? */
2511 clst
= ( fp
->fptr
== 0 ) ? /* On the top of the file? */
2512 fp
->org_clust
: get_fat( fp
->fs
, fp
->curr_clust
);
2515 ABORT( fp
->fs
, FR_INT_ERR
);
2518 if( clst
== 0xFFFFFFFF )
2520 ABORT( fp
->fs
, FR_DISK_ERR
);
2523 fp
->curr_clust
= clst
; /* Update current cluster */
2524 fp
->csect
= 0; /* Reset sector offset in the cluster */
2527 sect
= clust2sect( fp
->fs
, fp
->curr_clust
); /* Get current sector */
2530 ABORT( fp
->fs
, FR_INT_ERR
);
2534 cc
= btr
/ SS( fp
->fs
); /* When remaining bytes >= sector size, */
2536 { /* Read maximum contiguous sectors directly */
2537 if( fp
->csect
+ cc
> fp
->fs
->csize
)
2538 { /* Clip at cluster boundary */
2539 cc
= fp
->fs
->csize
- fp
->csect
;
2542 if( disk_read(fp
->fs
->drive
, rbuff
, sect
, (BYTE
) cc
) != RES_OK
)
2544 ABORT( fp
->fs
, FR_DISK_ERR
);
2547 #if !_FS_READONLY && _FS_MINIMIZE <= 2
2549 if( fp
->fs
->wflag
&& fp
->fs
->winsect
- sect
< cc
)
2550 { /* Replace one of the read sectors with cached data if it contains a dirty sector */
2551 mem_cpy( rbuff
+ ((fp
->fs
->winsect
- sect
) * SS(fp
->fs
)), fp
->fs
->win
, SS(fp
->fs
) );
2555 if( (fp
->flag
& FA__DIRTY
) && fp
->dsect
- sect
< cc
)
2556 { /* Replace one of the read sectors with cached data if it contains a dirty sector */
2557 mem_cpy( rbuff
+ ((fp
->dsect
- sect
) * SS(fp
->fs
)), fp
->buf
, SS(fp
->fs
) );
2562 fp
->csect
+= ( BYTE
) cc
; /* Next sector address in the cluster */
2563 rcnt
= SS( fp
->fs
) * cc
; /* Number of bytes transferred */
2569 if( fp
->flag
& FA__DIRTY
)
2570 { /* Write sector I/O buffer if needed */
2571 if( disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
2573 ABORT( fp
->fs
, FR_DISK_ERR
);
2576 fp
->flag
&= ~FA__DIRTY
;
2580 if( fp
->dsect
!= sect
)
2581 { /* Fill sector buffer with file data */
2582 if( disk_read(fp
->fs
->drive
, fp
->buf
, sect
, 1) != RES_OK
)
2584 ABORT( fp
->fs
, FR_DISK_ERR
);
2590 fp
->csect
++; /* Next sector address in the cluster */
2593 rcnt
= SS( fp
->fs
) - ( fp
->fptr
% SS(fp
->fs
) ); /* Get partial sector data from sector buffer */
2600 if( move_window(fp
->fs
, fp
->dsect
) )
2601 { /* Move sector window */
2602 ABORT( fp
->fs
, FR_DISK_ERR
);
2605 mem_cpy( rbuff
, &fp
->fs
->win
[fp
->fptr
% SS(fp
->fs
)], rcnt
); /* Pick partial sector */
2607 mem_cpy( rbuff
, &fp
->buf
[fp
->fptr
% SS(fp
->fs
)], rcnt
); /* Pick partial sector */
2611 LEAVE_FF( fp
->fs
, FR_OK
);
2616 /*-----------------------------------------------------------------------*/
2620 /*-----------------------------------------------------------------------*/
2623 FIL
*fp
, /* Pointer to the file object */
2624 const void *buff
, /* Pointer to the data to be written */
2625 UINT btw
, /* Number of bytes to write */
2626 UINT
*bw
/* Pointer to number of bytes written */
2632 const BYTE
*wbuff
= buff
;
2634 *bw
= 0; /* Initialize bytes written */
2636 res
= validate( fp
->fs
, fp
->id
); /* Check validity of the object */
2639 LEAVE_FF( fp
->fs
, res
);
2642 if( fp
->flag
& FA__ERROR
)
2643 { /* Check abort flag */
2644 LEAVE_FF( fp
->fs
, FR_INT_ERR
);
2647 if( !(fp
->flag
& FA_WRITE
) )
2648 { /* Check access mode */
2649 LEAVE_FF( fp
->fs
, FR_DENIED
);
2652 if( fp
->fsize
+ btw
< fp
->fsize
)
2654 btw
= 0; /* File size cannot reach 4GB */
2657 for( ; btw
; /* Repeat until all data transferred */ wbuff
+= wcnt
, fp
->fptr
+= wcnt
, *bw
+= wcnt
, btw
-= wcnt
)
2659 if( (fp
->fptr
% SS(fp
->fs
)) == 0 )
2660 { /* On the sector boundary? */
2661 if( fp
->csect
>= fp
->fs
->csize
)
2662 { /* On the cluster boundary? */
2664 { /* On the top of the file? */
2665 clst
= fp
->org_clust
; /* Follow from the origin */
2667 { /* When there is no cluster chain, */
2668 fp
->org_clust
= clst
= create_chain( fp
->fs
, 0 ); /* Create a new cluster chain */
2672 { /* Middle or end of the file */
2673 clst
= create_chain( fp
->fs
, fp
->curr_clust
); /* Follow or streach cluster chain */
2678 break; /* Could not allocate a new cluster (disk full) */
2683 ABORT( fp
->fs
, FR_INT_ERR
);
2686 if( clst
== 0xFFFFFFFF )
2688 ABORT( fp
->fs
, FR_DISK_ERR
);
2691 fp
->curr_clust
= clst
; /* Update current cluster */
2692 fp
->csect
= 0; /* Reset sector address in the cluster */
2696 if( fp
->fs
->winsect
== fp
->dsect
&& move_window(fp
->fs
, 0) )
2697 { /* Write back data buffer prior to following direct transfer */
2698 ABORT( fp
->fs
, FR_DISK_ERR
);
2702 if( fp
->flag
& FA__DIRTY
)
2703 { /* Write back data buffer prior to following direct transfer */
2704 if( disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
2706 ABORT( fp
->fs
, FR_DISK_ERR
);
2709 fp
->flag
&= ~FA__DIRTY
;
2713 sect
= clust2sect( fp
->fs
, fp
->curr_clust
); /* Get current sector */
2716 ABORT( fp
->fs
, FR_INT_ERR
);
2720 cc
= btw
/ SS( fp
->fs
); /* When remaining bytes >= sector size, */
2722 { /* Write maximum contiguous sectors directly */
2723 if( fp
->csect
+ cc
> fp
->fs
->csize
)
2724 { /* Clip at cluster boundary */
2725 cc
= fp
->fs
->csize
- fp
->csect
;
2728 if( disk_write(fp
->fs
->drive
, wbuff
, sect
, (BYTE
) cc
) != RES_OK
)
2730 ABORT( fp
->fs
, FR_DISK_ERR
);
2734 if( fp
->fs
->winsect
- sect
< cc
)
2735 { /* Refill sector cache if it gets dirty by the direct write */
2736 mem_cpy( fp
->fs
->win
, wbuff
+ ((fp
->fs
->winsect
- sect
) * SS(fp
->fs
)), SS(fp
->fs
) );
2741 if( fp
->dsect
- sect
< cc
)
2742 { /* Refill sector cache if it gets dirty by the direct write */
2743 mem_cpy( fp
->buf
, wbuff
+ ((fp
->dsect
- sect
) * SS(fp
->fs
)), SS(fp
->fs
) );
2744 fp
->flag
&= ~FA__DIRTY
;
2748 fp
->csect
+= ( BYTE
) cc
; /* Next sector address in the cluster */
2749 wcnt
= SS( fp
->fs
) * cc
; /* Number of bytes transferred */
2754 if( fp
->fptr
>= fp
->fsize
)
2755 { /* Avoid silly buffer filling at growing edge */
2756 if( move_window(fp
->fs
, 0) )
2758 ABORT( fp
->fs
, FR_DISK_ERR
);
2761 fp
->fs
->winsect
= sect
;
2765 if( fp
->dsect
!= sect
)
2766 { /* Fill sector buffer with file data */
2767 if( fp
->fptr
< fp
->fsize
&& disk_read(fp
->fs
->drive
, fp
->buf
, sect
, 1) != RES_OK
)
2769 ABORT( fp
->fs
, FR_DISK_ERR
);
2775 fp
->csect
++; /* Next sector address in the cluster */
2778 wcnt
= SS( fp
->fs
) - ( fp
->fptr
% SS(fp
->fs
) ); /* Put partial sector into file I/O buffer */
2785 if( move_window(fp
->fs
, fp
->dsect
) )
2786 { /* Move sector window */
2787 ABORT( fp
->fs
, FR_DISK_ERR
);
2790 mem_cpy( &fp
->fs
->win
[fp
->fptr
% SS(fp
->fs
)], wbuff
, wcnt
); /* Fit partial sector */
2793 mem_cpy( &fp
->buf
[fp
->fptr
% SS(fp
->fs
)], wbuff
, wcnt
); /* Fit partial sector */
2794 fp
->flag
|= FA__DIRTY
;
2798 if( fp
->fptr
> fp
->fsize
)
2800 fp
->fsize
= fp
->fptr
; /* Update file size if needed */
2803 fp
->flag
|= FA__WRITTEN
; /* Set file changed flag */
2805 LEAVE_FF( fp
->fs
, FR_OK
);
2808 /*-----------------------------------------------------------------------*/
2810 /* Synchronize the File Object */
2812 /*-----------------------------------------------------------------------*/
2813 FRESULT
f_sync( FIL
*fp
/* Pointer to the file object */ )
2819 res
= validate( fp
->fs
, fp
->id
); /* Check validity of the object */
2822 if( fp
->flag
& FA__WRITTEN
)
2823 { /* Has the file been written? */
2824 #if !_FS_TINY /* Write-back dirty buffer */
2825 if( fp
->flag
& FA__DIRTY
)
2827 if( disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
2829 LEAVE_FF( fp
->fs
, FR_DISK_ERR
);
2832 fp
->flag
&= ~FA__DIRTY
;
2837 /* Update the directory entry */
2838 res
= move_window( fp
->fs
, fp
->dir_sect
);
2842 dir
[DIR_Attr
] |= AM_ARC
; /* Set archive bit */
2843 ST_DWORD( dir
+ DIR_FileSize
, fp
->fsize
); /* Update file size */
2844 ST_WORD( dir
+ DIR_FstClusLO
, fp
->org_clust
); /* Update start cluster */
2845 ST_WORD( dir
+ DIR_FstClusHI
, fp
->org_clust
>> 16 );
2846 tim
= get_fattime(); /* Updated time */
2847 ST_DWORD( dir
+ DIR_WrtTime
, tim
);
2848 fp
->flag
&= ~FA__WRITTEN
;
2850 res
= sync( fp
->fs
);
2855 LEAVE_FF( fp
->fs
, res
);
2858 #endif /* !_FS_READONLY */
2860 /*-----------------------------------------------------------------------*/
2864 /*-----------------------------------------------------------------------*/
2865 FRESULT
f_close( FIL
*fp
/* Pointer to the file object to be closed */ )
2870 res
= validate( fp
->fs
, fp
->id
);
2876 LEAVE_FF( fp
->fs
, res
);
2888 /*-----------------------------------------------------------------------*/
2890 /* Change Current Drive/Directory */
2892 /*-----------------------------------------------------------------------*/
2894 FRESULT
f_chdrive( BYTE drv
/* Drive number */ )
2896 if( drv
>= _DRIVES
)
2898 return FR_INVALID_DRIVE
;
2906 FRESULT
f_chdir( const XCHAR
*path
/* Pointer to the directory path */ )
2910 NAMEBUF( sfn
, lfn
);
2914 res
= chk_mounted( &path
, &dj
.fs
, 0 );
2917 INITBUF( dj
, sfn
, lfn
);
2918 res
= follow_path( &dj
, path
); /* Follow the file path */
2920 { /* Follow completed */
2921 dir
= dj
.dir
; /* Pointer to the entry */
2924 dj
.fs
->cdir
= 0; /* No entry (root dir) */
2928 if( dir
[DIR_Attr
] & AM_DIR
)
2929 { /* Reached to the dir */
2930 dj
.fs
->cdir
= ( (DWORD
) LD_WORD(dir
+ DIR_FstClusHI
) << 16 ) | LD_WORD( dir
+ DIR_FstClusLO
);
2934 res
= FR_NO_PATH
; /* Could not reach the dir (it is a file) */
2939 if( res
== FR_NO_FILE
)
2945 LEAVE_FF( dj
.fs
, res
);
2949 #if _FS_MINIMIZE <= 2
2951 /*-----------------------------------------------------------------------*/
2953 /* Seek File R/W Pointer */
2955 /*-----------------------------------------------------------------------*/
2956 FRESULT
f_lseek( FIL
*fp
, /* Pointer to the file object */ DWORD ofs
/* File pointer from top of file */ )
2959 DWORD clst
, bcs
, nsect
, ifptr
;
2961 res
= validate( fp
->fs
, fp
->id
); /* Check validity of the object */
2964 LEAVE_FF( fp
->fs
, res
);
2967 if( fp
->flag
& FA__ERROR
)
2968 { /* Check abort flag */
2969 LEAVE_FF( fp
->fs
, FR_INT_ERR
);
2972 if( ofs
> fp
->fsize
/* In read-only mode, clip offset with the file size */
2974 && !(fp
->flag
& FA_WRITE
)
2982 fp
->fptr
= nsect
= 0;
2986 bcs
= ( DWORD
) fp
->fs
->csize
* SS( fp
->fs
); /* Cluster size (byte) */
2987 if( ifptr
> 0 && (ofs
- 1) / bcs
>= (ifptr
- 1) / bcs
)
2988 { /* When seek to same or following cluster, */
2989 fp
->fptr
= ( ifptr
- 1 ) &~( bcs
- 1 ); /* start from the current cluster */
2991 clst
= fp
->curr_clust
;
2994 { /* When seek to back cluster, */
2995 clst
= fp
->org_clust
; /* start from the first cluster */
2998 { /* If no cluster chain, create a new chain */
2999 clst
= create_chain( fp
->fs
, 0 );
3002 ABORT( fp
->fs
, FR_INT_ERR
);
3005 if( clst
== 0xFFFFFFFF )
3007 ABORT( fp
->fs
, FR_DISK_ERR
);
3010 fp
->org_clust
= clst
;
3014 fp
->curr_clust
= clst
;
3020 { /* Cluster following loop */
3022 if( fp
->flag
& FA_WRITE
)
3023 { /* Check if in write mode or not */
3024 clst
= create_chain( fp
->fs
, clst
); /* Force streached if in write mode */
3026 { /* When disk gets full, clip file size */
3033 clst
= get_fat( fp
->fs
, clst
); /* Follow cluster chain if not in write mode */
3034 if( clst
== 0xFFFFFFFF )
3036 ABORT( fp
->fs
, FR_DISK_ERR
);
3039 if( clst
<= 1 || clst
>= fp
->fs
->max_clust
)
3041 ABORT( fp
->fs
, FR_INT_ERR
);
3044 fp
->curr_clust
= clst
;
3050 fp
->csect
= ( BYTE
) ( ofs
/ SS(fp
->fs
) ); /* Sector offset in the cluster */
3051 if( ofs
% SS(fp
->fs
) )
3053 nsect
= clust2sect( fp
->fs
, clst
); /* Current sector */
3056 ABORT( fp
->fs
, FR_INT_ERR
);
3065 if( fp
->fptr
% SS(fp
->fs
) && nsect
!= fp
->dsect
)
3069 if( fp
->flag
& FA__DIRTY
)
3070 { /* Write-back dirty buffer if needed */
3071 if( disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
)
3073 ABORT( fp
->fs
, FR_DISK_ERR
);
3076 fp
->flag
&= ~FA__DIRTY
;
3080 if( disk_read(fp
->fs
->drive
, fp
->buf
, nsect
, 1) != RES_OK
)
3082 ABORT( fp
->fs
, FR_DISK_ERR
);
3090 if( fp
->fptr
> fp
->fsize
)
3091 { /* Set changed flag if the file size is extended */
3092 fp
->fsize
= fp
->fptr
;
3093 fp
->flag
|= FA__WRITTEN
;
3097 LEAVE_FF( fp
->fs
, res
);
3100 #if _FS_MINIMIZE <= 1
3102 /*-----------------------------------------------------------------------*/
3104 /* Create a Directroy Object */
3106 /*-----------------------------------------------------------------------*/
3107 FRESULT
f_opendir( DIR *dj
, /* Pointer to directory object to create */ const XCHAR
*path
/* Pointer to the directory path */ )
3110 NAMEBUF( sfn
, lfn
);
3114 res
= chk_mounted( &path
, &dj
->fs
, 0 );
3117 INITBUF( (*dj
), sfn
, lfn
);
3118 res
= follow_path( dj
, path
); /* Follow the path to the directory */
3120 { /* Follow completed */
3123 { /* It is not the root dir */
3124 if( dir
[DIR_Attr
] & AM_DIR
)
3125 { /* The object is a directory */
3126 dj
->sclust
= ( (DWORD
) LD_WORD(dir
+ DIR_FstClusHI
) << 16 ) | LD_WORD( dir
+ DIR_FstClusLO
);
3129 { /* The object is not a directory */
3136 dj
->id
= dj
->fs
->id
;
3137 res
= dir_seek( dj
, 0 ); /* Rewind dir */
3141 if( res
== FR_NO_FILE
)
3147 LEAVE_FF( dj
->fs
, res
);
3150 /*-----------------------------------------------------------------------*/
3152 /* Read Directory Entry in Sequense */
3154 /*-----------------------------------------------------------------------*/
3155 FRESULT
f_readdir( DIR *dj
, /* Pointer to the open directory object */ FILINFO
*fno
/* Pointer to file information to return */ )
3158 NAMEBUF( sfn
, lfn
);
3160 res
= validate( dj
->fs
, dj
->id
); /* Check validity of the object */
3163 INITBUF( (*dj
), sfn
, lfn
);
3166 res
= dir_seek( dj
, 0 );
3170 res
= dir_read( dj
);
3171 if( res
== FR_NO_FILE
)
3178 { /* A valid entry is found */
3179 get_fileinfo( dj
, fno
); /* Get the object information */
3180 res
= dir_next( dj
, FALSE
); /* Increment index for next */
3181 if( res
== FR_NO_FILE
)
3190 LEAVE_FF( dj
->fs
, res
);
3193 #if _FS_MINIMIZE == 0
3195 /*-----------------------------------------------------------------------*/
3197 /* Get File Status */
3199 /*-----------------------------------------------------------------------*/
3200 FRESULT
f_stat( const XCHAR
*path
, /* Pointer to the file path */ FILINFO
*fno
/* Pointer to file information to return */ )
3204 NAMEBUF( sfn
, lfn
);
3206 res
= chk_mounted( &path
, &dj
.fs
, 0 );
3209 INITBUF( dj
, sfn
, lfn
);
3210 res
= follow_path( &dj
, path
); /* Follow the file path */
3212 { /* Follwo completed */
3214 { /* Found an object */
3215 get_fileinfo( &dj
, fno
);
3218 { /* It is root dir */
3219 res
= FR_INVALID_NAME
;
3224 LEAVE_FF( dj
.fs
, res
);
3229 /*-----------------------------------------------------------------------*/
3231 /* Get Number of Free Clusters */
3233 /*-----------------------------------------------------------------------*/
3236 const XCHAR
*path
, /* Pointer to the logical drive number (root dir) */
3237 DWORD
*nclst
, /* Pointer to the variable to return number of free clusters */
3238 FATFS
**fatfs
/* Pointer to pointer to corresponding file system object to return */
3242 DWORD n
, clst
, sect
, stat
;
3246 /* Get drive number */
3247 res
= chk_mounted( &path
, fatfs
, 0 );
3250 LEAVE_FF( *fatfs
, res
);
3253 /* If number of free cluster is valid, return it without cluster scan. */
3254 if( (*fatfs
)->free_clust
<= (*fatfs
)->max_clust
- 2 )
3256 *nclst
= ( *fatfs
)->free_clust
;
3257 LEAVE_FF( *fatfs
, FR_OK
);
3260 /* Get number of free clusters */
3261 fat
= ( *fatfs
)->fs_type
;
3263 if( fat
== FS_FAT12
)
3268 stat
= get_fat( *fatfs
, clst
);
3269 if( stat
== 0xFFFFFFFF )
3271 LEAVE_FF( *fatfs
, FR_DISK_ERR
);
3276 LEAVE_FF( *fatfs
, FR_INT_ERR
);
3283 } while( ++clst
< (*fatfs
)->max_clust
);
3287 clst
= ( *fatfs
)->max_clust
;
3288 sect
= ( *fatfs
)->fatbase
;
3295 res
= move_window( *fatfs
, sect
++ );
3298 LEAVE_FF( *fatfs
, res
);
3301 p
= ( *fatfs
)->win
;
3305 if( fat
== FS_FAT16
)
3307 if( LD_WORD(p
) == 0 )
3317 if( (LD_DWORD(p
) & 0x0FFFFFFF) == 0 )
3328 ( *fatfs
)->free_clust
= n
;
3329 if( fat
== FS_FAT32
)
3331 ( *fatfs
)->fsi_flag
= 1;
3336 LEAVE_FF( *fatfs
, FR_OK
);
3339 /*-----------------------------------------------------------------------*/
3343 /*-----------------------------------------------------------------------*/
3344 FRESULT
f_truncate( FIL
*fp
/* Pointer to the file object */ )
3349 res
= validate( fp
->fs
, fp
->id
); /* Check validity of the object */
3352 LEAVE_FF( fp
->fs
, res
);
3355 if( fp
->flag
& FA__ERROR
)
3356 { /* Check abort flag */
3357 LEAVE_FF( fp
->fs
, FR_INT_ERR
);
3360 if( !(fp
->flag
& FA_WRITE
) )
3361 { /* Check access mode */
3362 LEAVE_FF( fp
->fs
, FR_DENIED
);
3365 if( fp
->fsize
> fp
->fptr
)
3367 fp
->fsize
= fp
->fptr
; /* Set file size to current R/W point */
3368 fp
->flag
|= FA__WRITTEN
;
3370 { /* When set file size to zero, remove entire cluster chain */
3371 res
= remove_chain( fp
->fs
, fp
->org_clust
);
3375 { /* When truncate a part of the file, remove remaining clusters */
3376 ncl
= get_fat( fp
->fs
, fp
->curr_clust
);
3378 if( ncl
== 0xFFFFFFFF )
3388 if( res
== FR_OK
&& ncl
< fp
->fs
->max_clust
)
3390 res
= put_fat( fp
->fs
, fp
->curr_clust
, 0x0FFFFFFF );
3393 res
= remove_chain( fp
->fs
, ncl
);
3401 fp
->flag
|= FA__ERROR
;
3404 LEAVE_FF( fp
->fs
, res
);
3407 /*-----------------------------------------------------------------------*/
3409 /* Delete a File or Directory */
3411 /*-----------------------------------------------------------------------*/
3412 FRESULT
f_unlink( const XCHAR
*path
/* Pointer to the file or directory path */ )
3416 NAMEBUF( sfn
, lfn
);
3421 res
= chk_mounted( &path
, &dj
.fs
, 1 );
3424 LEAVE_FF( dj
.fs
, res
);
3427 INITBUF( dj
, sfn
, lfn
);
3428 res
= follow_path( &dj
, path
); /* Follow the file path */
3429 if( _FS_RPATH
&& res
== FR_OK
&& (dj
.fn
[NS
] & NS_DOT
) )
3431 res
= FR_INVALID_NAME
;
3436 LEAVE_FF( dj
.fs
, res
); /* Follow failed */
3441 { /* Is it the root directory? */
3442 LEAVE_FF( dj
.fs
, FR_INVALID_NAME
);
3445 if( dir
[DIR_Attr
] & AM_RDO
)
3446 { /* Is it a R/O object? */
3447 LEAVE_FF( dj
.fs
, FR_DENIED
);
3450 dclst
= ( (DWORD
) LD_WORD(dir
+ DIR_FstClusHI
) << 16 ) | LD_WORD( dir
+ DIR_FstClusLO
);
3452 if( dir
[DIR_Attr
] & AM_DIR
)
3453 { /* It is a sub-directory */
3456 LEAVE_FF( dj
.fs
, FR_INT_ERR
);
3459 mem_cpy( &sdj
, &dj
, sizeof(DIR) ); /* Check if the sub-dir is empty or not */
3461 res
= dir_seek( &sdj
, 2 );
3464 LEAVE_FF( dj
.fs
, res
);
3467 res
= dir_read( &sdj
);
3470 res
= FR_DENIED
; /* Not empty sub-dir */
3473 if( res
!= FR_NO_FILE
)
3475 LEAVE_FF( dj
.fs
, res
);
3479 res
= dir_remove( &dj
); /* Remove directory entry */
3484 res
= remove_chain( dj
.fs
, dclst
); /* Remove the cluster chain */
3489 res
= sync( dj
.fs
);
3493 LEAVE_FF( dj
.fs
, res
);
3496 /*-----------------------------------------------------------------------*/
3498 /* Create a Directory */
3500 /*-----------------------------------------------------------------------*/
3501 FRESULT
f_mkdir( const XCHAR
*path
/* Pointer to the directory path */ )
3505 NAMEBUF( sfn
, lfn
);
3508 DWORD dsect
, dclst
, pclst
, tim
;
3510 res
= chk_mounted( &path
, &dj
.fs
, 1 );
3513 LEAVE_FF( dj
.fs
, res
);
3516 INITBUF( dj
, sfn
, lfn
);
3517 res
= follow_path( &dj
, path
); /* Follow the file path */
3520 res
= FR_EXIST
; /* Any file or directory is already existing */
3523 if( _FS_RPATH
&& res
== FR_NO_FILE
&& (dj
.fn
[NS
] & NS_DOT
) )
3525 res
= FR_INVALID_NAME
;
3528 if( res
!= FR_NO_FILE
)
3529 { /* Any error occured */
3530 LEAVE_FF( dj
.fs
, res
);
3533 dclst
= create_chain( dj
.fs
, 0 ); /* Allocate a new cluster for new directory table */
3545 if( dclst
== 0xFFFFFFFF )
3552 res
= move_window( dj
.fs
, 0 );
3557 LEAVE_FF( dj
.fs
, res
);
3560 dsect
= clust2sect( dj
.fs
, dclst
);
3562 dir
= dj
.fs
->win
; /* Initialize the new directory table */
3563 mem_set( dir
, 0, SS(dj
.fs
) );
3564 mem_set( dir
+ DIR_Name
, ' ', 8 + 3 ); /* Create "." entry */
3565 dir
[DIR_Name
] = '.';
3566 dir
[DIR_Attr
] = AM_DIR
;
3567 tim
= get_fattime();
3568 ST_DWORD( dir
+ DIR_WrtTime
, tim
);
3569 ST_WORD( dir
+ DIR_FstClusLO
, dclst
);
3570 ST_WORD( dir
+ DIR_FstClusHI
, dclst
>> 16 );
3571 mem_cpy( dir
+ 32, dir
, 32 ); /* Create ".." entry */
3574 if( dj
.fs
->fs_type
== FS_FAT32
&& pclst
== dj
.fs
->dirbase
)
3579 ST_WORD( dir
+ 32 + DIR_FstClusLO
, pclst
);
3580 ST_WORD( dir
+ 32 + DIR_FstClusHI
, pclst
>> 16 );
3581 for( n
= 0; n
< dj
.fs
->csize
; n
++ )
3582 { /* Write dot entries and clear left sectors */
3583 dj
.fs
->winsect
= dsect
++;
3585 res
= move_window( dj
.fs
, 0 );
3588 LEAVE_FF( dj
.fs
, res
);
3591 mem_set( dir
, 0, SS(dj
.fs
) );
3594 res
= dir_register( &dj
);
3597 remove_chain( dj
.fs
, dclst
);
3602 dir
[DIR_Attr
] = AM_DIR
; /* Attribute */
3603 ST_DWORD( dir
+ DIR_WrtTime
, tim
); /* Crated time */
3604 ST_WORD( dir
+ DIR_FstClusLO
, dclst
); /* Table start cluster */
3605 ST_WORD( dir
+ DIR_FstClusHI
, dclst
>> 16 );
3607 res
= sync( dj
.fs
);
3610 LEAVE_FF( dj
.fs
, res
);
3613 /*-----------------------------------------------------------------------*/
3615 /* Change File Attribute */
3617 /*-----------------------------------------------------------------------*/
3618 FRESULT
f_chmod( const XCHAR
*path
, /* Pointer to the file path */ BYTE value
, /* Attribute bits */ BYTE mask
/* Attribute mask to change */ )
3622 NAMEBUF( sfn
, lfn
);
3626 res
= chk_mounted( &path
, &dj
.fs
, 1 );
3629 INITBUF( dj
, sfn
, lfn
);
3630 res
= follow_path( &dj
, path
); /* Follow the file path */
3631 if( _FS_RPATH
&& res
== FR_OK
&& (dj
.fn
[NS
] & NS_DOT
) )
3633 res
= FR_INVALID_NAME
;
3640 { /* Is it a root directory? */
3641 res
= FR_INVALID_NAME
;
3644 { /* File or sub directory */
3645 mask
&= AM_RDO
| AM_HID
| AM_SYS
| AM_ARC
; /* Valid attribute mask */
3646 dir
[DIR_Attr
] = ( value
& mask
) | ( dir
[DIR_Attr
] & (BYTE
)~mask
); /* Apply attribute change */
3648 res
= sync( dj
.fs
);
3653 LEAVE_FF( dj
.fs
, res
);
3656 /*-----------------------------------------------------------------------*/
3658 /* Change Timestamp */
3660 /*-----------------------------------------------------------------------*/
3661 FRESULT
f_utime( const XCHAR
*path
, /* Pointer to the file/directory name */ const FILINFO
*fno
/* Pointer to the timestamp to be set */ )
3665 NAMEBUF( sfn
, lfn
);
3669 res
= chk_mounted( &path
, &dj
.fs
, 1 );
3672 INITBUF( dj
, sfn
, lfn
);
3673 res
= follow_path( &dj
, path
); /* Follow the file path */
3674 if( _FS_RPATH
&& res
== FR_OK
&& (dj
.fn
[NS
] & NS_DOT
) )
3676 res
= FR_INVALID_NAME
;
3683 { /* Root directory */
3684 res
= FR_INVALID_NAME
;
3687 { /* File or sub-directory */
3688 ST_WORD( dir
+ DIR_WrtTime
, fno
->ftime
);
3689 ST_WORD( dir
+ DIR_WrtDate
, fno
->fdate
);
3691 res
= sync( dj
.fs
);
3696 LEAVE_FF( dj
.fs
, res
);
3699 /*-----------------------------------------------------------------------*/
3701 /* Rename File/Directory */
3703 /*-----------------------------------------------------------------------*/
3704 FRESULT
f_rename( const XCHAR
*path_old
, /* Pointer to the old name */ const XCHAR
*path_new
/* Pointer to the new name */ )
3708 NAMEBUF( sfn
, lfn
);
3713 INITBUF( dj_old
, sfn
, lfn
);
3714 res
= chk_mounted( &path_old
, &dj_old
.fs
, 1 );
3717 dj_new
.fs
= dj_old
.fs
;
3718 res
= follow_path( &dj_old
, path_old
); /* Check old object */
3719 if( _FS_RPATH
&& res
== FR_OK
&& (dj_old
.fn
[NS
] & NS_DOT
) )
3721 res
= FR_INVALID_NAME
;
3727 LEAVE_FF( dj_old
.fs
, res
); /* The old object is not found */
3732 LEAVE_FF( dj_old
.fs
, FR_NO_FILE
); /* Is root dir? */
3735 mem_cpy( buf
, dj_old
.dir
+ DIR_Attr
, 21 ); /* Save the object information */
3737 mem_cpy( &dj_new
, &dj_old
, sizeof(DIR) );
3738 res
= follow_path( &dj_new
, path_new
); /* Check new object */
3741 res
= FR_EXIST
; /* The new object name is already existing */
3744 if( res
== FR_NO_FILE
)
3745 { /* Is it a valid path and no name collision? */
3746 res
= dir_register( &dj_new
); /* Register the new object */
3749 dir
= dj_new
.dir
; /* Copy object information into new entry */
3750 mem_cpy( dir
+ 13, buf
+ 2, 19 );
3751 dir
[DIR_Attr
] = buf
[0] | AM_ARC
;
3752 dj_old
.fs
->wflag
= 1;
3753 if( dir
[DIR_Attr
] & AM_DIR
)
3754 { /* Update .. entry in the directory if needed */
3755 dw
= clust2sect( dj_new
.fs
, (DWORD
) LD_WORD(dir
+ DIR_FstClusHI
) | LD_WORD(dir
+ DIR_FstClusLO
) );
3762 res
= move_window( dj_new
.fs
, dw
);
3763 dir
= dj_new
.fs
->win
+ 32;
3764 if( res
== FR_OK
&& dir
[1] == '.' )
3766 dw
= ( dj_new
.fs
->fs_type
== FS_FAT32
&& dj_new
.sclust
== dj_new
.fs
->dirbase
) ? 0 : dj_new
.sclust
;
3767 ST_WORD( dir
+ DIR_FstClusLO
, dw
);
3768 ST_WORD( dir
+ DIR_FstClusHI
, dw
>> 16 );
3769 dj_new
.fs
->wflag
= 1;
3776 res
= dir_remove( &dj_old
); /* Remove old entry */
3779 res
= sync( dj_old
.fs
);
3785 LEAVE_FF( dj_old
.fs
, res
);
3788 #endif /* !_FS_READONLY */
3789 #endif /* _FS_MINIMIZE == 0 */
3790 #endif /* _FS_MINIMIZE <= 1 */
3791 #endif /* _FS_MINIMIZE <= 2 */
3793 /*-----------------------------------------------------------------------*/
3795 /* Forward data to the stream directly (Available on only _FS_TINY cfg) */
3797 /*-----------------------------------------------------------------------*/
3798 #if _USE_FORWARD && _FS_TINY
3801 FIL
*fp
, /* Pointer to the file object */
3802 UINT (*func
) ( const BYTE
*, UINT
), /* Pointer to the streaming function */
3803 UINT btr
, /* Number of bytes to forward */
3804 UINT
*bf
/* Pointer to number of bytes forwarded */
3808 DWORD remain
, clst
, sect
;
3813 res
= validate( fp
->fs
, fp
->id
); /* Check validity of the object */
3816 LEAVE_FF( fp
->fs
, res
);
3819 if( fp
->flag
& FA__ERROR
)
3820 { /* Check error flag */
3821 LEAVE_FF( fp
->fs
, FR_INT_ERR
);
3824 if( !(fp
->flag
& FA_READ
) )
3825 { /* Check access mode */
3826 LEAVE_FF( fp
->fs
, FR_DENIED
);
3829 remain
= fp
->fsize
- fp
->fptr
;
3832 btr
= ( UINT
) remain
; /* Truncate btr by remaining bytes */
3838 btr
&& (*func
) (NULL
, 0); /* Repeat until all data transferred or stream becomes busy */
3839 fp
->fptr
+= rcnt
, *bf
+= rcnt
, btr
-= rcnt
3842 if( (fp
->fptr
% SS(fp
->fs
)) == 0 )
3843 { /* On the sector boundary? */
3844 if( fp
->csect
>= fp
->fs
->csize
)
3845 { /* On the cluster boundary? */
3846 clst
= ( fp
->fptr
== 0 ) ? /* On the top of the file? */
3847 fp
->org_clust
: get_fat( fp
->fs
, fp
->curr_clust
);
3850 ABORT( fp
->fs
, FR_INT_ERR
);
3853 if( clst
== 0xFFFFFFFF )
3855 ABORT( fp
->fs
, FR_DISK_ERR
);
3858 fp
->curr_clust
= clst
; /* Update current cluster */
3859 fp
->csect
= 0; /* Reset sector address in the cluster */
3862 fp
->csect
++; /* Next sector address in the cluster */
3865 sect
= clust2sect( fp
->fs
, fp
->curr_clust
); /* Get current data sector */
3868 ABORT( fp
->fs
, FR_INT_ERR
);
3871 sect
+= fp
->csect
- 1;
3872 if( move_window(fp
->fs
, sect
) )
3873 { /* Move sector window */
3874 ABORT( fp
->fs
, FR_DISK_ERR
);
3878 rcnt
= SS( fp
->fs
) - ( WORD
) ( fp
->fptr
% SS(fp
->fs
) ); /* Forward data from sector window */
3884 rcnt
= ( *func
) ( &fp
->fs
->win
[(WORD
) fp
->fptr
% SS(fp
->fs
)], rcnt
);
3887 ABORT( fp
->fs
, FR_INT_ERR
);
3891 LEAVE_FF( fp
->fs
, FR_OK
);
3894 #endif /* _USE_FORWARD */
3896 #if _USE_MKFS && !_FS_READONLY
3898 /*-----------------------------------------------------------------------*/
3900 /* Create File System on the Drive */
3902 /*-----------------------------------------------------------------------*/
3903 #define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */
3904 #define N_FATS 1 /* 1 or 2 */
3905 #define MAX_SECTOR 131072000UL /* Maximum partition size */
3906 #define MIN_SECTOR 2000UL /* Minimum partition size */
3910 BYTE drv
, /* Logical drive number */
3911 BYTE partition
, /* Partitioning rule 0:FDISK, 1:SFD */
3912 WORD allocsize
/* Allocation unit size [bytes] */
3915 static const DWORD sstbl
[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 };
3916 static const WORD cstbl
[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 };
3918 DWORD b_part
, b_fat
, b_dir
, b_data
; /* Area offset (LBA) */
3919 DWORD n_part
, n_rsv
, n_fat
, n_dir
; /* Area size */
3925 /* Check validity of the parameters */
3926 if( drv
>= _DRIVES
)
3928 return FR_INVALID_DRIVE
;
3931 if( partition
>= 2 )
3933 return FR_MKFS_ABORTED
;
3936 /* Check mounted drive and clear work area */
3940 return FR_NOT_ENABLED
;
3946 /* Get disk statics */
3947 stat
= disk_initialize( drv
);
3948 if( stat
& STA_NOINIT
)
3950 return FR_NOT_READY
;
3953 if( stat
& STA_PROTECT
)
3955 return FR_WRITE_PROTECTED
;
3958 #if _MAX_SS != 512 /* Get disk sector size */
3959 if( disk_ioctl(drv
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK
|| SS(fs
) > _MAX_SS
)
3961 return FR_MKFS_ABORTED
;
3965 if( disk_ioctl(drv
, GET_SECTOR_COUNT
, &n_part
) != RES_OK
|| n_part
< MIN_SECTOR
)
3967 return FR_MKFS_ABORTED
;
3970 if( n_part
> MAX_SECTOR
)
3972 n_part
= MAX_SECTOR
;
3975 b_part
= ( !partition
) ? 63 : 0; /* Boot sector */
3977 for( d
= 512; d
<= 32768U && d
!= allocsize
; d
<<= 1 );
3979 /* Check validity of the allocation unit size */
3980 if( d
!= allocsize
)
3986 { /* Auto selection of cluster size */
3988 for( as
= SS(fs
); as
> 512U; as
>>= 1 )
3993 for( n
= 0; d
< sstbl
[n
]; n
++ );
3994 allocsize
= cstbl
[n
];
3997 if( allocsize
< SS(fs
) )
3999 allocsize
= SS( fs
);
4002 allocsize
/= SS( fs
); /* Number of sectors per cluster */
4004 /* Pre-compute number of clusters and FAT type */
4005 n_clst
= n_part
/ allocsize
;
4007 if( n_clst
>= 0xFF5 )
4012 if( n_clst
>= 0xFFF5 )
4017 /* Determine offset and size of FAT structure */
4021 n_fat
= ( (n_clst
* 3 + 1) / 2 + 3 + SS(fs
) - 1 ) / SS( fs
);
4022 n_rsv
= 1 + partition
;
4023 n_dir
= N_ROOTDIR
* 32 / SS( fs
);
4027 n_fat
= ( (n_clst
* 2) + 4 + SS(fs
) - 1 ) / SS( fs
);
4028 n_rsv
= 1 + partition
;
4029 n_dir
= N_ROOTDIR
* 32 / SS( fs
);
4033 n_fat
= ( (n_clst
* 4) + 8 + SS(fs
) - 1 ) / SS( fs
);
4034 n_rsv
= 33 - partition
;
4038 b_fat
= b_part
+ n_rsv
; /* FATs start sector */
4039 b_dir
= b_fat
+ n_fat
* N_FATS
; /* Directory start sector */
4040 b_data
= b_dir
+ n_dir
; /* Data start sector */
4042 /* Align data start sector to erase block boundary (for flash memory media) */
4043 if( disk_ioctl(drv
, GET_BLOCK_SIZE
, &n
) != RES_OK
)
4045 return FR_MKFS_ABORTED
;
4048 n
= ( b_data
+ n
- 1 ) &~( n
- 1 );
4049 n_fat
+= ( n
- b_data
) / N_FATS
;
4051 /* b_dir and b_data are no longer used below */
4053 /* Determine number of cluster and final check of validity of the FAT type */
4054 n_clst
= ( n_part
- n_rsv
- n_fat
* N_FATS
- n_dir
) / allocsize
;
4055 if( (fmt
== FS_FAT16
&& n_clst
< 0xFF5) || (fmt
== FS_FAT32
&& n_clst
< 0xFFF5) )
4057 return FR_MKFS_ABORTED
;
4060 /* Create partition table if needed */
4063 DWORD n_disk
= b_part
+ n_part
;
4065 mem_set( fs
->win
, 0, SS(fs
) );
4066 tbl
= fs
->win
+ MBR_Table
;
4067 ST_DWORD( tbl
, 0x00010180 ); /* Partition start in CHS */
4068 if( n_disk
< 63UL * 255 * 1024 )
4069 { /* Partition end in CHS */
4070 n_disk
= n_disk
/ 63 / 255;
4071 tbl
[7] = ( BYTE
) n_disk
;
4072 tbl
[6] = ( BYTE
) ( (n_disk
>> 2) | 63 );
4076 ST_WORD( &tbl
[6], 0xFFFF );
4080 if( fmt
!= FS_FAT32
)
4082 tbl
[4] = ( n_part
< 0x10000 ) ? 0x04 : 0x06;
4089 ST_DWORD( tbl
+ 8, 63 ); /* Partition start in LBA */
4090 ST_DWORD( tbl
+ 12, n_part
); /* Partition size in LBA */
4091 ST_WORD( tbl
+ 64, 0xAA55 ); /* Signature */
4092 if( disk_write(drv
, fs
->win
, 0, 1) != RES_OK
)
4104 /* Create boot record */
4105 tbl
= fs
->win
; /* Clear buffer */
4106 mem_set( tbl
, 0, SS(fs
) );
4107 ST_DWORD( tbl
+ BS_jmpBoot
, 0x90FEEB ); /* Boot code (jmp $, nop) */
4108 ST_WORD( tbl
+ BPB_BytsPerSec
, SS(fs
) ); /* Sector size */
4109 tbl
[BPB_SecPerClus
] = ( BYTE
) allocsize
; /* Sectors per cluster */
4110 ST_WORD( tbl
+ BPB_RsvdSecCnt
, n_rsv
); /* Reserved sectors */
4111 tbl
[BPB_NumFATs
] = N_FATS
; /* Number of FATs */
4112 ST_WORD( tbl
+ BPB_RootEntCnt
, SS(fs
) / 32 * n_dir
); /* Number of rootdir entries */
4113 if( n_part
< 0x10000 )
4114 { /* Number of total sectors */
4115 ST_WORD( tbl
+ BPB_TotSec16
, n_part
);
4119 ST_DWORD( tbl
+ BPB_TotSec32
, n_part
);
4122 tbl
[BPB_Media
] = partition
; /* Media descripter */
4123 ST_WORD( tbl
+ BPB_SecPerTrk
, 63 ); /* Number of sectors per track */
4124 ST_WORD( tbl
+ BPB_NumHeads
, 255 ); /* Number of heads */
4125 ST_DWORD( tbl
+ BPB_HiddSec
, b_part
); /* Hidden sectors */
4126 n
= get_fattime(); /* Use current time as a VSN */
4127 if( fmt
!= FS_FAT32
)
4129 ST_DWORD( tbl
+ BS_VolID
, n
); /* Volume serial number */
4130 ST_WORD( tbl
+ BPB_FATSz16
, n_fat
); /* Number of secters per FAT */
4131 tbl
[BS_DrvNum
] = 0x80; /* Drive number */
4132 tbl
[BS_BootSig
] = 0x29; /* Extended boot signature */
4133 mem_cpy( tbl
+ BS_VolLab
, "NO NAME FAT ", 19 ); /* Volume lavel, FAT signature */
4137 ST_DWORD( tbl
+ BS_VolID32
, n
); /* Volume serial number */
4138 ST_DWORD( tbl
+ BPB_FATSz32
, n_fat
); /* Number of secters per FAT */
4139 ST_DWORD( tbl
+ BPB_RootClus
, 2 ); /* Root directory cluster (2) */
4140 ST_WORD( tbl
+ BPB_FSInfo
, 1 ); /* FSInfo record offset (bs+1) */
4141 ST_WORD( tbl
+ BPB_BkBootSec
, 6 ); /* Backup boot record offset (bs+6) */
4142 tbl
[BS_DrvNum32
] = 0x80; /* Drive number */
4143 tbl
[BS_BootSig32
] = 0x29; /* Extended boot signature */
4144 mem_cpy( tbl
+ BS_VolLab32
, "NO NAME FAT32 ", 19 ); /* Volume lavel, FAT signature */
4147 ST_WORD( tbl
+ BS_55AA
, 0xAA55 ); /* Signature */
4150 ST_WORD( tbl
+ SS(fs
) - 2, 0xAA55 );
4153 if( disk_write(drv
, tbl
, b_part
+ 0, 1) != RES_OK
)
4158 if( fmt
== FS_FAT32
)
4160 disk_write( drv
, tbl
, b_part
+ 6, 1 );
4163 /* Initialize FAT area */
4164 for( m
= 0; m
< N_FATS
; m
++ )
4166 mem_set( tbl
, 0, SS(fs
) ); /* 1st sector of the FAT */
4167 if( fmt
!= FS_FAT32
)
4169 n
= ( fmt
== FS_FAT12
) ? 0x00FFFF00 : 0xFFFFFF00;
4171 ST_DWORD( tbl
, n
); /* Reserve cluster #0-1 (FAT12/16) */
4175 ST_DWORD( tbl
+ 0, 0xFFFFFFF8 ); /* Reserve cluster #0-1 (FAT32) */
4176 ST_DWORD( tbl
+ 4, 0xFFFFFFFF );
4177 ST_DWORD( tbl
+ 8, 0x0FFFFFFF ); /* Reserve cluster #2 for root dir */
4180 if( disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
)
4185 mem_set( tbl
, 0, SS(fs
) ); /* Following FAT entries are filled by zero */
4186 for( n
= 1; n
< n_fat
; n
++ )
4188 if( disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
)
4195 /* Initialize Root directory */
4196 m
= ( BYTE
) ( (fmt
== FS_FAT32
) ? allocsize
: n_dir
);
4199 if( disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
)
4205 /* Create FSInfo record if needed */
4206 if( fmt
== FS_FAT32
)
4208 ST_WORD( tbl
+ BS_55AA
, 0xAA55 );
4209 ST_DWORD( tbl
+ FSI_LeadSig
, 0x41615252 );
4210 ST_DWORD( tbl
+ FSI_StrucSig
, 0x61417272 );
4211 ST_DWORD( tbl
+ FSI_Free_Count
, n_clst
- 1 );
4212 ST_DWORD( tbl
+ FSI_Nxt_Free
, 0xFFFFFFFF );
4213 disk_write( drv
, tbl
, b_part
+ 1, 1 );
4214 disk_write( drv
, tbl
, b_part
+ 7, 1 );
4217 return( disk_ioctl(drv
, CTRL_SYNC
, ( void * ) NULL
) == RES_OK
) ? FR_OK
: FR_DISK_ERR
;
4220 #endif /* _USE_MKFS && !_FS_READONLY */
4224 /*-----------------------------------------------------------------------*/
4226 /* Get a string from the file */
4228 /*-----------------------------------------------------------------------*/
4231 char *buff
, /* Pointer to the string buffer to read */
4232 int len
, /* Size of string buffer */
4233 FIL
*fil
/* Pointer to the file object */
4240 while( i
< len
- 1 )
4241 { /* Read bytes until buffer gets filled */
4242 f_read( fil
, p
, 1, &rc
);
4245 break; /* Break when no data to read */
4248 #if _USE_STRFUNC >= 2
4251 continue; /* Strip '\r' */
4258 break; /* Break when reached end of line */
4263 return i
? buff
: NULL
; /* When no data read (eof or error), return with error. */
4269 /*-----------------------------------------------------------------------*/
4271 /* Put a character to the file */
4273 /*-----------------------------------------------------------------------*/
4274 int f_putc( int chr
, /* A character to be output */ FIL
*fil
/* Ponter to the file object */ )
4279 #if _USE_STRFUNC >= 2
4282 f_putc( '\r', fil
); /* LF -> CRLF conversion */
4287 { /* Special value may be used to switch the destination to any other device */
4288 /* put_console(chr); */
4293 f_write( fil
, &c
, 1, &bw
); /* Write a byte to the file */
4294 return bw
? chr
: EOF
; /* Return the result */
4297 /*-----------------------------------------------------------------------*/
4299 /* Put a string to the file */
4301 /*-----------------------------------------------------------------------*/
4302 int f_puts( const char *str
, /* Pointer to the string to be output */ FIL
*fil
/* Pointer to the file object */ )
4306 for( n
= 0; *str
; str
++, n
++ )
4308 if( f_putc(*str
, fil
) == EOF
)
4317 /*-----------------------------------------------------------------------*/
4319 /* Put a formatted string to the file */
4321 /*-----------------------------------------------------------------------*/
4322 int f_printf( FIL
*fil
, /* Pointer to the file object */ const char *str
, /* Pointer to the format string */ ... /* Optional arguments... */ )
4330 va_start( arp
, str
);
4332 for( cc
= res
= 0; cc
!= EOF
; res
+= cc
)
4337 break; /* End of string */
4341 { /* Non escape cahracter */
4342 cc
= f_putc( c
, fil
);
4354 { /* Flag: '0' padding */
4359 while( c
>= '0' && c
<= '9' )
4361 w
= w
* 10 + ( c
- '0' );
4366 { /* Prefix: Size is long int */
4372 { /* Type is string */
4373 cc
= f_puts( va_arg(arp
, char *), fil
);
4378 { /* Type is character */
4379 cc
= f_putc( va_arg(arp
, int), fil
);
4391 r
= 10; /* Type is signed decimal */
4396 r
= 10; /* Type is unsigned decimal */
4401 r
= 16; /* Type is unsigned hexdecimal */
4406 break; /* Unknown type */
4410 { /* Get the value */
4411 val
= ( ULONG
) va_arg( arp
, long );
4415 val
= ( c
== 'd' ) ? ( ULONG
) ( long ) va_arg( arp
, int ) : ( ULONG
) va_arg( arp
, unsigned int );
4418 /* Put numeral string */
4421 if( val
& 0x80000000 )
4428 i
= sizeof( s
) - 1;
4432 c
= ( UCHAR
) ( val
% r
+ '0' );
4440 } while( i
&& val
);
4446 w
= sizeof( s
) - 1 - w
;
4449 s
[--i
] = ( f
& 1 ) ? '0' : ' ';
4452 cc
= f_puts( &s
[i
], fil
);
4456 return( cc
== EOF
) ? cc
: res
;
4459 #endif /* !_FS_READONLY */
4460 #endif /* _USE_STRFUNC */