1 /*----------------------------------------------------------------------------/ 
   2 /  FatFs - FAT file system module  R0.07e                    (C)ChaN, 2010 
   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) 2010, 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 /---------------------------------------------------------------------------*/ 
  79 #include "ff.h"                 /* FatFs configurations and declarations */ 
  80 #include "diskio.h"             /* Declarations of low level disk I/O functions */ 
  82 /*-------------------------------------------------------------------------- 
  84    Module Private Definitions 
  86 ---------------------------------------------------------------------------*/ 
  89 #error Wrong include file (ff.h). 
  94 #error Static LFN work area must not be used in re-entrant configuration. 
  96 #define ENTER_FF(fs)            { if (!lock_fs(fs)) return FR_TIMEOUT; } 
  97 #define LEAVE_FF(fs, res)       { unlock_fs(fs, res); return res; } 
 101 #define LEAVE_FF(fs, res)       return res 
 105 #define ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } 
 111 /* Name status flags */ 
 112 #define NS                      11              /* Offset of name status byte */ 
 113 #define NS_LOSS         0x01    /* Out of 8.3 format */ 
 114 #define NS_LFN          0x02    /* Force to create LFN entry */ 
 115 #define NS_LAST         0x04    /* Last segment */ 
 116 #define NS_BODY         0x08    /* Lower case flag (body) */ 
 117 #define NS_EXT          0x10    /* Lower case flag (ext) */ 
 118 #define NS_DOT          0x20    /* Dot entry */ 
 123 /*-------------------------------------------------------------------------- 
 127 ---------------------------------------------------------------------------*/ 
 129 #if _DRIVES < 1 || _DRIVES > 9 
 130 #error Number of drives must be 1-9. 
 133 FATFS 
*FatFs
[_DRIVES
];  /* Pointer to the file system objects (logical drives) */ 
 136 WORD Fsid
;                              /* File system mount ID */ 
 140 BYTE Drive
;                             /* Current drive */ 
 144 #if _USE_LFN == 1       /* LFN with static LFN working buffer */ 
 146 WCHAR LfnBuf
[_MAX_LFN 
+ 1]; 
 147 #define NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR *lp = LfnBuf 
 148 #define INITBUF(dj,sp,lp)       dj.fn = sp; dj.lfn = lp 
 150 #elif _USE_LFN > 1      /* LFN with dynamic LFN working buffer */ 
 151 #define NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf 
 152 #define INITBUF(dj,sp,lp)       dj.fn = sp; dj.lfn = lp 
 155 #define NAMEBUF(sp,lp)  BYTE sp[12] 
 156 #define INITBUF(dj,sp,lp)       dj.fn = sp 
 163 /*-------------------------------------------------------------------------- 
 165    Module Private Functions 
 167 ---------------------------------------------------------------------------*/ 
 170 /*-----------------------------------------------------------------------*/ 
 171 /* String functions                                                      */ 
 172 /*-----------------------------------------------------------------------*/ 
 174 /* Copy memory to memory */ 
 176 void mem_cpy (void* dst
, const void* src
, int cnt
) { 
 177         char *d 
= (char*)dst
; 
 178         const char *s 
= (const char *)src
; 
 179         while (cnt
--) *d
++ = *s
++; 
 184 void mem_set (void* dst
, int val
, int cnt
) { 
 185         char *d 
= (char*)dst
; 
 186         while (cnt
--) *d
++ = (char)val
; 
 189 /* Compare memory to memory */ 
 191 int mem_cmp (const void* dst
, const void* src
, int cnt
) { 
 192         const char *d 
= (const char *)dst
, *s 
= (const char *)src
; 
 194         while (cnt
-- && (r 
= *d
++ - *s
++) == 0) ; 
 198 /* Check if chr is contained in the string */ 
 200 int chk_chr (const char* str
, int chr
) { 
 201         while (*str 
&& *str 
!= chr
) str
++; 
 207 /*-----------------------------------------------------------------------*/ 
 208 /* Request/Release grant to access the volume                            */ 
 209 /*-----------------------------------------------------------------------*/ 
 214         FATFS 
*fs               
/* File system object */ 
 217         return ff_req_grant(fs
->sobj
); 
 223         FATFS 
*fs
,              /* File system object */ 
 224         FRESULT res             
/* Result code to be returned */ 
 227         if (res 
!= FR_NOT_ENABLED 
&& 
 228                 res 
!= FR_INVALID_DRIVE 
&& 
 229                 res 
!= FR_INVALID_OBJECT 
&& 
 231                 ff_rel_grant(fs
->sobj
); 
 238 /*-----------------------------------------------------------------------*/ 
 239 /* Change window offset                                                  */ 
 240 /*-----------------------------------------------------------------------*/ 
 243 FRESULT 
move_window ( 
 244         FATFS 
*fs
,              /* File system object */ 
 245         DWORD sector    
/* Sector number to make apperance in the fs->win[] */ 
 246 )                                       /* Move to zero only writes back dirty window */ 
 252         if (wsect 
!= sector
) {  /* Changed current window */ 
 254                 if (fs
->wflag
) {        /* Write back dirty window if needed */ 
 255                         if (disk_write(fs
->drive
, fs
->win
, wsect
, 1) != RES_OK
) 
 258                         if (wsect 
< (fs
->fatbase 
+ fs
->sects_fat
)) {    /* In FAT area */ 
 260                                 for (nf 
= fs
->n_fats
; nf 
> 1; nf
--) {   /* Refrect the change to all FAT copies */ 
 261                                         wsect 
+= fs
->sects_fat
; 
 262                                         disk_write(fs
->drive
, fs
->win
, wsect
, 1); 
 268                         if (disk_read(fs
->drive
, fs
->win
, sector
, 1) != RES_OK
) 
 270                         fs
->winsect 
= sector
; 
 280 /*-----------------------------------------------------------------------*/ 
 281 /* Clean-up cached data                                                  */ 
 282 /*-----------------------------------------------------------------------*/ 
 285 FRESULT 
sync (  /* FR_OK: successful, FR_DISK_ERR: failed */ 
 286         FATFS 
*fs       
/* File system object */ 
 292         res 
= move_window(fs
, 0); 
 294                 /* Update FSInfo sector if needed */ 
 295                 if (fs
->fs_type 
== FS_FAT32 
&& fs
->fsi_flag
) { 
 297                         mem_set(fs
->win
, 0, 512); 
 298                         ST_WORD(fs
->win
+BS_55AA
, 0xAA55); 
 299                         ST_DWORD(fs
->win
+FSI_LeadSig
, 0x41615252); 
 300                         ST_DWORD(fs
->win
+FSI_StrucSig
, 0x61417272); 
 301                         ST_DWORD(fs
->win
+FSI_Free_Count
, fs
->free_clust
); 
 302                         ST_DWORD(fs
->win
+FSI_Nxt_Free
, fs
->last_clust
); 
 303                         disk_write(fs
->drive
, fs
->win
, fs
->fsi_sector
, 1); 
 306                 /* Make sure that no pending write process in the physical drive */ 
 307                 if (disk_ioctl(fs
->drive
, CTRL_SYNC
, (void*)NULL
) != RES_OK
) 
 318 /*-----------------------------------------------------------------------*/ 
 319 /* FAT access - Read value of a FAT entry                                */ 
 320 /*-----------------------------------------------------------------------*/ 
 323 DWORD 
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ 
 324         FATFS 
*fs
,      /* File system object */ 
 325         DWORD clst      
/* Cluster# to get the link information */ 
 332         if (clst 
< 2 || clst 
>= fs
->max_clust
)  /* Range check */ 
 336         switch (fs
->fs_type
) { 
 338                 bc 
= clst
; bc 
+= bc 
/ 2; 
 339                 if (move_window(fs
, fsect 
+ (bc 
/ SS(fs
)))) break; 
 340                 wc 
= fs
->win
[bc 
& (SS(fs
) - 1)]; bc
++; 
 341                 if (move_window(fs
, fsect 
+ (bc 
/ SS(fs
)))) break; 
 342                 wc 
|= (WORD
)fs
->win
[bc 
& (SS(fs
) - 1)] << 8; 
 343                 return (clst 
& 1) ? 
(wc 
>> 4) : (wc 
& 0xFFF); 
 346                 if (move_window(fs
, fsect 
+ (clst 
/ (SS(fs
) / 2)))) break; 
 347                 return LD_WORD(&fs
->win
[((WORD
)clst 
* 2) & (SS(fs
) - 1)]); 
 350                 if (move_window(fs
, fsect 
+ (clst 
/ (SS(fs
) / 4)))) break; 
 351                 return LD_DWORD(&fs
->win
[((WORD
)clst 
* 4) & (SS(fs
) - 1)]) & 0x0FFFFFFF; 
 354         return 0xFFFFFFFF;      /* An error occured at the disk I/O layer */ 
 360 /*-----------------------------------------------------------------------*/ 
 361 /* FAT access - Change value of a FAT entry                              */ 
 362 /*-----------------------------------------------------------------------*/ 
 366         FATFS 
*fs
,      /* File system object */ 
 367         DWORD clst
,     /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */ 
 368         DWORD val       
/* New value to mark the cluster */ 
 377         if (clst 
< 2 || clst 
>= fs
->max_clust
) {        /* Range check */ 
 382                 switch (fs
->fs_type
) { 
 384                         bc 
= clst
; bc 
+= bc 
/ 2; 
 385                         res 
= move_window(fs
, fsect 
+ (bc 
/ SS(fs
))); 
 386                         if (res 
!= FR_OK
) break; 
 387                         p 
= &fs
->win
[bc 
& (SS(fs
) - 1)]; 
 388                         *p 
= (clst 
& 1) ? 
((*p 
& 0x0F) | ((BYTE
)val 
<< 4)) : (BYTE
)val
; 
 391                         res 
= move_window(fs
, fsect 
+ (bc 
/ SS(fs
))); 
 392                         if (res 
!= FR_OK
) break; 
 393                         p 
= &fs
->win
[bc 
& (SS(fs
) - 1)]; 
 394                         *p 
= (clst 
& 1) ? 
(BYTE
)(val 
>> 4) : ((*p 
& 0xF0) | ((BYTE
)(val 
>> 8) & 0x0F)); 
 398                         res 
= move_window(fs
, fsect 
+ (clst 
/ (SS(fs
) / 2))); 
 399                         if (res 
!= FR_OK
) break; 
 400                         ST_WORD(&fs
->win
[((WORD
)clst 
* 2) & (SS(fs
) - 1)], (WORD
)val
); 
 404                         res 
= move_window(fs
, fsect 
+ (clst 
/ (SS(fs
) / 4))); 
 405                         if (res 
!= FR_OK
) break; 
 406                         ST_DWORD(&fs
->win
[((WORD
)clst 
* 4) & (SS(fs
) - 1)], val
); 
 417 #endif /* !_FS_READONLY */ 
 422 /*-----------------------------------------------------------------------*/ 
 423 /* FAT handling - Remove a cluster chain                                 */ 
 424 /*-----------------------------------------------------------------------*/ 
 427 FRESULT 
remove_chain ( 
 428         FATFS 
*fs
,                      /* File system object */ 
 429         DWORD clst                      
/* Cluster# to remove a chain from */ 
 436         if (clst 
< 2 || clst 
>= fs
->max_clust
) {        /* Check the range of cluster# */ 
 441                 while (clst 
< fs
->max_clust
) {                  /* Not a last link? */ 
 442                         nxt 
= get_fat(fs
, clst
);                        /* Get cluster status */ 
 443                         if (nxt 
== 0) break;                            /* Empty cluster? */ 
 444                         if (nxt 
== 1) { res 
= FR_INT_ERR
; break; }      /* Internal error? */ 
 445                         if (nxt 
== 0xFFFFFFFF) { res 
= FR_DISK_ERR
; break; }    /* Disk error? */ 
 446                         res 
= put_fat(fs
, clst
, 0);                     /* Mark the cluster "empty" */ 
 447                         if (res 
!= FR_OK
) break; 
 448                         if (fs
->free_clust 
!= 0xFFFFFFFF) {     /* Update FSInfo */ 
 452                         clst 
= nxt
;     /* Next cluster */ 
 463 /*-----------------------------------------------------------------------*/ 
 464 /* FAT handling - Stretch or Create a cluster chain                      */ 
 465 /*-----------------------------------------------------------------------*/ 
 468 DWORD 
create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ 
 469         FATFS 
*fs
,                      /* File system object */ 
 470         DWORD clst                      
/* Cluster# to stretch. 0 means create a new chain. */ 
 473         DWORD cs
, ncl
, scl
, mcl
; 
 477         if (clst 
== 0) {                /* Create new chain */ 
 478                 scl 
= fs
->last_clust
;                   /* Get suggested start point */ 
 479                 if (scl 
== 0 || scl 
>= mcl
) scl 
= 1; 
 481         else {                                  /* Stretch existing chain */ 
 482                 cs 
= get_fat(fs
, clst
);                 /* Check the cluster status */ 
 483                 if (cs 
< 2) return 1;                   /* It is an invalid cluster */ 
 484                 if (cs 
< mcl
) return cs
;                /* It is already followed by next cluster */ 
 488         ncl 
= scl
;                              /* Start cluster */ 
 490                 ncl
++;                                                  /* Next cluster */ 
 491                 if (ncl 
>= mcl
) {                               /* Wrap around */ 
 493                         if (ncl 
> scl
) return 0;        /* No free custer */ 
 495                 cs 
= get_fat(fs
, ncl
);                  /* Get the cluster status */ 
 496                 if (cs 
== 0) break;                             /* Found a free cluster */ 
 497                 if (cs 
== 0xFFFFFFFF || cs 
== 1)/* An error occured */ 
 499                 if (ncl 
== scl
) return 0;               /* No free custer */ 
 502         if (put_fat(fs
, ncl
, 0x0FFFFFFF))       /* Mark the new cluster "in use" */ 
 504         if (clst 
!= 0) {                                        /* Link it to the previous one if needed */ 
 505                 if (put_fat(fs
, clst
, ncl
)) 
 509         fs
->last_clust 
= ncl
;                           /* Update FSINFO */ 
 510         if (fs
->free_clust 
!= 0xFFFFFFFF) { 
 515         return ncl
;             /* Return new cluster number */ 
 517 #endif /* !_FS_READONLY */ 
 522 /*-----------------------------------------------------------------------*/ 
 523 /* Get sector# from cluster#                                             */ 
 524 /*-----------------------------------------------------------------------*/ 
 527 DWORD 
clust2sect (      /* !=0: Sector number, 0: Failed - invalid cluster# */ 
 528         FATFS 
*fs
,              /* File system object */ 
 529         DWORD clst              
/* Cluster# to be converted */ 
 533         if (clst 
>= (fs
->max_clust 
- 2)) return 0;              /* Invalid cluster# */ 
 534         return clst 
* fs
->csize 
+ fs
->database
; 
 540 /*-----------------------------------------------------------------------*/ 
 541 /* Directory handling - Seek directory index                             */ 
 542 /*-----------------------------------------------------------------------*/ 
 546         DIR *dj
,                /* Pointer to directory object */ 
 547         WORD idx                
/* Directory index number */ 
 556         if (clst 
== 1 || clst 
>= dj
->fs
->max_clust
)     /* Check start cluster range */ 
 558         if (!clst 
&& dj
->fs
->fs_type 
== FS_FAT32
)       /* Replace cluster# 0 with root cluster# if in FAT32 */ 
 559                 clst 
= dj
->fs
->dirbase
; 
 561         if (clst 
== 0) {        /* Static table */ 
 563                 if (idx 
>= dj
->fs
->n_rootdir
)           /* Index is out of range */ 
 565                 dj
->sect 
= dj
->fs
->dirbase 
+ idx 
/ (SS(dj
->fs
) / 32);   /* Sector# */ 
 567         else {                          /* Dynamic table */ 
 568                 ic 
= SS(dj
->fs
) / 32 * dj
->fs
->csize
;   /* Entries per cluster */ 
 569                 while (idx 
>= ic
) {     /* Follow cluster chain */ 
 570                         clst 
= get_fat(dj
->fs
, clst
);                           /* Get next cluster */ 
 571                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
;     /* Disk error */ 
 572                         if (clst 
< 2 || clst 
>= dj
->fs
->max_clust
)      /* Reached to end of table or int error */ 
 577                 dj
->sect 
= clust2sect(dj
->fs
, clst
) + idx 
/ (SS(dj
->fs
) / 32);  /* Sector# */ 
 580         dj
->dir 
= dj
->fs
->win 
+ (idx 
% (SS(dj
->fs
) / 32)) * 32; /* Ptr to the entry in the sector */ 
 582         return FR_OK
;   /* Seek succeeded */ 
 588 /*-----------------------------------------------------------------------*/ 
 589 /* Directory handling - Move directory index next                        */ 
 590 /*-----------------------------------------------------------------------*/ 
 593 FRESULT 
dir_next (      /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */ 
 594         DIR *dj
,                /* Pointer to directory object */ 
 595         BOOL streach    
/* FALSE: Do not streach table, TRUE: Streach table if needed */ 
 603         if (!i 
|| !dj
->sect
)    /* Report EOT when index has reached 65535 */ 
 606         if (!(i 
% (SS(dj
->fs
) / 32))) { /* Sector changed? */ 
 607                 dj
->sect
++;                                     /* Next sector */ 
 609                 if (dj
->clust 
== 0) {   /* Static table */ 
 610                         if (i 
>= dj
->fs
->n_rootdir
)     /* Report EOT when end of table */ 
 613                 else {                                  /* Dynamic table */ 
 614                         if (((i 
/ (SS(dj
->fs
) / 32)) & (dj
->fs
->csize 
- 1)) == 0) {     /* Cluster changed? */ 
 615                                 clst 
= get_fat(dj
->fs
, dj
->clust
);                              /* Get next cluster */ 
 616                                 if (clst 
<= 1) return FR_INT_ERR
; 
 617                                 if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
 618                                 if (clst 
>= dj
->fs
->max_clust
) {                                /* When it reached end of dynamic table */ 
 621                                         if (!streach
) return FR_NO_FILE
;                        /* When do not streach, report EOT */ 
 622                                         clst 
= create_chain(dj
->fs
, dj
->clust
);         /* Streach cluster chain */ 
 623                                         if (clst 
== 0) return FR_DENIED
;                        /* No free cluster */ 
 624                                         if (clst 
== 1) return FR_INT_ERR
; 
 625                                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
 626                                         /* Clean-up streached table */ 
 627                                         if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; /* Flush active window */ 
 628                                         mem_set(dj
->fs
->win
, 0, SS(dj
->fs
));                    /* Clear window buffer */ 
 629                                         dj
->fs
->winsect 
= clust2sect(dj
->fs
, clst
);     /* Cluster start sector */ 
 630                                         for (c 
= 0; c 
< dj
->fs
->csize
; c
++) {           /* Fill the new cluster with 0 */ 
 632                                                 if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; 
 635                                         dj
->fs
->winsect 
-= c
;                                           /* Rewind window address */ 
 637                                         return FR_NO_FILE
;                      /* Report EOT */ 
 640                                 dj
->clust 
= clst
;                               /* Initialize data for new cluster */ 
 641                                 dj
->sect 
= clust2sect(dj
->fs
, clst
); 
 647         dj
->dir 
= dj
->fs
->win 
+ (i 
% (SS(dj
->fs
) / 32)) * 32; 
 655 /*-----------------------------------------------------------------------*/ 
 656 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */ 
 657 /*-----------------------------------------------------------------------*/ 
 660 const BYTE LfnOfs
[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};      /* Offset of LFN chars in the directory entry */ 
 664 BOOL 
cmp_lfn (                  /* TRUE:Matched, FALSE:Not matched */ 
 665         WCHAR 
*lfnbuf
,          /* Pointer to the LFN to be compared */ 
 666         BYTE 
*dir                       
/* Pointer to the directory entry containing a part of LFN */ 
 673         i 
= ((dir
[LDIR_Ord
] & 0xBF) - 1) * 13;  /* Get offset in the LFN buffer */ 
 676                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);    /* Pick an LFN character from the entry */ 
 677                 if (wc
) {       /* Last char has not been processed */ 
 678                         wc 
= ff_wtoupper(uc
);           /* Convert it to upper case */ 
 679                         if (i 
>= _MAX_LFN 
|| wc 
!= ff_wtoupper(lfnbuf
[i
++]))    /* Compare it */ 
 680                                 return FALSE
;                   /* Not matched */ 
 682                         if (uc 
!= 0xFFFF) return FALSE
; /* Check filler */ 
 684         } while (++s 
< 13);                             /* Repeat until all chars in the entry are checked */ 
 686         if ((dir
[LDIR_Ord
] & 0x40) && wc 
&& lfnbuf
[i
])  /* Last segment matched but different length */ 
 689         return TRUE
;                                    /* The part of LFN matched */ 
 695 BOOL 
pick_lfn (                 /* TRUE:Succeeded, FALSE:Buffer overflow */ 
 696         WCHAR 
*lfnbuf
,          /* Pointer to the Unicode-LFN buffer */ 
 697         BYTE 
*dir                       
/* Pointer to the directory entry */ 
 704         i 
= ((dir
[LDIR_Ord
] & 0x3F) - 1) * 13;  /* Offset in the LFN buffer */ 
 708                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);                    /* Pick an LFN character from the entry */ 
 709                 if (wc
) {       /* Last char has not been processed */ 
 710                         if (i 
>= _MAX_LFN
) return FALSE
;        /* Buffer overflow? */ 
 711                         lfnbuf
[i
++] = wc 
= uc
;                          /* Store it */ 
 713                         if (uc 
!= 0xFFFF) return FALSE
;         /* Check filler */ 
 715         } while (++s 
< 13);                                             /* Read all character in the entry */ 
 717         if (dir
[LDIR_Ord
] & 0x40) {                             /* Put terminator if it is the last LFN part */ 
 718                 if (i 
>= _MAX_LFN
) return FALSE
;        /* Buffer overflow? */ 
 729         const WCHAR 
*lfnbuf
,    /* Pointer to the LFN buffer */ 
 730         BYTE 
*dir
,                              /* Pointer to the directory entry */ 
 731         BYTE ord
,                               /* LFN order (1-20) */ 
 732         BYTE sum                                
/* SFN sum */ 
 739         dir
[LDIR_Chksum
] = sum
;                 /* Set check sum */ 
 740         dir
[LDIR_Attr
] = AM_LFN
;                /* Set attribute. LFN entry */ 
 742         ST_WORD(dir
+LDIR_FstClusLO
, 0); 
 744         i 
= (ord 
- 1) * 13;                             /* Get offset in the LFN buffer */ 
 747                 if (wc 
!= 0xFFFF) wc 
= lfnbuf
[i
++];     /* Get an effective char */ 
 748                 ST_WORD(dir
+LfnOfs
[s
], wc
);     /* Put it */ 
 749                 if (!wc
) wc 
= 0xFFFF;           /* Padding chars following last char */ 
 751         if (wc 
== 0xFFFF || !lfnbuf
[i
]) ord 
|= 0x40;    /* Bottom LFN part is the start of LFN sequence */ 
 752         dir
[LDIR_Ord
] = ord
;                    /* Set the LFN order */ 
 760 /*-----------------------------------------------------------------------*/ 
 761 /* Create numbered name                                                  */ 
 762 /*-----------------------------------------------------------------------*/ 
 765         BYTE 
*dst
,                      /* Pointer to genartated SFN */ 
 766         const BYTE 
*src
,        /* Pointer to source SFN to be modified */ 
 767         const WCHAR 
*lfn
,       /* Pointer to LFN */ 
 768         WORD num                        
/* Sequense number */ 
 775         mem_cpy(dst
, src
, 11); 
 777         if (num 
> 5) {  /* On many collisions, generate a hash number instead of sequencial number */ 
 778                 do num 
= (num 
>> 1) + (num 
<< 15) + (WORD
)*lfn
++; while (*lfn
); 
 784                 ns
[i
--] = (num 
% 10) + '0'; 
 789         /* Append the number */ 
 790         for (j 
= 0; j 
< i 
&& dst
[j
] != ' '; j
++) { 
 791                 if (IsDBCS1(dst
[j
])) { 
 792                         if (j 
== i 
- 1) break; 
 797                 dst
[j
++] = (i 
< 8) ? ns
[i
++] : ' '; 
 805 /*-----------------------------------------------------------------------*/ 
 806 /* Calculate sum of an SFN                                               */ 
 807 /*-----------------------------------------------------------------------*/ 
 811         const BYTE 
*dir         
/* Ptr to directory entry */ 
 817         do sum 
= (sum 
>> 1) + (sum 
<< 7) + *dir
++; while (--n
); 
 825 /*-----------------------------------------------------------------------*/ 
 826 /* Directory handling - Find an object in the directory                  */ 
 827 /*-----------------------------------------------------------------------*/ 
 831         DIR *dj                 
/* Pointer to the directory object linked to the file name */ 
 840         res 
= dir_seek(dj
, 0);                  /* Rewind directory object */ 
 841         if (res 
!= FR_OK
) return res
; 
 847                 res 
= move_window(dj
->fs
, dj
->sect
); 
 848                 if (res 
!= FR_OK
) break; 
 849                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
 851                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
 852 #if _USE_LFN    /* LFN configuration */ 
 853                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
 854                 if (c 
== 0xE5 || ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {       /* An entry without valid data */ 
 857                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
 859                                         if (c 
& 0x40) {         /* Is it start of LFN sequence? */ 
 860                                                 sum 
= dir
[LDIR_Chksum
]; 
 861                                                 c 
&= 0xBF; ord 
= c
;     /* LFN start order */ 
 862                                                 dj
->lfn_idx 
= dj
->index
; 
 864                                         /* Check validity of the LFN entry and compare it with given name */ 
 865                                         ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && cmp_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
 867                         } else {                                        /* An SFN entry is found */ 
 868                                 if (!ord 
&& sum 
== sum_sfn(dir
)) break; /* LFN matched? */ 
 869                                 ord 
= 0xFF; dj
->lfn_idx 
= 0xFFFF;       /* Reset LFN sequence */ 
 870                                 if (!(dj
->fn
[NS
] & NS_LOSS
) && !mem_cmp(dir
, dj
->fn
, 11)) break;        /* SFN matched? */ 
 873 #else           /* Non LFN configuration */ 
 874                 if (!(dir
[DIR_Attr
] & AM_VOL
) && !mem_cmp(dir
, dj
->fn
, 11)) /* Is it a valid entry? */ 
 877                 res 
= dir_next(dj
, FALSE
);              /* Next entry */ 
 878         } while (res 
== FR_OK
); 
 886 /*-----------------------------------------------------------------------*/ 
 887 /* Read an object from the directory                                     */ 
 888 /*-----------------------------------------------------------------------*/ 
 889 #if _FS_MINIMIZE <= 1 
 892         DIR *dj                 
/* Pointer to the directory object that pointing the entry to be read */ 
 898         BYTE a
, ord 
= 0xFF, sum 
= 0xFF; 
 903                 res 
= move_window(dj
->fs
, dj
->sect
); 
 904                 if (res 
!= FR_OK
) break; 
 905                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
 907                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
 908 #if _USE_LFN    /* LFN configuration */ 
 909                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
 910                 if (c 
== 0xE5 || (!_FS_RPATH 
&& c 
== '.') || ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {   /* An entry without valid data */ 
 913                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
 914                                 if (c 
& 0x40) {                 /* Is it start of LFN sequence? */ 
 915                                         sum 
= dir
[LDIR_Chksum
]; 
 917                                         dj
->lfn_idx 
= dj
->index
; 
 919                                 /* Check LFN validity and capture it */ 
 920                                 ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && pick_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
 921                         } else {                                        /* An SFN entry is found */ 
 922                                 if (ord 
|| sum 
!= sum_sfn(dir
)) /* Is there a valid LFN? */ 
 923                                         dj
->lfn_idx 
= 0xFFFF;           /* It has no LFN. */ 
 927 #else           /* Non LFN configuration */ 
 928                 if (c 
!= 0xE5 && (_FS_RPATH 
|| c 
!= '.') && !(dir
[DIR_Attr
] & AM_VOL
))  /* Is it a valid entry? */ 
 931                 res 
= dir_next(dj
, FALSE
);                              /* Next entry */ 
 932                 if (res 
!= FR_OK
) break; 
 935         if (res 
!= FR_OK
) dj
->sect 
= 0; 
 943 /*-----------------------------------------------------------------------*/ 
 944 /* Register an object to the directory                                   */ 
 945 /*-----------------------------------------------------------------------*/ 
 948 FRESULT 
dir_register (  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ 
 949         DIR *dj                         
/* Target directory with object name to be created */ 
 954 #if _USE_LFN    /* LFN configuration */ 
 956         BYTE sn
[12], *fn
, sum
; 
 960         fn 
= dj
->fn
; lfn 
= dj
->lfn
; 
 963         if (_FS_RPATH 
&& (sn
[NS
] & NS_DOT
)) return FR_INVALID_NAME
;     /* Cannot create dot entry */ 
 965         if (sn
[NS
] & NS_LOSS
) {                 /* When LFN is out of 8.3 format, generate a numbered name */ 
 966                 fn
[NS
] = 0; dj
->lfn 
= NULL
;                     /* Find only SFN */ 
 967                 for (n 
= 1; n 
< 100; n
++) { 
 968                         gen_numname(fn
, sn
, lfn
, n
);    /* Generate a numbered name */ 
 969                         res 
= dir_find(dj
);                             /* Check if the name collides with existing SFN */ 
 970                         if (res 
!= FR_OK
) break; 
 972                 if (n 
== 100) return FR_DENIED
;         /* Abort if too many collisions */ 
 973                 if (res 
!= FR_NO_FILE
) return res
;      /* Abort if the result is other than 'not collided' */ 
 974                 fn
[NS
] = sn
[NS
]; dj
->lfn 
= lfn
; 
 977         if (sn
[NS
] & NS_LFN
) {                  /* When LFN is to be created, reserve reserve an SFN + LFN entries. */ 
 978                 for (ne 
= 0; lfn
[ne
]; ne
++) ; 
 980         } else {                                                /* Otherwise reserve only an SFN entry. */ 
 984         /* Reserve contiguous entries */ 
 985         res 
= dir_seek(dj
, 0); 
 986         if (res 
!= FR_OK
) return res
; 
 989                 res 
= move_window(dj
->fs
, dj
->sect
); 
 990                 if (res 
!= FR_OK
) break; 
 991                 c 
= *dj
->dir
;                           /* Check the entry status */ 
 992                 if (c 
== 0xE5 || c 
== 0) {      /* Is it a blank entry? */ 
 993                         if (n 
== 0) is 
= dj
->index
;     /* First index of the contigulus entry */ 
 994                         if (++n 
== ne
) break;   /* A contiguous entry that requiered count is found */ 
 996                         n 
= 0;                                  /* Not a blank entry. Restart to search */ 
 998                 res 
= dir_next(dj
, TRUE
);       /* Next entry with table streach */ 
 999         } while (res 
== FR_OK
); 
1001         if (res 
== FR_OK 
&& ne 
> 1) {   /* Initialize LFN entry if needed */ 
1002                 res 
= dir_seek(dj
, is
); 
1004                         sum 
= sum_sfn(dj
->fn
);  /* Sum of the SFN tied to the LFN */ 
1006                         do {                                    /* Store LFN entries in bottom first */ 
1007                                 res 
= move_window(dj
->fs
, dj
->sect
); 
1008                                 if (res 
!= FR_OK
) break; 
1009                                 fit_lfn(dj
->lfn
, dj
->dir
, (BYTE
)ne
, sum
); 
1011                                 res 
= dir_next(dj
, FALSE
);      /* Next entry */ 
1012                         } while (res 
== FR_OK 
&& --ne
); 
1016 #else   /* Non LFN configuration */ 
1017         res 
= dir_seek(dj
, 0); 
1019                 do {    /* Find a blank entry for the SFN */ 
1020                         res 
= move_window(dj
->fs
, dj
->sect
); 
1021                         if (res 
!= FR_OK
) break; 
1023                         if (c 
== 0xE5 || c 
== 0) break; /* Is it a blank entry? */ 
1024                         res 
= dir_next(dj
, TRUE
);               /* Next entry with table streach */ 
1025                 } while (res 
== FR_OK
); 
1029         if (res 
== FR_OK
) {             /* Initialize the SFN entry */ 
1030                 res 
= move_window(dj
->fs
, dj
->sect
); 
1033                         mem_set(dir
, 0, 32);            /* Clean the entry */ 
1034                         mem_cpy(dir
, dj
->fn
, 11);       /* Put SFN */ 
1035                         dir
[DIR_NTres
] = *(dj
->fn
+NS
) & (NS_BODY 
| NS_EXT
);     /* Put NT flag */ 
1042 #endif /* !_FS_READONLY */ 
1047 /*-----------------------------------------------------------------------*/ 
1048 /* Remove an object from the directory                                   */ 
1049 /*-----------------------------------------------------------------------*/ 
1050 #if !_FS_READONLY && !_FS_MINIMIZE 
1052 FRESULT 
dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */ 
1053         DIR *dj                         
/* Directory object pointing the entry to be removed */ 
1057 #if _USE_LFN    /* LFN configuration */ 
1060         i 
= dj
->index
;  /* SFN index */ 
1061         res 
= dir_seek(dj
, (WORD
)((dj
->lfn_idx 
== 0xFFFF) ? i 
: dj
->lfn_idx
));  /* Goto the SFN or top of the LFN entries */ 
1064                         res 
= move_window(dj
->fs
, dj
->sect
); 
1065                         if (res 
!= FR_OK
) break; 
1066                         *dj
->dir 
= 0xE5;                        /* Mark the entry "deleted" */ 
1068                         if (dj
->index 
>= i
) break;      /* When reached SFN, all entries of the object has been deleted. */ 
1069                         res 
= dir_next(dj
, FALSE
);      /* Next entry */ 
1070                 } while (res 
== FR_OK
); 
1071                 if (res 
== FR_NO_FILE
) res 
= FR_INT_ERR
; 
1074 #else                   /* Non LFN configuration */ 
1075         res 
= dir_seek(dj
, dj
->index
); 
1077                 res 
= move_window(dj
->fs
, dj
->sect
); 
1079                         *dj
->dir 
= 0xE5;                        /* Mark the entry "deleted" */ 
1087 #endif /* !_FS_READONLY */ 
1092 /*-----------------------------------------------------------------------*/ 
1093 /* Pick a segment and create the object name in directory form           */ 
1094 /*-----------------------------------------------------------------------*/ 
1097 FRESULT 
create_name ( 
1098         DIR *dj
,                        /* Pointer to the directory object */ 
1099         const XCHAR 
**path      
/* Pointer to pointer to the segment in the path string */ 
1103         static const BYTE cvt
[] = _EXCVT
; 
1106 #if _USE_LFN    /* LFN configuration */ 
1112         /* Create LFN in Unicode */ 
1117                 w 
= p
[si
++];                                    /* Get a character */ 
1118                 if (w 
< ' ' || w 
== '/' || w 
== '\\') break;    /* Break on end of segment */ 
1119                 if (di 
>= _MAX_LFN
)                             /* Reject too long name */ 
1120                         return FR_INVALID_NAME
; 
1123                 if (IsDBCS1(w
)) {                               /* If it is a DBC 1st byte */ 
1124                         b 
= p
[si
++];                            /* Get 2nd byte */ 
1125                         if (!IsDBCS2(b
))                        /* Reject invalid code for DBC */ 
1126                                 return FR_INVALID_NAME
; 
1129                 w 
= ff_convert(w
, 1);                   /* Convert OEM to Unicode */ 
1130                 if (!w
) return FR_INVALID_NAME
; /* Reject invalid code */ 
1132                 if (w 
< 0x80 && chk_chr("\"*:<>\?|\x7F", w
)) /* Reject illegal chars for LFN */ 
1133                         return FR_INVALID_NAME
; 
1134                 lfn
[di
++] = w
;                                  /* Store the Unicode char */ 
1136         *path 
= &p
[si
];                                         /* Rerurn pointer to the next segment */ 
1137         cf 
= (w 
< ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1139         if ((di 
== 1 && lfn
[di 
- 1] == '.') || /* Is this a dot entry? */ 
1140                 (di 
== 2 && lfn
[di 
- 1] == '.' && lfn
[di 
- 2] == '.')) { 
1142                 for (i 
= 0; i 
< 11; i
++) 
1143                         dj
->fn
[i
] = (i 
< di
) ? 
'.' : ' '; 
1144                 dj
->fn
[i
] = cf 
| NS_DOT
;                /* This is a dot entry */ 
1148         while (di
) {                                            /* Strip trailing spaces and dots */ 
1150                 if (w 
!= ' ' && w 
!= '.') break; 
1153         if (!di
) return FR_INVALID_NAME
;        /* Reject null string */ 
1155         lfn
[di
] = 0;                                            /* LFN is created */ 
1157         /* Create SFN in directory form */ 
1158         mem_set(dj
->fn
, ' ', 11); 
1159         for (si 
= 0; lfn
[si
] == ' ' || lfn
[si
] == '.'; si
++) ;  /* Strip leading spaces and dots */ 
1160         if (si
) cf 
|= NS_LOSS 
| NS_LFN
; 
1161         while (di 
&& lfn
[di 
- 1] != '.') di
--;  /* Find extension (di<=si: no extension) */ 
1165                 w 
= lfn
[si
++];                                  /* Get an LFN char */ 
1166                 if (!w
) break;                                  /* Break on enf of the LFN */ 
1167                 if (w 
== ' ' || (w 
== '.' && si 
!= di
)) {       /* Remove spaces and dots */ 
1168                         cf 
|= NS_LOSS 
| NS_LFN
; continue; 
1171                 if (i 
>= ni 
|| si 
== di
) {              /* Extension or end of SFN */ 
1172                         if (ni 
== 11) {                         /* Long extension */ 
1173                                 cf 
|= NS_LOSS 
| NS_LFN
; break; 
1175                         if (si 
!= di
) cf 
|= NS_LOSS 
| NS_LFN
;   /* Out of 8.3 format */ 
1176                         if (si 
> di
) break;                     /* No extension */ 
1177                         si 
= di
; i 
= 8; ni 
= 11;        /* Enter extension section */ 
1181                 if (w 
>= 0x80) {                                /* Non ASCII char */ 
1183                         w 
= ff_convert(w
, 0);           /* Unicode -> OEM code */ 
1184                         if (w
) w 
= cvt
[w 
- 0x80];       /* Convert extended char to upper (SBCS) */ 
1186                         w 
= ff_convert(ff_wtoupper(w
), 0);      /* Upper converted Unicode -> OEM code */ 
1188                         cf 
|= NS_LFN
;                           /* Force create LFN entry */ 
1191                 if (_DF1S 
&& w 
>= 0x100) {              /* Double byte char */ 
1193                                 cf 
|= NS_LOSS 
| NS_LFN
; i 
= ni
; continue; 
1195                         dj
->fn
[i
++] = (BYTE
)(w 
>> 8); 
1196                 } else {                                                /* Single byte char */ 
1197                         if (!w 
|| chk_chr("+,;[=]", w
)) {               /* Replace illegal chars for SFN */ 
1198                                 w 
= '_'; cf 
|= NS_LOSS 
| NS_LFN
;        /* Lossy conversion */ 
1200                                 if (IsUpper(w
)) {               /* ASCII large capital */ 
1203                                         if (IsLower(w
)) {       /* ASCII small capital */ 
1209                 dj
->fn
[i
++] = (BYTE
)w
; 
1212         if (dj
->fn
[0] == 0xE5) dj
->fn
[0] = 0x05;        /* If the first char collides with deleted mark, replace it with 0x05 */ 
1214         if (ni 
== 8) b 
<<= 2; 
1215         if ((b 
& 0x0C) == 0x0C || (b 
& 0x03) == 0x03)   /* Create LFN entry when there are composite capitals */ 
1217         if (!(cf 
& NS_LFN
)) {                                           /* When LFN is in 8.3 format without extended char, NT flags are created */ 
1218                 if ((b 
& 0x03) == 0x01) cf 
|= NS_EXT
;   /* NT flag (Extension has only small capital) */ 
1219                 if ((b 
& 0x0C) == 0x04) cf 
|= NS_BODY
;  /* NT flag (Filename has only small capital) */ 
1222         dj
->fn
[NS
] = cf
;        /* SFN is created */ 
1227 #else   /* Non-LFN configuration */ 
1232         /* Create file name in directory form */ 
1234         mem_set(sfn
, ' ', 11); 
1235         si 
= i 
= b 
= 0; ni 
= 8; 
1238         if (p
[si
] == '.') { /* Is this a dot entry? */ 
1241                         if (c 
!= '.' || si 
>= 3) break; 
1244                 if (c 
!= '/' && c 
!= '\\' && c 
> ' ') return FR_INVALID_NAME
; 
1245                 *path 
= &p
[si
];                                                                 /* Rerurn pointer to the next segment */ 
1246                 sfn
[NS
] = (c 
<= ' ') ? NS_LAST 
| NS_DOT 
: NS_DOT
;       /* Set last segment flag if end of path */ 
1252                 if (c 
<= ' ' || c 
== '/' || c 
== '\\') break;   /* Break on end of segment */ 
1253                 if (c 
== '.' || i 
>= ni
) { 
1254                         if (ni 
!= 8 || c 
!= '.') return FR_INVALID_NAME
; 
1258                 if (c 
>= 0x80) {                                /* Extended char */ 
1260                         c 
= cvt
[c 
- 0x80];                      /* Convert extend char (SBCS) */ 
1262                         b 
|= 3;                                         /* Eliminate NT flag if ext char is exist */ 
1263 #if !_DF1S      /* ASCII only cfg */ 
1264                         return FR_INVALID_NAME
; 
1268                 if (IsDBCS1(c
)) {                               /* DBC 1st byte? */ 
1269                         d 
= p
[si
++];                            /* Get 2nd byte */ 
1270                         if (!IsDBCS2(d
) || i 
>= ni 
- 1) /* Reject invalid DBC */ 
1271                                 return FR_INVALID_NAME
; 
1274                 } else {                                                /* Single byte code */ 
1275                         if (chk_chr(" \"*+,[=]|\x7F", c
))       /* Reject illegal chrs for SFN */ 
1276                                 return FR_INVALID_NAME
; 
1277                         if (IsUpper(c
)) {                       /* ASCII large capital? */ 
1280                                 if (IsLower(c
)) {               /* ASCII small capital? */ 
1287         *path 
= &p
[si
];                                         /* Rerurn pointer to the next segment */ 
1288         c 
= (c 
<= ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1290         if (!i
) return FR_INVALID_NAME
;         /* Reject null string */ 
1291         if (sfn
[0] == 0xE5) sfn
[0] = 0x05;      /* When first char collides with 0xE5, replace it with 0x05 */ 
1293         if (ni 
== 8) b 
<<= 2; 
1294         if ((b 
& 0x03) == 0x01) c 
|= NS_EXT
;    /* NT flag (Extension has only small capital) */ 
1295         if ((b 
& 0x0C) == 0x04) c 
|= NS_BODY
;   /* NT flag (Filename has only small capital) */ 
1297         sfn
[NS
] = c
;            /* Store NT flag, File name is created */ 
1306 /*-----------------------------------------------------------------------*/ 
1307 /* Get file information from directory entry                             */ 
1308 /*-----------------------------------------------------------------------*/ 
1309 #if _FS_MINIMIZE <= 1 
1311 void get_fileinfo (             /* No return code */ 
1312         DIR *dj
,                        /* Pointer to the directory object */ 
1313         FILINFO 
*fno            
/* Pointer to the file information to be filled */ 
1324                 nt 
= dir
[DIR_NTres
];            /* NT flag */ 
1325                 for (i 
= 0; i 
< 8; i
++) {       /* Copy name body */ 
1327                         if (c 
== ' ') break; 
1328                         if (c 
== 0x05) c 
= 0xE5; 
1329                         if (_USE_LFN 
&& (nt 
& NS_BODY
) && IsUpper(c
)) c 
+= 0x20; 
1332                 if (dir
[8] != ' ') {            /* Copy name extension */ 
1334                         for (i 
= 8; i 
< 11; i
++) { 
1336                                 if (c 
== ' ') break; 
1337                                 if (_USE_LFN 
&& (nt 
& NS_EXT
) && IsUpper(c
)) c 
+= 0x20; 
1341                 fno
->fattrib 
= dir
[DIR_Attr
];                           /* Attribute */ 
1342                 fno
->fsize 
= LD_DWORD(dir
+DIR_FileSize
);        /* Size */ 
1343                 fno
->fdate 
= LD_WORD(dir
+DIR_WrtDate
);          /* Date */ 
1344                 fno
->ftime 
= LD_WORD(dir
+DIR_WrtTime
);          /* Time */ 
1350                 XCHAR 
*tp 
= fno
->lfname
; 
1354                 if (dj
->sect 
&& dj
->lfn_idx 
!= 0xFFFF) {/* Get LFN if available */ 
1356                         while ((w 
= *lfn
++) != 0) {                     /* Get an LFN char */ 
1358                                 w 
= ff_convert(w
, 0);                   /* Unicode -> OEM conversion */ 
1359                                 if (!w
) { i 
= 0; break; }               /* Could not convert, no LFN */ 
1360                                 if (_DF1S 
&& w 
>= 0x100)                /* Put 1st byte if it is a DBC */ 
1361                                         tp
[i
++] = (XCHAR
)(w 
>> 8); 
1363                                 if (i 
>= fno
->lfsize 
- 1) { i 
= 0; break; }     /* Buffer overrun, no LFN */ 
1367                 tp
[i
] = 0;      /* Terminator */ 
1371 #endif /* _FS_MINIMIZE <= 1 */ 
1376 /*-----------------------------------------------------------------------*/ 
1377 /* Follow a file path                                                    */ 
1378 /*-----------------------------------------------------------------------*/ 
1381 FRESULT 
follow_path (   /* FR_OK(0): successful, !=0: error code */ 
1382         DIR *dj
,                        /* Directory object to return last directory and found object */ 
1383         const XCHAR 
*path       
/* Full-path string to find a file or directory */ 
1390         while (!_USE_LFN 
&& *path 
== ' ') path
++;       /* Skip leading spaces */ 
1392         if (*path 
== '/' || *path 
== '\\') { /* There is a heading separator */ 
1393                 path
++; dj
->sclust 
= 0;         /* Strip it and start from the root dir */ 
1394         } else {                                                        /* No heading saparator */ 
1395                 dj
->sclust 
= dj
->fs
->cdir
;      /* Start from the current dir */ 
1398         if (*path 
== '/' || *path 
== '\\')      /* Strip heading separator if exist */ 
1400         dj
->sclust 
= 0;                                         /* Start from the root dir */ 
1403         if ((UINT
)*path 
< ' ') {                        /* Null path means the start directory itself */ 
1404                 res 
= dir_seek(dj
, 0); 
1407         } else {                                                        /* Follow path */ 
1409                         res 
= create_name(dj
, &path
);   /* Get a segment */ 
1410                         if (res 
!= FR_OK
) break; 
1411                         res 
= dir_find(dj
);                             /* Find it */ 
1412                         last 
= *(dj
->fn
+NS
) & NS_LAST
; 
1413                         if (res 
!= FR_OK
) {                             /* Could not find the object */ 
1414                                 if (res 
== FR_NO_FILE 
&& !last
) 
1418                         if (last
) break;                                /* Last segment match. Function completed. */ 
1419                         dir 
= dj
->dir
;                                  /* There is next segment. Follow the sub directory */ 
1420                         if (!(dir
[DIR_Attr
] & AM_DIR
)) { /* Cannot follow because it is a file */ 
1421                                 res 
= FR_NO_PATH
; break; 
1423                         dj
->sclust 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
1433 /*-----------------------------------------------------------------------*/ 
1434 /* Load boot record and check if it is an FAT boot record                */ 
1435 /*-----------------------------------------------------------------------*/ 
1438 BYTE 
check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */ 
1439         FATFS 
*fs
,      /* File system object */ 
1440         DWORD sect      
/* Sector# (lba) to check if it is an FAT boot record or not */ 
1443         if (disk_read(fs
->drive
, fs
->win
, sect
, 1) != RES_OK
)   /* Load boot record */ 
1445         if (LD_WORD(&fs
->win
[BS_55AA
]) != 0xAA55)               /* Check record signature (always placed at offset 510 even if the sector size is >512) */ 
1448         if ((LD_DWORD(&fs
->win
[BS_FilSysType
]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ 
1450         if ((LD_DWORD(&fs
->win
[BS_FilSysType32
]) & 0xFFFFFF) == 0x544146) 
1459 /*-----------------------------------------------------------------------*/ 
1460 /* Make sure that the file system is valid                               */ 
1461 /*-----------------------------------------------------------------------*/ 
1464 FRESULT 
chk_mounted (   /* FR_OK(0): successful, !=0: any error occured */ 
1465         const XCHAR 
**path
,     /* Pointer to pointer to the path name (drive number) */ 
1466         FATFS 
**rfs
,            /* Pointer to pointer to the found file system object */ 
1467         BYTE chk_wp                     
/* !=0: Check media write protection for write access */ 
1473         DWORD bsect
, fsize
, tsect
, mclst
; 
1474         const XCHAR 
*p 
= *path
; 
1477         /* Get logical drive number from the path name */ 
1478         vol 
= p
[0] - '0';                               /* Is there a drive number? */ 
1479         if (vol 
<= 9 && p
[1] == ':') {  /* Found a drive number, get and strip it */ 
1480                 p 
+= 2; *path 
= p
;                      /* Return pointer to the path name */ 
1481         } else {                                                /* No drive number is given */ 
1483                 vol 
= Drive
;                            /* Use current drive */ 
1485                 vol 
= 0;                                        /* Use drive 0 */ 
1489         /* Check if the logical drive is valid or not */ 
1490         if (vol 
>= _DRIVES
)                     /* Is the drive number valid? */ 
1491                 return FR_INVALID_DRIVE
; 
1492         *rfs 
= fs 
= FatFs
[vol
];                 /* Returen pointer to the corresponding file system object */ 
1493         if (!fs
) return FR_NOT_ENABLED
; /* Is the file system object available? */ 
1495         ENTER_FF(fs
);                                   /* Lock file system */ 
1497         if (fs
->fs_type
) {                              /* If the logical drive has been mounted */ 
1498                 stat 
= disk_status(fs
->drive
); 
1499                 if (!(stat 
& STA_NOINIT
)) {     /* and the physical drive is kept initialized (has not been changed), */ 
1501                         if (chk_wp 
&& (stat 
& STA_PROTECT
))     /* Check write protection if needed */ 
1502                                 return FR_WRITE_PROTECTED
; 
1504                         return FR_OK
;                   /* The file system object is valid */ 
1508         /* The logical drive must be mounted. Following code attempts to mount the volume */ 
1510         fs
->fs_type 
= 0;                                        /* Clear the file system object */ 
1511         fs
->drive 
= (BYTE
)LD2PD(vol
);           /* Bind the logical drive and a physical drive */ 
1512         stat 
= disk_initialize(fs
->drive
);      /* Initialize low level disk I/O layer */ 
1513         if (stat 
& STA_NOINIT
)                          /* Check if the drive is ready */ 
1514                 return FR_NOT_READY
; 
1515 #if _MAX_SS != 512                                              /* Get disk sector size if needed */ 
1516         if (disk_ioctl(fs
->drive
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK 
|| SS(fs
) > _MAX_SS
) 
1517                 return FR_NO_FILESYSTEM
; 
1520         if (chk_wp 
&& (stat 
& STA_PROTECT
))     /* Check disk write protection if needed */ 
1521                 return FR_WRITE_PROTECTED
; 
1523         /* Search FAT partition on the drive */ 
1524         fmt 
= check_fs(fs
, bsect 
= 0);          /* Check sector 0 as an SFD format */ 
1525         if (fmt 
== 1) {                                         /* Not an FAT boot record, it may be patitioned */ 
1526                 /* Check a partition listed in top of the partition table */ 
1527                 tbl 
= &fs
->win
[MBR_Table 
+ LD2PT(vol
) * 16];    /* Partition table */ 
1528                 if (tbl
[4]) {                                                                   /* Is the partition existing? */ 
1529                         bsect 
= LD_DWORD(&tbl
[8]);                                      /* Partition offset in LBA */ 
1530                         fmt 
= check_fs(fs
, bsect
);                                      /* Check the partition */ 
1533         if (fmt 
== 3) return FR_DISK_ERR
; 
1534         if (fmt 
|| LD_WORD(fs
->win
+BPB_BytsPerSec
) != SS(fs
))   /* No valid FAT patition is found */ 
1535                 return FR_NO_FILESYSTEM
; 
1537         /* Initialize the file system object */ 
1538         fsize 
= LD_WORD(fs
->win
+BPB_FATSz16
);                           /* Number of sectors per FAT */ 
1539         if (!fsize
) fsize 
= LD_DWORD(fs
->win
+BPB_FATSz32
); 
1540         fs
->sects_fat 
= fsize
; 
1541         fs
->n_fats 
= fs
->win
[BPB_NumFATs
];                                      /* Number of FAT copies */ 
1542         fsize 
*= fs
->n_fats
;                                                            /* (Number of sectors in FAT area) */ 
1543         fs
->fatbase 
= bsect 
+ LD_WORD(fs
->win
+BPB_RsvdSecCnt
); /* FAT start sector (lba) */ 
1544         fs
->csize 
= fs
->win
[BPB_SecPerClus
];                            /* Number of sectors per cluster */ 
1545         fs
->n_rootdir 
= LD_WORD(fs
->win
+BPB_RootEntCnt
);        /* Nmuber of root directory entries */ 
1546         tsect 
= LD_WORD(fs
->win
+BPB_TotSec16
);                          /* Number of sectors on the volume */ 
1547         if (!tsect
) tsect 
= LD_DWORD(fs
->win
+BPB_TotSec32
); 
1548         fs
->max_clust 
= mclst 
= (tsect                                          
/* Last cluster# + 1 (Number of clusters + 2) */ 
1549                 - LD_WORD(fs
->win
+BPB_RsvdSecCnt
) - fsize 
- fs
->n_rootdir 
/ (SS(fs
)/32) 
1552         fmt 
= FS_FAT12
;                                                                         /* Determine the FAT sub type */ 
1553         if (mclst 
>= 0xFF7) fmt 
= FS_FAT16
;                                     /* Number of clusters >= 0xFF5 */ 
1554         if (mclst 
>= 0xFFF7) fmt 
= FS_FAT32
;                            /* Number of clusters >= 0xFFF5 */ 
1556         if (fmt 
== FS_FAT32
) 
1557                 fs
->dirbase 
= LD_DWORD(fs
->win
+BPB_RootClus
);   /* Root directory start cluster */ 
1559                 fs
->dirbase 
= fs
->fatbase 
+ fsize
;                              /* Root directory start sector (lba) */ 
1560         fs
->database 
= fs
->fatbase 
+ fsize 
+ fs
->n_rootdir 
/ (SS(fs
)/32);       /* Data start sector (lba) */ 
1563         /* Initialize allocation information */ 
1564         fs
->free_clust 
= 0xFFFFFFFF; 
1566         /* Get fsinfo if needed */ 
1567         if (fmt 
== FS_FAT32
) { 
1569                 fs
->fsi_sector 
= bsect 
+ LD_WORD(fs
->win
+BPB_FSInfo
); 
1570                 if (disk_read(fs
->drive
, fs
->win
, fs
->fsi_sector
, 1) == RES_OK 
&& 
1571                         LD_WORD(fs
->win
+BS_55AA
) == 0xAA55 && 
1572                         LD_DWORD(fs
->win
+FSI_LeadSig
) == 0x41615252 && 
1573                         LD_DWORD(fs
->win
+FSI_StrucSig
) == 0x61417272) { 
1574                         fs
->last_clust 
= LD_DWORD(fs
->win
+FSI_Nxt_Free
); 
1575                         fs
->free_clust 
= LD_DWORD(fs
->win
+FSI_Free_Count
); 
1579         fs
->fs_type 
= fmt
;              /* FAT sub-type */ 
1580         fs
->winsect 
= 0;                /* Invalidate sector cache */ 
1582         fs
->cdir 
= 0;                   /* Current directory (root dir) */ 
1584         fs
->id 
= ++Fsid
;                /* File system mount ID */ 
1592 /*-----------------------------------------------------------------------*/ 
1593 /* Check if the file/dir object is valid or not                          */ 
1594 /*-----------------------------------------------------------------------*/ 
1597 FRESULT 
validate (      /* FR_OK(0): The object is valid, !=0: Invalid */ 
1598         FATFS 
*fs
,              /* Pointer to the file system object */ 
1599         WORD id                 
/* Member id of the target object to be checked */ 
1602         if (!fs 
|| !fs
->fs_type 
|| fs
->id 
!= id
) 
1603                 return FR_INVALID_OBJECT
; 
1605         ENTER_FF(fs
);           /* Lock file system */ 
1607         if (disk_status(fs
->drive
) & STA_NOINIT
) 
1608                 return FR_NOT_READY
; 
1616 /*-------------------------------------------------------------------------- 
1620 --------------------------------------------------------------------------*/ 
1624 /*-----------------------------------------------------------------------*/ 
1625 /* Mount/Unmount a Locical Drive                                         */ 
1626 /*-----------------------------------------------------------------------*/ 
1629         BYTE vol
,               /* Logical drive number to be mounted/unmounted */ 
1630         FATFS 
*fs               
/* Pointer to new file system object (NULL for unmount)*/ 
1636         if (vol 
>= _DRIVES
)                             /* Check if the drive number is valid */ 
1637                 return FR_INVALID_DRIVE
; 
1638         rfs 
= FatFs
[vol
];                               /* Get current fs object */ 
1641 #if _FS_REENTRANT                                       /* Discard sync object of the current volume */ 
1642                 if (!ff_del_syncobj(rfs
->sobj
)) return FR_INT_ERR
; 
1644                 rfs
->fs_type 
= 0;                       /* Clear old fs object */ 
1648                 fs
->fs_type 
= 0;                        /* Clear new fs object */ 
1649 #if _FS_REENTRANT                                       /* Create sync object for the new volume */ 
1650                 if (!ff_cre_syncobj(vol
, &fs
->sobj
)) return FR_INT_ERR
; 
1653         FatFs
[vol
] = fs
;                                /* Register new fs object */ 
1661 /*-----------------------------------------------------------------------*/ 
1662 /* Open or Create a File                                                 */ 
1663 /*-----------------------------------------------------------------------*/ 
1666         FIL 
*fp
,                        /* Pointer to the blank file object */ 
1667         const XCHAR 
*path
,      /* Pointer to the file name */ 
1668         BYTE mode                       
/* Access mode and file open mode flags */ 
1677         fp
->fs 
= NULL
;          /* Clear file object */ 
1679         mode 
&= (FA_READ 
| FA_WRITE 
| FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
); 
1680         res 
= chk_mounted(&path
, &dj
.fs
, (BYTE
)(mode 
& (FA_WRITE 
| FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
))); 
1683         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
1685         if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); 
1686         INITBUF(dj
, sfn
, lfn
); 
1687         res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
1690         /* Create or Open a file */ 
1691         if (mode 
& (FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
)) { 
1694                 if (res 
!= FR_OK
) {                     /* No file, create new */ 
1695                         if (res 
== FR_NO_FILE
)  /* There is no file to open, create a new entry */ 
1696                                 res 
= dir_register(&dj
); 
1697                         if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); 
1698                         mode 
|= FA_CREATE_ALWAYS
; 
1699                         dir 
= dj
.dir
;                   /* Created entry (SFN entry) */ 
1701                 else {                                          /* Any object is already existing */ 
1702                         if (mode 
& FA_CREATE_NEW
)                       /* Cannot create new */ 
1703                                 LEAVE_FF(dj
.fs
, FR_EXIST
); 
1705                         if (!dir 
|| (dir
[DIR_Attr
] & (AM_RDO 
| AM_DIR
)))        /* Cannot overwrite it (R/O or DIR) */ 
1706                                 LEAVE_FF(dj
.fs
, FR_DENIED
); 
1707                         if (mode 
& FA_CREATE_ALWAYS
) {          /* Resize it to zero on over write mode */ 
1708                                 cl 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
);    /* Get start cluster */ 
1709                                 ST_WORD(dir
+DIR_FstClusHI
, 0);  /* cluster = 0 */ 
1710                                 ST_WORD(dir
+DIR_FstClusLO
, 0); 
1711                                 ST_DWORD(dir
+DIR_FileSize
, 0);  /* size = 0 */ 
1713                                 ps 
= dj
.fs
->winsect
;                    /* Remove the cluster chain */ 
1715                                         res 
= remove_chain(dj
.fs
, cl
); 
1716                                         if (res
) LEAVE_FF(dj
.fs
, res
); 
1717                                         dj
.fs
->last_clust 
= cl 
- 1;     /* Reuse the cluster hole */ 
1719                                 res 
= move_window(dj
.fs
, ps
); 
1720                                 if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); 
1723                 if (mode 
& FA_CREATE_ALWAYS
) { 
1724                         dir
[DIR_Attr
] = 0;                                      /* Reset attribute */ 
1726                         ST_DWORD(dir
+DIR_CrtTime
, ps
);          /* Created time */ 
1728                         mode 
|= FA__WRITTEN
;                            /* Set file changed flag */ 
1731         /* Open an existing file */ 
1733 #endif /* !_FS_READONLY */ 
1734                 if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); /* Follow failed */ 
1736                 if (!dir 
|| (dir
[DIR_Attr
] & AM_DIR
))   /* It is a directory */ 
1737                         LEAVE_FF(dj
.fs
, FR_NO_FILE
); 
1739                 if ((mode 
& FA_WRITE
) && (dir
[DIR_Attr
] & AM_RDO
)) /* R/O violation */ 
1740                         LEAVE_FF(dj
.fs
, FR_DENIED
); 
1742         fp
->dir_sect 
= dj
.fs
->winsect
;          /* Pointer to the directory entry */ 
1743         fp
->dir_ptr 
= dj
.dir
; 
1745         fp
->flag 
= mode
;                                        /* File access mode */ 
1746         fp
->org_clust 
=                                         /* File start cluster */ 
1747                 ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
1748         fp
->fsize 
= LD_DWORD(dir
+DIR_FileSize
); /* File size */ 
1749         fp
->fptr 
= 0; fp
->csect 
= 255;          /* File pointer */ 
1751         fp
->fs 
= dj
.fs
; fp
->id 
= dj
.fs
->id
;     /* Owner file system object of the file */ 
1753         LEAVE_FF(dj
.fs
, FR_OK
); 
1759 /*-----------------------------------------------------------------------*/ 
1761 /*-----------------------------------------------------------------------*/ 
1764         FIL 
*fp
,                /* Pointer to the file object */ 
1765         void *buff
,             /* Pointer to data buffer */ 
1766         UINT btr
,               /* Number of bytes to read */ 
1767         UINT 
*br                
/* Pointer to number of bytes read */ 
1771         DWORD clst
, sect
, remain
; 
1776         *br 
= 0;        /* Initialize bytes read */ 
1778         res 
= validate(fp
->fs
, fp
->id
);                                 /* Check validity of the object */ 
1779         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
1780         if (fp
->flag 
& FA__ERROR
)                                               /* Check abort flag */ 
1781                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
1782         if (!(fp
->flag 
& FA_READ
))                                              /* Check access mode */ 
1783                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
1784         remain 
= fp
->fsize 
- fp
->fptr
; 
1785         if (btr 
> remain
) btr 
= (UINT
)remain
;                   /* Truncate btr by remaining bytes */ 
1787         for ( ;  btr
;                                                                   /* Repeat until all data transferred */ 
1788                 rbuff 
+= rcnt
, fp
->fptr 
+= rcnt
, *br 
+= rcnt
, btr 
-= rcnt
) { 
1789                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
1790                         if (fp
->csect 
>= fp
->fs
->csize
) {               /* On the cluster boundary? */ 
1791                                 clst 
= (fp
->fptr 
== 0) ?                        
/* On the top of the file? */ 
1792                                         fp
->org_clust 
: get_fat(fp
->fs
, fp
->curr_clust
); 
1793                                 if (clst 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
1794                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
1795                                 fp
->curr_clust 
= clst
;                          /* Update current cluster */ 
1796                                 fp
->csect 
= 0;                                          /* Reset sector offset in the cluster */ 
1798                         sect 
= clust2sect(fp
->fs
, fp
->curr_clust
);      /* Get current sector */ 
1799                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
1801                         cc 
= btr 
/ SS(fp
->fs
);                                  /* When remaining bytes >= sector size, */ 
1802                         if (cc
) {                                                               /* Read maximum contiguous sectors directly */ 
1803                                 if (fp
->csect 
+ cc 
> fp
->fs
->csize
)     /* Clip at cluster boundary */ 
1804                                         cc 
= fp
->fs
->csize 
- fp
->csect
; 
1805                                 if (disk_read(fp
->fs
->drive
, rbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
1806                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
1807 #if !_FS_READONLY && _FS_MINIMIZE <= 2 
1809                                 if (fp
->fs
->wflag 
&& fp
->fs
->winsect 
- sect 
< cc
)               /* Replace one of the read sectors with cached data if it contains a dirty sector */ 
1810                                         mem_cpy(rbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), fp
->fs
->win
, SS(fp
->fs
)); 
1812                                 if ((fp
->flag 
& FA__DIRTY
) && fp
->dsect 
- sect 
< cc
)    /* Replace one of the read sectors with cached data if it contains a dirty sector */ 
1813                                         mem_cpy(rbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), fp
->buf
, SS(fp
->fs
)); 
1816                                 fp
->csect 
+= (BYTE
)cc
;                          /* Next sector address in the cluster */ 
1817                                 rcnt 
= SS(fp
->fs
) * cc
;                         /* Number of bytes transferred */ 
1822                         if (fp
->flag 
& FA__DIRTY
) {                     /* Write sector I/O buffer if needed */ 
1823                                 if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
1824                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
1825                                 fp
->flag 
&= ~FA__DIRTY
; 
1828                         if (fp
->dsect 
!= sect
) {                        /* Fill sector buffer with file data */ 
1829                                 if (disk_read(fp
->fs
->drive
, fp
->buf
, sect
, 1) != RES_OK
) 
1830                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
1834                         fp
->csect
++;                                                    /* Next sector address in the cluster */ 
1836                 rcnt 
= SS(fp
->fs
) - (fp
->fptr 
% SS(fp
->fs
));    /* Get partial sector data from sector buffer */ 
1837                 if (rcnt 
> btr
) rcnt 
= btr
; 
1839                 if (move_window(fp
->fs
, fp
->dsect
))                     /* Move sector window */ 
1840                         ABORT(fp
->fs
, FR_DISK_ERR
); 
1841                 mem_cpy(rbuff
, &fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);      /* Pick partial sector */ 
1843                 mem_cpy(rbuff
, &fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);  /* Pick partial sector */ 
1847         LEAVE_FF(fp
->fs
, FR_OK
); 
1854 /*-----------------------------------------------------------------------*/ 
1856 /*-----------------------------------------------------------------------*/ 
1859         FIL 
*fp
,                        /* Pointer to the file object */ 
1860         const void *buff
,       /* Pointer to the data to be written */ 
1861         UINT btw
,                       /* Number of bytes to write */ 
1862         UINT 
*bw                        
/* Pointer to number of bytes written */ 
1868         const BYTE 
*wbuff 
= buff
; 
1871         *bw 
= 0;        /* Initialize bytes written */ 
1873         res 
= validate(fp
->fs
, fp
->id
);                                 /* Check validity of the object */ 
1874         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
1875         if (fp
->flag 
& FA__ERROR
)                                               /* Check abort flag */ 
1876                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
1877         if (!(fp
->flag 
& FA_WRITE
))                                             /* Check access mode */ 
1878                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
1879         if (fp
->fsize 
+ btw 
< fp
->fsize
) btw 
= 0;               /* File size cannot reach 4GB */ 
1881         for ( ;  btw
;                                                                   /* Repeat until all data transferred */ 
1882                 wbuff 
+= wcnt
, fp
->fptr 
+= wcnt
, *bw 
+= wcnt
, btw 
-= wcnt
) { 
1883                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
1884                         if (fp
->csect 
>= fp
->fs
->csize
) {               /* On the cluster boundary? */ 
1885                                 if (fp
->fptr 
== 0) {                            /* On the top of the file? */ 
1886                                         clst 
= fp
->org_clust
;                   /* Follow from the origin */ 
1887                                         if (clst 
== 0)                                  /* When there is no cluster chain, */ 
1888                                                 fp
->org_clust 
= clst 
= create_chain(fp
->fs
, 0); /* Create a new cluster chain */ 
1889                                 } else {                                                        /* Middle or end of the file */ 
1890                                         clst 
= create_chain(fp
->fs
, fp
->curr_clust
);                    /* Follow or streach cluster chain */ 
1892                                 if (clst 
== 0) break;                           /* Could not allocate a new cluster (disk full) */ 
1893                                 if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
1894                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
1895                                 fp
->curr_clust 
= clst
;                          /* Update current cluster */ 
1896                                 fp
->csect 
= 0;                                          /* Reset sector address in the cluster */ 
1899                         if (fp
->fs
->winsect 
== fp
->dsect 
&& move_window(fp
->fs
, 0))     /* Write back data buffer prior to following direct transfer */ 
1900                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
1902                         if (fp
->flag 
& FA__DIRTY
) {             /* Write back data buffer prior to following direct transfer */ 
1903                                 if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
1904                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
1905                                 fp
->flag 
&= ~FA__DIRTY
; 
1908                         sect 
= clust2sect(fp
->fs
, fp
->curr_clust
);      /* Get current sector */ 
1909                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
1911                         cc 
= btw 
/ SS(fp
->fs
);                                  /* When remaining bytes >= sector size, */ 
1912                         if (cc
) {                                                               /* Write maximum contiguous sectors directly */ 
1913                                 if (fp
->csect 
+ cc 
> fp
->fs
->csize
)     /* Clip at cluster boundary */ 
1914                                         cc 
= fp
->fs
->csize 
- fp
->csect
; 
1915                                 if (disk_write(fp
->fs
->drive
, wbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
1916                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
1918                                 if (fp
->fs
->winsect 
- sect 
< cc
) {      /* Refill sector cache if it gets dirty by the direct write */ 
1919                                         mem_cpy(fp
->fs
->win
, wbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
1923                                 if (fp
->dsect 
- sect 
< cc
) {            /* Refill sector cache if it gets dirty by the direct write */ 
1924                                         mem_cpy(fp
->buf
, wbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
1925                                         fp
->flag 
&= ~FA__DIRTY
; 
1928                                 fp
->csect 
+= (BYTE
)cc
;                          /* Next sector address in the cluster */ 
1929                                 wcnt 
= SS(fp
->fs
) * cc
;                         /* Number of bytes transferred */ 
1933                         if (fp
->fptr 
>= fp
->fsize
) {                    /* Avoid silly buffer filling at growing edge */ 
1934                                 if (move_window(fp
->fs
, 0)) ABORT(fp
->fs
, FR_DISK_ERR
); 
1935                                 fp
->fs
->winsect 
= sect
; 
1938                         if (fp
->dsect 
!= sect
) {                                /* Fill sector buffer with file data */ 
1939                                 if (fp
->fptr 
< fp
->fsize 
&& 
1940                                         disk_read(fp
->fs
->drive
, fp
->buf
, sect
, 1) != RES_OK
) 
1941                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
1945                         fp
->csect
++;                                                    /* Next sector address in the cluster */ 
1947                 wcnt 
= SS(fp
->fs
) - (fp
->fptr 
% SS(fp
->fs
));    /* Put partial sector into file I/O buffer */ 
1948                 if (wcnt 
> btw
) wcnt 
= btw
; 
1950                 if (move_window(fp
->fs
, fp
->dsect
))                     /* Move sector window */ 
1951                         ABORT(fp
->fs
, FR_DISK_ERR
); 
1952                 mem_cpy(&fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);      /* Fit partial sector */ 
1955                 mem_cpy(&fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);  /* Fit partial sector */ 
1956                 fp
->flag 
|= FA__DIRTY
; 
1960         if (fp
->fptr 
> fp
->fsize
) fp
->fsize 
= fp
->fptr
; /* Update file size if needed */ 
1961         fp
->flag 
|= FA__WRITTEN
;                                                /* Set file changed flag */ 
1963         LEAVE_FF(fp
->fs
, FR_OK
); 
1969 /*-----------------------------------------------------------------------*/ 
1970 /* Synchronize the File Object                                           */ 
1971 /*-----------------------------------------------------------------------*/ 
1974         FIL 
*fp         
/* Pointer to the file object */ 
1982         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
1984                 if (fp
->flag 
& FA__WRITTEN
) {   /* Has the file been written? */ 
1985 #if !_FS_TINY   /* Write-back dirty buffer */ 
1986                         if (fp
->flag 
& FA__DIRTY
) { 
1987                                 if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
1988                                         LEAVE_FF(fp
->fs
, FR_DISK_ERR
); 
1989                                 fp
->flag 
&= ~FA__DIRTY
; 
1992                         /* Update the directory entry */ 
1993                         res 
= move_window(fp
->fs
, fp
->dir_sect
); 
1996                                 dir
[DIR_Attr
] |= AM_ARC
;                                        /* Set archive bit */ 
1997                                 ST_DWORD(dir
+DIR_FileSize
, fp
->fsize
);          /* Update file size */ 
1998                                 ST_WORD(dir
+DIR_FstClusLO
, fp
->org_clust
);      /* Update start cluster */ 
1999                                 ST_WORD(dir
+DIR_FstClusHI
, fp
->org_clust 
>> 16); 
2000                                 tim 
= get_fattime();                    /* Updated time */ 
2001                                 ST_DWORD(dir
+DIR_WrtTime
, tim
); 
2002                                 fp
->flag 
&= ~FA__WRITTEN
; 
2009         LEAVE_FF(fp
->fs
, res
); 
2012 #endif /* !_FS_READONLY */ 
2017 /*-----------------------------------------------------------------------*/ 
2019 /*-----------------------------------------------------------------------*/ 
2022         FIL 
*fp         
/* Pointer to the file object to be closed */ 
2029         res 
= validate(fp
->fs
, fp
->id
); 
2030         if (res 
== FR_OK
) fp
->fs 
= NULL
; 
2031         LEAVE_FF(fp
->fs
, res
); 
2034         if (res 
== FR_OK
) fp
->fs 
= NULL
; 
2042 /*-----------------------------------------------------------------------*/ 
2043 /* Change Current Drive/Directory                                        */ 
2044 /*-----------------------------------------------------------------------*/ 
2049         BYTE drv                
/* Drive number */ 
2052         if (drv 
>= _DRIVES
) return FR_INVALID_DRIVE
; 
2063         const XCHAR 
*path       
/* Pointer to the directory path */ 
2072         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2074                 INITBUF(dj
, sfn
, lfn
); 
2075                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
2076                 if (res 
== FR_OK
) {                                     /* Follow completed */ 
2077                         dir 
= dj
.dir
;                                   /* Pointer to the entry */ 
2079                                 dj
.fs
->cdir 
= 0;                        /* No entry (root dir) */ 
2081                                 if (dir
[DIR_Attr
] & AM_DIR
)     /* Reached to the dir */ 
2082                                         dj
.fs
->cdir 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
2084                                         res 
= FR_NO_PATH
;               /* Could not reach the dir (it is a file) */ 
2087                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
2090         LEAVE_FF(dj
.fs
, res
); 
2097 #if _FS_MINIMIZE <= 2 
2098 /*-----------------------------------------------------------------------*/ 
2099 /* Seek File R/W Pointer                                                 */ 
2100 /*-----------------------------------------------------------------------*/ 
2103         FIL 
*fp
,                /* Pointer to the file object */ 
2104         DWORD ofs               
/* File pointer from top of file */ 
2108         DWORD clst
, bcs
, nsect
, ifptr
; 
2111         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
2112         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2113         if (fp
->flag 
& FA__ERROR
)                       /* Check abort flag */ 
2114                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2115         if (ofs 
> fp
->fsize                                     
/* In read-only mode, clip offset with the file size */ 
2117                  && !(fp
->flag 
& FA_WRITE
) 
2122         fp
->fptr 
= nsect 
= 0; fp
->csect 
= 255; 
2124                 bcs 
= (DWORD
)fp
->fs
->csize 
* SS(fp
->fs
);        /* Cluster size (byte) */ 
2126                         (ofs 
- 1) / bcs 
>= (ifptr 
- 1) / bcs
) { /* When seek to same or following cluster, */ 
2127                         fp
->fptr 
= (ifptr 
- 1) & ~(bcs 
- 1);    /* start from the current cluster */ 
2129                         clst 
= fp
->curr_clust
; 
2130                 } else {                                                                        /* When seek to back cluster, */ 
2131                         clst 
= fp
->org_clust
;                                   /* start from the first cluster */ 
2133                         if (clst 
== 0) {                                                /* If no cluster chain, create a new chain */ 
2134                                 clst 
= create_chain(fp
->fs
, 0); 
2135                                 if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2136                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2137                                 fp
->org_clust 
= clst
; 
2140                         fp
->curr_clust 
= clst
; 
2143                         while (ofs 
> bcs
) {                                             /* Cluster following loop */ 
2145                                 if (fp
->flag 
& FA_WRITE
) {                      /* Check if in write mode or not */ 
2146                                         clst 
= create_chain(fp
->fs
, clst
);      /* Force streached if in write mode */ 
2147                                         if (clst 
== 0) {                                /* When disk gets full, clip file size */ 
2152                                         clst 
= get_fat(fp
->fs
, clst
);   /* Follow cluster chain if not in write mode */ 
2153                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2154                                 if (clst 
<= 1 || clst 
>= fp
->fs
->max_clust
) ABORT(fp
->fs
, FR_INT_ERR
); 
2155                                 fp
->curr_clust 
= clst
; 
2160                         fp
->csect 
= (BYTE
)(ofs 
/ SS(fp
->fs
));   /* Sector offset in the cluster */ 
2161                         if (ofs 
% SS(fp
->fs
)) { 
2162                                 nsect 
= clust2sect(fp
->fs
, clst
);       /* Current sector */ 
2163                                 if (!nsect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2169         if (fp
->fptr 
% SS(fp
->fs
) && nsect 
!= fp
->dsect
) { 
2172                 if (fp
->flag 
& FA__DIRTY
) {                     /* Write-back dirty buffer if needed */ 
2173                         if (disk_write(fp
->fs
->drive
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2174                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2175                         fp
->flag 
&= ~FA__DIRTY
; 
2178                 if (disk_read(fp
->fs
->drive
, fp
->buf
, nsect
, 1) != RES_OK
) 
2179                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2184         if (fp
->fptr 
> fp
->fsize
) {                     /* Set changed flag if the file size is extended */ 
2185                 fp
->fsize 
= fp
->fptr
; 
2186                 fp
->flag 
|= FA__WRITTEN
; 
2190         LEAVE_FF(fp
->fs
, res
); 
2196 #if _FS_MINIMIZE <= 1 
2197 /*-----------------------------------------------------------------------*/ 
2198 /* Create a Directroy Object                                             */ 
2199 /*-----------------------------------------------------------------------*/ 
2202         DIR *dj
,                        /* Pointer to directory object to create */ 
2203         const XCHAR 
*path       
/* Pointer to the directory path */ 
2211         res 
= chk_mounted(&path
, &dj
->fs
, 0); 
2213                 INITBUF((*dj
), sfn
, lfn
); 
2214                 res 
= follow_path(dj
, path
);                    /* Follow the path to the directory */ 
2215                 if (res 
== FR_OK
) {                                             /* Follow completed */ 
2217                         if (dir
) {                                                      /* It is not the root dir */ 
2218                                 if (dir
[DIR_Attr
] & AM_DIR
) {   /* The object is a directory */ 
2219                                         dj
->sclust 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
2220                                 } else {                                                /* The object is not a directory */ 
2225                                 dj
->id 
= dj
->fs
->id
; 
2226                                 res 
= dir_seek(dj
, 0);                  /* Rewind dir */ 
2229                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
2232         LEAVE_FF(dj
->fs
, res
); 
2238 /*-----------------------------------------------------------------------*/ 
2239 /* Read Directory Entry in Sequense                                      */ 
2240 /*-----------------------------------------------------------------------*/ 
2243         DIR *dj
,                        /* Pointer to the open directory object */ 
2244         FILINFO 
*fno            
/* Pointer to file information to return */ 
2251         res 
= validate(dj
->fs
, dj
->id
);                 /* Check validity of the object */ 
2253                 INITBUF((*dj
), sfn
, lfn
); 
2255                         res 
= dir_seek(dj
, 0); 
2258                         if (res 
== FR_NO_FILE
) { 
2262                         if (res 
== FR_OK
) {                             /* A valid entry is found */ 
2263                                 get_fileinfo(dj
, fno
);          /* Get the object information */ 
2264                                 res 
= dir_next(dj
, FALSE
);      /* Increment index for next */ 
2265                                 if (res 
== FR_NO_FILE
) { 
2273         LEAVE_FF(dj
->fs
, res
); 
2278 #if _FS_MINIMIZE == 0 
2279 /*-----------------------------------------------------------------------*/ 
2280 /* Get File Status                                                       */ 
2281 /*-----------------------------------------------------------------------*/ 
2284         const XCHAR 
*path
,      /* Pointer to the file path */ 
2285         FILINFO 
*fno            
/* Pointer to file information to return */ 
2293         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2295                 INITBUF(dj
, sfn
, lfn
); 
2296                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
2297                 if (res 
== FR_OK
) {                             /* Follwo completed */ 
2298                         if (dj
.dir
)     /* Found an object */ 
2299                                 get_fileinfo(&dj
, fno
); 
2300                         else            /* It is root dir */ 
2301                                 res 
= FR_INVALID_NAME
; 
2305         LEAVE_FF(dj
.fs
, res
); 
2311 /*-----------------------------------------------------------------------*/ 
2312 /* Get Number of Free Clusters                                           */ 
2313 /*-----------------------------------------------------------------------*/ 
2316         const XCHAR 
*path
,      /* Pointer to the logical drive number (root dir) */ 
2317         DWORD 
*nclst
,           /* Pointer to the variable to return number of free clusters */ 
2318         FATFS 
**fatfs           
/* Pointer to pointer to corresponding file system object to return */ 
2322         DWORD n
, clst
, sect
, stat
; 
2327         /* Get drive number */ 
2328         res 
= chk_mounted(&path
, fatfs
, 0); 
2329         if (res 
!= FR_OK
) LEAVE_FF(*fatfs
, res
); 
2331         /* If number of free cluster is valid, return it without cluster scan. */ 
2332         if ((*fatfs
)->free_clust 
<= (*fatfs
)->max_clust 
- 2) { 
2333                 *nclst 
= (*fatfs
)->free_clust
; 
2334                 LEAVE_FF(*fatfs
, FR_OK
); 
2337         /* Get number of free clusters */ 
2338         fat 
= (*fatfs
)->fs_type
; 
2340         if (fat 
== FS_FAT12
) { 
2343                         stat 
= get_fat(*fatfs
, clst
); 
2344                         if (stat 
== 0xFFFFFFFF) LEAVE_FF(*fatfs
, FR_DISK_ERR
); 
2345                         if (stat 
== 1) LEAVE_FF(*fatfs
, FR_INT_ERR
); 
2347                 } while (++clst 
< (*fatfs
)->max_clust
); 
2349                 clst 
= (*fatfs
)->max_clust
; 
2350                 sect 
= (*fatfs
)->fatbase
; 
2354                                 res 
= move_window(*fatfs
, sect
++); 
2356                                         LEAVE_FF(*fatfs
, res
); 
2360                         if (fat 
== FS_FAT16
) { 
2361                                 if (LD_WORD(p
) == 0) n
++; 
2364                                 if ((LD_DWORD(p
) & 0x0FFFFFFF) == 0) n
++; 
2369         (*fatfs
)->free_clust 
= n
; 
2370         if (fat 
== FS_FAT32
) (*fatfs
)->fsi_flag 
= 1; 
2373         LEAVE_FF(*fatfs
, FR_OK
); 
2379 /*-----------------------------------------------------------------------*/ 
2381 /*-----------------------------------------------------------------------*/ 
2383 FRESULT 
f_truncate ( 
2384         FIL 
*fp         
/* Pointer to the file object */ 
2391         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
2392         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2393         if (fp
->flag 
& FA__ERROR
)                       /* Check abort flag */ 
2394                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2395         if (!(fp
->flag 
& FA_WRITE
))                     /* Check access mode */ 
2396                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2398         if (fp
->fsize 
> fp
->fptr
) { 
2399                 fp
->fsize 
= fp
->fptr
;   /* Set file size to current R/W point */ 
2400                 fp
->flag 
|= FA__WRITTEN
; 
2401                 if (fp
->fptr 
== 0) {    /* When set file size to zero, remove entire cluster chain */ 
2402                         res 
= remove_chain(fp
->fs
, fp
->org_clust
); 
2404                 } else {                                /* When truncate a part of the file, remove remaining clusters */ 
2405                         ncl 
= get_fat(fp
->fs
, fp
->curr_clust
); 
2407                         if (ncl 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
2408                         if (ncl 
== 1) res 
= FR_INT_ERR
; 
2409                         if (res 
== FR_OK 
&& ncl 
< fp
->fs
->max_clust
) { 
2410                                 res 
= put_fat(fp
->fs
, fp
->curr_clust
, 0x0FFFFFFF); 
2411                                 if (res 
== FR_OK
) res 
= remove_chain(fp
->fs
, ncl
); 
2415         if (res 
!= FR_OK
) fp
->flag 
|= FA__ERROR
; 
2417         LEAVE_FF(fp
->fs
, res
); 
2423 /*-----------------------------------------------------------------------*/ 
2424 /* Delete a File or Directory                                            */ 
2425 /*-----------------------------------------------------------------------*/ 
2428         const XCHAR 
*path               
/* Pointer to the file or directory path */ 
2438         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2439         if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); 
2441         INITBUF(dj
, sfn
, lfn
); 
2442         res 
= follow_path(&dj
, path
);                   /* Follow the file path */ 
2443         if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2444                 res 
= FR_INVALID_NAME
; 
2445         if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); /* Follow failed */ 
2448         if (!dir
)                                                               /* Is it the root directory? */ 
2449                 LEAVE_FF(dj
.fs
, FR_INVALID_NAME
); 
2450         if (dir
[DIR_Attr
] & AM_RDO
)                             /* Is it a R/O object? */ 
2451                 LEAVE_FF(dj
.fs
, FR_DENIED
); 
2452         dclst 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
2454         if (dir
[DIR_Attr
] & AM_DIR
) {                   /* It is a sub-directory */ 
2455                 if (dclst 
< 2) LEAVE_FF(dj
.fs
, FR_INT_ERR
); 
2456                 mem_cpy(&sdj
, &dj
, sizeof(DIR));        /* Check if the sub-dir is empty or not */ 
2458                 res 
= dir_seek(&sdj
, 2); 
2459                 if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); 
2460                 res 
= dir_read(&sdj
); 
2461                 if (res 
== FR_OK
) res 
= FR_DENIED
;      /* Not empty sub-dir */ 
2462                 if (res 
!= FR_NO_FILE
) LEAVE_FF(dj
.fs
, res
); 
2465         res 
= dir_remove(&dj
);                                  /* Remove directory entry */ 
2468                         res 
= remove_chain(dj
.fs
, dclst
);       /* Remove the cluster chain */ 
2469                 if (res 
== FR_OK
) res 
= sync(dj
.fs
); 
2472         LEAVE_FF(dj
.fs
, res
); 
2478 /*-----------------------------------------------------------------------*/ 
2479 /* Create a Directory                                                    */ 
2480 /*-----------------------------------------------------------------------*/ 
2483         const XCHAR 
*path               
/* Pointer to the directory path */ 
2490         DWORD dsect
, dclst
, pclst
, tim
; 
2493         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2494         if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); 
2496         INITBUF(dj
, sfn
, lfn
); 
2497         res 
= follow_path(&dj
, path
);                   /* Follow the file path */ 
2498         if (res 
== FR_OK
) res 
= FR_EXIST
;               /* Any file or directory is already existing */ 
2499         if (_FS_RPATH 
&& res 
== FR_NO_FILE 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2500                 res 
= FR_INVALID_NAME
; 
2501         if (res 
!= FR_NO_FILE
)                                  /* Any error occured */ 
2502                 LEAVE_FF(dj
.fs
, res
); 
2504         dclst 
= create_chain(dj
.fs
, 0);                 /* Allocate a new cluster for new directory table */ 
2506         if (dclst 
== 0) res 
= FR_DENIED
; 
2507         if (dclst 
== 1) res 
= FR_INT_ERR
; 
2508         if (dclst 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
2510                 res 
= move_window(dj
.fs
, 0); 
2511         if (res 
!= FR_OK
) LEAVE_FF(dj
.fs
, res
); 
2512         dsect 
= clust2sect(dj
.fs
, dclst
); 
2514         dir 
= dj
.fs
->win
;                                               /* Initialize the new directory table */ 
2515         mem_set(dir
, 0, SS(dj
.fs
)); 
2516         mem_set(dir
+DIR_Name
, ' ', 8+3);                /* Create "." entry */ 
2517         dir
[DIR_Name
] = '.'; 
2518         dir
[DIR_Attr
] = AM_DIR
; 
2519         tim 
= get_fattime(); 
2520         ST_DWORD(dir
+DIR_WrtTime
, tim
); 
2521         ST_WORD(dir
+DIR_FstClusLO
, dclst
); 
2522         ST_WORD(dir
+DIR_FstClusHI
, dclst 
>> 16); 
2523         mem_cpy(dir
+32, dir
, 32);                       /* Create ".." entry */ 
2526         if (dj
.fs
->fs_type 
== FS_FAT32 
&& pclst 
== dj
.fs
->dirbase
) 
2528         ST_WORD(dir
+32+DIR_FstClusLO
, pclst
); 
2529         ST_WORD(dir
+32+DIR_FstClusHI
, pclst 
>> 16); 
2530         for (n 
= 0; n 
< dj
.fs
->csize
; n
++) {    /* Write dot entries and clear left sectors */ 
2531                 dj
.fs
->winsect 
= dsect
++; 
2533                 res 
= move_window(dj
.fs
, 0); 
2534                 if (res
) LEAVE_FF(dj
.fs
, res
); 
2535                 mem_set(dir
, 0, SS(dj
.fs
)); 
2538         res 
= dir_register(&dj
); 
2540                 remove_chain(dj
.fs
, dclst
); 
2543                 dir
[DIR_Attr
] = AM_DIR
;                                 /* Attribute */ 
2544                 ST_DWORD(dir
+DIR_WrtTime
, tim
);                 /* Crated time */ 
2545                 ST_WORD(dir
+DIR_FstClusLO
, dclst
);              /* Table start cluster */ 
2546                 ST_WORD(dir
+DIR_FstClusHI
, dclst 
>> 16); 
2551         LEAVE_FF(dj
.fs
, res
); 
2557 /*-----------------------------------------------------------------------*/ 
2558 /* Change File Attribute                                                 */ 
2559 /*-----------------------------------------------------------------------*/ 
2562         const XCHAR 
*path
,      /* Pointer to the file path */ 
2563         BYTE value
,                     /* Attribute bits */ 
2564         BYTE mask                       
/* Attribute mask to change */ 
2573         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2575                 INITBUF(dj
, sfn
, lfn
); 
2576                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
2577                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2578                         res 
= FR_INVALID_NAME
; 
2581                         if (!dir
) {                                             /* Is it a root directory? */ 
2582                                 res 
= FR_INVALID_NAME
; 
2583                         } else {                                                /* File or sub directory */ 
2584                                 mask 
&= AM_RDO
|AM_HID
|AM_SYS
|AM_ARC
;    /* Valid attribute mask */ 
2585                                 dir
[DIR_Attr
] = (value 
& mask
) | (dir
[DIR_Attr
] & (BYTE
)~mask
); /* Apply attribute change */ 
2592         LEAVE_FF(dj
.fs
, res
); 
2598 /*-----------------------------------------------------------------------*/ 
2599 /* Change Timestamp                                                      */ 
2600 /*-----------------------------------------------------------------------*/ 
2603         const XCHAR 
*path
,      /* Pointer to the file/directory name */ 
2604         const FILINFO 
*fno      
/* Pointer to the timestamp to be set */ 
2613         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2615                 INITBUF(dj
, sfn
, lfn
); 
2616                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
2617                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2618                         res 
= FR_INVALID_NAME
; 
2621                         if (!dir
) {                             /* Root directory */ 
2622                                 res 
= FR_INVALID_NAME
; 
2623                         } else {                                /* File or sub-directory */ 
2624                                 ST_WORD(dir
+DIR_WrtTime
, fno
->ftime
); 
2625                                 ST_WORD(dir
+DIR_WrtDate
, fno
->fdate
); 
2632         LEAVE_FF(dj
.fs
, res
); 
2638 /*-----------------------------------------------------------------------*/ 
2639 /* Rename File/Directory                                                 */ 
2640 /*-----------------------------------------------------------------------*/ 
2643         const XCHAR 
*path_old
,  /* Pointer to the old name */ 
2644         const XCHAR 
*path_new   
/* Pointer to the new name */ 
2654         INITBUF(dj_old
, sfn
, lfn
); 
2655         res 
= chk_mounted(&path_old
, &dj_old
.fs
, 1); 
2657                 dj_new
.fs 
= dj_old
.fs
; 
2658                 res 
= follow_path(&dj_old
, path_old
);   /* Check old object */ 
2659                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj_old
.fn
[NS
] & NS_DOT
)) 
2660                         res 
= FR_INVALID_NAME
; 
2662         if (res 
!= FR_OK
) LEAVE_FF(dj_old
.fs
, res
);     /* The old object is not found */ 
2664         if (!dj_old
.dir
) LEAVE_FF(dj_old
.fs
, FR_NO_FILE
);       /* Is root dir? */ 
2665         mem_cpy(buf
, dj_old
.dir
+DIR_Attr
, 21);          /* Save the object information */ 
2667         mem_cpy(&dj_new
, &dj_old
, sizeof(DIR)); 
2668         res 
= follow_path(&dj_new
, path_new
);           /* Check new object */ 
2669         if (res 
== FR_OK
) res 
= FR_EXIST
;                       /* The new object name is already existing */ 
2670         if (res 
== FR_NO_FILE
) {                                        /* Is it a valid path and no name collision? */ 
2671                 res 
= dir_register(&dj_new
);                    /* Register the new object */ 
2673                         dir 
= dj_new
.dir
;                                       /* Copy object information into new entry */ 
2674                         mem_cpy(dir
+13, buf
+2, 19); 
2675                         dir
[DIR_Attr
] = buf
[0] | AM_ARC
; 
2676                         dj_old
.fs
->wflag 
= 1; 
2677                         if (dir
[DIR_Attr
] & AM_DIR
) {           /* Update .. entry in the directory if needed */ 
2678                                 dw 
= clust2sect(dj_new
.fs
, (DWORD
)LD_WORD(dir
+DIR_FstClusHI
) | LD_WORD(dir
+DIR_FstClusLO
)); 
2682                                         res 
= move_window(dj_new
.fs
, dw
); 
2683                                         dir 
= dj_new
.fs
->win
+32; 
2684                                         if (res 
== FR_OK 
&& dir
[1] == '.') { 
2685                                                 dw 
= (dj_new
.fs
->fs_type 
== FS_FAT32 
&& dj_new
.sclust 
== dj_new
.fs
->dirbase
) ? 
0 : dj_new
.sclust
; 
2686                                                 ST_WORD(dir
+DIR_FstClusLO
, dw
); 
2687                                                 ST_WORD(dir
+DIR_FstClusHI
, dw 
>> 16); 
2688                                                 dj_new
.fs
->wflag 
= 1; 
2693                                 res 
= dir_remove(&dj_old
);                      /* Remove old entry */ 
2695                                         res 
= sync(dj_old
.fs
); 
2700         LEAVE_FF(dj_old
.fs
, res
); 
2703 #endif /* !_FS_READONLY */ 
2704 #endif /* _FS_MINIMIZE == 0 */ 
2705 #endif /* _FS_MINIMIZE <= 1 */ 
2706 #endif /* _FS_MINIMIZE <= 2 */ 
2710 /*-----------------------------------------------------------------------*/ 
2711 /* Forward data to the stream directly (Available on only _FS_TINY cfg)  */ 
2712 /*-----------------------------------------------------------------------*/ 
2713 #if _USE_FORWARD && _FS_TINY 
2716         FIL 
*fp
,                                                /* Pointer to the file object */ 
2717         UINT (*func
)(const BYTE
*,UINT
), /* Pointer to the streaming function */ 
2718         UINT btr
,                                               /* Number of bytes to forward */ 
2719         UINT 
*bf                                                
/* Pointer to number of bytes forwarded */ 
2723         DWORD remain
, clst
, sect
; 
2729         res 
= validate(fp
->fs
, fp
->id
);                                 /* Check validity of the object */ 
2730         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2731         if (fp
->flag 
& FA__ERROR
)                                               /* Check error flag */ 
2732                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2733         if (!(fp
->flag 
& FA_READ
))                                              /* Check access mode */ 
2734                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2736         remain 
= fp
->fsize 
- fp
->fptr
; 
2737         if (btr 
> remain
) btr 
= (UINT
)remain
;                   /* Truncate btr by remaining bytes */ 
2739         for ( ;  btr 
&& (*func
)(NULL
, 0);                               /* Repeat until all data transferred or stream becomes busy */ 
2740                 fp
->fptr 
+= rcnt
, *bf 
+= rcnt
, btr 
-= rcnt
) { 
2741                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
2742                         if (fp
->csect 
>= fp
->fs
->csize
) {               /* On the cluster boundary? */ 
2743                                 clst 
= (fp
->fptr 
== 0) ?                        
/* On the top of the file? */ 
2744                                         fp
->org_clust 
: get_fat(fp
->fs
, fp
->curr_clust
); 
2745                                 if (clst 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2746                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2747                                 fp
->curr_clust 
= clst
;                          /* Update current cluster */ 
2748                                 fp
->csect 
= 0;                                          /* Reset sector address in the cluster */ 
2750                         fp
->csect
++;                                                    /* Next sector address in the cluster */ 
2752                 sect 
= clust2sect(fp
->fs
, fp
->curr_clust
);      /* Get current data sector */ 
2753                 if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2754                 sect 
+= fp
->csect 
- 1; 
2755                 if (move_window(fp
->fs
, sect
))                          /* Move sector window */ 
2756                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2758                 rcnt 
= SS(fp
->fs
) - (WORD
)(fp
->fptr 
% SS(fp
->fs
));      /* Forward data from sector window */ 
2759                 if (rcnt 
> btr
) rcnt 
= btr
; 
2760                 rcnt 
= (*func
)(&fp
->fs
->win
[(WORD
)fp
->fptr 
% SS(fp
->fs
)], rcnt
); 
2761                 if (!rcnt
) ABORT(fp
->fs
, FR_INT_ERR
); 
2764         LEAVE_FF(fp
->fs
, FR_OK
); 
2766 #endif /* _USE_FORWARD */ 
2770 #if _USE_MKFS && !_FS_READONLY 
2771 /*-----------------------------------------------------------------------*/ 
2772 /* Create File System on the Drive                                       */ 
2773 /*-----------------------------------------------------------------------*/ 
2774 #define N_ROOTDIR       512                     /* Multiple of 32 and <= 2048 */ 
2775 #define N_FATS          1                       /* 1 or 2 */ 
2776 #define MAX_SECTOR      131072000UL     /* Maximum partition size */ 
2777 #define MIN_SECTOR      2000UL          /* Minimum partition size */ 
2781         BYTE drv
,                       /* Logical drive number */ 
2782         BYTE partition
,         /* Partitioning rule 0:FDISK, 1:SFD */ 
2783         WORD allocsize          
/* Allocation unit size [bytes] */ 
2786         static const DWORD sstbl
[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000,   0 }; 
2787         static const WORD cstbl
[] =  {   32768,   16384,   8192,   4096,   2048, 16384,  8192,  4096, 2048, 1024, 512 }; 
2789         DWORD b_part
, b_fat
, b_dir
, b_data
;             /* Area offset (LBA) */ 
2790         DWORD n_part
, n_rsv
, n_fat
, n_dir
;              /* Area size */ 
2797         /* Check validity of the parameters */ 
2798         if (drv 
>= _DRIVES
) return FR_INVALID_DRIVE
; 
2799         if (partition 
>= 2) return FR_MKFS_ABORTED
; 
2801         /* Check mounted drive and clear work area */ 
2803         if (!fs
) return FR_NOT_ENABLED
; 
2807         /* Get disk statics */ 
2808         stat 
= disk_initialize(drv
); 
2809         if (stat 
& STA_NOINIT
) return FR_NOT_READY
; 
2810         if (stat 
& STA_PROTECT
) return FR_WRITE_PROTECTED
; 
2811 #if _MAX_SS != 512                                              /* Get disk sector size */ 
2812         if (disk_ioctl(drv
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK
 
2813                 || SS(fs
) > _MAX_SS
) 
2814                 return FR_MKFS_ABORTED
; 
2816         if (disk_ioctl(drv
, GET_SECTOR_COUNT
, &n_part
) != RES_OK 
|| n_part 
< MIN_SECTOR
) 
2817                 return FR_MKFS_ABORTED
; 
2818         if (n_part 
> MAX_SECTOR
) n_part 
= MAX_SECTOR
; 
2819         b_part 
= (!partition
) ? 
63 : 0;         /* Boot sector */ 
2821         for (d 
= 512; d 
<= 32768U && d 
!= allocsize
; d 
<<= 1) ; /* Check validity of the allocation unit size */ 
2822         if (d 
!= allocsize
) allocsize 
= 0; 
2823         if (!allocsize
) {                                       /* Auto selection of cluster size */ 
2825                 for (as 
= SS(fs
); as 
> 512U; as 
>>= 1) d 
>>= 1; 
2826                 for (n 
= 0; d 
< sstbl
[n
]; n
++) ; 
2827                 allocsize 
= cstbl
[n
]; 
2829         if (allocsize 
< SS(fs
)) allocsize 
= SS(fs
); 
2831         allocsize 
/= SS(fs
);            /* Number of sectors per cluster */ 
2833         /* Pre-compute number of clusters and FAT type */ 
2834         n_clst 
= n_part 
/ allocsize
; 
2836         if (n_clst 
>= 0xFF5) fmt 
= FS_FAT16
; 
2837         if (n_clst 
>= 0xFFF5) fmt 
= FS_FAT32
; 
2839         /* Determine offset and size of FAT structure */ 
2842                 n_fat 
= ((n_clst 
* 3 + 1) / 2 + 3 + SS(fs
) - 1) / SS(fs
); 
2843                 n_rsv 
= 1 + partition
; 
2844                 n_dir 
= N_ROOTDIR 
* 32 / SS(fs
); 
2847                 n_fat 
= ((n_clst 
* 2) + 4 + SS(fs
) - 1) / SS(fs
); 
2848                 n_rsv 
= 1 + partition
; 
2849                 n_dir 
= N_ROOTDIR 
* 32 / SS(fs
); 
2852                 n_fat 
= ((n_clst 
* 4) + 8 + SS(fs
) - 1) / SS(fs
); 
2853                 n_rsv 
= 33 - partition
; 
2856         b_fat 
= b_part 
+ n_rsv
;                 /* FATs start sector */ 
2857         b_dir 
= b_fat 
+ n_fat 
* N_FATS
; /* Directory start sector */ 
2858         b_data 
= b_dir 
+ n_dir
;                 /* Data start sector */ 
2860         /* Align data start sector to erase block boundary (for flash memory media) */ 
2861         if (disk_ioctl(drv
, GET_BLOCK_SIZE
, &n
) != RES_OK
) return FR_MKFS_ABORTED
; 
2862         n 
= (b_data 
+ n 
- 1) & ~(n 
- 1); 
2863         n_fat 
+= (n 
- b_data
) / N_FATS
; 
2864         /* b_dir and b_data are no longer used below */ 
2866         /* Determine number of cluster and final check of validity of the FAT type */ 
2867         n_clst 
= (n_part 
- n_rsv 
- n_fat 
* N_FATS 
- n_dir
) / allocsize
; 
2868         if (   (fmt 
== FS_FAT16 
&& n_clst 
< 0xFF5) 
2869                 || (fmt 
== FS_FAT32 
&& n_clst 
< 0xFFF5)) 
2870                 return FR_MKFS_ABORTED
; 
2872         /* Create partition table if needed */ 
2874                 DWORD n_disk 
= b_part 
+ n_part
; 
2876                 mem_set(fs
->win
, 0, SS(fs
)); 
2877                 tbl 
= fs
->win
+MBR_Table
; 
2878                 ST_DWORD(tbl
, 0x00010180);              /* Partition start in CHS */ 
2879                 if (n_disk 
< 63UL * 255 * 1024) {       /* Partition end in CHS */ 
2880                         n_disk 
= n_disk 
/ 63 / 255; 
2881                         tbl
[7] = (BYTE
)n_disk
; 
2882                         tbl
[6] = (BYTE
)((n_disk 
>> 2) | 63); 
2884                         ST_WORD(&tbl
[6], 0xFFFF); 
2887                 if (fmt 
!= FS_FAT32
)                    /* System ID */ 
2888                         tbl
[4] = (n_part 
< 0x10000) ? 
0x04 : 0x06; 
2891                 ST_DWORD(tbl
+8, 63);                    /* Partition start in LBA */ 
2892                 ST_DWORD(tbl
+12, n_part
);               /* Partition size in LBA */ 
2893                 ST_WORD(tbl
+64, 0xAA55);                /* Signature */ 
2894                 if (disk_write(drv
, fs
->win
, 0, 1) != RES_OK
) 
2901         /* Create boot record */ 
2902         tbl 
= fs
->win
;                                                          /* Clear buffer */ 
2903         mem_set(tbl
, 0, SS(fs
)); 
2904         ST_DWORD(tbl
+BS_jmpBoot
, 0x90FEEB);                     /* Boot code (jmp $, nop) */ 
2905         ST_WORD(tbl
+BPB_BytsPerSec
, SS(fs
));            /* Sector size */ 
2906         tbl
[BPB_SecPerClus
] = (BYTE
)allocsize
;          /* Sectors per cluster */ 
2907         ST_WORD(tbl
+BPB_RsvdSecCnt
, n_rsv
);                     /* Reserved sectors */ 
2908         tbl
[BPB_NumFATs
] = N_FATS
;                                      /* Number of FATs */ 
2909         ST_WORD(tbl
+BPB_RootEntCnt
, SS(fs
) / 32 * n_dir
); /* Number of rootdir entries */ 
2910         if (n_part 
< 0x10000) {                                         /* Number of total sectors */ 
2911                 ST_WORD(tbl
+BPB_TotSec16
, n_part
); 
2913                 ST_DWORD(tbl
+BPB_TotSec32
, n_part
); 
2915         tbl
[BPB_Media
] = partition
;                                     /* Media descripter */ 
2916         ST_WORD(tbl
+BPB_SecPerTrk
, 63);                         /* Number of sectors per track */ 
2917         ST_WORD(tbl
+BPB_NumHeads
, 255);                         /* Number of heads */ 
2918         ST_DWORD(tbl
+BPB_HiddSec
, b_part
);                      /* Hidden sectors */ 
2919         n 
= get_fattime();                                                      /* Use current time as a VSN */ 
2920         if (fmt 
!= FS_FAT32
) { 
2921                 ST_DWORD(tbl
+BS_VolID
, n
);                              /* Volume serial number */ 
2922                 ST_WORD(tbl
+BPB_FATSz16
, n_fat
);                /* Number of secters per FAT */ 
2923                 tbl
[BS_DrvNum
] = 0x80;                                  /* Drive number */ 
2924                 tbl
[BS_BootSig
] = 0x29;                                 /* Extended boot signature */ 
2925                 mem_cpy(tbl
+BS_VolLab
, "NO NAME    FAT     ", 19);      /* Volume lavel, FAT signature */ 
2927                 ST_DWORD(tbl
+BS_VolID32
, n
);                    /* Volume serial number */ 
2928                 ST_DWORD(tbl
+BPB_FATSz32
, n_fat
);               /* Number of secters per FAT */ 
2929                 ST_DWORD(tbl
+BPB_RootClus
, 2);                  /* Root directory cluster (2) */ 
2930                 ST_WORD(tbl
+BPB_FSInfo
, 1);                             /* FSInfo record offset (bs+1) */ 
2931                 ST_WORD(tbl
+BPB_BkBootSec
, 6);                  /* Backup boot record offset (bs+6) */ 
2932                 tbl
[BS_DrvNum32
] = 0x80;                                /* Drive number */ 
2933                 tbl
[BS_BootSig32
] = 0x29;                               /* Extended boot signature */ 
2934                 mem_cpy(tbl
+BS_VolLab32
, "NO NAME    FAT32   ", 19);    /* Volume lavel, FAT signature */ 
2936         ST_WORD(tbl
+BS_55AA
, 0xAA55);                           /* Signature */ 
2937         if (SS(fs
) > 512U) { 
2938                 ST_WORD(tbl
+SS(fs
)-2, 0xAA55); 
2940         if (disk_write(drv
, tbl
, b_part
+0, 1) != RES_OK
) 
2942         if (fmt 
== FS_FAT32
) 
2943                 disk_write(drv
, tbl
, b_part
+6, 1); 
2945         /* Initialize FAT area */ 
2946         for (m 
= 0; m 
< N_FATS
; m
++) { 
2947                 mem_set(tbl
, 0, SS(fs
));                /* 1st sector of the FAT  */ 
2948                 if (fmt 
!= FS_FAT32
) { 
2949                         n 
= (fmt 
== FS_FAT12
) ? 
0x00FFFF00 : 0xFFFFFF00; 
2951                         ST_DWORD(tbl
, n
);                               /* Reserve cluster #0-1 (FAT12/16) */ 
2953                         ST_DWORD(tbl
+0, 0xFFFFFFF8);    /* Reserve cluster #0-1 (FAT32) */ 
2954                         ST_DWORD(tbl
+4, 0xFFFFFFFF); 
2955                         ST_DWORD(tbl
+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */ 
2957                 if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
) 
2959                 mem_set(tbl
, 0, SS(fs
));                /* Following FAT entries are filled by zero */ 
2960                 for (n 
= 1; n 
< n_fat
; n
++) { 
2961                         if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
) 
2966         /* Initialize Root directory */ 
2967         m 
= (BYTE
)((fmt 
== FS_FAT32
) ? allocsize 
: n_dir
); 
2969                 if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
) 
2973         /* Create FSInfo record if needed */ 
2974         if (fmt 
== FS_FAT32
) { 
2975                 ST_WORD(tbl
+BS_55AA
, 0xAA55); 
2976                 ST_DWORD(tbl
+FSI_LeadSig
, 0x41615252); 
2977                 ST_DWORD(tbl
+FSI_StrucSig
, 0x61417272); 
2978                 ST_DWORD(tbl
+FSI_Free_Count
, n_clst 
- 1); 
2979                 ST_DWORD(tbl
+FSI_Nxt_Free
, 0xFFFFFFFF); 
2980                 disk_write(drv
, tbl
, b_part
+1, 1); 
2981                 disk_write(drv
, tbl
, b_part
+7, 1); 
2984         return (disk_ioctl(drv
, CTRL_SYNC
, (void*)NULL
) == RES_OK
) ? FR_OK 
: FR_DISK_ERR
; 
2987 #endif /* _USE_MKFS && !_FS_READONLY */ 
2993 /*-----------------------------------------------------------------------*/ 
2994 /* Get a string from the file                                            */ 
2995 /*-----------------------------------------------------------------------*/ 
2997         char* buff
,     /* Pointer to the string buffer to read */ 
2998         int len
,        /* Size of string buffer */ 
2999         FIL
* fil        
/* Pointer to the file object */ 
3007         while (i 
< len 
- 1) {                   /* Read bytes until buffer gets filled */ 
3008                 f_read(fil
, p
, 1, &rc
); 
3009                 if (rc 
!= 1) break;                     /* Break when no data to read */ 
3010 #if _USE_STRFUNC >= 2 
3011                 if (*p 
== '\r') continue;       /* Strip '\r' */ 
3014                 if (*p
++ == '\n') break;        /* Break when reached end of line */ 
3017         return i ? buff 
: NULL
;                 /* When no data read (eof or error), return with error. */ 
3024 /*-----------------------------------------------------------------------*/ 
3025 /* Put a character to the file                                           */ 
3026 /*-----------------------------------------------------------------------*/ 
3028         int chr
,        /* A character to be output */ 
3029         FIL
* fil        
/* Ponter to the file object */ 
3036 #if _USE_STRFUNC >= 2 
3037         if (chr 
== '\n') f_putc ('\r', fil
);    /* LF -> CRLF conversion */ 
3039         if (!fil
) {     /* Special value may be used to switch the destination to any other device */ 
3040         /*      put_console(chr);       */ 
3044         f_write(fil
, &c
, 1, &bw
);       /* Write a byte to the file */ 
3045         return bw ? chr 
: EOF
;          /* Return the result */ 
3051 /*-----------------------------------------------------------------------*/ 
3052 /* Put a string to the file                                              */ 
3053 /*-----------------------------------------------------------------------*/ 
3055         const char* str
,        /* Pointer to the string to be output */ 
3056         FIL
* fil                        
/* Pointer to the file object */ 
3062         for (n 
= 0; *str
; str
++, n
++) { 
3063                 if (f_putc(*str
, fil
) == EOF
) return EOF
; 
3071 /*-----------------------------------------------------------------------*/ 
3072 /* Put a formatted string to the file                                    */ 
3073 /*-----------------------------------------------------------------------*/ 
3075         FIL
* fil
,                       /* Pointer to the file object */ 
3076         const char* str
,        /* Pointer to the format string */ 
3077         ...                                     /* Optional arguments... */ 
3089         for (cc 
= res 
= 0; cc 
!= EOF
; res 
+= cc
) { 
3091                 if (c 
== 0) break;                      /* End of string */ 
3092                 if (c 
!= '%') {                         /* Non escape cahracter */ 
3093                         cc 
= f_putc(c
, fil
); 
3094                         if (cc 
!= EOF
) cc 
= 1; 
3099                 if (c 
== '0') {                         /* Flag: '0' padding */ 
3102                 while (c 
>= '0' && c 
<= '9') {  /* Precision */ 
3103                         w 
= w 
* 10 + (c 
- '0'); 
3106                 if (c 
== 'l') {                         /* Prefix: Size is long int */ 
3109                 if (c 
== 's') {                         /* Type is string */ 
3110                         cc 
= f_puts(va_arg(arp
, char*), fil
); 
3113                 if (c 
== 'c') {                         /* Type is character */ 
3114                         cc 
= f_putc(va_arg(arp
, int), fil
); 
3115                         if (cc 
!= EOF
) cc 
= 1; 
3119                 if (c 
== 'd') r 
= 10;           /* Type is signed decimal */ 
3120                 if (c 
== 'u') r 
= 10;           /* Type is unsigned decimal */ 
3121                 if (c 
== 'X') r 
= 16;           /* Type is unsigned hexdecimal */ 
3122                 if (r 
== 0) break;                      /* Unknown type */ 
3123                 if (f 
& 2) {                            /* Get the value */ 
3124                         val 
= (ULONG
)va_arg(arp
, long); 
3126                         val 
= (c 
== 'd') ? 
(ULONG
)(long)va_arg(arp
, int) : (ULONG
)va_arg(arp
, unsigned int); 
3128                 /* Put numeral string */ 
3130                         if (val 
& 0x80000000) { 
3135                 i 
= sizeof(s
) - 1; s
[i
] = 0; 
3137                         c 
= (UCHAR
)(val 
% r 
+ '0'); 
3138                         if (c 
> '9') c 
+= 7; 
3142                 if (i 
&& (f 
& 4)) s
[--i
] = '-'; 
3143                 w 
= sizeof(s
) - 1 - w
; 
3144                 while (i 
&& i 
> w
) s
[--i
] = (f 
& 1) ? 
'0' : ' '; 
3145                 cc 
= f_puts(&s
[i
], fil
); 
3149         return (cc 
== EOF
) ? cc 
: res
; 
3152 #endif /* !_FS_READONLY */ 
3153 #endif /* _USE_STRFUNC */