1 /*----------------------------------------------------------------------------/ 
   2 /  FatFs - FAT file system module  R0.08                  (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 terms. 
   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) partition. 
  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 algorithm 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 physical 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 mis-truncated. 
  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. 
  78 / May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN) 
  79 /                   Added file lock feature. (_FS_SHARE) 
  80 /                   Added fast seek feature. (_USE_FASTSEEK) 
  81 /                   Changed some types on the API, XCHAR->TCHAR. 
  82 /                   Changed fname member in the FILINFO structure on Unicode cfg. 
  83 /                   String functions support UTF-8 encoding files on Unicode cfg. 
  84 /---------------------------------------------------------------------------*/ 
  86 #include "ff.h"                 /* FatFs configurations and declarations */ 
  87 #include "diskio.h"             /* Declarations of low level disk I/O functions */ 
  90 /*-------------------------------------------------------------------------- 
  92    Module Private Definitions 
  94 ---------------------------------------------------------------------------*/ 
  97 #error Wrong include file (ff.h). 
 101 /* FAT sub-type boundaries */ 
 102 /* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ 
 103 #define MIN_FAT16       4086    /* Minimum number of clusters for FAT16 */ 
 104 #define MIN_FAT32       65526   /* Minimum number of clusters for FAT32 */ 
 107 /* Definitions corresponds to multiple sector size */ 
 108 #if _MAX_SS == 512              /* Single sector size */ 
 110 #elif _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096     /* Multiple sector size */ 
 111 #define SS(fs)  ((fs)->ssize) 
 113 #error Wrong sector size. 
 117 /* Re-entrancy related */ 
 120 #error Static LFN work area must not be used in re-entrant configuration. 
 122 #define ENTER_FF(fs)            { if (!lock_fs(fs)) return FR_TIMEOUT; } 
 123 #define LEAVE_FF(fs, res)       { unlock_fs(fs, res); return res; } 
 127 #define LEAVE_FF(fs, res)       return res 
 131 #define ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } 
 134 /* Character code support macros */ 
 135 #define IsUpper(c)      (((c)>='A')&&((c)<='Z')) 
 136 #define IsLower(c)      (((c)>='a')&&((c)<='z')) 
 137 #define IsDigit(c)      (((c)>='0')&&((c)<='9')) 
 139 #if _DF1S               /* Code page is DBCS */ 
 141 #ifdef _DF2S    /* Two 1st byte areas */ 
 142 #define IsDBCS1(c)      (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) 
 143 #else                   /* One 1st byte area */ 
 144 #define IsDBCS1(c)      ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) 
 147 #ifdef _DS3S    /* Three 2nd byte areas */ 
 148 #define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) 
 149 #else                   /* Two 2nd byte areas */ 
 150 #define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) 
 153 #else                   /* Code page is SBCS */ 
 161 /* Name status flags */ 
 162 #define NS                      11              /* Offset of name status byte */ 
 163 #define NS_LOSS         0x01    /* Out of 8.3 format */ 
 164 #define NS_LFN          0x02    /* Force to create LFN entry */ 
 165 #define NS_LAST         0x04    /* Last segment */ 
 166 #define NS_BODY         0x08    /* Lower case flag (body) */ 
 167 #define NS_EXT          0x10    /* Lower case flag (ext) */ 
 168 #define NS_DOT          0x20    /* Dot entry */ 
 172 /*------------------------------------------------------------*/ 
 176 #error Number of drives must not be 0. 
 179 WORD Fsid
;                              /* File system mount ID */ 
 181 FATFS 
*FatFs
[_DRIVES
];  /* Pointer to the file system objects (logical drives) */ 
 185 BYTE Drive
;                             /* Current drive */ 
 189 #if _USE_LFN == 0                       /* No LFN */ 
 190 #define DEF_NAMEBUF                     BYTE sfn[12] 
 191 #define INIT_BUF(dobj)          (dobj).fn = sfn 
 194 #elif _USE_LFN == 1                     /* LFN with static LFN working buffer */ 
 195 static WCHAR LfnBuf
[_MAX_LFN 
+ 1]; 
 196 #define DEF_NAMEBUF                     BYTE sfn[12] 
 197 #define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } 
 200 #elif _USE_LFN == 2             /* LFN with dynamic LFN working buffer on the stack */ 
 201 #define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1] 
 202 #define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = lbuf; } 
 205 #elif _USE_LFN == 3             /* LFN with dynamic LFN working buffer on the heap */ 
 206 #define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR *lfn 
 207 #define INIT_BUF(dobj)          { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ 
 208                                                           if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ 
 209                                                           (dobj).lfn = lfn;     (dobj).fn = sfn; } 
 210 #define FREE_BUF()                      ff_memfree(lfn) 
 213 #error Wrong LFN configuration. 
 219 /*-------------------------------------------------------------------------- 
 221    Module Private Functions 
 223 ---------------------------------------------------------------------------*/ 
 226 /*-----------------------------------------------------------------------*/ 
 227 /* String functions                                                      */ 
 228 /*-----------------------------------------------------------------------*/ 
 230 /* Copy memory to memory */ 
 232 void mem_cpy (void* dst
, const void* src
, int cnt
) { 
 233         BYTE 
*d 
= (BYTE
*)dst
; 
 234         const BYTE 
*s 
= (const BYTE
*)src
; 
 236 #if _WORD_ACCESS == 1 
 237         while (cnt 
>= sizeof(int)) { 
 239                 d 
+= sizeof(int); s 
+= sizeof(int); 
 249 void mem_set (void* dst
, int val
, int cnt
) { 
 250         BYTE 
*d 
= (BYTE
*)dst
; 
 256 /* Compare memory to memory */ 
 258 int mem_cmp (const void* dst
, const void* src
, int cnt
) { 
 259         const BYTE 
*d 
= (const BYTE 
*)dst
, *s 
= (const BYTE 
*)src
; 
 262         while (cnt
-- && (r 
= *d
++ - *s
++) == 0) ; 
 266 /* Check if chr is contained in the string */ 
 268 int chk_chr (const char* str
, int chr
) { 
 269         while (*str 
&& *str 
!= chr
) str
++; 
 275 /*-----------------------------------------------------------------------*/ 
 276 /* Request/Release grant to access the volume                            */ 
 277 /*-----------------------------------------------------------------------*/ 
 282         FATFS 
*fs               
/* File system object */ 
 285         return ff_req_grant(fs
->sobj
); 
 291         FATFS 
*fs
,              /* File system object */ 
 292         FRESULT res             
/* Result code to be returned */ 
 295         if (res 
!= FR_NOT_ENABLED 
&& 
 296                 res 
!= FR_INVALID_DRIVE 
&& 
 297                 res 
!= FR_INVALID_OBJECT 
&& 
 299                 ff_rel_grant(fs
->sobj
); 
 306 /*-----------------------------------------------------------------------*/ 
 307 /* File sharing control functions                                       */ 
 308 /*-----------------------------------------------------------------------*/ 
 312 FRESULT 
chk_lock (      /* Check if the file can be accessed */ 
 313         DIR* dj
,                /* Directory object pointing the file to be checked */ 
 314         int acc                 
/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ 
 319         /* Search file semaphore table */ 
 320         for (i 
= be 
= 0; i 
< _FS_SHARE
; i
++) { 
 321                 if (dj
->fs
->flsem
[i
].ctr
) {     /* Existing entry */ 
 322                         if (dj
->fs
->flsem
[i
].clu 
== dj
->sclust 
&&       /* The file is found (identified with its location) */ 
 323                                 dj
->fs
->flsem
[i
].idx 
== dj
->index
) break; 
 324                 } else {                                        /* Blank entry */ 
 328         if (i 
== _FS_SHARE
)     /* The file has not been opened */ 
 329                 return (be 
|| acc 
!= 2) ? FR_OK 
: FR_TOO_MANY_OPEN_FILES
;       /* Is there a blank entry for new file? */ 
 331         /* The file has been opened. Reject any open against writing file and all write mode open */ 
 332         return (acc 
|| dj
->fs
->flsem
[i
].ctr 
== 0x100) ? FR_LOCKED 
: FR_OK
; 
 337 int enq_lock (  /* Check if an entry is available for a new file */ 
 338         FATFS
* fs       
/* File system object */ 
 343         for (i 
= 0; i 
< _FS_SHARE 
&& fs
->flsem
[i
].ctr
; i
++) ; 
 344         return (i 
== _FS_SHARE
) ? 
0 : 1; 
 349 UINT 
inc_lock ( /* Increment file open counter and returns its index (0:int error) */ 
 350         DIR* dj
,        /* Directory object pointing the file to register or increment */ 
 351         int acc         
/* Desired access mode (0:Read, !0:Write) */ 
 357         for (i 
= 0; i 
< _FS_SHARE
; i
++) {       /* Find the file */ 
 358                 if (dj
->fs
->flsem
[i
].ctr 
&& 
 359                         dj
->fs
->flsem
[i
].clu 
== dj
->sclust 
&& 
 360                         dj
->fs
->flsem
[i
].idx 
== dj
->index
) break; 
 363         if (i 
== _FS_SHARE
) {                           /* Not opened. Register it as new. */ 
 364                 for (i 
= 0; i 
< _FS_SHARE 
&& dj
->fs
->flsem
[i
].ctr
; i
++) ; 
 365                 if (i 
== _FS_SHARE
) return 0;   /* No space to register (int err) */ 
 366                 dj
->fs
->flsem
[i
].clu 
= dj
->sclust
; 
 367                 dj
->fs
->flsem
[i
].idx 
= dj
->index
; 
 370         if (acc 
&& dj
->fs
->flsem
[i
].ctr
) return 0;      /* Access violation (int err) */ 
 372         dj
->fs
->flsem
[i
].ctr 
= acc ? 
0x100 : dj
->fs
->flsem
[i
].ctr 
+ 1;  /* Set semaphore value */ 
 379 FRESULT 
dec_lock (      /* Decrement file open counter */ 
 380         FATFS
* fs
,              /* File system object */ 
 381         UINT i                  
/* Semaphore index */ 
 388         if (--i 
< _FS_SHARE
) { 
 389                 n 
= fs
->flsem
[i
].ctr
; 
 390                 if (n 
>= 0x100) n 
= 0; 
 392                 fs
->flsem
[i
].ctr 
= n
; 
 404 /*-----------------------------------------------------------------------*/ 
 405 /* Change window offset                                                  */ 
 406 /*-----------------------------------------------------------------------*/ 
 409 FRESULT 
move_window ( 
 410         FATFS 
*fs
,              /* File system object */ 
 411         DWORD sector    
/* Sector number to make appearance in the fs->win[] */ 
 412 )                                       /* Move to zero only writes back dirty window */ 
 418         if (wsect 
!= sector
) {  /* Changed current window */ 
 420                 if (fs
->wflag
) {        /* Write back dirty window if needed */ 
 421                         if (disk_write(fs
->drv
, fs
->win
, wsect
, 1) != RES_OK
) 
 424                         if (wsect 
< (fs
->fatbase 
+ fs
->fsize
)) {        /* In FAT area */ 
 426                                 for (nf 
= fs
->n_fats
; nf 
> 1; nf
--) {   /* Reflect the change to all FAT copies */ 
 428                                         disk_write(fs
->drv
, fs
->win
, wsect
, 1); 
 434                         if (disk_read(fs
->drv
, fs
->win
, sector
, 1) != RES_OK
) 
 436                         fs
->winsect 
= sector
; 
 446 /*-----------------------------------------------------------------------*/ 
 447 /* Clean-up cached data                                                  */ 
 448 /*-----------------------------------------------------------------------*/ 
 451 FRESULT 
sync (  /* FR_OK: successful, FR_DISK_ERR: failed */ 
 452         FATFS 
*fs       
/* File system object */ 
 458         res 
= move_window(fs
, 0); 
 460                 /* Update FSInfo sector if needed */ 
 461                 if (fs
->fs_type 
== FS_FAT32 
&& fs
->fsi_flag
) { 
 463                         mem_set(fs
->win
, 0, 512); 
 464                         ST_WORD(fs
->win
+BS_55AA
, 0xAA55); 
 465                         ST_DWORD(fs
->win
+FSI_LeadSig
, 0x41615252); 
 466                         ST_DWORD(fs
->win
+FSI_StrucSig
, 0x61417272); 
 467                         ST_DWORD(fs
->win
+FSI_Free_Count
, fs
->free_clust
); 
 468                         ST_DWORD(fs
->win
+FSI_Nxt_Free
, fs
->last_clust
); 
 469                         disk_write(fs
->drv
, fs
->win
, fs
->fsi_sector
, 1); 
 472                 /* Make sure that no pending write process in the physical drive */ 
 473                 if (disk_ioctl(fs
->drv
, CTRL_SYNC
, (void*)0) != RES_OK
) 
 484 /*-----------------------------------------------------------------------*/ 
 485 /* FAT access - Read value of a FAT entry                                */ 
 486 /*-----------------------------------------------------------------------*/ 
 489 DWORD 
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ 
 490         FATFS 
*fs
,      /* File system object */ 
 491         DWORD clst      
/* Cluster# to get the link information */ 
 498         if (clst 
< 2 || clst 
>= fs
->n_fatent
)   /* Check range */ 
 501         switch (fs
->fs_type
) { 
 503                 bc 
= (UINT
)clst
; bc 
+= bc 
/ 2; 
 504                 if (move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
)))) break; 
 505                 wc 
= fs
->win
[bc 
% SS(fs
)]; bc
++; 
 506                 if (move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
)))) break; 
 507                 wc 
|= fs
->win
[bc 
% SS(fs
)] << 8; 
 508                 return (clst 
& 1) ? 
(wc 
>> 4) : (wc 
& 0xFFF); 
 511                 if (move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 2)))) break; 
 512                 p 
= &fs
->win
[clst 
* 2 % SS(fs
)]; 
 516                 if (move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 4)))) break; 
 517                 p 
= &fs
->win
[clst 
* 4 % SS(fs
)]; 
 518                 return LD_DWORD(p
) & 0x0FFFFFFF; 
 521         return 0xFFFFFFFF;      /* An error occurred at the disk I/O layer */ 
 527 /*-----------------------------------------------------------------------*/ 
 528 /* FAT access - Change value of a FAT entry                              */ 
 529 /*-----------------------------------------------------------------------*/ 
 533         FATFS 
*fs
,      /* File system object */ 
 534         DWORD clst
,     /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ 
 535         DWORD val       
/* New value to mark the cluster */ 
 543         if (clst 
< 2 || clst 
>= fs
->n_fatent
) { /* Check range */ 
 547                 switch (fs
->fs_type
) { 
 549                         bc 
= clst
; bc 
+= bc 
/ 2; 
 550                         res 
= move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
))); 
 551                         if (res 
!= FR_OK
) break; 
 552                         p 
= &fs
->win
[bc 
% SS(fs
)]; 
 553                         *p 
= (clst 
& 1) ? 
((*p 
& 0x0F) | ((BYTE
)val 
<< 4)) : (BYTE
)val
; 
 556                         res 
= move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
))); 
 557                         if (res 
!= FR_OK
) break; 
 558                         p 
= &fs
->win
[bc 
% SS(fs
)]; 
 559                         *p 
= (clst 
& 1) ? 
(BYTE
)(val 
>> 4) : ((*p 
& 0xF0) | ((BYTE
)(val 
>> 8) & 0x0F)); 
 563                         res 
= move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 2))); 
 564                         if (res 
!= FR_OK
) break; 
 565                         p 
= &fs
->win
[clst 
* 2 % SS(fs
)]; 
 566                         ST_WORD(p
, (WORD
)val
); 
 570                         res 
= move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 4))); 
 571                         if (res 
!= FR_OK
) break; 
 572                         p 
= &fs
->win
[clst 
* 4 % SS(fs
)]; 
 573                         val 
|= LD_DWORD(p
) & 0xF0000000; 
 585 #endif /* !_FS_READONLY */ 
 590 /*-----------------------------------------------------------------------*/ 
 591 /* FAT handling - Remove a cluster chain                                 */ 
 592 /*-----------------------------------------------------------------------*/ 
 595 FRESULT 
remove_chain ( 
 596         FATFS 
*fs
,                      /* File system object */ 
 597         DWORD clst                      
/* Cluster# to remove a chain from */ 
 604         if (clst 
< 2 || clst 
>= fs
->n_fatent
) { /* Check range */ 
 609                 while (clst 
< fs
->n_fatent
) {                   /* Not a last link? */ 
 610                         nxt 
= get_fat(fs
, clst
);                        /* Get cluster status */ 
 611                         if (nxt 
== 0) break;                            /* Empty cluster? */ 
 612                         if (nxt 
== 1) { res 
= FR_INT_ERR
; break; }      /* Internal error? */ 
 613                         if (nxt 
== 0xFFFFFFFF) { res 
= FR_DISK_ERR
; break; }    /* Disk error? */ 
 614                         res 
= put_fat(fs
, clst
, 0);                     /* Mark the cluster "empty" */ 
 615                         if (res 
!= FR_OK
) break; 
 616                         if (fs
->free_clust 
!= 0xFFFFFFFF) {     /* Update FSInfo */ 
 620                         clst 
= nxt
;     /* Next cluster */ 
 631 /*-----------------------------------------------------------------------*/ 
 632 /* FAT handling - Stretch or Create a cluster chain                      */ 
 633 /*-----------------------------------------------------------------------*/ 
 636 DWORD 
create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ 
 637         FATFS 
*fs
,                      /* File system object */ 
 638         DWORD clst                      
/* Cluster# to stretch. 0 means create a new chain. */ 
 644         if (clst 
== 0) {                /* Create a new chain */ 
 645                 scl 
= fs
->last_clust
;                   /* Get suggested start point */ 
 646                 if (!scl 
|| scl 
>= fs
->n_fatent
) scl 
= 1; 
 648         else {                                  /* Stretch the current chain */ 
 649                 cs 
= get_fat(fs
, clst
);                 /* Check the cluster status */ 
 650                 if (cs 
< 2) return 1;                   /* It is an invalid cluster */ 
 651                 if (cs 
< fs
->n_fatent
) return cs
;       /* It is already followed by next cluster */ 
 655         ncl 
= scl
;                              /* Start cluster */ 
 657                 ncl
++;                                                  /* Next cluster */ 
 658                 if (ncl 
>= fs
->n_fatent
) {              /* Wrap around */ 
 660                         if (ncl 
> scl
) return 0;        /* No free cluster */ 
 662                 cs 
= get_fat(fs
, ncl
);                  /* Get the cluster status */ 
 663                 if (cs 
== 0) break;                             /* Found a free cluster */ 
 664                 if (cs 
== 0xFFFFFFFF || cs 
== 1)/* An error occurred */ 
 666                 if (ncl 
== scl
) return 0;               /* No free cluster */ 
 669         if (put_fat(fs
, ncl
, 0x0FFFFFFF))       /* Mark the new cluster "last link" */ 
 671         if (clst 
!= 0) {                                        /* Link it to the previous one if needed */ 
 672                 if (put_fat(fs
, clst
, ncl
)) 
 676         fs
->last_clust 
= ncl
;                           /* Update FSINFO */ 
 677         if (fs
->free_clust 
!= 0xFFFFFFFF) { 
 682         return ncl
;             /* Return new cluster number */ 
 684 #endif /* !_FS_READONLY */ 
 689 /*-----------------------------------------------------------------------*/ 
 690 /* Get sector# from cluster#                                             */ 
 691 /*-----------------------------------------------------------------------*/ 
 694 DWORD 
clust2sect (      /* !=0: Sector number, 0: Failed - invalid cluster# */ 
 695         FATFS 
*fs
,              /* File system object */ 
 696         DWORD clst              
/* Cluster# to be converted */ 
 700         if (clst 
>= (fs
->n_fatent 
- 2)) return 0;               /* Invalid cluster# */ 
 701         return clst 
* fs
->csize 
+ fs
->database
; 
 707 /*-----------------------------------------------------------------------*/ 
 708 /* Directory handling - Set directory index                              */ 
 709 /*-----------------------------------------------------------------------*/ 
 713         DIR *dj
,                /* Pointer to directory object */ 
 714         WORD idx                
/* Directory index number */ 
 723         if (clst 
== 1 || clst 
>= dj
->fs
->n_fatent
)      /* Check start cluster range */ 
 725         if (!clst 
&& dj
->fs
->fs_type 
== FS_FAT32
)       /* Replace cluster# 0 with root cluster# if in FAT32 */ 
 726                 clst 
= dj
->fs
->dirbase
; 
 728         if (clst 
== 0) {        /* Static table */ 
 730                 if (idx 
>= dj
->fs
->n_rootdir
)           /* Index is out of range */ 
 732                 dj
->sect 
= dj
->fs
->dirbase 
+ idx 
/ (SS(dj
->fs
) / 32);   /* Sector# */ 
 734         else {                          /* Dynamic table */ 
 735                 ic 
= SS(dj
->fs
) / 32 * dj
->fs
->csize
;   /* Entries per cluster */ 
 736                 while (idx 
>= ic
) {     /* Follow cluster chain */ 
 737                         clst 
= get_fat(dj
->fs
, clst
);                           /* Get next cluster */ 
 738                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
;     /* Disk error */ 
 739                         if (clst 
< 2 || clst 
>= dj
->fs
->n_fatent
)       /* Reached to end of table or int error */ 
 744                 dj
->sect 
= clust2sect(dj
->fs
, clst
) + idx 
/ (SS(dj
->fs
) / 32);  /* Sector# */ 
 747         dj
->dir 
= dj
->fs
->win 
+ (idx 
% (SS(dj
->fs
) / 32)) * 32; /* Ptr to the entry in the sector */ 
 749         return FR_OK
;   /* Seek succeeded */ 
 755 /*-----------------------------------------------------------------------*/ 
 756 /* Directory handling - Move directory index next                        */ 
 757 /*-----------------------------------------------------------------------*/ 
 760 FRESULT 
dir_next (      /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ 
 761         DIR *dj
,                /* Pointer to directory object */ 
 762         int stretch             
/* 0: Do not stretch table, 1: Stretch table if needed */ 
 770         if (!i 
|| !dj
->sect
)    /* Report EOT when index has reached 65535 */ 
 773         if (!(i 
% (SS(dj
->fs
) / 32))) { /* Sector changed? */ 
 774                 dj
->sect
++;                                     /* Next sector */ 
 776                 if (dj
->clust 
== 0) {   /* Static table */ 
 777                         if (i 
>= dj
->fs
->n_rootdir
)     /* Report EOT when end of table */ 
 780                 else {                                  /* Dynamic table */ 
 781                         if (((i 
/ (SS(dj
->fs
) / 32)) & (dj
->fs
->csize 
- 1)) == 0) {     /* Cluster changed? */ 
 782                                 clst 
= get_fat(dj
->fs
, dj
->clust
);                              /* Get next cluster */ 
 783                                 if (clst 
<= 1) return FR_INT_ERR
; 
 784                                 if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
 785                                 if (clst 
>= dj
->fs
->n_fatent
) {                                 /* When it reached end of dynamic table */ 
 788                                         if (!stretch
) return FR_NO_FILE
;                        /* When do not stretch, report EOT */ 
 789                                         clst 
= create_chain(dj
->fs
, dj
->clust
);         /* Stretch cluster chain */ 
 790                                         if (clst 
== 0) return FR_DENIED
;                        /* No free cluster */ 
 791                                         if (clst 
== 1) return FR_INT_ERR
; 
 792                                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
 793                                         /* Clean-up stretched table */ 
 794                                         if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; /* Flush active window */ 
 795                                         mem_set(dj
->fs
->win
, 0, SS(dj
->fs
));                    /* Clear window buffer */ 
 796                                         dj
->fs
->winsect 
= clust2sect(dj
->fs
, clst
);     /* Cluster start sector */ 
 797                                         for (c 
= 0; c 
< dj
->fs
->csize
; c
++) {           /* Fill the new cluster with 0 */ 
 799                                                 if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; 
 802                                         dj
->fs
->winsect 
-= c
;                                           /* Rewind window address */ 
 804                                         return FR_NO_FILE
;                      /* Report EOT */ 
 807                                 dj
->clust 
= clst
;                               /* Initialize data for new cluster */ 
 808                                 dj
->sect 
= clust2sect(dj
->fs
, clst
); 
 814         dj
->dir 
= dj
->fs
->win 
+ (i 
% (SS(dj
->fs
) / 32)) * 32; 
 822 /*-----------------------------------------------------------------------*/ 
 823 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */ 
 824 /*-----------------------------------------------------------------------*/ 
 827 const BYTE LfnOfs
[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};      /* Offset of LFN chars in the directory entry */ 
 831 int cmp_lfn (                   /* 1:Matched, 0:Not matched */ 
 832         WCHAR 
*lfnbuf
,          /* Pointer to the LFN to be compared */ 
 833         BYTE 
*dir                       
/* Pointer to the directory entry containing a part of LFN */ 
 840         i 
= ((dir
[LDIR_Ord
] & 0xBF) - 1) * 13;  /* Get offset in the LFN buffer */ 
 843                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);    /* Pick an LFN character from the entry */ 
 844                 if (wc
) {       /* Last char has not been processed */ 
 845                         wc 
= ff_wtoupper(uc
);           /* Convert it to upper case */ 
 846                         if (i 
>= _MAX_LFN 
|| wc 
!= ff_wtoupper(lfnbuf
[i
++]))    /* Compare it */ 
 847                                 return 0;                               /* Not matched */ 
 849                         if (uc 
!= 0xFFFF) return 0;     /* Check filler */ 
 851         } while (++s 
< 13);                             /* Repeat until all chars in the entry are checked */ 
 853         if ((dir
[LDIR_Ord
] & 0x40) && wc 
&& lfnbuf
[i
])  /* Last segment matched but different length */ 
 856         return 1;                                               /* The part of LFN matched */ 
 862 int pick_lfn (                  /* 1:Succeeded, 0:Buffer overflow */ 
 863         WCHAR 
*lfnbuf
,          /* Pointer to the Unicode-LFN buffer */ 
 864         BYTE 
*dir                       
/* Pointer to the directory entry */ 
 871         i 
= ((dir
[LDIR_Ord
] & 0x3F) - 1) * 13;  /* Offset in the LFN buffer */ 
 875                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);            /* Pick an LFN character from the entry */ 
 876                 if (wc
) {       /* Last char has not been processed */ 
 877                         if (i 
>= _MAX_LFN
) return 0;    /* Buffer overflow? */ 
 878                         lfnbuf
[i
++] = wc 
= uc
;                  /* Store it */ 
 880                         if (uc 
!= 0xFFFF) return 0;             /* Check filler */ 
 882         } while (++s 
< 13);                                             /* Read all character in the entry */ 
 884         if (dir
[LDIR_Ord
] & 0x40) {                             /* Put terminator if it is the last LFN part */ 
 885                 if (i 
>= _MAX_LFN
) return 0;            /* Buffer overflow? */ 
 896         const WCHAR 
*lfnbuf
,    /* Pointer to the LFN buffer */ 
 897         BYTE 
*dir
,                              /* Pointer to the directory entry */ 
 898         BYTE ord
,                               /* LFN order (1-20) */ 
 899         BYTE sum                                
/* SFN sum */ 
 906         dir
[LDIR_Chksum
] = sum
;                 /* Set check sum */ 
 907         dir
[LDIR_Attr
] = AM_LFN
;                /* Set attribute. LFN entry */ 
 909         ST_WORD(dir
+LDIR_FstClusLO
, 0); 
 911         i 
= (ord 
- 1) * 13;                             /* Get offset in the LFN buffer */ 
 914                 if (wc 
!= 0xFFFF) wc 
= lfnbuf
[i
++];     /* Get an effective char */ 
 915                 ST_WORD(dir
+LfnOfs
[s
], wc
);     /* Put it */ 
 916                 if (!wc
) wc 
= 0xFFFF;           /* Padding chars following last char */ 
 918         if (wc 
== 0xFFFF || !lfnbuf
[i
]) ord 
|= 0x40;    /* Bottom LFN part is the start of LFN sequence */ 
 919         dir
[LDIR_Ord
] = ord
;                    /* Set the LFN order */ 
 927 /*-----------------------------------------------------------------------*/ 
 928 /* Create numbered name                                                  */ 
 929 /*-----------------------------------------------------------------------*/ 
 932         BYTE 
*dst
,                      /* Pointer to generated SFN */ 
 933         const BYTE 
*src
,        /* Pointer to source SFN to be modified */ 
 934         const WCHAR 
*lfn
,       /* Pointer to LFN */ 
 935         WORD seq                        
/* Sequence number */ 
 942         mem_cpy(dst
, src
, 11); 
 944         if (seq 
> 5) {  /* On many collisions, generate a hash number instead of sequential number */ 
 945                 do seq 
= (seq 
>> 1) + (seq 
<< 15) + (WORD
)*lfn
++; while (*lfn
); 
 951                 c 
= (seq 
% 16) + '0'; 
 958         /* Append the number */ 
 959         for (j 
= 0; j 
< i 
&& dst
[j
] != ' '; j
++) { 
 960                 if (IsDBCS1(dst
[j
])) { 
 961                         if (j 
== i 
- 1) break; 
 966                 dst
[j
++] = (i 
< 8) ? ns
[i
++] : ' '; 
 974 /*-----------------------------------------------------------------------*/ 
 975 /* Calculate sum of an SFN                                               */ 
 976 /*-----------------------------------------------------------------------*/ 
 980         const BYTE 
*dir         
/* Ptr to directory entry */ 
 986         do sum 
= (sum 
>> 1) + (sum 
<< 7) + *dir
++; while (--n
); 
 994 /*-----------------------------------------------------------------------*/ 
 995 /* Directory handling - Find an object in the directory                  */ 
 996 /*-----------------------------------------------------------------------*/ 
1000         DIR *dj                 
/* Pointer to the directory object linked to the file name */ 
1009         res 
= dir_sdi(dj
, 0);                   /* Rewind directory object */ 
1010         if (res 
!= FR_OK
) return res
; 
1016                 res 
= move_window(dj
->fs
, dj
->sect
); 
1017                 if (res 
!= FR_OK
) break; 
1018                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
1020                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
1021 #if _USE_LFN    /* LFN configuration */ 
1022                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
1023                 if (c 
== 0xE5 || ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {       /* An entry without valid data */ 
1026                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
1028                                         if (c 
& 0x40) {         /* Is it start of LFN sequence? */ 
1029                                                 sum 
= dir
[LDIR_Chksum
]; 
1030                                                 c 
&= 0xBF; ord 
= c
;     /* LFN start order */ 
1031                                                 dj
->lfn_idx 
= dj
->index
; 
1033                                         /* Check validity of the LFN entry and compare it with given name */ 
1034                                         ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && cmp_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
1036                         } else {                                        /* An SFN entry is found */ 
1037                                 if (!ord 
&& sum 
== sum_sfn(dir
)) break; /* LFN matched? */ 
1038                                 ord 
= 0xFF; dj
->lfn_idx 
= 0xFFFF;       /* Reset LFN sequence */ 
1039                                 if (!(dj
->fn
[NS
] & NS_LOSS
) && !mem_cmp(dir
, dj
->fn
, 11)) break;        /* SFN matched? */ 
1042 #else           /* Non LFN configuration */ 
1043                 if (!(dir
[DIR_Attr
] & AM_VOL
) && !mem_cmp(dir
, dj
->fn
, 11)) /* Is it a valid entry? */ 
1046                 res 
= dir_next(dj
, 0);          /* Next entry */ 
1047         } while (res 
== FR_OK
); 
1055 /*-----------------------------------------------------------------------*/ 
1056 /* Read an object from the directory                                     */ 
1057 /*-----------------------------------------------------------------------*/ 
1058 #if _FS_MINIMIZE <= 1 
1061         DIR *dj                 
/* Pointer to the directory object that pointing the entry to be read */ 
1067         BYTE a
, ord 
= 0xFF, sum 
= 0xFF; 
1072                 res 
= move_window(dj
->fs
, dj
->sect
); 
1073                 if (res 
!= FR_OK
) break; 
1074                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
1076                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
1077 #if _USE_LFN    /* LFN configuration */ 
1078                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
1079                 if (c 
== 0xE5 || (!_FS_RPATH 
&& c 
== '.') || ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {   /* An entry without valid data */ 
1082                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
1083                                 if (c 
& 0x40) {                 /* Is it start of LFN sequence? */ 
1084                                         sum 
= dir
[LDIR_Chksum
]; 
1086                                         dj
->lfn_idx 
= dj
->index
; 
1088                                 /* Check LFN validity and capture it */ 
1089                                 ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && pick_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
1090                         } else {                                        /* An SFN entry is found */ 
1091                                 if (ord 
|| sum 
!= sum_sfn(dir
)) /* Is there a valid LFN? */ 
1092                                         dj
->lfn_idx 
= 0xFFFF;           /* It has no LFN. */ 
1096 #else           /* Non LFN configuration */ 
1097                 if (c 
!= 0xE5 && (_FS_RPATH 
|| c 
!= '.') && !(dir
[DIR_Attr
] & AM_VOL
))  /* Is it a valid entry? */ 
1100                 res 
= dir_next(dj
, 0);                          /* Next entry */ 
1101                 if (res 
!= FR_OK
) break; 
1104         if (res 
!= FR_OK
) dj
->sect 
= 0; 
1112 /*-----------------------------------------------------------------------*/ 
1113 /* Register an object to the directory                                   */ 
1114 /*-----------------------------------------------------------------------*/ 
1117 FRESULT 
dir_register (  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ 
1118         DIR *dj                         
/* Target directory with object name to be created */ 
1123 #if _USE_LFN    /* LFN configuration */ 
1125         BYTE sn
[12], *fn
, sum
; 
1129         fn 
= dj
->fn
; lfn 
= dj
->lfn
; 
1130         mem_cpy(sn
, fn
, 12); 
1132         if (_FS_RPATH 
&& (sn
[NS
] & NS_DOT
)) return FR_INVALID_NAME
;     /* Cannot create dot entry */ 
1134         if (sn
[NS
] & NS_LOSS
) {                 /* When LFN is out of 8.3 format, generate a numbered name */ 
1135                 fn
[NS
] = 0; dj
->lfn 
= 0;                        /* Find only SFN */ 
1136                 for (n 
= 1; n 
< 100; n
++) { 
1137                         gen_numname(fn
, sn
, lfn
, n
);    /* Generate a numbered name */ 
1138                         res 
= dir_find(dj
);                             /* Check if the name collides with existing SFN */ 
1139                         if (res 
!= FR_OK
) break; 
1141                 if (n 
== 100) return FR_DENIED
;         /* Abort if too many collisions */ 
1142                 if (res 
!= FR_NO_FILE
) return res
;      /* Abort if the result is other than 'not collided' */ 
1143                 fn
[NS
] = sn
[NS
]; dj
->lfn 
= lfn
; 
1146         if (sn
[NS
] & NS_LFN
) {                  /* When LFN is to be created, reserve an SFN + LFN entries. */ 
1147                 for (ne 
= 0; lfn
[ne
]; ne
++) ; 
1148                 ne 
= (ne 
+ 25) / 13; 
1149         } else {                                                /* Otherwise reserve only an SFN entry. */ 
1153         /* Reserve contiguous entries */ 
1154         res 
= dir_sdi(dj
, 0); 
1155         if (res 
!= FR_OK
) return res
; 
1158                 res 
= move_window(dj
->fs
, dj
->sect
); 
1159                 if (res 
!= FR_OK
) break; 
1160                 c 
= *dj
->dir
;                           /* Check the entry status */ 
1161                 if (c 
== 0xE5 || c 
== 0) {      /* Is it a blank entry? */ 
1162                         if (n 
== 0) is 
= dj
->index
;     /* First index of the contiguous entry */ 
1163                         if (++n 
== ne
) break;   /* A contiguous entry that required count is found */ 
1165                         n 
= 0;                                  /* Not a blank entry. Restart to search */ 
1167                 res 
= dir_next(dj
, 1);          /* Next entry with table stretch */ 
1168         } while (res 
== FR_OK
); 
1170         if (res 
== FR_OK 
&& ne 
> 1) {   /* Initialize LFN entry if needed */ 
1171                 res 
= dir_sdi(dj
, is
); 
1173                         sum 
= sum_sfn(dj
->fn
);  /* Sum of the SFN tied to the LFN */ 
1175                         do {                                    /* Store LFN entries in bottom first */ 
1176                                 res 
= move_window(dj
->fs
, dj
->sect
); 
1177                                 if (res 
!= FR_OK
) break; 
1178                                 fit_lfn(dj
->lfn
, dj
->dir
, (BYTE
)ne
, sum
); 
1180                                 res 
= dir_next(dj
, 0);  /* Next entry */ 
1181                         } while (res 
== FR_OK 
&& --ne
); 
1185 #else   /* Non LFN configuration */ 
1186         res 
= dir_sdi(dj
, 0); 
1188                 do {    /* Find a blank entry for the SFN */ 
1189                         res 
= move_window(dj
->fs
, dj
->sect
); 
1190                         if (res 
!= FR_OK
) break; 
1192                         if (c 
== 0xE5 || c 
== 0) break; /* Is it a blank entry? */ 
1193                         res 
= dir_next(dj
, 1);                  /* Next entry with table stretch */ 
1194                 } while (res 
== FR_OK
); 
1198         if (res 
== FR_OK
) {             /* Initialize the SFN entry */ 
1199                 res 
= move_window(dj
->fs
, dj
->sect
); 
1202                         mem_set(dir
, 0, 32);            /* Clean the entry */ 
1203                         mem_cpy(dir
, dj
->fn
, 11);       /* Put SFN */ 
1205                         dir
[DIR_NTres
] = *(dj
->fn
+NS
) & (NS_BODY 
| NS_EXT
);     /* Put NT flag */ 
1213 #endif /* !_FS_READONLY */ 
1218 /*-----------------------------------------------------------------------*/ 
1219 /* Remove an object from the directory                                   */ 
1220 /*-----------------------------------------------------------------------*/ 
1221 #if !_FS_READONLY && !_FS_MINIMIZE 
1223 FRESULT 
dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */ 
1224         DIR *dj                         
/* Directory object pointing the entry to be removed */ 
1228 #if _USE_LFN    /* LFN configuration */ 
1231         i 
= dj
->index
;  /* SFN index */ 
1232         res 
= dir_sdi(dj
, (WORD
)((dj
->lfn_idx 
== 0xFFFF) ? i 
: dj
->lfn_idx
));   /* Goto the SFN or top of the LFN entries */ 
1235                         res 
= move_window(dj
->fs
, dj
->sect
); 
1236                         if (res 
!= FR_OK
) break; 
1237                         *dj
->dir 
= 0xE5;                        /* Mark the entry "deleted" */ 
1239                         if (dj
->index 
>= i
) break;      /* When reached SFN, all entries of the object has been deleted. */ 
1240                         res 
= dir_next(dj
, 0);          /* Next entry */ 
1241                 } while (res 
== FR_OK
); 
1242                 if (res 
== FR_NO_FILE
) res 
= FR_INT_ERR
; 
1245 #else                   /* Non LFN configuration */ 
1246         res 
= dir_sdi(dj
, dj
->index
); 
1248                 res 
= move_window(dj
->fs
, dj
->sect
); 
1250                         *dj
->dir 
= 0xE5;                        /* Mark the entry "deleted" */ 
1258 #endif /* !_FS_READONLY */ 
1263 /*-----------------------------------------------------------------------*/ 
1264 /* Pick a segment and create the object name in directory form           */ 
1265 /*-----------------------------------------------------------------------*/ 
1268 FRESULT 
create_name ( 
1269         DIR *dj
,                        /* Pointer to the directory object */ 
1270         const TCHAR 
**path      
/* Pointer to pointer to the segment in the path string */ 
1274         static const BYTE excvt
[] = _EXCVT
;     /* Upper conversion table for extended chars */ 
1277 #if _USE_LFN    /* LFN configuration */ 
1283         /* Create LFN in Unicode */ 
1288                 w 
= p
[si
++];                                    /* Get a character */ 
1289                 if (w 
< ' ' || w 
== '/' || w 
== '\\') break;    /* Break on end of segment */ 
1290                 if (di 
>= _MAX_LFN
)                             /* Reject too long name */ 
1291                         return FR_INVALID_NAME
; 
1294                 if (IsDBCS1(w
)) {                               /* If it is a DBC 1st byte */ 
1295                         b 
= p
[si
++];                            /* Get 2nd byte */ 
1296                         if (!IsDBCS2(b
))                        /* Reject invalid code for DBC */ 
1297                                 return FR_INVALID_NAME
; 
1300                 w 
= ff_convert(w
, 1);                   /* Convert OEM to Unicode */ 
1301                 if (!w
) return FR_INVALID_NAME
; /* Reject invalid code */ 
1303                 if (w 
< 0x80 && chk_chr("\"*:<>\?|\x7F", w
)) /* Reject illegal chars for LFN */ 
1304                         return FR_INVALID_NAME
; 
1305                 lfn
[di
++] = w
;                                  /* Store the Unicode char */ 
1307         *path 
= &p
[si
];                                         /* Return pointer to the next segment */ 
1308         cf 
= (w 
< ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1310         if ((di 
== 1 && lfn
[di 
- 1] == '.') || /* Is this a dot entry? */ 
1311                 (di 
== 2 && lfn
[di 
- 1] == '.' && lfn
[di 
- 2] == '.')) { 
1313                 for (i 
= 0; i 
< 11; i
++) 
1314                         dj
->fn
[i
] = (i 
< di
) ? 
'.' : ' '; 
1315                 dj
->fn
[i
] = cf 
| NS_DOT
;                /* This is a dot entry */ 
1319         while (di
) {                                            /* Strip trailing spaces and dots */ 
1321                 if (w 
!= ' ' && w 
!= '.') break; 
1324         if (!di
) return FR_INVALID_NAME
;        /* Reject null string */ 
1326         lfn
[di
] = 0;                                            /* LFN is created */ 
1328         /* Create SFN in directory form */ 
1329         mem_set(dj
->fn
, ' ', 11); 
1330         for (si 
= 0; lfn
[si
] == ' ' || lfn
[si
] == '.'; si
++) ;  /* Strip leading spaces and dots */ 
1331         if (si
) cf 
|= NS_LOSS 
| NS_LFN
; 
1332         while (di 
&& lfn
[di 
- 1] != '.') di
--;  /* Find extension (di<=si: no extension) */ 
1336                 w 
= lfn
[si
++];                                  /* Get an LFN char */ 
1337                 if (!w
) break;                                  /* Break on end of the LFN */ 
1338                 if (w 
== ' ' || (w 
== '.' && si 
!= di
)) {       /* Remove spaces and dots */ 
1339                         cf 
|= NS_LOSS 
| NS_LFN
; continue; 
1342                 if (i 
>= ni 
|| si 
== di
) {              /* Extension or end of SFN */ 
1343                         if (ni 
== 11) {                         /* Long extension */ 
1344                                 cf 
|= NS_LOSS 
| NS_LFN
; break; 
1346                         if (si 
!= di
) cf 
|= NS_LOSS 
| NS_LFN
;   /* Out of 8.3 format */ 
1347                         if (si 
> di
) break;                     /* No extension */ 
1348                         si 
= di
; i 
= 8; ni 
= 11;        /* Enter extension section */ 
1352                 if (w 
>= 0x80) {                                /* Non ASCII char */ 
1354                         w 
= ff_convert(w
, 0);           /* Unicode -> OEM code */ 
1355                         if (w
) w 
= excvt
[w 
- 0x80];     /* Convert extended char to upper (SBCS) */ 
1357                         w 
= ff_convert(ff_wtoupper(w
), 0);      /* Upper converted Unicode -> OEM code */ 
1359                         cf 
|= NS_LFN
;                           /* Force create LFN entry */ 
1362                 if (_DF1S 
&& w 
>= 0x100) {              /* Double byte char */ 
1364                                 cf 
|= NS_LOSS 
| NS_LFN
; i 
= ni
; continue; 
1366                         dj
->fn
[i
++] = (BYTE
)(w 
>> 8); 
1367                 } else {                                                /* Single byte char */ 
1368                         if (!w 
|| chk_chr("+,;=[]", w
)) {               /* Replace illegal chars for SFN */ 
1369                                 w 
= '_'; cf 
|= NS_LOSS 
| NS_LFN
;        /* Lossy conversion */ 
1371                                 if (IsUpper(w
)) {               /* ASCII large capital */ 
1374                                         if (IsLower(w
)) {       /* ASCII small capital */ 
1380                 dj
->fn
[i
++] = (BYTE
)w
; 
1383         if (dj
->fn
[0] == 0xE5) dj
->fn
[0] = 0x05;        /* If the first char collides with deleted mark, replace it with 0x05 */ 
1385         if (ni 
== 8) b 
<<= 2; 
1386         if ((b 
& 0x0C) == 0x0C || (b 
& 0x03) == 0x03)   /* Create LFN entry when there are composite capitals */ 
1388         if (!(cf 
& NS_LFN
)) {                                           /* When LFN is in 8.3 format without extended char, NT flags are created */ 
1389                 if ((b 
& 0x03) == 0x01) cf 
|= NS_EXT
;   /* NT flag (Extension has only small capital) */ 
1390                 if ((b 
& 0x0C) == 0x04) cf 
|= NS_BODY
;  /* NT flag (Filename has only small capital) */ 
1393         dj
->fn
[NS
] = cf
;        /* SFN is created */ 
1398 #else   /* Non-LFN configuration */ 
1403         /* Create file name in directory form */ 
1405         mem_set(sfn
, ' ', 11); 
1406         si 
= i 
= b 
= 0; ni 
= 8; 
1409         if (p
[si
] == '.') { /* Is this a dot entry? */ 
1412                         if (c 
!= '.' || si 
>= 3) break; 
1415                 if (c 
!= '/' && c 
!= '\\' && c 
> ' ') return FR_INVALID_NAME
; 
1416                 *path 
= &p
[si
];                                                                 /* Return pointer to the next segment */ 
1417                 sfn
[NS
] = (c 
<= ' ') ? NS_LAST 
| NS_DOT 
: NS_DOT
;       /* Set last segment flag if end of path */ 
1423                 if (c 
<= ' ' || c 
== '/' || c 
== '\\') break;   /* Break on end of segment */ 
1424                 if (c 
== '.' || i 
>= ni
) { 
1425                         if (ni 
!= 8 || c 
!= '.') return FR_INVALID_NAME
; 
1429                 if (c 
>= 0x80) {                                /* Extended char */ 
1431                         c 
= excvt
[c 
- 0x80];            /* Convert extend char (SBCS) */ 
1433                         b 
|= 3;                                         /* Eliminate NT flag if extended char is exist */ 
1434 #if !_DF1S      /* ASCII only cfg */ 
1435                         return FR_INVALID_NAME
; 
1439                 if (IsDBCS1(c
)) {                               /* DBC 1st byte? */ 
1440                         d 
= (BYTE
)p
[si
++];                      /* Get 2nd byte */ 
1441                         if (!IsDBCS2(d
) || i 
>= ni 
- 1) /* Reject invalid DBC */ 
1442                                 return FR_INVALID_NAME
; 
1445                 } else {                                                /* Single byte code */ 
1446                         if (chk_chr("\"*+,:<=>\?[]|\x7F", c
))   /* Reject illegal chrs for SFN */ 
1447                                 return FR_INVALID_NAME
; 
1448                         if (IsUpper(c
)) {                       /* ASCII large capital? */ 
1451                                 if (IsLower(c
)) {               /* ASCII small capital? */ 
1458         *path 
= &p
[si
];                                         /* Return pointer to the next segment */ 
1459         c 
= (c 
<= ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1461         if (!i
) return FR_INVALID_NAME
;         /* Reject null string */ 
1462         if (sfn
[0] == 0xE5) sfn
[0] = 0x05;      /* When first char collides with 0xE5, replace it with 0x05 */ 
1464         if (ni 
== 8) b 
<<= 2; 
1465         if ((b 
& 0x03) == 0x01) c 
|= NS_EXT
;    /* NT flag (Name extension has only small capital) */ 
1466         if ((b 
& 0x0C) == 0x04) c 
|= NS_BODY
;   /* NT flag (Name body has only small capital) */ 
1468         sfn
[NS
] = c
;            /* Store NT flag, File name is created */ 
1477 /*-----------------------------------------------------------------------*/ 
1478 /* Get file information from directory entry                             */ 
1479 /*-----------------------------------------------------------------------*/ 
1480 #if _FS_MINIMIZE <= 1 
1482 void get_fileinfo (             /* No return code */ 
1483         DIR *dj
,                        /* Pointer to the directory object */ 
1484         FILINFO 
*fno            
/* Pointer to the file information to be filled */ 
1495                 nt 
= dir
[DIR_NTres
];            /* NT flag */ 
1496                 for (i 
= 0; i 
< 8; i
++) {       /* Copy name body */ 
1498                         if (c 
== ' ') break; 
1499                         if (c 
== 0x05) c 
= (TCHAR
)0xE5; 
1500                         if (_USE_LFN 
&& (nt 
& NS_BODY
) && IsUpper(c
)) c 
+= 0x20; 
1502                         if (IsDBCS1(c
) && i 
< 7 && IsDBCS2(dir
[i 
+ 1])) 
1503                                 c 
= (c 
<< 8) | dir
[++i
]; 
1504                         c 
= ff_convert(c
, 1); 
1509                 if (dir
[8] != ' ') {            /* Copy name extension */ 
1511                         for (i 
= 8; i 
< 11; i
++) { 
1513                                 if (c 
== ' ') break; 
1514                                 if (_USE_LFN 
&& (nt 
& NS_EXT
) && IsUpper(c
)) c 
+= 0x20; 
1516                                 if (IsDBCS1(c
) && i 
< 10 && IsDBCS2(dir
[i 
+ 1])) 
1517                                         c 
= (c 
<< 8) | dir
[++i
]; 
1518                                 c 
= ff_convert(c
, 1); 
1524                 fno
->fattrib 
= dir
[DIR_Attr
];                           /* Attribute */ 
1525                 fno
->fsize 
= LD_DWORD(dir
+DIR_FileSize
);        /* Size */ 
1526                 fno
->fdate 
= LD_WORD(dir
+DIR_WrtDate
);          /* Date */ 
1527                 fno
->ftime 
= LD_WORD(dir
+DIR_WrtTime
);          /* Time */ 
1533                 TCHAR 
*tp 
= fno
->lfname
; 
1537                 if (dj
->sect 
&& dj
->lfn_idx 
!= 0xFFFF) {/* Get LFN if available */ 
1539                         while ((w 
= *lfn
++) != 0) {                     /* Get an LFN char */ 
1541                                 w 
= ff_convert(w
, 0);                   /* Unicode -> OEM conversion */ 
1542                                 if (!w
) { i 
= 0; break; }               /* Could not convert, no LFN */ 
1543                                 if (_DF1S 
&& w 
>= 0x100)                /* Put 1st byte if it is a DBC */ 
1544                                         tp
[i
++] = (TCHAR
)(w 
>> 8); 
1546                                 if (i 
>= fno
->lfsize 
- 1) { i 
= 0; break; }     /* Buffer overrun, no LFN */ 
1550                 tp
[i
] = 0;      /* Terminator */ 
1554 #endif /* _FS_MINIMIZE <= 1 */ 
1559 /*-----------------------------------------------------------------------*/ 
1560 /* Follow a file path                                                    */ 
1561 /*-----------------------------------------------------------------------*/ 
1564 FRESULT 
follow_path (   /* FR_OK(0): successful, !=0: error code */ 
1565         DIR *dj
,                        /* Directory object to return last directory and found object */ 
1566         const TCHAR 
*path       
/* Full-path string to find a file or directory */ 
1574         if (*path 
== '/' || *path 
== '\\') { /* There is a heading separator */ 
1575                 path
++; dj
->sclust 
= 0;         /* Strip it and start from the root dir */ 
1576         } else {                                                        /* No heading separator */ 
1577                 dj
->sclust 
= dj
->fs
->cdir
;      /* Start from the current dir */ 
1580         if (*path 
== '/' || *path 
== '\\')      /* Strip heading separator if exist */ 
1582         dj
->sclust 
= 0;                                         /* Start from the root dir */ 
1585         if ((UINT
)*path 
< ' ') {                        /* Null path means the start directory itself */ 
1586                 res 
= dir_sdi(dj
, 0); 
1589         } else {                                                        /* Follow path */ 
1591                         res 
= create_name(dj
, &path
);   /* Get a segment */ 
1592                         if (res 
!= FR_OK
) break; 
1593                         res 
= dir_find(dj
);                             /* Find it */ 
1595                         if (res 
!= FR_OK
) {                             /* Failed to find the object */ 
1596                                 if (res 
!= FR_NO_FILE
) break;   /* Abort if any hard error occurred */ 
1597                                 /* Object not found */ 
1598                                 if (_FS_RPATH 
&& (ns 
& NS_DOT
)) {       /* If dot entry is not exit */ 
1599                                         dj
->sclust 
= 0; dj
->dir 
= 0;    /* It is the root dir */ 
1601                                         if (!(ns 
& NS_LAST
)) continue; 
1602                                 } else {                                                        /* Could not find the object */ 
1603                                         if (!(ns 
& NS_LAST
)) res 
= FR_NO_PATH
; 
1607                         if (ns 
& NS_LAST
) break;                        /* Last segment match. Function completed. */ 
1608                         dir 
= dj
->dir
;                                          /* There is next segment. Follow the sub directory */ 
1609                         if (!(dir
[DIR_Attr
] & AM_DIR
)) {        /* Cannot follow because it is a file */ 
1610                                 res 
= FR_NO_PATH
; break; 
1612                         dj
->sclust 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
1622 /*-----------------------------------------------------------------------*/ 
1623 /* Load boot record and check if it is an FAT boot record                */ 
1624 /*-----------------------------------------------------------------------*/ 
1627 BYTE 
check_fs ( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk error */ 
1628         FATFS 
*fs
,      /* File system object */ 
1629         DWORD sect      
/* Sector# (lba) to check if it is an FAT boot record or not */ 
1632         if (disk_read(fs
->drv
, fs
->win
, sect
, 1) != RES_OK
)     /* Load boot record */ 
1634         if (LD_WORD(&fs
->win
[BS_55AA
]) != 0xAA55)               /* Check record signature (always placed at offset 510 even if the sector size is >512) */ 
1637         if ((LD_DWORD(&fs
->win
[BS_FilSysType
]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ 
1639         if ((LD_DWORD(&fs
->win
[BS_FilSysType32
]) & 0xFFFFFF) == 0x544146) 
1648 /*-----------------------------------------------------------------------*/ 
1649 /* Make sure that the file system is valid                               */ 
1650 /*-----------------------------------------------------------------------*/ 
1653 FRESULT 
chk_mounted (   /* FR_OK(0): successful, !=0: any error occurred */ 
1654         const TCHAR 
**path
,     /* Pointer to pointer to the path name (drive number) */ 
1655         FATFS 
**rfs
,            /* Pointer to pointer to the found file system object */ 
1656         BYTE chk_wp                     
/* !=0: Check media write protection for write access */ 
1662         DWORD bsect
, fasize
, tsect
, sysect
, nclst
, szbfat
; 
1664         const TCHAR 
*p 
= *path
; 
1667         /* Get logical drive number from the path name */ 
1668         vol 
= p
[0] - '0';                               /* Is there a drive number? */ 
1669         if (vol 
<= 9 && p
[1] == ':') {  /* Found a drive number, get and strip it */ 
1670                 p 
+= 2; *path 
= p
;                      /* Return pointer to the path name */ 
1671         } else {                                                /* No drive number is given */ 
1673                 vol 
= Drive
;                            /* Use current drive */ 
1675                 vol 
= 0;                                        /* Use drive 0 */ 
1679         /* Check if the logical drive is valid or not */ 
1680         if (vol 
>= _DRIVES
)                     /* Is the drive number valid? */ 
1681                 return FR_INVALID_DRIVE
; 
1682         *rfs 
= fs 
= FatFs
[vol
];                 /* Return pointer to the corresponding file system object */ 
1683         if (!fs
) return FR_NOT_ENABLED
; /* Is the file system object available? */ 
1685         ENTER_FF(fs
);                                   /* Lock file system */ 
1687         if (fs
->fs_type
) {                              /* If the logical drive has been mounted */ 
1688                 stat 
= disk_status(fs
->drv
); 
1689                 if (!(stat 
& STA_NOINIT
)) {     /* and the physical drive is kept initialized (has not been changed), */ 
1691                         if (chk_wp 
&& (stat 
& STA_PROTECT
))     /* Check write protection if needed */ 
1692                                 return FR_WRITE_PROTECTED
; 
1694                         return FR_OK
;                   /* The file system object is valid */ 
1698         /* The logical drive must be mounted. Following code attempts to mount the volume (initialize the file system object) */ 
1700         fs
->fs_type 
= 0;                                        /* Clear the file system object */ 
1701         fs
->drv 
= (BYTE
)LD2PD(vol
);                     /* Bind the logical drive and a physical drive */ 
1702         stat 
= disk_initialize(fs
->drv
);        /* Initialize low level disk I/O layer */ 
1703         if (stat 
& STA_NOINIT
)                          /* Check if the drive is ready */ 
1704                 return FR_NOT_READY
; 
1705 #if _MAX_SS != 512                                              /* Get disk sector size if needed */ 
1706         if (disk_ioctl(fs
->drv
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK 
|| SS(fs
) > _MAX_SS
) 
1707                 return FR_NO_FILESYSTEM
; 
1710         if (chk_wp 
&& (stat 
& STA_PROTECT
))     /* Check disk write protection if needed */ 
1711                 return FR_WRITE_PROTECTED
; 
1713         /* Search FAT partition on the drive (Supports only generic partitions, FDISK and SFD) */ 
1714         fmt 
= check_fs(fs
, bsect 
= 0);          /* Check sector 0 if it is a VBR */ 
1715         if (fmt 
== 1) {                                         /* Not an FAT-VBR, the disk may be partitioned */ 
1716                 /* Check the partition listed in top of the partition table */ 
1717                 tbl 
= &fs
->win
[MBR_Table 
+ LD2PT(vol
) * 16];    /* Partition table */ 
1718                 if (tbl
[4]) {                                                                   /* Is the partition existing? */ 
1719                         bsect 
= LD_DWORD(&tbl
[8]);                                      /* Partition offset in LBA */ 
1720                         fmt 
= check_fs(fs
, bsect
);                                      /* Check the partition */ 
1723         if (fmt 
== 3) return FR_DISK_ERR
; 
1724         if (fmt
) return FR_NO_FILESYSTEM
;                                       /* No FAT volume is found */ 
1726         /* Following code initializes the file system object */ 
1728         if (LD_WORD(fs
->win
+BPB_BytsPerSec
) != SS(fs
))          /* (BPB_BytsPerSec must be equal to the physical sector size) */ 
1729                 return FR_NO_FILESYSTEM
; 
1731         fasize 
= LD_WORD(fs
->win
+BPB_FATSz16
);                          /* Number of sectors per FAT */ 
1732         if (!fasize
) fasize 
= LD_DWORD(fs
->win
+BPB_FATSz32
); 
1735         fs
->n_fats 
= b 
= fs
->win
[BPB_NumFATs
];                          /* Number of FAT copies */ 
1736         if (b 
!= 1 && b 
!= 2) return FR_NO_FILESYSTEM
;          /* (Must be 1 or 2) */ 
1737         fasize 
*= b
;                                                                            /* Number of sectors for FAT area */ 
1739         fs
->csize 
= b 
= fs
->win
[BPB_SecPerClus
];                        /* Number of sectors per cluster */ 
1740         if (!b 
|| (b 
& (b 
- 1))) return FR_NO_FILESYSTEM
;       /* (Must be 1,2,4...128) */ 
1742         fs
->n_rootdir 
= LD_WORD(fs
->win
+BPB_RootEntCnt
);        /* Number of root directory entries */ 
1743         if (fs
->n_rootdir 
% (SS(fs
) / 32)) return FR_NO_FILESYSTEM
;     /* (BPB_RootEntCnt must be sector aligned) */ 
1745         tsect 
= LD_WORD(fs
->win
+BPB_TotSec16
);                          /* Number of sectors on the volume */ 
1746         if (!tsect
) tsect 
= LD_DWORD(fs
->win
+BPB_TotSec32
); 
1748         nrsv 
= LD_WORD(fs
->win
+BPB_RsvdSecCnt
);                         /* Number of reserved sectors */ 
1749         if (!nrsv
) return FR_NO_FILESYSTEM
;                                     /* (BPB_RsvdSecCnt must not be 0) */ 
1751         /* Determine the FAT sub type */ 
1752         sysect 
= nrsv 
+ fasize 
+ fs
->n_rootdir 
/ (SS(fs
) / 32); /* RSV+FAT+DIR */ 
1753         if (tsect 
< sysect
) return FR_NO_FILESYSTEM
;            /* (Invalid volume size) */ 
1754         nclst 
= (tsect 
- sysect
) / fs
->csize
;                           /* Number of clusters */ 
1755         if (!nclst
) return FR_NO_FILESYSTEM
;                            /* (Invalid volume size) */ 
1757         if (nclst 
>= MIN_FAT16
) fmt 
= FS_FAT16
; 
1758         if (nclst 
>= MIN_FAT32
) fmt 
= FS_FAT32
; 
1760         /* Boundaries and Limits */ 
1761         fs
->n_fatent 
= nclst 
+ 2;                                                       /* Number of FAT entries */ 
1762         fs
->database 
= bsect 
+ sysect
;                                          /* Data start sector */ 
1763         fs
->fatbase 
= bsect 
+ nrsv
;                                             /* FAT start sector */ 
1764         if (fmt 
== FS_FAT32
) { 
1765                 if (fs
->n_rootdir
) return FR_NO_FILESYSTEM
;             /* (BPB_RootEntCnt must be 0) */ 
1766                 fs
->dirbase 
= LD_DWORD(fs
->win
+BPB_RootClus
);   /* Root directory start cluster */ 
1767                 szbfat 
= fs
->n_fatent 
* 4;                                              /* (Required FAT size) */ 
1769                 if (!fs
->n_rootdir
)     return FR_NO_FILESYSTEM
;        /* (BPB_RootEntCnt must not be 0) */ 
1770                 fs
->dirbase 
= fs
->fatbase 
+ fasize
;                             /* Root directory start sector */ 
1771                 szbfat 
= (fmt 
== FS_FAT16
) ?                                    
/* (Required FAT size) */ 
1772                         fs
->n_fatent 
* 2 : fs
->n_fatent 
* 3 / 2 + (fs
->n_fatent 
& 1); 
1774         if (fs
->fsize 
< (szbfat 
+ (SS(fs
) - 1)) / SS(fs
))       /* (FAT size must not be less than FAT sectors */ 
1775                 return FR_NO_FILESYSTEM
; 
1778         /* Initialize cluster allocation information */ 
1779         fs
->free_clust 
= 0xFFFFFFFF; 
1782         /* Get fsinfo if available */ 
1783         if (fmt 
== FS_FAT32
) { 
1785                 fs
->fsi_sector 
= bsect 
+ LD_WORD(fs
->win
+BPB_FSInfo
); 
1786                 if (disk_read(fs
->drv
, fs
->win
, fs
->fsi_sector
, 1) == RES_OK 
&& 
1787                         LD_WORD(fs
->win
+BS_55AA
) == 0xAA55 && 
1788                         LD_DWORD(fs
->win
+FSI_LeadSig
) == 0x41615252 && 
1789                         LD_DWORD(fs
->win
+FSI_StrucSig
) == 0x61417272) { 
1790                                 fs
->last_clust 
= LD_DWORD(fs
->win
+FSI_Nxt_Free
); 
1791                                 fs
->free_clust 
= LD_DWORD(fs
->win
+FSI_Free_Count
); 
1795         fs
->fs_type 
= fmt
;              /* FAT sub-type */ 
1796         fs
->id 
= ++Fsid
;                /* File system mount ID */ 
1797         fs
->winsect 
= 0;                /* Invalidate sector cache */ 
1800         fs
->cdir 
= 0;                   /* Current directory (root dir) */ 
1802 #if _FS_SHARE                           /* Clear file lock semaphores */ 
1803         for (vol 
= 0; vol 
< _FS_SHARE
; vol
++) 
1804                 fs
->flsem
[vol
].ctr 
= 0; 
1813 /*-----------------------------------------------------------------------*/ 
1814 /* Check if the file/dir object is valid or not                          */ 
1815 /*-----------------------------------------------------------------------*/ 
1818 FRESULT 
validate (      /* FR_OK(0): The object is valid, !=0: Invalid */ 
1819         FATFS 
*fs
,              /* Pointer to the file system object */ 
1820         WORD id                 
/* Member id of the target object to be checked */ 
1823         if (!fs 
|| !fs
->fs_type 
|| fs
->id 
!= id
) 
1824                 return FR_INVALID_OBJECT
; 
1826         ENTER_FF(fs
);           /* Lock file system */ 
1828         if (disk_status(fs
->drv
) & STA_NOINIT
) 
1829                 return FR_NOT_READY
; 
1837 /*-------------------------------------------------------------------------- 
1841 --------------------------------------------------------------------------*/ 
1845 /*-----------------------------------------------------------------------*/ 
1846 /* Mount/Unmount a Logical Drive                                         */ 
1847 /*-----------------------------------------------------------------------*/ 
1850         BYTE vol
,               /* Logical drive number to be mounted/unmounted */ 
1851         FATFS 
*fs               
/* Pointer to new file system object (NULL for unmount)*/ 
1857         if (vol 
>= _DRIVES
)                             /* Check if the drive number is valid */ 
1858                 return FR_INVALID_DRIVE
; 
1859         rfs 
= FatFs
[vol
];                               /* Get current fs object */ 
1862 #if _FS_REENTRANT                                       /* Discard sync object of the current volume */ 
1863                 if (!ff_del_syncobj(rfs
->sobj
)) return FR_INT_ERR
; 
1865                 rfs
->fs_type 
= 0;                       /* Clear old fs object */ 
1869                 fs
->fs_type 
= 0;                        /* Clear new fs object */ 
1870 #if _FS_REENTRANT                                       /* Create sync object for the new volume */ 
1871                 if (!ff_cre_syncobj(vol
, &fs
->sobj
)) return FR_INT_ERR
; 
1874         FatFs
[vol
] = fs
;                                /* Register new fs object */ 
1882 /*-----------------------------------------------------------------------*/ 
1883 /* Open or Create a File                                                 */ 
1884 /*-----------------------------------------------------------------------*/ 
1887         FIL 
*fp
,                        /* Pointer to the blank file object */ 
1888         const TCHAR 
*path
,      /* Pointer to the file name */ 
1889         BYTE mode                       
/* Access mode and file open mode flags */ 
1898         fp
->fs 
= 0;                     /* Clear file object */ 
1901         mode 
&= FA_READ 
| FA_WRITE 
| FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
; 
1902         res 
= chk_mounted(&path
, &dj
.fs
, (BYTE
)(mode 
& ~FA_READ
)); 
1905         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
1909                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
1912 #if !_FS_READONLY       /* R/W configuration */ 
1914                 if (!dir
)       /* Current dir itself */ 
1915                         res 
= FR_INVALID_NAME
; 
1918                         res 
= chk_lock(&dj
, (mode 
& ~FA_READ
) ? 
1 : 0); 
1921         /* Create or Open a file */ 
1922         if (mode 
& (FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
)) { 
1925                 if (res 
!= FR_OK
) {                             /* No file, create new */ 
1926                         if (res 
== FR_NO_FILE
)          /* There is no file to open, create a new entry */ 
1928                                 res 
= enq_lock(dj
.fs
) ? 
dir_register(&dj
) : FR_TOO_MANY_OPEN_FILES
; 
1930                                 res 
= dir_register(&dj
); 
1932                         mode 
|= FA_CREATE_ALWAYS
; 
1933                         dir 
= dj
.dir
;                           /* New entry */ 
1935                 else {                                                  /* Any object is already existing */ 
1936                         if (mode 
& FA_CREATE_NEW
) {                     /* Cannot create new */ 
1939                                 if (dir
[DIR_Attr
] & (AM_RDO 
| AM_DIR
))  /* Cannot overwrite it (R/O or DIR) */ 
1943                 if (res 
== FR_OK 
&& (mode 
& FA_CREATE_ALWAYS
)) {        /* Truncate it if overwrite mode */ 
1944                         dw 
= get_fattime();                                             /* Created time */ 
1945                         ST_DWORD(dir
+DIR_CrtTime
, dw
); 
1946                         dir
[DIR_Attr
] = 0;                                      /* Reset attribute */ 
1947                         ST_DWORD(dir
+DIR_FileSize
, 0);          /* size = 0 */ 
1948                         cl 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
);    /* Get start cluster */ 
1949                         ST_WORD(dir
+DIR_FstClusHI
, 0);          /* cluster = 0 */ 
1950                         ST_WORD(dir
+DIR_FstClusLO
, 0); 
1952                         if (cl
) {                                                       /* Remove the cluster chain if exist */ 
1953                                 dw 
= dj
.fs
->winsect
; 
1954                                 res 
= remove_chain(dj
.fs
, cl
); 
1956                                         dj
.fs
->last_clust 
= cl 
- 1;     /* Reuse the cluster hole */ 
1957                                         res 
= move_window(dj
.fs
, dw
); 
1962         else {  /* Open an existing file */ 
1963                 if (res 
== FR_OK
) {                                             /* Follow succeeded */ 
1964                         if (dir
[DIR_Attr
] & AM_DIR
) {           /* It is a directory */ 
1967                                 if ((mode 
& FA_WRITE
) && (dir
[DIR_Attr
] & AM_RDO
)) /* R/O violation */ 
1973                 if (mode 
& (FA_WRITE 
| FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
)) 
1974                         mode 
|= FA__WRITTEN
;                            /* Set file changed flag */ 
1975                 fp
->dir_sect 
= dj
.fs
->winsect
;                  /* Pointer to the directory entry */ 
1978                 fp
->lockid 
= inc_lock(&dj
, (mode 
& ~FA_READ
) ? 
1 : 0); 
1979                 if (!fp
->lockid
) res 
= FR_INT_ERR
; 
1983 #else                           /* R/O configuration */ 
1984         if (res 
== FR_OK
) {                                     /* Follow succeeded */ 
1985                 if (!dir
) {                                             /* Current dir itself */ 
1986                         res 
= FR_INVALID_NAME
; 
1988                         if (dir
[DIR_Attr
] & AM_DIR
)     /* It is a directory */ 
1996                 fp
->flag 
= mode
;                                        /* File access mode */ 
1997                 fp
->org_clust 
=                                         /* File start cluster */ 
1998                         ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
1999                 fp
->fsize 
= LD_DWORD(dir
+DIR_FileSize
); /* File size */ 
2000                 fp
->fptr 
= 0;                                           /* File pointer */ 
2003                 fp
->cltbl 
= 0;                                          /* No cluster link map table */ 
2005                 fp
->fs 
= dj
.fs
; fp
->id 
= dj
.fs
->id
;     /* Validate file object */ 
2008         LEAVE_FF(dj
.fs
, res
); 
2014 /*-----------------------------------------------------------------------*/ 
2016 /*-----------------------------------------------------------------------*/ 
2019         FIL 
*fp
,                /* Pointer to the file object */ 
2020         void *buff
,             /* Pointer to data buffer */ 
2021         UINT btr
,               /* Number of bytes to read */ 
2022         UINT 
*br                
/* Pointer to number of bytes read */ 
2026         DWORD clst
, sect
, remain
; 
2028         BYTE csect
, *rbuff 
= buff
; 
2031         *br 
= 0;        /* Initialize byte counter */ 
2033         res 
= validate(fp
->fs
, fp
->id
);                                 /* Check validity of the object */ 
2034         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2035         if (fp
->flag 
& FA__ERROR
)                                               /* Check abort flag */ 
2036                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2037         if (!(fp
->flag 
& FA_READ
))                                              /* Check access mode */ 
2038                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2039         remain 
= fp
->fsize 
- fp
->fptr
; 
2040         if (btr 
> remain
) btr 
= (UINT
)remain
;                   /* Truncate btr by remaining bytes */ 
2042         for ( ;  btr
;                                                                   /* Repeat until all data transferred */ 
2043                 rbuff 
+= rcnt
, fp
->fptr 
+= rcnt
, *br 
+= rcnt
, btr 
-= rcnt
) { 
2044                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
2045                         csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
2046                         if (!csect
) {                                                   /* On the cluster boundary? */ 
2047                                 clst 
= (fp
->fptr 
== 0) ?                        
/* On the top of the file? */ 
2048                                         fp
->org_clust 
: get_fat(fp
->fs
, fp
->curr_clust
); 
2049                                 if (clst 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2050                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2051                                 fp
->curr_clust 
= clst
;                          /* Update current cluster */ 
2053                         sect 
= clust2sect(fp
->fs
, fp
->curr_clust
);      /* Get current sector */ 
2054                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2056                         cc 
= btr 
/ SS(fp
->fs
);                                  /* When remaining bytes >= sector size, */ 
2057                         if (cc
) {                                                               /* Read maximum contiguous sectors directly */ 
2058                                 if (csect 
+ cc 
> fp
->fs
->csize
)         /* Clip at cluster boundary */ 
2059                                         cc 
= fp
->fs
->csize 
- csect
; 
2060                                 if (disk_read(fp
->fs
->drv
, rbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
2061                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2062 #if !_FS_READONLY && _FS_MINIMIZE <= 2                          /* Replace one of the read sectors with cached data if it contains a dirty sector */ 
2064                                 if (fp
->fs
->wflag 
&& fp
->fs
->winsect 
- sect 
< cc
) 
2065                                         mem_cpy(rbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), fp
->fs
->win
, SS(fp
->fs
)); 
2067                                 if ((fp
->flag 
& FA__DIRTY
) && fp
->dsect 
- sect 
< cc
) 
2068                                         mem_cpy(rbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), fp
->buf
, SS(fp
->fs
)); 
2071                                 rcnt 
= SS(fp
->fs
) * cc
;                         /* Number of bytes transferred */ 
2076                         if (fp
->flag 
& FA__DIRTY
) {                     /* Write sector I/O buffer if needed */ 
2077                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2078                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2079                                 fp
->flag 
&= ~FA__DIRTY
; 
2082                         if (fp
->dsect 
!= sect
) {                        /* Fill sector buffer with file data */ 
2083                                 if (disk_read(fp
->fs
->drv
, fp
->buf
, sect
, 1) != RES_OK
) 
2084                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2089                 rcnt 
= SS(fp
->fs
) - (fp
->fptr 
% SS(fp
->fs
));    /* Get partial sector data from sector buffer */ 
2090                 if (rcnt 
> btr
) rcnt 
= btr
; 
2092                 if (move_window(fp
->fs
, fp
->dsect
))                     /* Move sector window */ 
2093                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2094                 mem_cpy(rbuff
, &fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);      /* Pick partial sector */ 
2096                 mem_cpy(rbuff
, &fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);  /* Pick partial sector */ 
2100         LEAVE_FF(fp
->fs
, FR_OK
); 
2107 /*-----------------------------------------------------------------------*/ 
2109 /*-----------------------------------------------------------------------*/ 
2112         FIL 
*fp
,                        /* Pointer to the file object */ 
2113         const void *buff
,       /* Pointer to the data to be written */ 
2114         UINT btw
,                       /* Number of bytes to write */ 
2115         UINT 
*bw                        
/* Pointer to number of bytes written */ 
2121         const BYTE 
*wbuff 
= buff
; 
2125         *bw 
= 0;        /* Initialize byte counter */ 
2127         res 
= validate(fp
->fs
, fp
->id
);                                 /* Check validity of the object */ 
2128         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2129         if (fp
->flag 
& FA__ERROR
)                                               /* Check abort flag */ 
2130                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2131         if (!(fp
->flag 
& FA_WRITE
))                                             /* Check access mode */ 
2132                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2133         if (fp
->fsize 
+ btw 
< fp
->fsize
) btw 
= 0;               /* File size cannot reach 4GB */ 
2135         for ( ;  btw
;                                                                   /* Repeat until all data transferred */ 
2136                 wbuff 
+= wcnt
, fp
->fptr 
+= wcnt
, *bw 
+= wcnt
, btw 
-= wcnt
) { 
2137                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
2138                         csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
2139                         if (!csect
) {                                                   /* On the cluster boundary? */ 
2140                                 if (fp
->fptr 
== 0) {                            /* On the top of the file? */ 
2141                                         clst 
= fp
->org_clust
;                   /* Follow from the origin */ 
2142                                         if (clst 
== 0)                                  /* When there is no cluster chain, */ 
2143                                                 fp
->org_clust 
= clst 
= create_chain(fp
->fs
, 0); /* Create a new cluster chain */ 
2144                                 } else {                                                        /* Middle or end of the file */ 
2145                                         clst 
= create_chain(fp
->fs
, fp
->curr_clust
);                    /* Follow or stretch cluster chain */ 
2147                                 if (clst 
== 0) break;                           /* Could not allocate a new cluster (disk full) */ 
2148                                 if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2149                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2150                                 fp
->curr_clust 
= clst
;                          /* Update current cluster */ 
2153                         if (fp
->fs
->winsect 
== fp
->dsect 
&& move_window(fp
->fs
, 0))     /* Write back data buffer prior to following direct transfer */ 
2154                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2156                         if (fp
->flag 
& FA__DIRTY
) {             /* Write back data buffer prior to following direct transfer */ 
2157                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2158                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2159                                 fp
->flag 
&= ~FA__DIRTY
; 
2162                         sect 
= clust2sect(fp
->fs
, fp
->curr_clust
);      /* Get current sector */ 
2163                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2165                         cc 
= btw 
/ SS(fp
->fs
);                                  /* When remaining bytes >= sector size, */ 
2166                         if (cc
) {                                                               /* Write maximum contiguous sectors directly */ 
2167                                 if (csect 
+ cc 
> fp
->fs
->csize
)         /* Clip at cluster boundary */ 
2168                                         cc 
= fp
->fs
->csize 
- csect
; 
2169                                 if (disk_write(fp
->fs
->drv
, wbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
2170                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2172                                 if (fp
->fs
->winsect 
- sect 
< cc
) {      /* Refill sector cache if it gets dirty by the direct write */ 
2173                                         mem_cpy(fp
->fs
->win
, wbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
2177                                 if (fp
->dsect 
- sect 
< cc
) {            /* Refill sector cache if it gets dirty by the direct write */ 
2178                                         mem_cpy(fp
->buf
, wbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
2179                                         fp
->flag 
&= ~FA__DIRTY
; 
2182                                 wcnt 
= SS(fp
->fs
) * cc
;                         /* Number of bytes transferred */ 
2186                         if (fp
->fptr 
>= fp
->fsize
) {                    /* Avoid silly buffer filling at growing edge */ 
2187                                 if (move_window(fp
->fs
, 0)) ABORT(fp
->fs
, FR_DISK_ERR
); 
2188                                 fp
->fs
->winsect 
= sect
; 
2191                         if (fp
->dsect 
!= sect
) {                                /* Fill sector buffer with file data */ 
2192                                 if (fp
->fptr 
< fp
->fsize 
&& 
2193                                         disk_read(fp
->fs
->drv
, fp
->buf
, sect
, 1) != RES_OK
) 
2194                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2199                 wcnt 
= SS(fp
->fs
) - (fp
->fptr 
% SS(fp
->fs
));    /* Put partial sector into file I/O buffer */ 
2200                 if (wcnt 
> btw
) wcnt 
= btw
; 
2202                 if (move_window(fp
->fs
, fp
->dsect
))                     /* Move sector window */ 
2203                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2204                 mem_cpy(&fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);      /* Fit partial sector */ 
2207                 mem_cpy(&fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);  /* Fit partial sector */ 
2208                 fp
->flag 
|= FA__DIRTY
; 
2212         if (fp
->fptr 
> fp
->fsize
) fp
->fsize 
= fp
->fptr
; /* Update file size if needed */ 
2213         fp
->flag 
|= FA__WRITTEN
;                                                /* Set file changed flag */ 
2215         LEAVE_FF(fp
->fs
, FR_OK
); 
2221 /*-----------------------------------------------------------------------*/ 
2222 /* Synchronize the File Object                                           */ 
2223 /*-----------------------------------------------------------------------*/ 
2226         FIL 
*fp         
/* Pointer to the file object */ 
2234         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
2236                 if (fp
->flag 
& FA__WRITTEN
) {   /* Has the file been written? */ 
2237 #if !_FS_TINY   /* Write-back dirty buffer */ 
2238                         if (fp
->flag 
& FA__DIRTY
) { 
2239                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2240                                         LEAVE_FF(fp
->fs
, FR_DISK_ERR
); 
2241                                 fp
->flag 
&= ~FA__DIRTY
; 
2244                         /* Update the directory entry */ 
2245                         res 
= move_window(fp
->fs
, fp
->dir_sect
); 
2248                                 dir
[DIR_Attr
] |= AM_ARC
;                                        /* Set archive bit */ 
2249                                 ST_DWORD(dir
+DIR_FileSize
, fp
->fsize
);          /* Update file size */ 
2250                                 ST_WORD(dir
+DIR_FstClusLO
, fp
->org_clust
);      /* Update start cluster */ 
2251                                 ST_WORD(dir
+DIR_FstClusHI
, fp
->org_clust 
>> 16); 
2252                                 tim 
= get_fattime();                                            /* Update updated time */ 
2253                                 ST_DWORD(dir
+DIR_WrtTime
, tim
); 
2254                                 fp
->flag 
&= ~FA__WRITTEN
; 
2261         LEAVE_FF(fp
->fs
, res
); 
2264 #endif /* !_FS_READONLY */ 
2269 /*-----------------------------------------------------------------------*/ 
2271 /*-----------------------------------------------------------------------*/ 
2274         FIL 
*fp         
/* Pointer to the file object to be closed */ 
2281         res 
= validate(fs
, fp
->id
); 
2282         if (res 
== FR_OK
) fp
->fs 
= 0;   /* Discard file object */ 
2286         res 
= f_sync(fp
);               /* Flush cached data */ 
2288         if (res 
== FR_OK
) {             /* Decrement open counter */ 
2290                 res 
= validate(fp
->fs
, fp
->id
); 
2292                         res 
= dec_lock(fp
->fs
, fp
->lockid
); 
2293                         unlock_fs(fp
->fs
, FR_OK
); 
2296                 res 
= dec_lock(fp
->fs
, fp
->lockid
); 
2300         if (res 
== FR_OK
) fp
->fs 
= 0;   /* Discard file object */ 
2308 /*-----------------------------------------------------------------------*/ 
2309 /* Change Current Drive/Directory                                        */ 
2310 /*-----------------------------------------------------------------------*/ 
2315         BYTE drv                
/* Drive number */ 
2318         if (drv 
>= _DRIVES
) return FR_INVALID_DRIVE
; 
2329         const TCHAR 
*path       
/* Pointer to the directory path */ 
2338         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2341                 res 
= follow_path(&dj
, path
);           /* Follow the path */ 
2343                 if (res 
== FR_OK
) {                                     /* Follow completed */ 
2344                         dir 
= dj
.dir
;                                   /* Pointer to the entry */ 
2346                                 dj
.fs
->cdir 
= dj
.sclust
;        /* Start directory itself */ 
2348                                 if (dir
[DIR_Attr
] & AM_DIR
)     /* Reached to the directory */ 
2349                                         dj
.fs
->cdir 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
2351                                         res 
= FR_NO_PATH
;               /* Reached but a file */ 
2354                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
2357         LEAVE_FF(dj
.fs
, res
); 
2364 #if _FS_MINIMIZE <= 2 
2365 /*-----------------------------------------------------------------------*/ 
2366 /* Seek File R/W Pointer                                                 */ 
2367 /*-----------------------------------------------------------------------*/ 
2370         FIL 
*fp
,                /* Pointer to the file object */ 
2371         DWORD ofs               
/* File pointer from top of file */ 
2377         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
2378         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2379         if (fp
->flag 
& FA__ERROR
)                       /* Check abort flag */ 
2380                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2383         if (fp
->cltbl
) {        /* Fast seek */ 
2384                 DWORD cl
, pcl
, ncl
, tcl
, dsc
, tlen
, *tbl 
= fp
->cltbl
; 
2388                 if (ofs 
== CREATE_LINKMAP
) {    /* Create link map table */ 
2392                                         if (tlen 
< 4) { /* Not enough table items */ 
2393                                                 res 
= FR_NOT_ENOUGH_CORE
; break; 
2396                                         do {            /* Get a fragment and store the top and length */ 
2398                                                 cl 
= get_fat(fp
->fs
, cl
); 
2399                                                 if (cl 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2400                                                 if (cl 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2401                                         } while (cl 
== pcl 
+ 1); 
2402                                         *tbl
++ = ncl
; *tbl
++ = tcl
; 
2404                                 } while (cl 
< fp
->fs
->n_fatent
); 
2406                         *tbl 
= 0;       /* Terminate table */ 
2408                 } else {                                                /* Fast seek */ 
2409                         if (ofs 
> fp
->fsize
)            /* Clip offset at the file size */ 
2411                         fp
->fptr 
= ofs
;                         /* Set file pointer */ 
2413                                 dsc 
= (ofs 
- 1) / SS(fp
->fs
); 
2414                                 cl 
= dsc 
/ fp
->fs
->csize
; 
2417                                         if (!ncl
) ABORT(fp
->fs
, FR_INT_ERR
); 
2418                                         if (cl 
< ncl
) break; 
2421                                 fp
->curr_clust 
= cl 
+ *tbl
; 
2422                                 csc 
= (BYTE
)(dsc 
& (fp
->fs
->csize 
- 1)); 
2423                                 dsc 
= clust2sect(fp
->fs
, fp
->curr_clust
); 
2424                                 if (!dsc
) ABORT(fp
->fs
, FR_INT_ERR
); 
2426                                 if (fp
->fptr 
% SS(fp
->fs
) && dsc 
!= fp
->dsect
) { 
2429                                         if (fp
->flag 
& FA__DIRTY
) {             /* Flush dirty buffer if needed */ 
2430                                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2431                                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2432                                                 fp
->flag 
&= ~FA__DIRTY
; 
2435                                         if (disk_read(fp
->fs
->drv
, fp
->buf
, dsc
, 1) != RES_OK
) 
2436                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2447                 DWORD clst
, bcs
, nsect
, ifptr
; 
2449                 if (ofs 
> fp
->fsize                                     
/* In read-only mode, clip offset with the file size */ 
2451                          && !(fp
->flag 
& FA_WRITE
) 
2456                 fp
->fptr 
= nsect 
= 0; 
2458                         bcs 
= (DWORD
)fp
->fs
->csize 
* SS(fp
->fs
);        /* Cluster size (byte) */ 
2460                                 (ofs 
- 1) / bcs 
>= (ifptr 
- 1) / bcs
) { /* When seek to same or following cluster, */ 
2461                                 fp
->fptr 
= (ifptr 
- 1) & ~(bcs 
- 1);    /* start from the current cluster */ 
2463                                 clst 
= fp
->curr_clust
; 
2464                         } else {                                                                        /* When seek to back cluster, */ 
2465                                 clst 
= fp
->org_clust
;                                   /* start from the first cluster */ 
2467                                 if (clst 
== 0) {                                                /* If no cluster chain, create a new chain */ 
2468                                         clst 
= create_chain(fp
->fs
, 0); 
2469                                         if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2470                                         if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2471                                         fp
->org_clust 
= clst
; 
2474                                 fp
->curr_clust 
= clst
; 
2477                                 while (ofs 
> bcs
) {                                             /* Cluster following loop */ 
2479                                         if (fp
->flag 
& FA_WRITE
) {                      /* Check if in write mode or not */ 
2480                                                 clst 
= create_chain(fp
->fs
, clst
);      /* Force stretch if in write mode */ 
2481                                                 if (clst 
== 0) {                                /* When disk gets full, clip file size */ 
2486                                                 clst 
= get_fat(fp
->fs
, clst
);   /* Follow cluster chain if not in write mode */ 
2487                                         if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2488                                         if (clst 
<= 1 || clst 
>= fp
->fs
->n_fatent
) ABORT(fp
->fs
, FR_INT_ERR
); 
2489                                         fp
->curr_clust 
= clst
; 
2494                                 if (ofs 
% SS(fp
->fs
)) { 
2495                                         nsect 
= clust2sect(fp
->fs
, clst
);       /* Current sector */ 
2496                                         if (!nsect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2497                                         nsect 
+= ofs 
/ SS(fp
->fs
); 
2501                 if (fp
->fptr 
% SS(fp
->fs
) && nsect 
!= fp
->dsect
) { 
2504                         if (fp
->flag 
& FA__DIRTY
) {                     /* Flush dirty buffer if needed */ 
2505                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2506                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2507                                 fp
->flag 
&= ~FA__DIRTY
; 
2510                         if (disk_read(fp
->fs
->drv
, fp
->buf
, nsect
, 1) != RES_OK
) 
2511                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2516                 if (fp
->fptr 
> fp
->fsize
) {                     /* Set changed flag if the file size is extended */ 
2517                         fp
->fsize 
= fp
->fptr
; 
2518                         fp
->flag 
|= FA__WRITTEN
; 
2523         LEAVE_FF(fp
->fs
, res
); 
2528 #if _FS_MINIMIZE <= 1 
2529 /*-----------------------------------------------------------------------*/ 
2530 /* Create a Directory Object                                             */ 
2531 /*-----------------------------------------------------------------------*/ 
2534         DIR *dj
,                        /* Pointer to directory object to create */ 
2535         const TCHAR 
*path       
/* Pointer to the directory path */ 
2543         res 
= chk_mounted(&path
, &dj
->fs
, 0); 
2546                 res 
= follow_path(dj
, path
);                    /* Follow the path to the directory */ 
2548                 if (res 
== FR_OK
) {                                             /* Follow completed */ 
2550                         if (dir
) {                                                      /* It is not the current dir */ 
2551                                 if (dir
[DIR_Attr
] & AM_DIR
) {   /* The object is a directory */ 
2552                                         dj
->sclust 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
2553                                 } else {                                                /* The object is not a directory */ 
2558                                 dj
->id 
= dj
->fs
->id
; 
2559                                 res 
= dir_sdi(dj
, 0);                   /* Rewind dir */ 
2562                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
2565         LEAVE_FF(dj
->fs
, res
); 
2571 /*-----------------------------------------------------------------------*/ 
2572 /* Read Directory Entry in Sequence                                      */ 
2573 /*-----------------------------------------------------------------------*/ 
2576         DIR *dj
,                        /* Pointer to the open directory object */ 
2577         FILINFO 
*fno            
/* Pointer to file information to return */ 
2584         res 
= validate(dj
->fs
, dj
->id
);                 /* Check validity of the object */ 
2587                         res 
= dir_sdi(dj
, 0); 
2591                         if (res 
== FR_NO_FILE
) { 
2595                         if (res 
== FR_OK
) {                             /* A valid entry is found */ 
2596                                 get_fileinfo(dj
, fno
);          /* Get the object information */ 
2597                                 res 
= dir_next(dj
, 0);          /* Increment index for next */ 
2598                                 if (res 
== FR_NO_FILE
) { 
2607         LEAVE_FF(dj
->fs
, res
); 
2612 #if _FS_MINIMIZE == 0 
2613 /*-----------------------------------------------------------------------*/ 
2614 /* Get File Status                                                       */ 
2615 /*-----------------------------------------------------------------------*/ 
2618         const TCHAR 
*path
,      /* Pointer to the file path */ 
2619         FILINFO 
*fno            
/* Pointer to file information to return */ 
2627         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2630                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
2631                 if (res 
== FR_OK
) {                             /* Follow completed */ 
2632                         if (dj
.dir
)             /* Found an object */ 
2633                                 get_fileinfo(&dj
, fno
); 
2634                         else                    /* It is root dir */ 
2635                                 res 
= FR_INVALID_NAME
; 
2640         LEAVE_FF(dj
.fs
, res
); 
2646 /*-----------------------------------------------------------------------*/ 
2647 /* Get Number of Free Clusters                                           */ 
2648 /*-----------------------------------------------------------------------*/ 
2651         const TCHAR 
*path
,      /* Pointer to the logical drive number (root dir) */ 
2652         DWORD 
*nclst
,           /* Pointer to the variable to return number of free clusters */ 
2653         FATFS 
**fatfs           
/* Pointer to pointer to corresponding file system object to return */ 
2657         DWORD n
, clst
, sect
, stat
; 
2662         /* Get drive number */ 
2663         res 
= chk_mounted(&path
, fatfs
, 0); 
2665                 /* If free_clust is valid, return it without full cluster scan */ 
2666                 if ((*fatfs
)->free_clust 
<= (*fatfs
)->n_fatent 
- 2) { 
2667                         *nclst 
= (*fatfs
)->free_clust
; 
2669                         /* Get number of free clusters */ 
2670                         fat 
= (*fatfs
)->fs_type
; 
2672                         if (fat 
== FS_FAT12
) { 
2675                                         stat 
= get_fat(*fatfs
, clst
); 
2676                                         if (stat 
== 0xFFFFFFFF) { res 
= FR_DISK_ERR
; break; } 
2677                                         if (stat 
== 1) { res 
= FR_INT_ERR
; break; } 
2679                                 } while (++clst 
< (*fatfs
)->n_fatent
); 
2681                                 clst 
= (*fatfs
)->n_fatent
; 
2682                                 sect 
= (*fatfs
)->fatbase
; 
2686                                                 res 
= move_window(*fatfs
, sect
++); 
2687                                                 if (res 
!= FR_OK
) break; 
2691                                         if (fat 
== FS_FAT16
) { 
2692                                                 if (LD_WORD(p
) == 0) n
++; 
2695                                                 if ((LD_DWORD(p
) & 0x0FFFFFFF) == 0) n
++; 
2700                         (*fatfs
)->free_clust 
= n
; 
2701                         if (fat 
== FS_FAT32
) (*fatfs
)->fsi_flag 
= 1; 
2705         LEAVE_FF(*fatfs
, res
); 
2711 /*-----------------------------------------------------------------------*/ 
2713 /*-----------------------------------------------------------------------*/ 
2715 FRESULT 
f_truncate ( 
2716         FIL 
*fp         
/* Pointer to the file object */ 
2723         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
2725                 if (fp
->flag 
& FA__ERROR
) {                     /* Check abort flag */ 
2728                         if (!(fp
->flag 
& FA_WRITE
))             /* Check access mode */ 
2733                 if (fp
->fsize 
> fp
->fptr
) { 
2734                         fp
->fsize 
= fp
->fptr
;   /* Set file size to current R/W point */ 
2735                         fp
->flag 
|= FA__WRITTEN
; 
2736                         if (fp
->fptr 
== 0) {    /* When set file size to zero, remove entire cluster chain */ 
2737                                 res 
= remove_chain(fp
->fs
, fp
->org_clust
); 
2739                         } else {                                /* When truncate a part of the file, remove remaining clusters */ 
2740                                 ncl 
= get_fat(fp
->fs
, fp
->curr_clust
); 
2742                                 if (ncl 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
2743                                 if (ncl 
== 1) res 
= FR_INT_ERR
; 
2744                                 if (res 
== FR_OK 
&& ncl 
< fp
->fs
->n_fatent
) { 
2745                                         res 
= put_fat(fp
->fs
, fp
->curr_clust
, 0x0FFFFFFF); 
2746                                         if (res 
== FR_OK
) res 
= remove_chain(fp
->fs
, ncl
); 
2750                 if (res 
!= FR_OK
) fp
->flag 
|= FA__ERROR
; 
2753         LEAVE_FF(fp
->fs
, res
); 
2759 /*-----------------------------------------------------------------------*/ 
2760 /* Delete a File or Directory                                            */ 
2761 /*-----------------------------------------------------------------------*/ 
2764         const TCHAR 
*path               
/* Pointer to the file or directory path */ 
2774         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2777                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
2778                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2779                         res 
= FR_INVALID_NAME
;                  /* Cannot remove dot entry */ 
2781                 if (res 
== FR_OK
) res 
= chk_lock(&dj
, 2);       /* Cannot remove open file */ 
2783                 if (res 
== FR_OK
) {                                     /* The object is accessible */ 
2786                                 res 
= FR_INVALID_NAME
;          /* Cannot remove the start directory */ 
2788                                 if (dir
[DIR_Attr
] & AM_RDO
) 
2789                                         res 
= FR_DENIED
;                /* Cannot remove R/O object */ 
2791                         dclst 
= ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) | LD_WORD(dir
+DIR_FstClusLO
); 
2792                         if (res 
== FR_OK 
&& (dir
[DIR_Attr
] & AM_DIR
)) { /* Is it a sub-dir? */ 
2796                                         mem_cpy(&sdj
, &dj
, sizeof(DIR));        /* Check if the sub-dir is empty or not */ 
2798                                         res 
= dir_sdi(&sdj
, 2);         /* Exclude dot entries */ 
2800                                                 res 
= dir_read(&sdj
); 
2801                                                 if (res 
== FR_OK                        
/* Not empty dir */ 
2803                                                 || dclst 
== sdj
.fs
->cdir        
/* Current dir */ 
2806                                                 if (res 
== FR_NO_FILE
) res 
= FR_OK
;     /* Empty */ 
2811                                 res 
= dir_remove(&dj
);          /* Remove the directory entry */ 
2813                                         if (dclst
)                              /* Remove the cluster chain if exist */ 
2814                                                 res 
= remove_chain(dj
.fs
, dclst
); 
2815                                         if (res 
== FR_OK
) res 
= sync(dj
.fs
); 
2821         LEAVE_FF(dj
.fs
, res
); 
2827 /*-----------------------------------------------------------------------*/ 
2828 /* Create a Directory                                                    */ 
2829 /*-----------------------------------------------------------------------*/ 
2832         const TCHAR 
*path               
/* Pointer to the directory path */ 
2838         DWORD dsc
, dcl
, pcl
, tim 
= get_fattime(); 
2842         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2845                 res 
= follow_path(&dj
, path
);                   /* Follow the file path */ 
2846                 if (res 
== FR_OK
) res 
= FR_EXIST
;               /* Any object with same name is already existing */ 
2847                 if (_FS_RPATH 
&& res 
== FR_NO_FILE 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2848                         res 
= FR_INVALID_NAME
; 
2849                 if (res 
== FR_NO_FILE
) {                                /* Can create a new directory */ 
2850                         dcl 
= create_chain(dj
.fs
, 0);           /* Allocate a cluster for the new directory table */ 
2852                         if (dcl 
== 0) res 
= FR_DENIED
;          /* No space to allocate a new cluster */ 
2853                         if (dcl 
== 1) res 
= FR_INT_ERR
; 
2854                         if (dcl 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
2855                         if (res 
== FR_OK
)                                       /* Flush FAT */ 
2856                                 res 
= move_window(dj
.fs
, 0); 
2857                         if (res 
== FR_OK
) {                                     /* Initialize the new directory table */ 
2858                                 dsc 
= clust2sect(dj
.fs
, dcl
); 
2860                                 mem_set(dir
, 0, SS(dj
.fs
)); 
2861                                 mem_set(dir
+DIR_Name
, ' ', 8+3);        /* Create "." entry */ 
2862                                 dir
[DIR_Name
] = '.'; 
2863                                 dir
[DIR_Attr
] = AM_DIR
; 
2864                                 ST_DWORD(dir
+DIR_WrtTime
, tim
); 
2865                                 ST_WORD(dir
+DIR_FstClusLO
, dcl
); 
2866                                 ST_WORD(dir
+DIR_FstClusHI
, dcl 
>> 16); 
2867                                 mem_cpy(dir
+32, dir
, 32);                       /* Create ".." entry */ 
2868                                 dir
[33] = '.'; pcl 
= dj
.sclust
; 
2869                                 if (dj
.fs
->fs_type 
== FS_FAT32 
&& pcl 
== dj
.fs
->dirbase
) 
2871                                 ST_WORD(dir
+32+DIR_FstClusLO
, pcl
); 
2872                                 ST_WORD(dir
+32+DIR_FstClusHI
, pcl 
>> 16); 
2873                                 for (n 
= dj
.fs
->csize
; n
; n
--) {        /* Write dot entries and clear following sectors */ 
2874                                         dj
.fs
->winsect 
= dsc
++; 
2876                                         res 
= move_window(dj
.fs
, 0); 
2877                                         if (res 
!= FR_OK
) break; 
2878                                         mem_set(dir
, 0, SS(dj
.fs
)); 
2881                         if (res 
== FR_OK
) res 
= dir_register(&dj
);      /* Register the object to the directory */ 
2883                                 remove_chain(dj
.fs
, dcl
);                               /* Could not register, remove cluster chain */ 
2886                                 dir
[DIR_Attr
] = AM_DIR
;                                 /* Attribute */ 
2887                                 ST_DWORD(dir
+DIR_WrtTime
, tim
);                 /* Created time */ 
2888                                 ST_WORD(dir
+DIR_FstClusLO
, dcl
);                /* Table start cluster */ 
2889                                 ST_WORD(dir
+DIR_FstClusHI
, dcl 
>> 16); 
2897         LEAVE_FF(dj
.fs
, res
); 
2903 /*-----------------------------------------------------------------------*/ 
2904 /* Change Attribute                                                      */ 
2905 /*-----------------------------------------------------------------------*/ 
2908         const TCHAR 
*path
,      /* Pointer to the file path */ 
2909         BYTE value
,                     /* Attribute bits */ 
2910         BYTE mask                       
/* Attribute mask to change */ 
2919         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2922                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
2924                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2925                         res 
= FR_INVALID_NAME
; 
2928                         if (!dir
) {                                             /* Is it a root directory? */ 
2929                                 res 
= FR_INVALID_NAME
; 
2930                         } else {                                                /* File or sub directory */ 
2931                                 mask 
&= AM_RDO
|AM_HID
|AM_SYS
|AM_ARC
;    /* Valid attribute mask */ 
2932                                 dir
[DIR_Attr
] = (value 
& mask
) | (dir
[DIR_Attr
] & (BYTE
)~mask
); /* Apply attribute change */ 
2939         LEAVE_FF(dj
.fs
, res
); 
2945 /*-----------------------------------------------------------------------*/ 
2946 /* Change Time-stamp                                                      */ 
2947 /*-----------------------------------------------------------------------*/ 
2950         const TCHAR 
*path
,      /* Pointer to the file/directory name */ 
2951         const FILINFO 
*fno      
/* Pointer to the time stamp to be set */ 
2960         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
2963                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
2965                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
2966                         res 
= FR_INVALID_NAME
; 
2969                         if (!dir
) {                                     /* Root directory */ 
2970                                 res 
= FR_INVALID_NAME
; 
2971                         } else {                                        /* File or sub-directory */ 
2972                                 ST_WORD(dir
+DIR_WrtTime
, fno
->ftime
); 
2973                                 ST_WORD(dir
+DIR_WrtDate
, fno
->fdate
); 
2980         LEAVE_FF(dj
.fs
, res
); 
2986 /*-----------------------------------------------------------------------*/ 
2987 /* Rename File/Directory                                                 */ 
2988 /*-----------------------------------------------------------------------*/ 
2991         const TCHAR 
*path_old
,  /* Pointer to the old name */ 
2992         const TCHAR 
*path_new   
/* Pointer to the new name */ 
3002         res 
= chk_mounted(&path_old
, &djo
.fs
, 1); 
3006                 res 
= follow_path(&djo
, path_old
);              /* Check old object */ 
3007                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (djo
.fn
[NS
] & NS_DOT
)) 
3008                         res 
= FR_INVALID_NAME
; 
3010                 if (res 
== FR_OK
) res 
= chk_lock(&djo
, 2); 
3012                 if (res 
== FR_OK
) {                                             /* Old object is found */ 
3013                         if (!djo
.dir
) {                                         /* Is root dir? */ 
3016                                 mem_cpy(buf
, djo
.dir
+DIR_Attr
, 21);             /* Save the object information except for name */ 
3017                                 mem_cpy(&djn
, &djo
, sizeof(DIR));               /* Check new object */ 
3018                                 res 
= follow_path(&djn
, path_new
); 
3019                                 if (res 
== FR_OK
) res 
= FR_EXIST
;                       /* The new object name is already existing */ 
3020                                 if (res 
== FR_NO_FILE
) {                                        /* Is it a valid path and no name collision? */ 
3021 /* Start critical section that any interruption or error can cause cross-link */ 
3022                                         res 
= dir_register(&djn
);                       /* Register the new entry */ 
3024                                                 dir 
= djn
.dir
;                                  /* Copy object information except for name */ 
3025                                                 mem_cpy(dir
+13, buf
+2, 19); 
3026                                                 dir
[DIR_Attr
] = buf
[0] | AM_ARC
; 
3028                                                 if (djo
.sclust 
!= djn
.sclust 
&& (dir
[DIR_Attr
] & AM_DIR
)) {             /* Update .. entry in the directory if needed */ 
3029                                                         dw 
= clust2sect(djn
.fs
, (DWORD
)LD_WORD(dir
+DIR_FstClusHI
) | LD_WORD(dir
+DIR_FstClusLO
)); 
3033                                                                 res 
= move_window(djn
.fs
, dw
); 
3034                                                                 dir 
= djn
.fs
->win
+32;   /* .. entry */ 
3035                                                                 if (res 
== FR_OK 
&& dir
[1] == '.') { 
3036                                                                         dw 
= (djn
.fs
->fs_type 
== FS_FAT32 
&& djn
.sclust 
== djn
.fs
->dirbase
) ? 
0 : djn
.sclust
; 
3037                                                                         ST_WORD(dir
+DIR_FstClusLO
, dw
); 
3038                                                                         ST_WORD(dir
+DIR_FstClusHI
, dw 
>> 16); 
3044                                                         res 
= dir_remove(&djo
);         /* Remove old entry */ 
3049 /* End critical section */ 
3055         LEAVE_FF(djo
.fs
, res
); 
3058 #endif /* !_FS_READONLY */ 
3059 #endif /* _FS_MINIMIZE == 0 */ 
3060 #endif /* _FS_MINIMIZE <= 1 */ 
3061 #endif /* _FS_MINIMIZE <= 2 */ 
3065 /*-----------------------------------------------------------------------*/ 
3066 /* Forward data to the stream directly (available on only tiny cfg)      */ 
3067 /*-----------------------------------------------------------------------*/ 
3068 #if _USE_FORWARD && _FS_TINY 
3071         FIL 
*fp
,                                                /* Pointer to the file object */ 
3072         UINT (*func
)(const BYTE
*,UINT
), /* Pointer to the streaming function */ 
3073         UINT btr
,                                               /* Number of bytes to forward */ 
3074         UINT 
*bf                                                
/* Pointer to number of bytes forwarded */ 
3078         DWORD remain
, clst
, sect
; 
3083         *bf 
= 0;        /* Initialize byte counter */ 
3085         res 
= validate(fp
->fs
, fp
->id
);                                 /* Check validity of the object */ 
3086         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
3087         if (fp
->flag 
& FA__ERROR
)                                               /* Check error flag */ 
3088                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
3089         if (!(fp
->flag 
& FA_READ
))                                              /* Check access mode */ 
3090                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
3092         remain 
= fp
->fsize 
- fp
->fptr
; 
3093         if (btr 
> remain
) btr 
= (UINT
)remain
;                   /* Truncate btr by remaining bytes */ 
3095         for ( ;  btr 
&& (*func
)(0, 0);                                  /* Repeat until all data transferred or stream becomes busy */ 
3096                 fp
->fptr 
+= rcnt
, *bf 
+= rcnt
, btr 
-= rcnt
) { 
3097                 csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
3098                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
3099                         if (!csect
) {                                                   /* On the cluster boundary? */ 
3100                                 clst 
= (fp
->fptr 
== 0) ?                        
/* On the top of the file? */ 
3101                                         fp
->org_clust 
: get_fat(fp
->fs
, fp
->curr_clust
); 
3102                                 if (clst 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
3103                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
3104                                 fp
->curr_clust 
= clst
;                          /* Update current cluster */ 
3107                 sect 
= clust2sect(fp
->fs
, fp
->curr_clust
);      /* Get current data sector */ 
3108                 if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
3110                 if (move_window(fp
->fs
, sect
))                          /* Move sector window */ 
3111                         ABORT(fp
->fs
, FR_DISK_ERR
); 
3113                 rcnt 
= SS(fp
->fs
) - (WORD
)(fp
->fptr 
% SS(fp
->fs
));      /* Forward data from sector window */ 
3114                 if (rcnt 
> btr
) rcnt 
= btr
; 
3115                 rcnt 
= (*func
)(&fp
->fs
->win
[(WORD
)fp
->fptr 
% SS(fp
->fs
)], rcnt
); 
3116                 if (!rcnt
) ABORT(fp
->fs
, FR_INT_ERR
); 
3119         LEAVE_FF(fp
->fs
, FR_OK
); 
3121 #endif /* _USE_FORWARD */ 
3125 #if _USE_MKFS && !_FS_READONLY 
3126 /*-----------------------------------------------------------------------*/ 
3127 /* Create File System on the Drive                                       */ 
3128 /*-----------------------------------------------------------------------*/ 
3129 #define N_ROOTDIR       512                     /* Multiple of 32 */ 
3130 #define N_FATS          1                       /* 1 or 2 */ 
3134         BYTE drv
,               /* Logical drive number */ 
3135         BYTE sfd
,               /* Partitioning rule 0:FDISK, 1:SFD */ 
3136         UINT au                 
/* Allocation unit size [bytes] */ 
3139         static const WORD vst
[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0}; 
3140         static const WORD cst
[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; 
3142         DWORD n_clst
, vs
, n
; 
3144         DWORD b_vol
, b_fat
, b_dir
, b_data
;              /* Area offset (LBA) */ 
3145         DWORD n_vol
, n_rsv
, n_fat
, n_dir
;               /* Area size */ 
3150         /* Check mounted drive and clear work area */ 
3151         if (drv 
>= _DRIVES
) return FR_INVALID_DRIVE
; 
3153         if (!fs
) return FR_NOT_ENABLED
; 
3157         /* Get disk statics */ 
3158         stat 
= disk_initialize(drv
); 
3159         if (stat 
& STA_NOINIT
) return FR_NOT_READY
; 
3160         if (stat 
& STA_PROTECT
) return FR_WRITE_PROTECTED
; 
3161 #if _MAX_SS != 512                                      /* Get disk sector size */ 
3162         if (disk_ioctl(drv
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK 
|| SS(fs
) > _MAX_SS
) 
3165         if (disk_ioctl(drv
, GET_SECTOR_COUNT
, &n_vol
) != RES_OK 
|| n_vol 
< 128) 
3167         b_vol 
= (sfd 
== 1) ? 
0 : 63;    /* Volume start sector */ 
3169         if (au 
& (au 
- 1)) au 
= 0;              /* Check validity of the allocation unit size */ 
3170         if (!au
) {                                              /* AU auto selection */ 
3171                 vs 
= n_vol 
/ (2000 / (SS(fs
) / 512)); 
3172                 for (i 
= 0; vs 
< vst
[i
]; i
++) ; 
3175         if (_MAX_SS 
!= 512 && au 
< SS(fs
)) au 
= SS(fs
); 
3176         au 
/= SS(fs
);           /* Number of sectors per cluster */ 
3177         if (au 
== 0) au 
= 1; 
3178         if (au 
> 128) au 
= 128; 
3180         /* Pre-compute number of clusters and FAT sub-type */ 
3181         n_clst 
= n_vol 
/ au
; 
3183         if (n_clst 
>= MIN_FAT16
) fmt 
= FS_FAT16
; 
3184         if (n_clst 
>= MIN_FAT32
) fmt 
= FS_FAT32
; 
3186         /* Determine offset and size of FAT structure */ 
3187         if (fmt 
== FS_FAT32
) { 
3188                 n_fat 
= ((n_clst 
* 4) + 8 + SS(fs
) - 1) / SS(fs
); 
3192                 n_fat 
= (fmt 
== FS_FAT12
) ? 
(n_clst 
* 3 + 1) / 2 + 3 : (n_clst 
* 2) + 4; 
3193                 n_fat 
= (n_fat 
+ SS(fs
) - 1) / SS(fs
); 
3195                 n_dir 
= N_ROOTDIR 
* 32UL / SS(fs
); 
3197         b_fat 
= b_vol 
+ n_rsv
;                          /* FAT area start sector */ 
3198         b_dir 
= b_fat 
+ n_fat 
* N_FATS
;         /* Directory area start sector */ 
3199         b_data 
= b_dir 
+ n_dir
;                         /* Data area start sector */ 
3200         if (n_vol 
< b_data 
+ au
) return FR_MKFS_ABORTED
;        /* Too small volume */ 
3202         /* Align data start sector to erase block boundary (for flash memory media) */ 
3203         if (disk_ioctl(drv
, GET_BLOCK_SIZE
, &n
) != RES_OK
) return FR_DISK_ERR
; 
3204         if (!n 
|| n 
> 32768) return FR_MKFS_ABORTED
; 
3205         n 
= (b_data 
+ n 
- 1) & ~(n 
- 1);        /* Next nearest boundary from current data start */ 
3206         n 
= (n 
- b_data
) / N_FATS
; 
3207         if (fmt 
== FS_FAT32
) {          /* FAT32: Move FAT start */ 
3210         } else {                                        /* FAT12/16: Expand FAT size */ 
3213         /* b_dir and b_data are no longer used below */ 
3215         /* Determine number of cluster and final check of validity of the FAT sub-type */ 
3216         n_clst 
= (n_vol 
- n_rsv 
- n_fat 
* N_FATS 
- n_dir
) / au
; 
3217         if (   (fmt 
== FS_FAT16 
&& n_clst 
< MIN_FAT16
) 
3218                 || (fmt 
== FS_FAT32 
&& n_clst 
< MIN_FAT32
)) 
3219                 return FR_MKFS_ABORTED
; 
3221         /* Create partition table if required */ 
3225                 DWORD n_disk 
= b_vol 
+ n_vol
; 
3227                 mem_set(fs
->win
, 0, SS(fs
)); 
3228                 tbl 
= fs
->win
+MBR_Table
; 
3229                 ST_DWORD(tbl
, 0x00010180);              /* Partition start in CHS */ 
3230                 if (n_disk 
< 63UL * 255 * 1024) {       /* Partition end in CHS */ 
3231                         n_disk 
= n_disk 
/ 63 / 255; 
3232                         tbl
[7] = (BYTE
)n_disk
; 
3233                         tbl
[6] = (BYTE
)((n_disk 
>> 2) | 63); 
3235                         ST_WORD(&tbl
[6], 0xFFFF); 
3238                 if (fmt 
!= FS_FAT32
)                    /* System ID */ 
3239                         tbl
[4] = (n_vol 
< 0x10000) ? 
0x04 : 0x06; 
3242                 ST_DWORD(tbl
+8, 63);                    /* Partition start in LBA */ 
3243                 ST_DWORD(tbl
+12, n_vol
);                /* Partition size in LBA */ 
3244                 ST_WORD(tbl
+64, 0xAA55);                /* Signature */ 
3245                 if (disk_write(drv
, fs
->win
, 0, 1) != RES_OK
) 
3251         tbl 
= fs
->win
;                                                          /* Clear buffer */ 
3252         mem_set(tbl
, 0, SS(fs
)); 
3253         ST_DWORD(tbl
+BS_jmpBoot
, 0x90FEEB);                     /* Boot code (jmp $, nop) */ 
3254         as 
= SS(fs
);                                                            /* Sector size */ 
3255         ST_WORD(tbl
+BPB_BytsPerSec
, as
); 
3256         tbl
[BPB_SecPerClus
] = (BYTE
)au
;                         /* Sectors per cluster */ 
3257         ST_WORD(tbl
+BPB_RsvdSecCnt
, n_rsv
);                     /* Reserved sectors */ 
3258         tbl
[BPB_NumFATs
] = N_FATS
;                                      /* Number of FATs */ 
3259         as 
= (fmt 
== FS_FAT32
) ? 
0 : N_ROOTDIR
;         /* Number of rootdir entries */ 
3260         ST_WORD(tbl
+BPB_RootEntCnt
, as
); 
3261         if (n_vol 
< 0x10000) {                                          /* Number of total sectors */ 
3262                 ST_WORD(tbl
+BPB_TotSec16
, n_vol
); 
3264                 ST_DWORD(tbl
+BPB_TotSec32
, n_vol
); 
3266         tbl
[BPB_Media
] = md
;                                            /* Media descriptor */ 
3267         ST_WORD(tbl
+BPB_SecPerTrk
, 63);                         /* Number of sectors per track */ 
3268         ST_WORD(tbl
+BPB_NumHeads
, 255);                         /* Number of heads */ 
3269         ST_DWORD(tbl
+BPB_HiddSec
, b_vol
);                       /* Hidden sectors */ 
3270         n 
= get_fattime();                                                      /* Use current time as VSN */ 
3271         if (fmt 
== FS_FAT32
) { 
3272                 ST_DWORD(tbl
+BS_VolID32
, n
);                    /* VSN */ 
3273                 ST_DWORD(tbl
+BPB_FATSz32
, n_fat
);               /* Number of sectors per FAT */ 
3274                 ST_DWORD(tbl
+BPB_RootClus
, 2);                  /* Root directory start cluster (2) */ 
3275                 ST_WORD(tbl
+BPB_FSInfo
, 1);                             /* FSInfo record offset (VBR+1) */ 
3276                 ST_WORD(tbl
+BPB_BkBootSec
, 6);                  /* Backup boot record offset (VBR+6) */ 
3277                 tbl
[BS_DrvNum32
] = 0x80;                                /* Drive number */ 
3278                 tbl
[BS_BootSig32
] = 0x29;                               /* Extended boot signature */ 
3279                 mem_cpy(tbl
+BS_VolLab32
, "NO NAME    FAT32   ", 19);    /* Volume label, FAT signature */ 
3281                 ST_DWORD(tbl
+BS_VolID
, n
);                              /* VSN */ 
3282                 ST_WORD(tbl
+BPB_FATSz16
, n_fat
);                /* Number of sectors per FAT */ 
3283                 tbl
[BS_DrvNum
] = 0x80;                                  /* Drive number */ 
3284                 tbl
[BS_BootSig
] = 0x29;                                 /* Extended boot signature */ 
3285                 mem_cpy(tbl
+BS_VolLab
, "NO NAME    FAT     ", 19);      /* Volume label, FAT signature */ 
3287         ST_WORD(tbl
+BS_55AA
, 0xAA55);                           /* Signature (Offset is fixed here regardless of sector size) */ 
3288         if (disk_write(drv
, tbl
, b_vol
, 1) != RES_OK
)   /* Original (VBR) */ 
3290         if (fmt 
== FS_FAT32
)                                            /* Backup (VBR+6) */ 
3291                 disk_write(drv
, tbl
, b_vol 
+ 6, 1); 
3293         /* Initialize FAT area */ 
3294         for (i 
= 0; i 
< N_FATS
; i
++) { 
3295                 mem_set(tbl
, 0, SS(fs
));                        /* 1st sector of the FAT  */ 
3296                 n 
= md
;                                                         /* Media descriptor byte */ 
3297                 if (fmt 
!= FS_FAT32
) { 
3298                         n 
|= (fmt 
== FS_FAT12
) ? 
0x00FFFF00 : 0xFFFFFF00; 
3299                         ST_DWORD(tbl
+0, n
);                             /* Reserve cluster #0-1 (FAT12/16) */ 
3302                         ST_DWORD(tbl
+0, n
);                             /* Reserve cluster #0-1 (FAT32) */ 
3303                         ST_DWORD(tbl
+4, 0x0FFFFFFF); 
3304                         ST_DWORD(tbl
+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */ 
3306                 if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
) 
3308                 mem_set(tbl
, 0, SS(fs
));                /* Fill following FAT entries with zero */ 
3309                 for (n 
= 1; n 
< n_fat
; n
++) {   /* This loop may take a time on FAT32 volume due to many single sector write */ 
3310                         if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
) 
3315         /* Initialize root directory */ 
3316         n 
= (fmt 
== FS_FAT32
) ? as 
: n_dir
; 
3318                 if (disk_write(drv
, tbl
, b_fat
++, 1) != RES_OK
) 
3322         /* Create FSInfo record if needed */ 
3323         if (fmt 
== FS_FAT32
) { 
3324                 ST_WORD(tbl
+BS_55AA
, 0xAA55); 
3325                 ST_DWORD(tbl
+FSI_LeadSig
, 0x41615252); 
3326                 ST_DWORD(tbl
+FSI_StrucSig
, 0x61417272); 
3327                 ST_DWORD(tbl
+FSI_Free_Count
, n_clst 
- 1); 
3328                 ST_DWORD(tbl
+FSI_Nxt_Free
, 0xFFFFFFFF); 
3329                 disk_write(drv
, tbl
, b_vol 
+ 1, 1);     /* Original (VBR+1) */ 
3330                 disk_write(drv
, tbl
, b_vol 
+ 7, 1);     /* Backup  (VBR+7) */ 
3333         return (disk_ioctl(drv
, CTRL_SYNC
, (void*)0) == RES_OK
) ? FR_OK 
: FR_DISK_ERR
; 
3336 #endif /* _USE_MKFS && !_FS_READONLY */ 
3342 /*-----------------------------------------------------------------------*/ 
3343 /* Get a string from the file                                            */ 
3344 /*-----------------------------------------------------------------------*/ 
3346         TCHAR
* buff
,    /* Pointer to the string buffer to read */ 
3347         int len
,                /* Size of string buffer (characters) */ 
3348         FIL
* fil                
/* Pointer to the file object */ 
3357         while (n 
< len 
- 1) {                   /* Read bytes until buffer gets filled */ 
3358                 f_read(fil
, s
, 1, &rc
); 
3359                 if (rc 
!= 1) break;                     /* Break on EOF or error */ 
3361 #if _LFN_UNICODE                                        /* Read a character in UTF-8 encoding */ 
3363                         if (c 
< 0xC0) continue; /* Skip stray trailer */ 
3364                         if (c 
< 0xE0) {                 /* Two-byte sequence */ 
3365                                 f_read(fil
, s
, 1, &rc
); 
3367                                 c 
= ((c 
& 0x1F) << 6) | (s
[0] & 0x3F); 
3368                                 if (c 
< 0x80) c 
= '?'; 
3370                                 if (c 
< 0xF0) {         /* Three-byte sequence */ 
3371                                         f_read(fil
, s
, 2, &rc
); 
3373                                         c 
= (c 
<< 12) | ((s
[0] & 0x3F) << 6) | (s
[1] & 0x3F); 
3374                                         if (c 
< 0x800) c 
= '?'; 
3375                                 } else {                        /* Reject four-byte sequence */ 
3381 #if _USE_STRFUNC >= 2 
3382                 if (c 
== '\r') continue;        /* Strip '\r' */ 
3386                 if (c 
== '\n') break;           /* Break on EOL */ 
3389         return n ? buff 
: 0;                    /* When no data read (eof or error), return with error. */ 
3396 /*-----------------------------------------------------------------------*/ 
3397 /* Put a character to the file                                           */ 
3398 /*-----------------------------------------------------------------------*/ 
3400         TCHAR c
,        /* A character to be output */ 
3401         FIL
* fil        
/* Pointer to the file object */ 
3408 #if _USE_STRFUNC >= 2 
3409         if (c 
== '\n') f_putc ('\r', fil
);      /* LF -> CRLF conversion */ 
3412 #if _LFN_UNICODE        /* Write the character in UTF-8 encoding */ 
3413         if (c 
< 0x80) {                 /* 7-bit */ 
3417                 if (c 
< 0x800) {        /* 11-bit */ 
3418                         s
[0] = (BYTE
)(0xC0 | (c 
>> 6)); 
3419                         s
[1] = (BYTE
)(0x80 | (c 
& 0x3F)); 
3421                 } else {                        /* 16-bit */ 
3422                         s
[0] = (BYTE
)(0xE0 | (c 
>> 12)); 
3423                         s
[1] = (BYTE
)(0x80 | ((c 
>> 6) & 0x3F)); 
3424                         s
[2] = (BYTE
)(0x80 | (c 
& 0x3F)); 
3428 #else                           /* Write the character without conversion */ 
3432         f_write(fil
, s
, btw
, &bw
);              /* Write the char to the file */ 
3433         return (bw 
== btw
) ? 
1 : EOF
;   /* Return the result */ 
3439 /*-----------------------------------------------------------------------*/ 
3440 /* Put a string to the file                                              */ 
3441 /*-----------------------------------------------------------------------*/ 
3443         const TCHAR
* str
,       /* Pointer to the string to be output */ 
3444         FIL
* fil                        
/* Pointer to the file object */ 
3450         for (n 
= 0; *str
; str
++, n
++) { 
3451                 if (f_putc(*str
, fil
) == EOF
) return EOF
; 
3459 /*-----------------------------------------------------------------------*/ 
3460 /* Put a formatted string to the file                                    */ 
3461 /*-----------------------------------------------------------------------*/ 
3463         FIL
* fil
,                       /* Pointer to the file object */ 
3464         const TCHAR
* str
,       /* Pointer to the format string */ 
3465         ...                                     /* Optional arguments... */ 
3478         for (cc 
= res 
= 0; cc 
!= EOF
; res 
+= cc
) { 
3480                 if (c 
== 0) break;                      /* End of string */ 
3481                 if (c 
!= '%') {                         /* Non escape character */ 
3482                         cc 
= f_putc(c
, fil
); 
3483                         if (cc 
!= EOF
) cc 
= 1; 
3488                 if (c 
== '0') {                         /* Flag: '0' padding */ 
3491                 while (IsDigit(c
)) {            /* Precision */ 
3492                         w 
= w 
* 10 + c 
- '0'; 
3495                 if (c 
== 'l' || c 
== 'L') {     /* Prefix: Size is long int */ 
3500                 if (IsLower(d
)) d 
-= 0x20; 
3501                 switch (d
) {                            /* Type is... */ 
3502                 case 'S' :                                      /* String */ 
3503                         cc 
= f_puts(va_arg(arp
, TCHAR
*), fil
); continue; 
3504                 case 'C' :                                      /* Character */ 
3505                         cc 
= f_putc((TCHAR
)va_arg(arp
, int), fil
); continue; 
3506                 case 'B' :                                      /* Binary */ 
3508                 case 'O' :                                      /* Octal */ 
3510                 case 'D' :                                      /* Signed decimal */ 
3511                 case 'U' :                                      /* Unsigned decimal */ 
3513                 case 'X' :                                      /* Hexadecimal */ 
3515                 default:                                        /* Unknown */ 
3516                         cc 
= f_putc(c
, fil
); continue; 
3519                 /* Get an argument */ 
3520                 val 
= (f 
& 2) ? 
va_arg(arp
, long) : ((d 
== 'D') ? 
(long)va_arg(arp
, int) : va_arg(arp
, unsigned int)); 
3521                 if (d 
== 'D' && (val 
& 0x80000000)) { 
3525                 /* Put it in numeral string */ 
3528                         d 
= (TCHAR
)(val 
% r
); val 
/= r
; 
3531                                 if (c 
== 'x') d 
+= 0x20; 
3534                 } while (val 
&& i 
< sizeof(s
) / sizeof(s
[0])); 
3535                 if (f 
& 4) s
[i
++] = '-'; 
3537                 while (i 
< w
-- && cc 
!= EOF
) { 
3538                         cc 
= f_putc((TCHAR
)((f 
& 1) ? 
'0' : ' '), fil
); 
3542                         cc 
= f_putc(s
[--i
], fil
); 
3544                 } while (i 
&& cc 
!= EOF
); 
3545                 if (cc 
!= EOF
) cc 
= 0; 
3549         return (cc 
== EOF
) ? cc 
: res
; 
3552 #endif /* !_FS_READONLY */ 
3553 #endif /* _USE_STRFUNC */