1 /*----------------------------------------------------------------------------/ 
   2 /  FatFs - FAT file system module  R0.08b                 (C)ChaN, 2011 
   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) 2011, 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 mistruncated. 
  49 /                   Fixed cached sector is not flushed when create and close without write. 
  51 / Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets(). 
  52 /                   Improved performance of f_lseek() on moving to the same or following cluster. 
  54 / Apr 01,'09 R0.07  Merged Tiny-FatFs as a configuration option. (_FS_TINY) 
  55 /                   Added long file name feature. 
  56 /                   Added multiple code page feature. 
  57 /                   Added re-entrancy for multitask operation. 
  58 /                   Added auto cluster size selection to f_mkfs(). 
  59 /                   Added rewind option to f_readdir(). 
  60 /                   Changed result code of critical errors. 
  61 /                   Renamed string functions to avoid name collision. 
  62 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. 
  63 /                   Added multiple sector size feature. 
  64 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. 
  65 /                   Fixed wrong cache control in f_lseek(). 
  66 /                   Added relative path feature. 
  67 /                   Added f_chdir() and f_chdrive(). 
  68 /                   Added proper case conversion to extended char. 
  69 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. 
  70 /                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. 
  71 /                   Fixed name matching error on the 13 char boundary. 
  72 /                   Added a configuration option, _LFN_UNICODE. 
  73 /                   Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. 
  75 / May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN = 3) 
  76 /                   Added file lock feature. (_FS_SHARE) 
  77 /                   Added fast seek feature. (_USE_FASTSEEK) 
  78 /                   Changed some types on the API, XCHAR->TCHAR. 
  79 /                   Changed fname member in the FILINFO structure on Unicode cfg. 
  80 /                   String functions support UTF-8 encoding files on Unicode cfg. 
  81 / Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) 
  82 /                   Added sector erase feature. (_USE_ERASE) 
  83 /                   Moved file lock semaphore table from fs object to the bss. 
  84 /                   Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. 
  85 /                   Fixed f_mkfs() creates wrong FAT32 volume. 
  86 / Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write(). 
  87 /                   f_lseek() reports required table size on creating CLMP. 
  88 /                   Extended format syntax of f_printf function. 
  89 /                   Ignores duplicated directory separators in given path names. 
  90 /---------------------------------------------------------------------------*/ 
  92 #include "ff.h"                 /* FatFs configurations and declarations */ 
  93 #include "diskio.h"             /* Declarations of low level disk I/O functions */ 
  96 /*-------------------------------------------------------------------------- 
  98    Module Private Definitions 
 100 ---------------------------------------------------------------------------*/ 
 103 #error Wrong include file (ff.h). 
 107 /* Definitions on sector size */ 
 108 #if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 
 109 #error Wrong sector size. 
 112 #define SS(fs)  ((fs)->ssize)   /* Multiple sector size */ 
 114 #define SS(fs)  512U                    /* Fixed sector size */ 
 118 /* Reentrancy related */ 
 121 #error Static LFN work area must not be used in re-entrant configuration. 
 123 #define ENTER_FF(fs)            { if (!lock_fs(fs)) return FR_TIMEOUT; } 
 124 #define LEAVE_FF(fs, res)       { unlock_fs(fs, res); return res; } 
 127 #define LEAVE_FF(fs, res)       return res 
 130 #define ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } 
 133 /* File shareing feature */ 
 136 #error _FS_SHARE must be 0 on read-only cfg. 
 139         FATFS 
*fs
;                              /* File ID 1, volume (NULL:blank entry) */ 
 140         DWORD clu
;                              /* File ID 2, directory */ 
 141         WORD idx
;                               /* File ID 3, directory index */ 
 142         WORD ctr
;                               /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */ 
 147 /* Misc definitions */ 
 148 #define LD_CLUST(dir)   (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO)) 
 149 #define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);} 
 152 /* DBCS code ranges and SBCS extend char conversion table */ 
 154 #if _CODE_PAGE == 932   /* Japanese Shift-JIS */ 
 155 #define _DF1S   0x81    /* DBC 1st byte range 1 start */ 
 156 #define _DF1E   0x9F    /* DBC 1st byte range 1 end */ 
 157 #define _DF2S   0xE0    /* DBC 1st byte range 2 start */ 
 158 #define _DF2E   0xFC    /* DBC 1st byte range 2 end */ 
 159 #define _DS1S   0x40    /* DBC 2nd byte range 1 start */ 
 160 #define _DS1E   0x7E    /* DBC 2nd byte range 1 end */ 
 161 #define _DS2S   0x80    /* DBC 2nd byte range 2 start */ 
 162 #define _DS2E   0xFC    /* DBC 2nd byte range 2 end */ 
 164 #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ 
 172 #elif _CODE_PAGE == 949 /* Korean */ 
 182 #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ 
 190 #elif _CODE_PAGE == 437 /* U.S. (OEM) */ 
 192 #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 193                                 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 194                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 195                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 197 #elif _CODE_PAGE == 720 /* Arabic (OEM) */ 
 199 #define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 200                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 201                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 202                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 204 #elif _CODE_PAGE == 737 /* Greek (OEM) */ 
 206 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ 
 207                                 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 208                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 209                                 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 211 #elif _CODE_PAGE == 775 /* Baltic (OEM) */ 
 213 #define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 
 214                                 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 215                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 216                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 218 #elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ 
 220 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 
 221                                 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 222                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 223                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 225 #elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ 
 227 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ 
 228                                 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ 
 229                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 230                                 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} 
 232 #elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ 
 234 #define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ 
 235                                 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ 
 236                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ 
 237                                 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} 
 239 #elif _CODE_PAGE == 857 /* Turkish (OEM) */ 
 241 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ 
 242                                 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 243                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 244                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 246 #elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ 
 248 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 
 249                                 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 250                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 251                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 253 #elif _CODE_PAGE == 862 /* Hebrew (OEM) */ 
 255 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 256                                 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 257                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 258                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 260 #elif _CODE_PAGE == 866 /* Russian (OEM) */ 
 262 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 263                                 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 264                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 265                                 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 267 #elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ 
 269 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 270                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 271                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 272                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 274 #elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ 
 276 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ 
 277                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ 
 278                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 279                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} 
 281 #elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ 
 283 #define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ 
 284                                 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ 
 285                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 286                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} 
 288 #elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ 
 290 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ 
 291                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 292                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 293                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} 
 295 #elif _CODE_PAGE == 1253 /* Greek (Windows) */ 
 297 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 298                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 299                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ 
 300                                 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} 
 302 #elif _CODE_PAGE == 1254 /* Turkish (Windows) */ 
 304 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ 
 305                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 306                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 307                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} 
 309 #elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ 
 311 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 312                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 313                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 314                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 316 #elif _CODE_PAGE == 1256 /* Arabic (Windows) */ 
 318 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ 
 319                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 320                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 321                                 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} 
 323 #elif _CODE_PAGE == 1257 /* Baltic (Windows) */ 
 325 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 326                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ 
 327                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 328                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} 
 330 #elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ 
 332 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ 
 333                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 334                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 335                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} 
 337 #elif _CODE_PAGE == 1   /* ASCII (for only non-LFN cfg) */ 
 339 #error Cannot use LFN feature without valid code page. 
 344 #error Unknown code page 
 349 /* Character code support macros */ 
 350 #define IsUpper(c)      (((c)>='A')&&((c)<='Z')) 
 351 #define IsLower(c)      (((c)>='a')&&((c)<='z')) 
 352 #define IsDigit(c)      (((c)>='0')&&((c)<='9')) 
 354 #if _DF1S               /* Code page is DBCS */ 
 356 #ifdef _DF2S    /* Two 1st byte areas */ 
 357 #define IsDBCS1(c)      (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) 
 358 #else                   /* One 1st byte area */ 
 359 #define IsDBCS1(c)      ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) 
 362 #ifdef _DS3S    /* Three 2nd byte areas */ 
 363 #define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) 
 364 #else                   /* Two 2nd byte areas */ 
 365 #define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) 
 368 #else                   /* Code page is SBCS */ 
 376 /* Name status flags */ 
 377 #define NS                      11              /* Index of name status byte in fn[] */ 
 378 #define NS_LOSS         0x01    /* Out of 8.3 format */ 
 379 #define NS_LFN          0x02    /* Force to create LFN entry */ 
 380 #define NS_LAST         0x04    /* Last segment */ 
 381 #define NS_BODY         0x08    /* Lower case flag (body) */ 
 382 #define NS_EXT          0x10    /* Lower case flag (ext) */ 
 383 #define NS_DOT          0x20    /* Dot entry */ 
 386 /* FAT sub-type boundaries */ 
 387 /* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ 
 388 #define MIN_FAT16       4086    /* Minimum number of clusters for FAT16 */ 
 389 #define MIN_FAT32       65526   /* Minimum number of clusters for FAT32 */ 
 392 /* FatFs refers the members in the FAT structures as byte array instead of 
 393 / structure member because the structure is not binary compatible between 
 394 / different platforms */ 
 396 #define BS_jmpBoot                      0       /* Jump instruction (3) */ 
 397 #define BS_OEMName                      3       /* OEM name (8) */ 
 398 #define BPB_BytsPerSec          11      /* Sector size [byte] (2) */ 
 399 #define BPB_SecPerClus          13      /* Cluster size [sector] (1) */ 
 400 #define BPB_RsvdSecCnt          14      /* Size of reserved area [sector] (2) */ 
 401 #define BPB_NumFATs                     16      /* Number of FAT copies (1) */ 
 402 #define BPB_RootEntCnt          17      /* Number of root dir entries for FAT12/16 (2) */ 
 403 #define BPB_TotSec16            19      /* Volume size [sector] (2) */ 
 404 #define BPB_Media                       21      /* Media descriptor (1) */ 
 405 #define BPB_FATSz16                     22      /* FAT size [sector] (2) */ 
 406 #define BPB_SecPerTrk           24      /* Track size [sector] (2) */ 
 407 #define BPB_NumHeads            26      /* Number of heads (2) */ 
 408 #define BPB_HiddSec                     28      /* Number of special hidden sectors (4) */ 
 409 #define BPB_TotSec32            32      /* Volume size [sector] (4) */ 
 410 #define BS_DrvNum                       36      /* Physical drive number (2) */ 
 411 #define BS_BootSig                      38      /* Extended boot signature (1) */ 
 412 #define BS_VolID                        39      /* Volume serial number (4) */ 
 413 #define BS_VolLab                       43      /* Volume label (8) */ 
 414 #define BS_FilSysType           54      /* File system type (1) */ 
 415 #define BPB_FATSz32                     36      /* FAT size [sector] (4) */ 
 416 #define BPB_ExtFlags            40      /* Extended flags (2) */ 
 417 #define BPB_FSVer                       42      /* File system version (2) */ 
 418 #define BPB_RootClus            44      /* Root dir first cluster (4) */ 
 419 #define BPB_FSInfo                      48      /* Offset of FSInfo sector (2) */ 
 420 #define BPB_BkBootSec           50      /* Offset of backup boot sectot (2) */ 
 421 #define BS_DrvNum32                     64      /* Physical drive number (2) */ 
 422 #define BS_BootSig32            66      /* Extended boot signature (1) */ 
 423 #define BS_VolID32                      67      /* Volume serial number (4) */ 
 424 #define BS_VolLab32                     71      /* Volume label (8) */ 
 425 #define BS_FilSysType32         82      /* File system type (1) */ 
 426 #define FSI_LeadSig                     0       /* FSI: Leading signature (4) */ 
 427 #define FSI_StrucSig            484     /* FSI: Structure signature (4) */ 
 428 #define FSI_Free_Count          488     /* FSI: Number of free clusters (4) */ 
 429 #define FSI_Nxt_Free            492     /* FSI: Last allocated cluster (4) */ 
 430 #define MBR_Table                       446     /* MBR: Partition table offset (2) */ 
 431 #define SZ_PTE                          16      /* MBR: Size of a partition table entry */ 
 432 #define BS_55AA                         510     /* Boot sector signature (2) */ 
 434 #define DIR_Name                        0       /* Short file name (11) */ 
 435 #define DIR_Attr                        11      /* Attribute (1) */ 
 436 #define DIR_NTres                       12      /* NT flag (1) */ 
 437 #define DIR_CrtTime                     14      /* Created time (2) */ 
 438 #define DIR_CrtDate                     16      /* Created date (2) */ 
 439 #define DIR_FstClusHI           20      /* Higher 16-bit of first cluster (2) */ 
 440 #define DIR_WrtTime                     22      /* Modified time (2) */ 
 441 #define DIR_WrtDate                     24      /* Modified date (2) */ 
 442 #define DIR_FstClusLO           26      /* Lower 16-bit of first cluster (2) */ 
 443 #define DIR_FileSize            28      /* File size (4) */ 
 444 #define LDIR_Ord                        0       /* LFN entry order and LLE flag (1) */ 
 445 #define LDIR_Attr                       11      /* LFN attribute (1) */ 
 446 #define LDIR_Type                       12      /* LFN type (1) */ 
 447 #define LDIR_Chksum                     13      /* Sum of corresponding SFN entry */ 
 448 #define LDIR_FstClusLO          26      /* Filled by zero (0) */ 
 449 #define SZ_DIR                          32              /* Size of a directory entry */ 
 450 #define LLE                                     0x40    /* Last long entry flag in LDIR_Ord */ 
 451 #define DDE                                     0xE5    /* Deleted directory enrty mark in DIR_Name[0] */ 
 452 #define NDDE                            0x05    /* Replacement of a character collides with DDE */ 
 455 /*------------------------------------------------------------*/ 
 460 FATFS 
*FatFs
[_VOLUMES
]; /* Pointer to the file system objects (logical drives) */ 
 462 #error Number of drives must not be 0. 
 466 WORD Fsid
;                              /* File system mount ID */ 
 470 BYTE CurrVol
;                   /* Current drive */ 
 475 FILESEM Files
[_FS_SHARE
];       /* File lock semaphores */ 
 478 #if _USE_LFN == 0                       /* No LFN */ 
 479 #define DEF_NAMEBUF                     BYTE sfn[12] 
 480 #define INIT_BUF(dobj)          (dobj).fn = sfn 
 483 #elif _USE_LFN == 1                     /* LFN with static LFN working buffer */ 
 484 static WCHAR LfnBuf
[_MAX_LFN
+1]; 
 485 #define DEF_NAMEBUF                     BYTE sfn[12] 
 486 #define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } 
 489 #elif _USE_LFN == 2             /* LFN with dynamic LFN working buffer on the stack */ 
 490 #define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] 
 491 #define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = lbuf; } 
 494 #elif _USE_LFN == 3             /* LFN with dynamic LFN working buffer on the heap */ 
 495 #define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR *lfn 
 496 #define INIT_BUF(dobj)          { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ 
 497                                                           if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ 
 498                                                           (dobj).lfn = lfn;     (dobj).fn = sfn; } 
 499 #define FREE_BUF()                      ff_memfree(lfn) 
 502 #error Wrong LFN configuration. 
 508 /*-------------------------------------------------------------------------- 
 510    Module Private Functions 
 512 ---------------------------------------------------------------------------*/ 
 515 /*-----------------------------------------------------------------------*/ 
 516 /* String functions                                                      */ 
 517 /*-----------------------------------------------------------------------*/ 
 519 /* Copy memory to memory */ 
 521 void mem_cpy (void* dst
, const void* src
, UINT cnt
) { 
 522         BYTE 
*d 
= (BYTE
*)dst
; 
 523         const BYTE 
*s 
= (const BYTE
*)src
; 
 525 #if _WORD_ACCESS == 1 
 526         while (cnt 
>= sizeof(int)) { 
 528                 d 
+= sizeof(int); s 
+= sizeof(int); 
 538 void mem_set (void* dst
, int val
, UINT cnt
) { 
 539         BYTE 
*d 
= (BYTE
*)dst
; 
 545 /* Compare memory to memory */ 
 547 int mem_cmp (const void* dst
, const void* src
, UINT cnt
) { 
 548         const BYTE 
*d 
= (const BYTE 
*)dst
, *s 
= (const BYTE 
*)src
; 
 551         while (cnt
-- && (r 
= *d
++ - *s
++) == 0) ; 
 555 /* Check if chr is contained in the string */ 
 557 int chk_chr (const char* str
, int chr
) { 
 558         while (*str 
&& *str 
!= chr
) str
++; 
 564 /*-----------------------------------------------------------------------*/ 
 565 /* Request/Release grant to access the volume                            */ 
 566 /*-----------------------------------------------------------------------*/ 
 571         FATFS 
*fs               
/* File system object */ 
 574         return ff_req_grant(fs
->sobj
); 
 580         FATFS 
*fs
,              /* File system object */ 
 581         FRESULT res             
/* Result code to be returned */ 
 584         if (res 
!= FR_NOT_ENABLED 
&& 
 585                 res 
!= FR_INVALID_DRIVE 
&& 
 586                 res 
!= FR_INVALID_OBJECT 
&& 
 588                 ff_rel_grant(fs
->sobj
); 
 595 /*-----------------------------------------------------------------------*/ 
 596 /* File shareing control functions                                       */ 
 597 /*-----------------------------------------------------------------------*/ 
 601 FRESULT 
chk_lock (      /* Check if the file can be accessed */ 
 602         DIR* dj
,                /* Directory object pointing the file to be checked */ 
 603         int acc                 
/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ 
 608         /* Search file semaphore table */ 
 609         for (i 
= be 
= 0; i 
< _FS_SHARE
; i
++) { 
 610                 if (Files
[i
].fs
) {      /* Existing entry */ 
 611                         if (Files
[i
].fs 
== dj
->fs 
&&            /* Check if the file matched with an open file */ 
 612                                 Files
[i
].clu 
== dj
->sclust 
&& 
 613                                 Files
[i
].idx 
== dj
->index
) break; 
 614                 } else {                        /* Blank entry */ 
 618         if (i 
== _FS_SHARE
)     /* The file is not opened */ 
 619                 return (be 
|| acc 
== 2) ? FR_OK 
: FR_TOO_MANY_OPEN_FILES
;       /* Is there a blank entry for new file? */ 
 621         /* The file has been opened. Reject any open against writing file and all write mode open */ 
 622         return (acc 
|| Files
[i
].ctr 
== 0x100) ? FR_LOCKED 
: FR_OK
; 
 627 int enq_lock (  /* Check if an entry is available for a new file */ 
 628         FATFS
* fs       
/* File system object */ 
 633         for (i 
= 0; i 
< _FS_SHARE 
&& Files
[i
].fs
; i
++) ; 
 634         return (i 
== _FS_SHARE
) ? 
0 : 1; 
 639 UINT 
inc_lock ( /* Increment file open counter and returns its index (0:int error) */ 
 640         DIR* dj
,        /* Directory object pointing the file to register or increment */ 
 641         int acc         
/* Desired access mode (0:Read, !0:Write) */ 
 647         for (i 
= 0; i 
< _FS_SHARE
; i
++) {       /* Find the file */ 
 648                 if (Files
[i
].fs 
== dj
->fs 
&& 
 649                         Files
[i
].clu 
== dj
->sclust 
&& 
 650                         Files
[i
].idx 
== dj
->index
) break; 
 653         if (i 
== _FS_SHARE
) {                           /* Not opened. Register it as new. */ 
 654                 for (i 
= 0; i 
< _FS_SHARE 
&& Files
[i
].fs
; i
++) ; 
 655                 if (i 
== _FS_SHARE
) return 0;   /* No space to register (int err) */ 
 656                 Files
[i
].fs 
= dj
->fs
; 
 657                 Files
[i
].clu 
= dj
->sclust
; 
 658                 Files
[i
].idx 
= dj
->index
; 
 662         if (acc 
&& Files
[i
].ctr
) return 0;      /* Access violation (int err) */ 
 664         Files
[i
].ctr 
= acc ? 
0x100 : Files
[i
].ctr 
+ 1;  /* Set semaphore value */ 
 671 FRESULT 
dec_lock (      /* Decrement file open counter */ 
 672         UINT i                  
/* Semaphore index */ 
 679         if (--i 
< _FS_SHARE
) { 
 681                 if (n 
== 0x100) n 
= 0; 
 684                 if (!n
) Files
[i
].fs 
= 0; 
 694 void clear_lock (       /* Clear lock entries of the volume */ 
 700         for (i 
= 0; i 
< _FS_SHARE
; i
++) { 
 701                 if (Files
[i
].fs 
== fs
) Files
[i
].fs 
= 0; 
 708 /*-----------------------------------------------------------------------*/ 
 709 /* Change window offset                                                  */ 
 710 /*-----------------------------------------------------------------------*/ 
 713 FRESULT 
move_window ( 
 714         FATFS 
*fs
,              /* File system object */ 
 715         DWORD sector    
/* Sector number to make appearance in the fs->win[] */ 
 716 )                                       /* Move to zero only writes back dirty window */ 
 722         if (wsect 
!= sector
) {  /* Changed current window */ 
 724                 if (fs
->wflag
) {        /* Write back dirty window if needed */ 
 725                         if (disk_write(fs
->drv
, fs
->win
, wsect
, 1) != RES_OK
) 
 728                         if (wsect 
< (fs
->fatbase 
+ fs
->fsize
)) {        /* In FAT area */ 
 730                                 for (nf 
= fs
->n_fats
; nf 
> 1; nf
--) {   /* Reflect the change to all FAT copies */ 
 732                                         disk_write(fs
->drv
, fs
->win
, wsect
, 1); 
 738                         if (disk_read(fs
->drv
, fs
->win
, sector
, 1) != RES_OK
) 
 740                         fs
->winsect 
= sector
; 
 750 /*-----------------------------------------------------------------------*/ 
 751 /* Clean-up cached data                                                  */ 
 752 /*-----------------------------------------------------------------------*/ 
 755 FRESULT 
sync (  /* FR_OK: successful, FR_DISK_ERR: failed */ 
 756         FATFS 
*fs       
/* File system object */ 
 762         res 
= move_window(fs
, 0); 
 764                 /* Update FSInfo sector if needed */ 
 765                 if (fs
->fs_type 
== FS_FAT32 
&& fs
->fsi_flag
) { 
 767                         /* Create FSInfo structure */ 
 768                         mem_set(fs
->win
, 0, 512); 
 769                         ST_WORD(fs
->win
+BS_55AA
, 0xAA55); 
 770                         ST_DWORD(fs
->win
+FSI_LeadSig
, 0x41615252); 
 771                         ST_DWORD(fs
->win
+FSI_StrucSig
, 0x61417272); 
 772                         ST_DWORD(fs
->win
+FSI_Free_Count
, fs
->free_clust
); 
 773                         ST_DWORD(fs
->win
+FSI_Nxt_Free
, fs
->last_clust
); 
 774                         /* Write it into the FSInfo sector */ 
 775                         disk_write(fs
->drv
, fs
->win
, fs
->fsi_sector
, 1); 
 778                 /* Make sure that no pending write process in the physical drive */ 
 779                 if (disk_ioctl(fs
->drv
, CTRL_SYNC
, (void*)0) != RES_OK
) 
 790 /*-----------------------------------------------------------------------*/ 
 791 /* Get sector# from cluster#                                             */ 
 792 /*-----------------------------------------------------------------------*/ 
 795 DWORD 
clust2sect (      /* !=0: Sector number, 0: Failed - invalid cluster# */ 
 796         FATFS 
*fs
,              /* File system object */ 
 797         DWORD clst              
/* Cluster# to be converted */ 
 801         if (clst 
>= (fs
->n_fatent 
- 2)) return 0;               /* Invalid cluster# */ 
 802         return clst 
* fs
->csize 
+ fs
->database
; 
 808 /*-----------------------------------------------------------------------*/ 
 809 /* FAT access - Read value of a FAT entry                                */ 
 810 /*-----------------------------------------------------------------------*/ 
 813 DWORD 
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ 
 814         FATFS 
*fs
,      /* File system object */ 
 815         DWORD clst      
/* Cluster# to get the link information */ 
 822         if (clst 
< 2 || clst 
>= fs
->n_fatent
)   /* Chack range */ 
 825         switch (fs
->fs_type
) { 
 827                 bc 
= (UINT
)clst
; bc 
+= bc 
/ 2; 
 828                 if (move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
)))) break; 
 829                 wc 
= fs
->win
[bc 
% SS(fs
)]; bc
++; 
 830                 if (move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
)))) break; 
 831                 wc 
|= fs
->win
[bc 
% SS(fs
)] << 8; 
 832                 return (clst 
& 1) ? 
(wc 
>> 4) : (wc 
& 0xFFF); 
 835                 if (move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 2)))) break; 
 836                 p 
= &fs
->win
[clst 
* 2 % SS(fs
)]; 
 840                 if (move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 4)))) break; 
 841                 p 
= &fs
->win
[clst 
* 4 % SS(fs
)]; 
 842                 return LD_DWORD(p
) & 0x0FFFFFFF; 
 845         return 0xFFFFFFFF;      /* An error occurred at the disk I/O layer */ 
 851 /*-----------------------------------------------------------------------*/ 
 852 /* FAT access - Change value of a FAT entry                              */ 
 853 /*-----------------------------------------------------------------------*/ 
 857         FATFS 
*fs
,      /* File system object */ 
 858         DWORD clst
,     /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ 
 859         DWORD val       
/* New value to mark the cluster */ 
 867         if (clst 
< 2 || clst 
>= fs
->n_fatent
) { /* Check range */ 
 871                 switch (fs
->fs_type
) { 
 873                         bc 
= clst
; bc 
+= bc 
/ 2; 
 874                         res 
= move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
))); 
 875                         if (res 
!= FR_OK
) break; 
 876                         p 
= &fs
->win
[bc 
% SS(fs
)]; 
 877                         *p 
= (clst 
& 1) ? 
((*p 
& 0x0F) | ((BYTE
)val 
<< 4)) : (BYTE
)val
; 
 880                         res 
= move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
))); 
 881                         if (res 
!= FR_OK
) break; 
 882                         p 
= &fs
->win
[bc 
% SS(fs
)]; 
 883                         *p 
= (clst 
& 1) ? 
(BYTE
)(val 
>> 4) : ((*p 
& 0xF0) | ((BYTE
)(val 
>> 8) & 0x0F)); 
 887                         res 
= move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 2))); 
 888                         if (res 
!= FR_OK
) break; 
 889                         p 
= &fs
->win
[clst 
* 2 % SS(fs
)]; 
 890                         ST_WORD(p
, (WORD
)val
); 
 894                         res 
= move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 4))); 
 895                         if (res 
!= FR_OK
) break; 
 896                         p 
= &fs
->win
[clst 
* 4 % SS(fs
)]; 
 897                         val 
|= LD_DWORD(p
) & 0xF0000000; 
 909 #endif /* !_FS_READONLY */ 
 914 /*-----------------------------------------------------------------------*/ 
 915 /* FAT handling - Remove a cluster chain                                 */ 
 916 /*-----------------------------------------------------------------------*/ 
 919 FRESULT 
remove_chain ( 
 920         FATFS 
*fs
,                      /* File system object */ 
 921         DWORD clst                      
/* Cluster# to remove a chain from */ 
 927         DWORD scl 
= clst
, ecl 
= clst
, resion
[2]; 
 930         if (clst 
< 2 || clst 
>= fs
->n_fatent
) { /* Check range */ 
 935                 while (clst 
< fs
->n_fatent
) {                   /* Not a last link? */ 
 936                         nxt 
= get_fat(fs
, clst
);                        /* Get cluster status */ 
 937                         if (nxt 
== 0) break;                            /* Empty cluster? */ 
 938                         if (nxt 
== 1) { res 
= FR_INT_ERR
; break; }      /* Internal error? */ 
 939                         if (nxt 
== 0xFFFFFFFF) { res 
= FR_DISK_ERR
; break; }    /* Disk error? */ 
 940                         res 
= put_fat(fs
, clst
, 0);                     /* Mark the cluster "empty" */ 
 941                         if (res 
!= FR_OK
) break; 
 942                         if (fs
->free_clust 
!= 0xFFFFFFFF) {     /* Update FSInfo */ 
 947                         if (ecl 
+ 1 == nxt
) {   /* Next cluster is contiguous */ 
 949                         } else {                                /* End of contiguous clusters */ 
 950                                 resion
[0] = clust2sect(fs
, scl
);                                        /* Start sector */ 
 951                                 resion
[1] = clust2sect(fs
, ecl
) + fs
->csize 
- 1;        /* End sector */ 
 952                                 disk_ioctl(fs
->drv
, CTRL_ERASE_SECTOR
, resion
);         /* Erase the block */ 
 956                         clst 
= nxt
;     /* Next cluster */ 
 967 /*-----------------------------------------------------------------------*/ 
 968 /* FAT handling - Stretch or Create a cluster chain                      */ 
 969 /*-----------------------------------------------------------------------*/ 
 972 DWORD 
create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ 
 973         FATFS 
*fs
,                      /* File system object */ 
 974         DWORD clst                      
/* Cluster# to stretch. 0 means create a new chain. */ 
 981         if (clst 
== 0) {                /* Create a new chain */ 
 982                 scl 
= fs
->last_clust
;                   /* Get suggested start point */ 
 983                 if (!scl 
|| scl 
>= fs
->n_fatent
) scl 
= 1; 
 985         else {                                  /* Stretch the current chain */ 
 986                 cs 
= get_fat(fs
, clst
);                 /* Check the cluster status */ 
 987                 if (cs 
< 2) return 1;                   /* It is an invalid cluster */ 
 988                 if (cs 
< fs
->n_fatent
) return cs
;       /* It is already followed by next cluster */ 
 992         ncl 
= scl
;                              /* Start cluster */ 
 994                 ncl
++;                                                  /* Next cluster */ 
 995                 if (ncl 
>= fs
->n_fatent
) {              /* Wrap around */ 
 997                         if (ncl 
> scl
) return 0;        /* No free cluster */ 
 999                 cs 
= get_fat(fs
, ncl
);                  /* Get the cluster status */ 
1000                 if (cs 
== 0) break;                             /* Found a free cluster */ 
1001                 if (cs 
== 0xFFFFFFFF || cs 
== 1)/* An error occurred */ 
1003                 if (ncl 
== scl
) return 0;               /* No free cluster */ 
1006         res 
= put_fat(fs
, ncl
, 0x0FFFFFFF);     /* Mark the new cluster "last link" */ 
1007         if (res 
== FR_OK 
&& clst 
!= 0) { 
1008                 res 
= put_fat(fs
, clst
, ncl
);   /* Link it to the previous one if needed */ 
1011                 fs
->last_clust 
= ncl
;                   /* Update FSINFO */ 
1012                 if (fs
->free_clust 
!= 0xFFFFFFFF) { 
1017                 ncl 
= (res 
== FR_DISK_ERR
) ? 
0xFFFFFFFF : 1; 
1020         return ncl
;             /* Return new cluster number or error code */ 
1022 #endif /* !_FS_READONLY */ 
1026 /*-----------------------------------------------------------------------*/ 
1027 /* FAT handling - Convert offset into cluster with link map table        */ 
1028 /*-----------------------------------------------------------------------*/ 
1032 DWORD 
clmt_clust (      /* <2:Error, >=2:Cluster number */ 
1033         FIL
* fp
,                /* Pointer to the file object */ 
1034         DWORD ofs               
/* File offset to be converted to cluster# */ 
1037         DWORD cl
, ncl
, *tbl
; 
1040         tbl 
= fp
->cltbl 
+ 1;    /* Top of CLMT */ 
1041         cl 
= ofs 
/ SS(fp
->fs
) / fp
->fs
->csize
;  /* Cluster order from top of the file */ 
1043                 ncl 
= *tbl
++;                   /* Number of cluters in the fragment */ 
1044                 if (!ncl
) return 0;             /* End of table? (error) */ 
1045                 if (cl 
< ncl
) break;    /* In this fragment? */ 
1046                 cl 
-= ncl
; tbl
++;               /* Next fragment */ 
1048         return cl 
+ *tbl
;       /* Return the cluster number */ 
1050 #endif  /* _USE_FASTSEEK */ 
1054 /*-----------------------------------------------------------------------*/ 
1055 /* Directory handling - Set directory index                              */ 
1056 /*-----------------------------------------------------------------------*/ 
1060         DIR *dj
,                /* Pointer to directory object */ 
1061         WORD idx                
/* Directory index number */ 
1070         if (clst 
== 1 || clst 
>= dj
->fs
->n_fatent
)      /* Check start cluster range */ 
1072         if (!clst 
&& dj
->fs
->fs_type 
== FS_FAT32
)       /* Replace cluster# 0 with root cluster# if in FAT32 */ 
1073                 clst 
= dj
->fs
->dirbase
; 
1075         if (clst 
== 0) {        /* Static table (root-dir in FAT12/16) */ 
1077                 if (idx 
>= dj
->fs
->n_rootdir
)           /* Index is out of range */ 
1079                 dj
->sect 
= dj
->fs
->dirbase 
+ idx 
/ (SS(dj
->fs
) / SZ_DIR
);       /* Sector# */ 
1081         else {                          /* Dynamic table (sub-dirs or root-dir in FAT32) */ 
1082                 ic 
= SS(dj
->fs
) / SZ_DIR 
* dj
->fs
->csize
;       /* Entries per cluster */ 
1083                 while (idx 
>= ic
) {     /* Follow cluster chain */ 
1084                         clst 
= get_fat(dj
->fs
, clst
);                           /* Get next cluster */ 
1085                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
;     /* Disk error */ 
1086                         if (clst 
< 2 || clst 
>= dj
->fs
->n_fatent
)       /* Reached to end of table or int error */ 
1091                 dj
->sect 
= clust2sect(dj
->fs
, clst
) + idx 
/ (SS(dj
->fs
) / SZ_DIR
);      /* Sector# */ 
1094         dj
->dir 
= dj
->fs
->win 
+ (idx 
% (SS(dj
->fs
) / SZ_DIR
)) * SZ_DIR
; /* Ptr to the entry in the sector */ 
1096         return FR_OK
;   /* Seek succeeded */ 
1102 /*-----------------------------------------------------------------------*/ 
1103 /* Directory handling - Move directory index next                        */ 
1104 /*-----------------------------------------------------------------------*/ 
1107 FRESULT 
dir_next (      /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ 
1108         DIR *dj
,                /* Pointer to directory object */ 
1109         int stretch             
/* 0: Do not stretch table, 1: Stretch table if needed */ 
1117         if (!i 
|| !dj
->sect
)    /* Report EOT when index has reached 65535 */ 
1120         if (!(i 
% (SS(dj
->fs
) / SZ_DIR
))) {     /* Sector changed? */ 
1121                 dj
->sect
++;                                     /* Next sector */ 
1123                 if (dj
->clust 
== 0) {   /* Static table */ 
1124                         if (i 
>= dj
->fs
->n_rootdir
)     /* Report EOT when end of table */ 
1127                 else {                                  /* Dynamic table */ 
1128                         if (((i 
/ (SS(dj
->fs
) / SZ_DIR
)) & (dj
->fs
->csize 
- 1)) == 0) { /* Cluster changed? */ 
1129                                 clst 
= get_fat(dj
->fs
, dj
->clust
);                              /* Get next cluster */ 
1130                                 if (clst 
<= 1) return FR_INT_ERR
; 
1131                                 if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
1132                                 if (clst 
>= dj
->fs
->n_fatent
) {                                 /* When it reached end of dynamic table */ 
1135                                         if (!stretch
) return FR_NO_FILE
;                        /* When do not stretch, report EOT */ 
1136                                         clst 
= create_chain(dj
->fs
, dj
->clust
);         /* Stretch cluster chain */ 
1137                                         if (clst 
== 0) return FR_DENIED
;                        /* No free cluster */ 
1138                                         if (clst 
== 1) return FR_INT_ERR
; 
1139                                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
1140                                         /* Clean-up stretched table */ 
1141                                         if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; /* Flush active window */ 
1142                                         mem_set(dj
->fs
->win
, 0, SS(dj
->fs
));                    /* Clear window buffer */ 
1143                                         dj
->fs
->winsect 
= clust2sect(dj
->fs
, clst
);     /* Cluster start sector */ 
1144                                         for (c 
= 0; c 
< dj
->fs
->csize
; c
++) {           /* Fill the new cluster with 0 */ 
1146                                                 if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; 
1149                                         dj
->fs
->winsect 
-= c
;                                           /* Rewind window address */ 
1151                                         return FR_NO_FILE
;                      /* Report EOT */ 
1154                                 dj
->clust 
= clst
;                               /* Initialize data for new cluster */ 
1155                                 dj
->sect 
= clust2sect(dj
->fs
, clst
); 
1161         dj
->dir 
= dj
->fs
->win 
+ (i 
% (SS(dj
->fs
) / SZ_DIR
)) * SZ_DIR
; 
1169 /*-----------------------------------------------------------------------*/ 
1170 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */ 
1171 /*-----------------------------------------------------------------------*/ 
1174 const BYTE LfnOfs
[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};      /* Offset of LFN chars in the directory entry */ 
1178 int cmp_lfn (                   /* 1:Matched, 0:Not matched */ 
1179         WCHAR 
*lfnbuf
,          /* Pointer to the LFN to be compared */ 
1180         BYTE 
*dir                       
/* Pointer to the directory entry containing a part of LFN */ 
1187         i 
= ((dir
[LDIR_Ord
] & ~LLE
) - 1) * 13;  /* Get offset in the LFN buffer */ 
1190                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);    /* Pick an LFN character from the entry */ 
1191                 if (wc
) {       /* Last char has not been processed */ 
1192                         wc 
= ff_wtoupper(uc
);           /* Convert it to upper case */ 
1193                         if (i 
>= _MAX_LFN 
|| wc 
!= ff_wtoupper(lfnbuf
[i
++]))    /* Compare it */ 
1194                                 return 0;                               /* Not matched */ 
1196                         if (uc 
!= 0xFFFF) return 0;     /* Check filler */ 
1198         } while (++s 
< 13);                             /* Repeat until all chars in the entry are checked */ 
1200         if ((dir
[LDIR_Ord
] & LLE
) && wc 
&& lfnbuf
[i
])   /* Last segment matched but different length */ 
1203         return 1;                                               /* The part of LFN matched */ 
1209 int pick_lfn (                  /* 1:Succeeded, 0:Buffer overflow */ 
1210         WCHAR 
*lfnbuf
,          /* Pointer to the Unicode-LFN buffer */ 
1211         BYTE 
*dir                       
/* Pointer to the directory entry */ 
1218         i 
= ((dir
[LDIR_Ord
] & 0x3F) - 1) * 13;  /* Offset in the LFN buffer */ 
1222                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);            /* Pick an LFN character from the entry */ 
1223                 if (wc
) {       /* Last char has not been processed */ 
1224                         if (i 
>= _MAX_LFN
) return 0;    /* Buffer overflow? */ 
1225                         lfnbuf
[i
++] = wc 
= uc
;                  /* Store it */ 
1227                         if (uc 
!= 0xFFFF) return 0;             /* Check filler */ 
1229         } while (++s 
< 13);                                             /* Read all character in the entry */ 
1231         if (dir
[LDIR_Ord
] & LLE
) {                              /* Put terminator if it is the last LFN part */ 
1232                 if (i 
>= _MAX_LFN
) return 0;            /* Buffer overflow? */ 
1243         const WCHAR 
*lfnbuf
,    /* Pointer to the LFN buffer */ 
1244         BYTE 
*dir
,                              /* Pointer to the directory entry */ 
1245         BYTE ord
,                               /* LFN order (1-20) */ 
1246         BYTE sum                                
/* SFN sum */ 
1253         dir
[LDIR_Chksum
] = sum
;                 /* Set check sum */ 
1254         dir
[LDIR_Attr
] = AM_LFN
;                /* Set attribute. LFN entry */ 
1256         ST_WORD(dir
+LDIR_FstClusLO
, 0); 
1258         i 
= (ord 
- 1) * 13;                             /* Get offset in the LFN buffer */ 
1261                 if (wc 
!= 0xFFFF) wc 
= lfnbuf
[i
++];     /* Get an effective char */ 
1262                 ST_WORD(dir
+LfnOfs
[s
], wc
);     /* Put it */ 
1263                 if (!wc
) wc 
= 0xFFFF;           /* Padding chars following last char */ 
1265         if (wc 
== 0xFFFF || !lfnbuf
[i
]) ord 
|= LLE
;     /* Bottom LFN part is the start of LFN sequence */ 
1266         dir
[LDIR_Ord
] = ord
;                    /* Set the LFN order */ 
1274 /*-----------------------------------------------------------------------*/ 
1275 /* Create numbered name                                                  */ 
1276 /*-----------------------------------------------------------------------*/ 
1279         BYTE 
*dst
,                      /* Pointer to generated SFN */ 
1280         const BYTE 
*src
,        /* Pointer to source SFN to be modified */ 
1281         const WCHAR 
*lfn
,       /* Pointer to LFN */ 
1282         WORD seq                        
/* Sequence number */ 
1289         mem_cpy(dst
, src
, 11); 
1291         if (seq 
> 5) {  /* On many collisions, generate a hash number instead of sequential number */ 
1292                 do seq 
= (seq 
>> 1) + (seq 
<< 15) + (WORD
)*lfn
++; while (*lfn
); 
1295         /* itoa (hexdecimal) */ 
1298                 c 
= (seq 
% 16) + '0'; 
1299                 if (c 
> '9') c 
+= 7; 
1305         /* Append the number */ 
1306         for (j 
= 0; j 
< i 
&& dst
[j
] != ' '; j
++) { 
1307                 if (IsDBCS1(dst
[j
])) { 
1308                         if (j 
== i 
- 1) break; 
1313                 dst
[j
++] = (i 
< 8) ? ns
[i
++] : ' '; 
1321 /*-----------------------------------------------------------------------*/ 
1322 /* Calculate sum of an SFN                                               */ 
1323 /*-----------------------------------------------------------------------*/ 
1327         const BYTE 
*dir         
/* Ptr to directory entry */ 
1333         do sum 
= (sum 
>> 1) + (sum 
<< 7) + *dir
++; while (--n
); 
1341 /*-----------------------------------------------------------------------*/ 
1342 /* Directory handling - Find an object in the directory                  */ 
1343 /*-----------------------------------------------------------------------*/ 
1347         DIR *dj                 
/* Pointer to the directory object linked to the file name */ 
1356         res 
= dir_sdi(dj
, 0);                   /* Rewind directory object */ 
1357         if (res 
!= FR_OK
) return res
; 
1363                 res 
= move_window(dj
->fs
, dj
->sect
); 
1364                 if (res 
!= FR_OK
) break; 
1365                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
1367                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
1368 #if _USE_LFN    /* LFN configuration */ 
1369                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
1370                 if (c 
== DDE 
|| ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {        /* An entry without valid data */ 
1373                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
1375                                         if (c 
& LLE
) {          /* Is it start of LFN sequence? */ 
1376                                                 sum 
= dir
[LDIR_Chksum
]; 
1377                                                 c 
&= ~LLE
; ord 
= c
;     /* LFN start order */ 
1378                                                 dj
->lfn_idx 
= dj
->index
; 
1380                                         /* Check validity of the LFN entry and compare it with given name */ 
1381                                         ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && cmp_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
1383                         } else {                                        /* An SFN entry is found */ 
1384                                 if (!ord 
&& sum 
== sum_sfn(dir
)) break; /* LFN matched? */ 
1385                                 ord 
= 0xFF; dj
->lfn_idx 
= 0xFFFF;       /* Reset LFN sequence */ 
1386                                 if (!(dj
->fn
[NS
] & NS_LOSS
) && !mem_cmp(dir
, dj
->fn
, 11)) break;        /* SFN matched? */ 
1389 #else           /* Non LFN configuration */ 
1390                 if (!(dir
[DIR_Attr
] & AM_VOL
) && !mem_cmp(dir
, dj
->fn
, 11)) /* Is it a valid entry? */ 
1393                 res 
= dir_next(dj
, 0);          /* Next entry */ 
1394         } while (res 
== FR_OK
); 
1402 /*-----------------------------------------------------------------------*/ 
1403 /* Read an object from the directory                                     */ 
1404 /*-----------------------------------------------------------------------*/ 
1405 #if _FS_MINIMIZE <= 1 
1408         DIR *dj                 
/* Pointer to the directory object that pointing the entry to be read */ 
1414         BYTE a
, ord 
= 0xFF, sum 
= 0xFF; 
1419                 res 
= move_window(dj
->fs
, dj
->sect
); 
1420                 if (res 
!= FR_OK
) break; 
1421                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
1423                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
1424 #if _USE_LFN    /* LFN configuration */ 
1425                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
1426                 if (c 
== DDE 
|| (!_FS_RPATH 
&& c 
== '.') || ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {    /* An entry without valid data */ 
1429                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
1430                                 if (c 
& LLE
) {                  /* Is it start of LFN sequence? */ 
1431                                         sum 
= dir
[LDIR_Chksum
]; 
1433                                         dj
->lfn_idx 
= dj
->index
; 
1435                                 /* Check LFN validity and capture it */ 
1436                                 ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && pick_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
1437                         } else {                                        /* An SFN entry is found */ 
1438                                 if (ord 
|| sum 
!= sum_sfn(dir
)) /* Is there a valid LFN? */ 
1439                                         dj
->lfn_idx 
= 0xFFFF;           /* It has no LFN. */ 
1443 #else           /* Non LFN configuration */ 
1444                 if (c 
!= DDE 
&& (_FS_RPATH 
|| c 
!= '.') && !(dir
[DIR_Attr
] & AM_VOL
))   /* Is it a valid entry? */ 
1447                 res 
= dir_next(dj
, 0);                          /* Next entry */ 
1448                 if (res 
!= FR_OK
) break; 
1451         if (res 
!= FR_OK
) dj
->sect 
= 0; 
1459 /*-----------------------------------------------------------------------*/ 
1460 /* Register an object to the directory                                   */ 
1461 /*-----------------------------------------------------------------------*/ 
1464 FRESULT 
dir_register (  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ 
1465         DIR *dj                         
/* Target directory with object name to be created */ 
1470 #if _USE_LFN    /* LFN configuration */ 
1472         BYTE sn
[12], *fn
, sum
; 
1476         fn 
= dj
->fn
; lfn 
= dj
->lfn
; 
1477         mem_cpy(sn
, fn
, 12); 
1479         if (_FS_RPATH 
&& (sn
[NS
] & NS_DOT
))             /* Cannot create dot entry */ 
1480                 return FR_INVALID_NAME
; 
1482         if (sn
[NS
] & NS_LOSS
) {                 /* When LFN is out of 8.3 format, generate a numbered name */ 
1483                 fn
[NS
] = 0; dj
->lfn 
= 0;                        /* Find only SFN */ 
1484                 for (n 
= 1; n 
< 100; n
++) { 
1485                         gen_numname(fn
, sn
, lfn
, n
);    /* Generate a numbered name */ 
1486                         res 
= dir_find(dj
);                             /* Check if the name collides with existing SFN */ 
1487                         if (res 
!= FR_OK
) break; 
1489                 if (n 
== 100) return FR_DENIED
;         /* Abort if too many collisions */ 
1490                 if (res 
!= FR_NO_FILE
) return res
;      /* Abort if the result is other than 'not collided' */ 
1491                 fn
[NS
] = sn
[NS
]; dj
->lfn 
= lfn
; 
1494         if (sn
[NS
] & NS_LFN
) {                  /* When LFN is to be created, reserve an SFN + LFN entries. */ 
1495                 for (ne 
= 0; lfn
[ne
]; ne
++) ; 
1496                 ne 
= (ne 
+ 25) / 13; 
1497         } else {                                                /* Otherwise reserve only an SFN entry. */ 
1501         /* Reserve contiguous entries */ 
1502         res 
= dir_sdi(dj
, 0); 
1503         if (res 
!= FR_OK
) return res
; 
1506                 res 
= move_window(dj
->fs
, dj
->sect
); 
1507                 if (res 
!= FR_OK
) break; 
1508                 c 
= *dj
->dir
;                           /* Check the entry status */ 
1509                 if (c 
== DDE 
|| c 
== 0) {       /* Is it a blank entry? */ 
1510                         if (n 
== 0) is 
= dj
->index
;     /* First index of the contiguous entry */ 
1511                         if (++n 
== ne
) break;   /* A contiguous entry that required count is found */ 
1513                         n 
= 0;                                  /* Not a blank entry. Restart to search */ 
1515                 res 
= dir_next(dj
, 1);          /* Next entry with table stretch */ 
1516         } while (res 
== FR_OK
); 
1518         if (res 
== FR_OK 
&& ne 
> 1) {   /* Initialize LFN entry if needed */ 
1519                 res 
= dir_sdi(dj
, is
); 
1521                         sum 
= sum_sfn(dj
->fn
);  /* Sum of the SFN tied to the LFN */ 
1523                         do {                                    /* Store LFN entries in bottom first */ 
1524                                 res 
= move_window(dj
->fs
, dj
->sect
); 
1525                                 if (res 
!= FR_OK
) break; 
1526                                 fit_lfn(dj
->lfn
, dj
->dir
, (BYTE
)ne
, sum
); 
1528                                 res 
= dir_next(dj
, 0);  /* Next entry */ 
1529                         } while (res 
== FR_OK 
&& --ne
); 
1533 #else   /* Non LFN configuration */ 
1534         res 
= dir_sdi(dj
, 0); 
1536                 do {    /* Find a blank entry for the SFN */ 
1537                         res 
= move_window(dj
->fs
, dj
->sect
); 
1538                         if (res 
!= FR_OK
) break; 
1540                         if (c 
== DDE 
|| c 
== 0) break;  /* Is it a blank entry? */ 
1541                         res 
= dir_next(dj
, 1);                  /* Next entry with table stretch */ 
1542                 } while (res 
== FR_OK
); 
1546         if (res 
== FR_OK
) {             /* Initialize the SFN entry */ 
1547                 res 
= move_window(dj
->fs
, dj
->sect
); 
1550                         mem_set(dir
, 0, SZ_DIR
);        /* Clean the entry */ 
1551                         mem_cpy(dir
, dj
->fn
, 11);       /* Put SFN */ 
1553                         dir
[DIR_NTres
] = *(dj
->fn
+NS
) & (NS_BODY 
| NS_EXT
);     /* Put NT flag */ 
1561 #endif /* !_FS_READONLY */ 
1566 /*-----------------------------------------------------------------------*/ 
1567 /* Remove an object from the directory                                   */ 
1568 /*-----------------------------------------------------------------------*/ 
1569 #if !_FS_READONLY && !_FS_MINIMIZE 
1571 FRESULT 
dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */ 
1572         DIR *dj                         
/* Directory object pointing the entry to be removed */ 
1576 #if _USE_LFN    /* LFN configuration */ 
1579         i 
= dj
->index
;  /* SFN index */ 
1580         res 
= dir_sdi(dj
, (WORD
)((dj
->lfn_idx 
== 0xFFFF) ? i 
: dj
->lfn_idx
));   /* Goto the SFN or top of the LFN entries */ 
1583                         res 
= move_window(dj
->fs
, dj
->sect
); 
1584                         if (res 
!= FR_OK
) break; 
1585                         *dj
->dir 
= DDE
;                 /* Mark the entry "deleted" */ 
1587                         if (dj
->index 
>= i
) break;      /* When reached SFN, all entries of the object has been deleted. */ 
1588                         res 
= dir_next(dj
, 0);          /* Next entry */ 
1589                 } while (res 
== FR_OK
); 
1590                 if (res 
== FR_NO_FILE
) res 
= FR_INT_ERR
; 
1593 #else                   /* Non LFN configuration */ 
1594         res 
= dir_sdi(dj
, dj
->index
); 
1596                 res 
= move_window(dj
->fs
, dj
->sect
); 
1598                         *dj
->dir 
= DDE
;                 /* Mark the entry "deleted" */ 
1606 #endif /* !_FS_READONLY */ 
1611 /*-----------------------------------------------------------------------*/ 
1612 /* Pick a segment and create the object name in directory form           */ 
1613 /*-----------------------------------------------------------------------*/ 
1616 FRESULT 
create_name ( 
1617         DIR *dj
,                        /* Pointer to the directory object */ 
1618         const TCHAR 
**path      
/* Pointer to pointer to the segment in the path string */ 
1622         static const BYTE excvt
[] = _EXCVT
;     /* Upper conversion table for extended chars */ 
1625 #if _USE_LFN    /* LFN configuration */ 
1631         /* Create LFN in Unicode */ 
1632         for (p 
= *path
; *p 
== '/' || *p 
== '\\'; p
++) ; /* Strip duplicated separator */ 
1636                 w 
= p
[si
++];                                    /* Get a character */ 
1637                 if (w 
< ' ' || w 
== '/' || w 
== '\\') break;    /* Break on end of segment */ 
1638                 if (di 
>= _MAX_LFN
)                             /* Reject too long name */ 
1639                         return FR_INVALID_NAME
; 
1642                 if (IsDBCS1(w
)) {                               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ 
1643                         b 
= (BYTE
)p
[si
++];                      /* Get 2nd byte */ 
1645                                 return FR_INVALID_NAME
; /* Reject invalid sequence */ 
1646                         w 
= (w 
<< 8) + b
;                       /* Create a DBC */ 
1648                 w 
= ff_convert(w
, 1);                   /* Convert ANSI/OEM to Unicode */ 
1649                 if (!w
) return FR_INVALID_NAME
; /* Reject invalid code */ 
1651                 if (w 
< 0x80 && chk_chr("\"*:<>\?|\x7F", w
)) /* Reject illegal chars for LFN */ 
1652                         return FR_INVALID_NAME
; 
1653                 lfn
[di
++] = w
;                                  /* Store the Unicode char */ 
1655         *path 
= &p
[si
];                                         /* Return pointer to the next segment */ 
1656         cf 
= (w 
< ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1658         if ((di 
== 1 && lfn
[di
-1] == '.') || /* Is this a dot entry? */ 
1659                 (di 
== 2 && lfn
[di
-1] == '.' && lfn
[di
-2] == '.')) { 
1661                 for (i 
= 0; i 
< 11; i
++) 
1662                         dj
->fn
[i
] = (i 
< di
) ? 
'.' : ' '; 
1663                 dj
->fn
[i
] = cf 
| NS_DOT
;                /* This is a dot entry */ 
1667         while (di
) {                                            /* Strip trailing spaces and dots */ 
1669                 if (w 
!= ' ' && w 
!= '.') break; 
1672         if (!di
) return FR_INVALID_NAME
;        /* Reject nul string */ 
1674         lfn
[di
] = 0;                                            /* LFN is created */ 
1676         /* Create SFN in directory form */ 
1677         mem_set(dj
->fn
, ' ', 11); 
1678         for (si 
= 0; lfn
[si
] == ' ' || lfn
[si
] == '.'; si
++) ;  /* Strip leading spaces and dots */ 
1679         if (si
) cf 
|= NS_LOSS 
| NS_LFN
; 
1680         while (di 
&& lfn
[di 
- 1] != '.') di
--;  /* Find extension (di<=si: no extension) */ 
1684                 w 
= lfn
[si
++];                                  /* Get an LFN char */ 
1685                 if (!w
) break;                                  /* Break on end of the LFN */ 
1686                 if (w 
== ' ' || (w 
== '.' && si 
!= di
)) {       /* Remove spaces and dots */ 
1687                         cf 
|= NS_LOSS 
| NS_LFN
; continue; 
1690                 if (i 
>= ni 
|| si 
== di
) {              /* Extension or end of SFN */ 
1691                         if (ni 
== 11) {                         /* Long extension */ 
1692                                 cf 
|= NS_LOSS 
| NS_LFN
; break; 
1694                         if (si 
!= di
) cf 
|= NS_LOSS 
| NS_LFN
;   /* Out of 8.3 format */ 
1695                         if (si 
> di
) break;                     /* No extension */ 
1696                         si 
= di
; i 
= 8; ni 
= 11;        /* Enter extension section */ 
1700                 if (w 
>= 0x80) {                                /* Non ASCII char */ 
1702                         w 
= ff_convert(w
, 0);           /* Unicode -> OEM code */ 
1703                         if (w
) w 
= excvt
[w 
- 0x80];     /* Convert extended char to upper (SBCS) */ 
1705                         w 
= ff_convert(ff_wtoupper(w
), 0);      /* Upper converted Unicode -> OEM code */ 
1707                         cf 
|= NS_LFN
;                           /* Force create LFN entry */ 
1710                 if (_DF1S 
&& w 
>= 0x100) {              /* Double byte char (always false on SBCS cfg) */ 
1712                                 cf 
|= NS_LOSS 
| NS_LFN
; i 
= ni
; continue; 
1714                         dj
->fn
[i
++] = (BYTE
)(w 
>> 8); 
1715                 } else {                                                /* Single byte char */ 
1716                         if (!w 
|| chk_chr("+,;=[]", w
)) {       /* Replace illegal chars for SFN */ 
1717                                 w 
= '_'; cf 
|= NS_LOSS 
| NS_LFN
;/* Lossy conversion */ 
1719                                 if (IsUpper(w
)) {               /* ASCII large capital */ 
1722                                         if (IsLower(w
)) {       /* ASCII small capital */ 
1728                 dj
->fn
[i
++] = (BYTE
)w
; 
1731         if (dj
->fn
[0] == DDE
) dj
->fn
[0] = NDDE
; /* If the first char collides with deleted mark, replace it with 0x05 */ 
1733         if (ni 
== 8) b 
<<= 2; 
1734         if ((b 
& 0x0C) == 0x0C || (b 
& 0x03) == 0x03)   /* Create LFN entry when there are composite capitals */ 
1736         if (!(cf 
& NS_LFN
)) {                                           /* When LFN is in 8.3 format without extended char, NT flags are created */ 
1737                 if ((b 
& 0x03) == 0x01) cf 
|= NS_EXT
;   /* NT flag (Extension has only small capital) */ 
1738                 if ((b 
& 0x0C) == 0x04) cf 
|= NS_BODY
;  /* NT flag (Filename has only small capital) */ 
1741         dj
->fn
[NS
] = cf
;        /* SFN is created */ 
1746 #else   /* Non-LFN configuration */ 
1751         /* Create file name in directory form */ 
1752         for (p 
= *path
; *p 
== '/' || *p 
== '\\'; p
++) ; /* Strip duplicated separator */ 
1754         mem_set(sfn
, ' ', 11); 
1755         si 
= i 
= b 
= 0; ni 
= 8; 
1757         if (p
[si
] == '.') { /* Is this a dot entry? */ 
1760                         if (c 
!= '.' || si 
>= 3) break; 
1763                 if (c 
!= '/' && c 
!= '\\' && c 
> ' ') return FR_INVALID_NAME
; 
1764                 *path 
= &p
[si
];                                                                 /* Return pointer to the next segment */ 
1765                 sfn
[NS
] = (c 
<= ' ') ? NS_LAST 
| NS_DOT 
: NS_DOT
;       /* Set last segment flag if end of path */ 
1771                 if (c 
<= ' ' || c 
== '/' || c 
== '\\') break;   /* Break on end of segment */ 
1772                 if (c 
== '.' || i 
>= ni
) { 
1773                         if (ni 
!= 8 || c 
!= '.') return FR_INVALID_NAME
; 
1777                 if (c 
>= 0x80) {                                /* Extended char? */ 
1778                         b 
|= 3;                                         /* Eliminate NT flag */ 
1780                         c 
= excvt
[c
-0x80];                      /* Upper conversion (SBCS) */ 
1782 #if !_DF1S      /* ASCII only cfg */ 
1783                         return FR_INVALID_NAME
; 
1787                 if (IsDBCS1(c
)) {                               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ 
1788                         d 
= (BYTE
)p
[si
++];                      /* Get 2nd byte */ 
1789                         if (!IsDBCS2(d
) || i 
>= ni 
- 1) /* Reject invalid DBC */ 
1790                                 return FR_INVALID_NAME
; 
1793                 } else {                                                /* Single byte code */ 
1794                         if (chk_chr("\"*+,:;<=>\?[]|\x7F", c
))  /* Reject illegal chrs for SFN */ 
1795                                 return FR_INVALID_NAME
; 
1796                         if (IsUpper(c
)) {                       /* ASCII large capital? */ 
1799                                 if (IsLower(c
)) {               /* ASCII small capital? */ 
1806         *path 
= &p
[si
];                                         /* Return pointer to the next segment */ 
1807         c 
= (c 
<= ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1809         if (!i
) return FR_INVALID_NAME
;         /* Reject nul string */ 
1810         if (sfn
[0] == DDE
) sfn
[0] = NDDE
;       /* When first char collides with DDE, replace it with 0x05 */ 
1812         if (ni 
== 8) b 
<<= 2; 
1813         if ((b 
& 0x03) == 0x01) c 
|= NS_EXT
;    /* NT flag (Name extension has only small capital) */ 
1814         if ((b 
& 0x0C) == 0x04) c 
|= NS_BODY
;   /* NT flag (Name body has only small capital) */ 
1816         sfn
[NS
] = c
;            /* Store NT flag, File name is created */ 
1825 /*-----------------------------------------------------------------------*/ 
1826 /* Get file information from directory entry                             */ 
1827 /*-----------------------------------------------------------------------*/ 
1828 #if _FS_MINIMIZE <= 1 
1830 void get_fileinfo (             /* No return code */ 
1831         DIR *dj
,                        /* Pointer to the directory object */ 
1832         FILINFO 
*fno            
/* Pointer to the file information to be filled */ 
1843                 nt 
= dir
[DIR_NTres
];            /* NT flag */ 
1844                 for (i 
= 0; i 
< 8; i
++) {       /* Copy name body */ 
1846                         if (c 
== ' ') break; 
1847                         if (c 
== NDDE
) c 
= (TCHAR
)DDE
; 
1848                         if (_USE_LFN 
&& (nt 
& NS_BODY
) && IsUpper(c
)) c 
+= 0x20; 
1850                         if (IsDBCS1(c
) && i 
< 7 && IsDBCS2(dir
[i
+1])) 
1851                                 c 
= (c 
<< 8) | dir
[++i
]; 
1852                         c 
= ff_convert(c
, 1); 
1857                 if (dir
[8] != ' ') {            /* Copy name extension */ 
1859                         for (i 
= 8; i 
< 11; i
++) { 
1861                                 if (c 
== ' ') break; 
1862                                 if (_USE_LFN 
&& (nt 
& NS_EXT
) && IsUpper(c
)) c 
+= 0x20; 
1864                                 if (IsDBCS1(c
) && i 
< 10 && IsDBCS2(dir
[i
+1])) 
1865                                         c 
= (c 
<< 8) | dir
[++i
]; 
1866                                 c 
= ff_convert(c
, 1); 
1872                 fno
->fattrib 
= dir
[DIR_Attr
];                           /* Attribute */ 
1873                 fno
->fsize 
= LD_DWORD(dir
+DIR_FileSize
);        /* Size */ 
1874                 fno
->fdate 
= LD_WORD(dir
+DIR_WrtDate
);          /* Date */ 
1875                 fno
->ftime 
= LD_WORD(dir
+DIR_WrtTime
);          /* Time */ 
1877         *p 
= 0;         /* Terminate SFN str by a \0 */ 
1880         if (fno
->lfname 
&& fno
->lfsize
) { 
1881                 TCHAR 
*tp 
= fno
->lfname
; 
1885                 if (dj
->sect 
&& dj
->lfn_idx 
!= 0xFFFF) {/* Get LFN if available */ 
1887                         while ((w 
= *lfn
++) != 0) {                     /* Get an LFN char */ 
1889                                 w 
= ff_convert(w
, 0);                   /* Unicode -> OEM conversion */ 
1890                                 if (!w
) { i 
= 0; break; }               /* Could not convert, no LFN */ 
1891                                 if (_DF1S 
&& w 
>= 0x100)                /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ 
1892                                         tp
[i
++] = (TCHAR
)(w 
>> 8); 
1894                                 if (i 
>= fno
->lfsize 
- 1) { i 
= 0; break; }     /* Buffer overflow, no LFN */ 
1898                 tp
[i
] = 0;      /* Terminate the LFN str by a \0 */ 
1902 #endif /* _FS_MINIMIZE <= 1 */ 
1907 /*-----------------------------------------------------------------------*/ 
1908 /* Follow a file path                                                    */ 
1909 /*-----------------------------------------------------------------------*/ 
1912 FRESULT 
follow_path (   /* FR_OK(0): successful, !=0: error code */ 
1913         DIR *dj
,                        /* Directory object to return last directory and found object */ 
1914         const TCHAR 
*path       
/* Full-path string to find a file or directory */ 
1922         if (*path 
== '/' || *path 
== '\\') { /* There is a heading separator */ 
1923                 path
++; dj
->sclust 
= 0;         /* Strip it and start from the root dir */ 
1924         } else {                                                        /* No heading separator */ 
1925                 dj
->sclust 
= dj
->fs
->cdir
;      /* Start from the current dir */ 
1928         if (*path 
== '/' || *path 
== '\\')      /* Strip heading separator if exist */ 
1930         dj
->sclust 
= 0;                                         /* Start from the root dir */ 
1933         if ((UINT
)*path 
< ' ') {                        /* Nul path means the start directory itself */ 
1934                 res 
= dir_sdi(dj
, 0); 
1937         } else {                                                        /* Follow path */ 
1939                         res 
= create_name(dj
, &path
);   /* Get a segment */ 
1940                         if (res 
!= FR_OK
) break; 
1941                         res 
= dir_find(dj
);                             /* Find it */ 
1943                         if (res 
!= FR_OK
) {                             /* Failed to find the object */ 
1944                                 if (res 
!= FR_NO_FILE
) break;   /* Abort if any hard error occured */ 
1945                                 /* Object not found */ 
1946                                 if (_FS_RPATH 
&& (ns 
& NS_DOT
)) {       /* If dot entry is not exit */ 
1947                                         dj
->sclust 
= 0; dj
->dir 
= 0;    /* It is the root dir */ 
1949                                         if (!(ns 
& NS_LAST
)) continue; 
1950                                 } else {                                                        /* Could not find the object */ 
1951                                         if (!(ns 
& NS_LAST
)) res 
= FR_NO_PATH
; 
1955                         if (ns 
& NS_LAST
) break;                        /* Last segment match. Function completed. */ 
1956                         dir 
= dj
->dir
;                                          /* There is next segment. Follow the sub directory */ 
1957                         if (!(dir
[DIR_Attr
] & AM_DIR
)) {        /* Cannot follow because it is a file */ 
1958                                 res 
= FR_NO_PATH
; break; 
1960                         dj
->sclust 
= LD_CLUST(dir
); 
1970 /*-----------------------------------------------------------------------*/ 
1971 /* Load boot record and check if it is an FAT boot record                */ 
1972 /*-----------------------------------------------------------------------*/ 
1975 BYTE 
check_fs ( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk error */ 
1976         FATFS 
*fs
,      /* File system object */ 
1977         DWORD sect      
/* Sector# (lba) to check if it is an FAT boot record or not */ 
1980         if (disk_read(fs
->drv
, fs
->win
, sect
, 1) != RES_OK
)     /* Load boot record */ 
1982         if (LD_WORD(&fs
->win
[BS_55AA
]) != 0xAA55)               /* Check record signature (always placed at offset 510 even if the sector size is >512) */ 
1985         if ((LD_DWORD(&fs
->win
[BS_FilSysType
]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ 
1987         if ((LD_DWORD(&fs
->win
[BS_FilSysType32
]) & 0xFFFFFF) == 0x544146) 
1996 /*-----------------------------------------------------------------------*/ 
1997 /* Check if the file system object is valid or not                       */ 
1998 /*-----------------------------------------------------------------------*/ 
2001 FRESULT 
chk_mounted (   /* FR_OK(0): successful, !=0: any error occurred */ 
2002         const TCHAR 
**path
,     /* Pointer to pointer to the path name (drive number) */ 
2003         FATFS 
**rfs
,            /* Pointer to pointer to the found file system object */ 
2004         BYTE chk_wp                     
/* !=0: Check media write protection for write access */ 
2010         DWORD bsect
, fasize
, tsect
, sysect
, nclst
, szbfat
; 
2012         const TCHAR 
*p 
= *path
; 
2015         /* Get logical drive number from the path name */ 
2016         vol 
= p
[0] - '0';                                       /* Is there a drive number? */ 
2017         if (vol 
<= 9 && p
[1] == ':') {          /* Found a drive number, get and strip it */ 
2018                 p 
+= 2; *path 
= p
;                              /* Return pointer to the path name */ 
2019         } else {                                                        /* No drive number is given */ 
2021                 vol 
= CurrVol
;                                  /* Use current drive */ 
2023                 vol 
= 0;                                                /* Use drive 0 */ 
2027         /* Check if the logical drive is valid or not */ 
2028         if (vol 
>= _VOLUMES
)                            /* Is the drive number valid? */ 
2029                 return FR_INVALID_DRIVE
; 
2030         *rfs 
= fs 
= FatFs
[vol
];                         /* Return pointer to the corresponding file system object */ 
2031         if (!fs
) return FR_NOT_ENABLED
;         /* Is the file system object available? */ 
2033         ENTER_FF(fs
);                                           /* Lock file system */ 
2035         if (fs
->fs_type
) {                                      /* If the logical drive has been mounted */ 
2036                 stat 
= disk_status(fs
->drv
); 
2037                 if (!(stat 
& STA_NOINIT
)) {             /* and the physical drive is kept initialized (has not been changed), */ 
2039                         if (chk_wp 
&& (stat 
& STA_PROTECT
))     /* Check write protection if needed */ 
2040                                 return FR_WRITE_PROTECTED
; 
2042                         return FR_OK
;                           /* The file system object is valid */ 
2046         /* The logical drive must be mounted. */ 
2047         /* Following code attempts to mount a volume. (analyze BPB and initialize the fs object) */ 
2049         fs
->fs_type 
= 0;                                        /* Clear the file system object */ 
2050         fs
->drv 
= (BYTE
)LD2PD(vol
);                     /* Bind the logical drive and a physical drive */ 
2051         stat 
= disk_initialize(fs
->drv
);        /* Initialize low level disk I/O layer */ 
2052         if (stat 
& STA_NOINIT
)                          /* Check if the initialization succeeded */ 
2053                 return FR_NOT_READY
;                    /* Failed to initialize due to no media or hard error */ 
2054 #if _MAX_SS != 512                                              /* Get disk sector size (variable sector size cfg only) */ 
2055         if (disk_ioctl(fs
->drv
, GET_SECTOR_SIZE
, &fs
->ssize
) != RES_OK
) 
2059         if (chk_wp 
&& (stat 
& STA_PROTECT
))     /* Check disk write protection if needed */ 
2060                 return FR_WRITE_PROTECTED
; 
2062         /* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */ 
2063         fmt 
= check_fs(fs
, bsect 
= 0);          /* Check sector 0 if it is a VBR */ 
2064         if (fmt 
== 1) {                                         /* Not an FAT-VBR, the disk may be partitioned */ 
2065                 /* Check the partition listed in top of the partition table */ 
2066                 tbl 
= &fs
->win
[MBR_Table 
+ LD2PT(vol
) * SZ_PTE
];/* Partition table */ 
2067                 if (tbl
[4]) {                                                                   /* Is the partition existing? */ 
2068                         bsect 
= LD_DWORD(&tbl
[8]);                                      /* Partition offset in LBA */ 
2069                         fmt 
= check_fs(fs
, bsect
);                                      /* Check the partition */ 
2072         if (fmt 
== 3) return FR_DISK_ERR
; 
2073         if (fmt
) return FR_NO_FILESYSTEM
;                                       /* No FAT volume is found */ 
2075         /* Following code initializes the file system object */ 
2077         if (LD_WORD(fs
->win
+BPB_BytsPerSec
) != SS(fs
))          /* (BPB_BytsPerSec must be equal to the physical sector size) */ 
2078                 return FR_NO_FILESYSTEM
; 
2080         fasize 
= LD_WORD(fs
->win
+BPB_FATSz16
);                          /* Number of sectors per FAT */ 
2081         if (!fasize
) fasize 
= LD_DWORD(fs
->win
+BPB_FATSz32
); 
2084         fs
->n_fats 
= b 
= fs
->win
[BPB_NumFATs
];                          /* Number of FAT copies */ 
2085         if (b 
!= 1 && b 
!= 2) return FR_NO_FILESYSTEM
;          /* (Must be 1 or 2) */ 
2086         fasize 
*= b
;                                                                            /* Number of sectors for FAT area */ 
2088         fs
->csize 
= b 
= fs
->win
[BPB_SecPerClus
];                        /* Number of sectors per cluster */ 
2089         if (!b 
|| (b 
& (b 
- 1))) return FR_NO_FILESYSTEM
;       /* (Must be power of 2) */ 
2091         fs
->n_rootdir 
= LD_WORD(fs
->win
+BPB_RootEntCnt
);        /* Number of root directory entries */ 
2092         if (fs
->n_rootdir 
% (SS(fs
) / SZ_DIR
)) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must be sector aligned) */ 
2094         tsect 
= LD_WORD(fs
->win
+BPB_TotSec16
);                          /* Number of sectors on the volume */ 
2095         if (!tsect
) tsect 
= LD_DWORD(fs
->win
+BPB_TotSec32
); 
2097         nrsv 
= LD_WORD(fs
->win
+BPB_RsvdSecCnt
);                         /* Number of reserved sectors */ 
2098         if (!nrsv
) return FR_NO_FILESYSTEM
;                                     /* (BPB_RsvdSecCnt must not be 0) */ 
2100         /* Determine the FAT sub type */ 
2101         sysect 
= nrsv 
+ fasize 
+ fs
->n_rootdir 
/ (SS(fs
) / SZ_DIR
);     /* RSV+FAT+DIR */ 
2102         if (tsect 
< sysect
) return FR_NO_FILESYSTEM
;            /* (Invalid volume size) */ 
2103         nclst 
= (tsect 
- sysect
) / fs
->csize
;                           /* Number of clusters */ 
2104         if (!nclst
) return FR_NO_FILESYSTEM
;                            /* (Invalid volume size) */ 
2106         if (nclst 
>= MIN_FAT16
) fmt 
= FS_FAT16
; 
2107         if (nclst 
>= MIN_FAT32
) fmt 
= FS_FAT32
; 
2109         /* Boundaries and Limits */ 
2110         fs
->n_fatent 
= nclst 
+ 2;                                                       /* Number of FAT entries */ 
2111         fs
->database 
= bsect 
+ sysect
;                                          /* Data start sector */ 
2112         fs
->fatbase 
= bsect 
+ nrsv
;                                             /* FAT start sector */ 
2113         if (fmt 
== FS_FAT32
) { 
2114                 if (fs
->n_rootdir
) return FR_NO_FILESYSTEM
;             /* (BPB_RootEntCnt must be 0) */ 
2115                 fs
->dirbase 
= LD_DWORD(fs
->win
+BPB_RootClus
);   /* Root directory start cluster */ 
2116                 szbfat 
= fs
->n_fatent 
* 4;                                              /* (Required FAT size) */ 
2118                 if (!fs
->n_rootdir
)     return FR_NO_FILESYSTEM
;        /* (BPB_RootEntCnt must not be 0) */ 
2119                 fs
->dirbase 
= fs
->fatbase 
+ fasize
;                             /* Root directory start sector */ 
2120                 szbfat 
= (fmt 
== FS_FAT16
) ?                                    
/* (Required FAT size) */ 
2121                         fs
->n_fatent 
* 2 : fs
->n_fatent 
* 3 / 2 + (fs
->n_fatent 
& 1); 
2123         if (fs
->fsize 
< (szbfat 
+ (SS(fs
) - 1)) / SS(fs
))       /* (BPB_FATSz must not be less than required) */ 
2124                 return FR_NO_FILESYSTEM
; 
2127         /* Initialize cluster allocation information */ 
2128         fs
->free_clust 
= 0xFFFFFFFF; 
2131         /* Get fsinfo if available */ 
2132         if (fmt 
== FS_FAT32
) { 
2134                 fs
->fsi_sector 
= bsect 
+ LD_WORD(fs
->win
+BPB_FSInfo
); 
2135                 if (disk_read(fs
->drv
, fs
->win
, fs
->fsi_sector
, 1) == RES_OK 
&& 
2136                         LD_WORD(fs
->win
+BS_55AA
) == 0xAA55 && 
2137                         LD_DWORD(fs
->win
+FSI_LeadSig
) == 0x41615252 && 
2138                         LD_DWORD(fs
->win
+FSI_StrucSig
) == 0x61417272) { 
2139                                 fs
->last_clust 
= LD_DWORD(fs
->win
+FSI_Nxt_Free
); 
2140                                 fs
->free_clust 
= LD_DWORD(fs
->win
+FSI_Free_Count
); 
2144         fs
->fs_type 
= fmt
;              /* FAT sub-type */ 
2145         fs
->id 
= ++Fsid
;                /* File system mount ID */ 
2146         fs
->winsect 
= 0;                /* Invalidate sector cache */ 
2149         fs
->cdir 
= 0;                   /* Current directory (root dir) */ 
2151 #if _FS_SHARE                           /* Clear file lock semaphores */ 
2161 /*-----------------------------------------------------------------------*/ 
2162 /* Check if the file/dir object is valid or not                          */ 
2163 /*-----------------------------------------------------------------------*/ 
2166 FRESULT 
validate (      /* FR_OK(0): The object is valid, !=0: Invalid */ 
2167         FATFS 
*fs
,              /* Pointer to the file system object */ 
2168         WORD id                 
/* Member id of the target object to be checked */ 
2171         if (!fs 
|| !fs
->fs_type 
|| fs
->id 
!= id
) 
2172                 return FR_INVALID_OBJECT
; 
2174         ENTER_FF(fs
);           /* Lock file system */ 
2176         if (disk_status(fs
->drv
) & STA_NOINIT
) 
2177                 return FR_NOT_READY
; 
2185 /*-------------------------------------------------------------------------- 
2189 --------------------------------------------------------------------------*/ 
2193 /*-----------------------------------------------------------------------*/ 
2194 /* Mount/Unmount a Logical Drive                                         */ 
2195 /*-----------------------------------------------------------------------*/ 
2198         BYTE vol
,               /* Logical drive number to be mounted/unmounted */ 
2199         FATFS 
*fs               
/* Pointer to new file system object (NULL for unmount)*/ 
2205         if (vol 
>= _VOLUMES
)                    /* Check if the drive number is valid */ 
2206                 return FR_INVALID_DRIVE
; 
2207         rfs 
= FatFs
[vol
];                               /* Get current fs object */ 
2213 #if _FS_REENTRANT                                       /* Discard sync object of the current volume */ 
2214                 if (!ff_del_syncobj(rfs
->sobj
)) return FR_INT_ERR
; 
2216                 rfs
->fs_type 
= 0;                       /* Clear old fs object */ 
2220                 fs
->fs_type 
= 0;                        /* Clear new fs object */ 
2221 #if _FS_REENTRANT                                       /* Create sync object for the new volume */ 
2222                 if (!ff_cre_syncobj(vol
, &fs
->sobj
)) return FR_INT_ERR
; 
2225         FatFs
[vol
] = fs
;                                /* Register new fs object */ 
2233 /*-----------------------------------------------------------------------*/ 
2234 /* Open or Create a File                                                 */ 
2235 /*-----------------------------------------------------------------------*/ 
2238         FIL 
*fp
,                        /* Pointer to the blank file object */ 
2239         const TCHAR 
*path
,      /* Pointer to the file name */ 
2240         BYTE mode                       
/* Access mode and file open mode flags */ 
2249         fp
->fs 
= 0;                     /* Clear file object */ 
2252         mode 
&= FA_READ 
| FA_WRITE 
| FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
; 
2253         res 
= chk_mounted(&path
, &dj
.fs
, (BYTE
)(mode 
& ~FA_READ
)); 
2256         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2260                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
2263 #if !_FS_READONLY       /* R/W configuration */ 
2265                 if (!dir
)       /* Current dir itself */ 
2266                         res 
= FR_INVALID_NAME
; 
2269                         res 
= chk_lock(&dj
, (mode 
& ~FA_READ
) ? 
1 : 0); 
2272         /* Create or Open a file */ 
2273         if (mode 
& (FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
)) { 
2276                 if (res 
!= FR_OK
) {                                     /* No file, create new */ 
2277                         if (res 
== FR_NO_FILE
)                  /* There is no file to open, create a new entry */ 
2279                                 res 
= enq_lock(dj
.fs
) ? 
dir_register(&dj
) : FR_TOO_MANY_OPEN_FILES
; 
2281                                 res 
= dir_register(&dj
); 
2283                         mode 
|= FA_CREATE_ALWAYS
;               /* File is created */ 
2284                         dir 
= dj
.dir
;                                   /* New entry */ 
2286                 else {                                                          /* Any object is already existing */ 
2287                         if (dir
[DIR_Attr
] & (AM_RDO 
| AM_DIR
)) {        /* Cannot overwrite it (R/O or DIR) */ 
2290                                 if (mode 
& FA_CREATE_NEW
)       /* Cannot create as new file */ 
2294                 if (res 
== FR_OK 
&& (mode 
& FA_CREATE_ALWAYS
)) {        /* Truncate it if overwrite mode */ 
2295                         dw 
= get_fattime();                                     /* Created time */ 
2296                         ST_DWORD(dir
+DIR_CrtTime
, dw
); 
2297                         dir
[DIR_Attr
] = 0;                                      /* Reset attribute */ 
2298                         ST_DWORD(dir
+DIR_FileSize
, 0);          /* size = 0 */ 
2299                         cl 
= LD_CLUST(dir
);                                     /* Get start cluster */ 
2300                         ST_CLUST(dir
, 0);                                       /* cluster = 0 */ 
2302                         if (cl
) {                                                       /* Remove the cluster chain if exist */ 
2303                                 dw 
= dj
.fs
->winsect
; 
2304                                 res 
= remove_chain(dj
.fs
, cl
); 
2306                                         dj
.fs
->last_clust 
= cl 
- 1;     /* Reuse the cluster hole */ 
2307                                         res 
= move_window(dj
.fs
, dw
); 
2312         else {  /* Open an existing file */ 
2313                 if (res 
== FR_OK
) {                                             /* Follow succeeded */ 
2314                         if (dir
[DIR_Attr
] & AM_DIR
) {           /* It is a directory */ 
2317                                 if ((mode 
& FA_WRITE
) && (dir
[DIR_Attr
] & AM_RDO
)) /* R/O violation */ 
2323                 if (mode 
& FA_CREATE_ALWAYS
)                    /* Set file change flag if created or overwritten */ 
2324                         mode 
|= FA__WRITTEN
; 
2325                 fp
->dir_sect 
= dj
.fs
->winsect
;                  /* Pointer to the directory entry */ 
2328                 fp
->lockid 
= inc_lock(&dj
, (mode 
& ~FA_READ
) ? 
1 : 0); 
2329                 if (!fp
->lockid
) res 
= FR_INT_ERR
; 
2333 #else                           /* R/O configuration */ 
2334         if (res 
== FR_OK
) {                                     /* Follow succeeded */ 
2335                 if (!dir
) {                                             /* Current dir itself */ 
2336                         res 
= FR_INVALID_NAME
; 
2338                         if (dir
[DIR_Attr
] & AM_DIR
)     /* It is a directory */ 
2346                 fp
->flag 
= mode
;                                        /* File access mode */ 
2347                 fp
->sclust 
= LD_CLUST(dir
);                     /* File start cluster */ 
2348                 fp
->fsize 
= LD_DWORD(dir
+DIR_FileSize
); /* File size */ 
2349                 fp
->fptr 
= 0;                                           /* File pointer */ 
2352                 fp
->cltbl 
= 0;                                          /* Normal seek mode */ 
2354                 fp
->fs 
= dj
.fs
; fp
->id 
= dj
.fs
->id
;     /* Validate file object */ 
2357         LEAVE_FF(dj
.fs
, res
); 
2363 /*-----------------------------------------------------------------------*/ 
2365 /*-----------------------------------------------------------------------*/ 
2368         FIL 
*fp
,                /* Pointer to the file object */ 
2369         void *buff
,             /* Pointer to data buffer */ 
2370         UINT btr
,               /* Number of bytes to read */ 
2371         UINT 
*br                
/* Pointer to number of bytes read */ 
2375         DWORD clst
, sect
, remain
; 
2377         BYTE csect
, *rbuff 
= buff
; 
2380         *br 
= 0;        /* Initialize byte counter */ 
2382         res 
= validate(fp
->fs
, fp
->id
);                         /* Check validity */ 
2383         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2384         if (fp
->flag 
& FA__ERROR
)                                       /* Aborted file? */ 
2385                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2386         if (!(fp
->flag 
& FA_READ
))                                      /* Check access mode */ 
2387                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2388         remain 
= fp
->fsize 
- fp
->fptr
; 
2389         if (btr 
> remain
) btr 
= (UINT
)remain
;           /* Truncate btr by remaining bytes */ 
2391         for ( ;  btr
;                                                           /* Repeat until all data read */ 
2392                 rbuff 
+= rcnt
, fp
->fptr 
+= rcnt
, *br 
+= rcnt
, btr 
-= rcnt
) { 
2393                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {             /* On the sector boundary? */ 
2394                         csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
2395                         if (!csect
) {                                           /* On the cluster boundary? */ 
2396                                 if (fp
->fptr 
== 0) {                    /* On the top of the file? */ 
2397                                         clst 
= fp
->sclust
;                      /* Follow from the origin */ 
2398                                 } else {                                                /* Middle or end of the file */ 
2401                                                 clst 
= clmt_clust(fp
, fp
->fptr
);        /* Get cluster# from the CLMT */ 
2404                                                 clst 
= get_fat(fp
->fs
, fp
->clust
);      /* Follow cluster chain on the FAT */ 
2406                                 if (clst 
< 2) ABORT(fp
->fs
, FR_INT_ERR
); 
2407                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2408                                 fp
->clust 
= clst
;                               /* Update current cluster */ 
2410                         sect 
= clust2sect(fp
->fs
, fp
->clust
);   /* Get current sector */ 
2411                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2413                         cc 
= btr 
/ SS(fp
->fs
);                          /* When remaining bytes >= sector size, */ 
2414                         if (cc
) {                                                       /* Read maximum contiguous sectors directly */ 
2415                                 if (csect 
+ cc 
> fp
->fs
->csize
) /* Clip at cluster boundary */ 
2416                                         cc 
= fp
->fs
->csize 
- csect
; 
2417                                 if (disk_read(fp
->fs
->drv
, rbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
2418                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2419 #if !_FS_READONLY && _FS_MINIMIZE <= 2                  /* Replace one of the read sectors with cached data if it contains a dirty sector */ 
2421                                 if (fp
->fs
->wflag 
&& fp
->fs
->winsect 
- sect 
< cc
) 
2422                                         mem_cpy(rbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), fp
->fs
->win
, SS(fp
->fs
)); 
2424                                 if ((fp
->flag 
& FA__DIRTY
) && fp
->dsect 
- sect 
< cc
) 
2425                                         mem_cpy(rbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), fp
->buf
, SS(fp
->fs
)); 
2428                                 rcnt 
= SS(fp
->fs
) * cc
;                 /* Number of bytes transferred */ 
2432                         if (fp
->dsect 
!= sect
) {                        /* Load data sector if not in cache */ 
2434                                 if (fp
->flag 
& FA__DIRTY
) {             /* Write-back dirty sector cache */ 
2435                                         if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2436                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2437                                         fp
->flag 
&= ~FA__DIRTY
; 
2440                                 if (disk_read(fp
->fs
->drv
, fp
->buf
, sect
, 1) != RES_OK
) /* Fill sector cache */ 
2441                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2446                 rcnt 
= SS(fp
->fs
) - (fp
->fptr 
% SS(fp
->fs
));    /* Get partial sector data from sector buffer */ 
2447                 if (rcnt 
> btr
) rcnt 
= btr
; 
2449                 if (move_window(fp
->fs
, fp
->dsect
))             /* Move sector window */ 
2450                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2451                 mem_cpy(rbuff
, &fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);      /* Pick partial sector */ 
2453                 mem_cpy(rbuff
, &fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);  /* Pick partial sector */ 
2457         LEAVE_FF(fp
->fs
, FR_OK
); 
2464 /*-----------------------------------------------------------------------*/ 
2466 /*-----------------------------------------------------------------------*/ 
2469         FIL 
*fp
,                        /* Pointer to the file object */ 
2470         const void *buff
,       /* Pointer to the data to be written */ 
2471         UINT btw
,                       /* Number of bytes to write */ 
2472         UINT 
*bw                        
/* Pointer to number of bytes written */ 
2478         const BYTE 
*wbuff 
= buff
; 
2482         *bw 
= 0;        /* Initialize byte counter */ 
2484         res 
= validate(fp
->fs
, fp
->id
);                 /* Check validity */ 
2485         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2486         if (fp
->flag 
& FA__ERROR
)                               /* Aborted file? */ 
2487                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2488         if (!(fp
->flag 
& FA_WRITE
))                             /* Check access mode */ 
2489                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2490         if ((DWORD
)(fp
->fsize 
+ btw
) < fp
->fsize
) btw 
= 0;      /* File size cannot reach 4GB */ 
2492         for ( ;  btw
;                                                   /* Repeat until all data written */ 
2493                 wbuff 
+= wcnt
, fp
->fptr 
+= wcnt
, *bw 
+= wcnt
, btw 
-= wcnt
) { 
2494                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {     /* On the sector boundary? */ 
2495                         csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
2496                         if (!csect
) {                                   /* On the cluster boundary? */ 
2497                                 if (fp
->fptr 
== 0) {            /* On the top of the file? */ 
2498                                         clst 
= fp
->sclust
;              /* Follow from the origin */ 
2499                                         if (clst 
== 0)                  /* When no cluster is allocated, */ 
2500                                                 fp
->sclust 
= clst 
= create_chain(fp
->fs
, 0);    /* Create a new cluster chain */ 
2501                                 } else {                                        /* Middle or end of the file */ 
2504                                                 clst 
= clmt_clust(fp
, fp
->fptr
);        /* Get cluster# from the CLMT */ 
2507                                                 clst 
= create_chain(fp
->fs
, fp
->clust
); /* Follow or stretch cluster chain on the FAT */ 
2509                                 if (clst 
== 0) break;           /* Could not allocate a new cluster (disk full) */ 
2510                                 if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2511                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2512                                 fp
->clust 
= clst
;                       /* Update current cluster */ 
2515                         if (fp
->fs
->winsect 
== fp
->dsect 
&& move_window(fp
->fs
, 0))     /* Write-back sector cache */ 
2516                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2518                         if (fp
->flag 
& FA__DIRTY
) {             /* Write-back sector cache */ 
2519                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2520                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2521                                 fp
->flag 
&= ~FA__DIRTY
; 
2524                         sect 
= clust2sect(fp
->fs
, fp
->clust
);   /* Get current sector */ 
2525                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2527                         cc 
= btw 
/ SS(fp
->fs
);                  /* When remaining bytes >= sector size, */ 
2528                         if (cc
) {                                               /* Write maximum contiguous sectors directly */ 
2529                                 if (csect 
+ cc 
> fp
->fs
->csize
) /* Clip at cluster boundary */ 
2530                                         cc 
= fp
->fs
->csize 
- csect
; 
2531                                 if (disk_write(fp
->fs
->drv
, wbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
2532                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2534                                 if (fp
->fs
->winsect 
- sect 
< cc
) {      /* Refill sector cache if it gets invalidated by the direct write */ 
2535                                         mem_cpy(fp
->fs
->win
, wbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
2539                                 if (fp
->dsect 
- sect 
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */ 
2540                                         mem_cpy(fp
->buf
, wbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
2541                                         fp
->flag 
&= ~FA__DIRTY
; 
2544                                 wcnt 
= SS(fp
->fs
) * cc
;         /* Number of bytes transferred */ 
2548                         if (fp
->fptr 
>= fp
->fsize
) {    /* Avoid silly cache filling at growing edge */ 
2549                                 if (move_window(fp
->fs
, 0)) ABORT(fp
->fs
, FR_DISK_ERR
); 
2550                                 fp
->fs
->winsect 
= sect
; 
2553                         if (fp
->dsect 
!= sect
) {                /* Fill sector cache with file data */ 
2554                                 if (fp
->fptr 
< fp
->fsize 
&& 
2555                                         disk_read(fp
->fs
->drv
, fp
->buf
, sect
, 1) != RES_OK
) 
2556                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2561                 wcnt 
= SS(fp
->fs
) - (fp
->fptr 
% SS(fp
->fs
));/* Put partial sector into file I/O buffer */ 
2562                 if (wcnt 
> btw
) wcnt 
= btw
; 
2564                 if (move_window(fp
->fs
, fp
->dsect
))     /* Move sector window */ 
2565                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2566                 mem_cpy(&fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);      /* Fit partial sector */ 
2569                 mem_cpy(&fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);  /* Fit partial sector */ 
2570                 fp
->flag 
|= FA__DIRTY
; 
2574         if (fp
->fptr 
> fp
->fsize
) fp
->fsize 
= fp
->fptr
; /* Update file size if needed */ 
2575         fp
->flag 
|= FA__WRITTEN
;                                                /* Set file change flag */ 
2577         LEAVE_FF(fp
->fs
, FR_OK
); 
2583 /*-----------------------------------------------------------------------*/ 
2584 /* Synchronize the File Object                                           */ 
2585 /*-----------------------------------------------------------------------*/ 
2588         FIL 
*fp         
/* Pointer to the file object */ 
2596         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
2598                 if (fp
->flag 
& FA__WRITTEN
) {   /* Has the file been written? */ 
2599 #if !_FS_TINY   /* Write-back dirty buffer */ 
2600                         if (fp
->flag 
& FA__DIRTY
) { 
2601                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2602                                         LEAVE_FF(fp
->fs
, FR_DISK_ERR
); 
2603                                 fp
->flag 
&= ~FA__DIRTY
; 
2606                         /* Update the directory entry */ 
2607                         res 
= move_window(fp
->fs
, fp
->dir_sect
); 
2610                                 dir
[DIR_Attr
] |= AM_ARC
;                                        /* Set archive bit */ 
2611                                 ST_DWORD(dir
+DIR_FileSize
, fp
->fsize
);          /* Update file size */ 
2612                                 ST_CLUST(dir
, fp
->sclust
);                                      /* Update start cluster */ 
2613                                 tim 
= get_fattime();                                            /* Update updated time */ 
2614                                 ST_DWORD(dir
+DIR_WrtTime
, tim
); 
2615                                 fp
->flag 
&= ~FA__WRITTEN
; 
2622         LEAVE_FF(fp
->fs
, res
); 
2625 #endif /* !_FS_READONLY */ 
2630 /*-----------------------------------------------------------------------*/ 
2632 /*-----------------------------------------------------------------------*/ 
2635         FIL 
*fp         
/* Pointer to the file object to be closed */ 
2642         res 
= validate(fs
, fp
->id
); 
2643         if (res 
== FR_OK
) fp
->fs 
= 0;   /* Discard file object */ 
2647         res 
= f_sync(fp
);               /* Flush cached data */ 
2649         if (res 
== FR_OK
) {             /* Decrement open counter */ 
2651                 res 
= validate(fp
->fs
, fp
->id
); 
2653                         res 
= dec_lock(fp
->lockid
); 
2654                         unlock_fs(fp
->fs
, FR_OK
); 
2657                 res 
= dec_lock(fp
->lockid
); 
2661         if (res 
== FR_OK
) fp
->fs 
= 0;   /* Discard file object */ 
2669 /*-----------------------------------------------------------------------*/ 
2670 /* Current Drive/Directory Handlings                                     */ 
2671 /*-----------------------------------------------------------------------*/ 
2676         BYTE drv                
/* Drive number */ 
2679         if (drv 
>= _VOLUMES
) return FR_INVALID_DRIVE
; 
2689         const TCHAR 
*path       
/* Pointer to the directory path */ 
2697         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2700                 res 
= follow_path(&dj
, path
);           /* Follow the path */ 
2702                 if (res 
== FR_OK
) {                                     /* Follow completed */ 
2704                                 dj
.fs
->cdir 
= dj
.sclust
;        /* Start directory itself */ 
2706                                 if (dj
.dir
[DIR_Attr
] & AM_DIR
)  /* Reached to the directory */ 
2707                                         dj
.fs
->cdir 
= LD_CLUST(dj
.dir
); 
2709                                         res 
= FR_NO_PATH
;               /* Reached but a file */ 
2712                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
2715         LEAVE_FF(dj
.fs
, res
); 
2721         TCHAR 
*path
,    /* Pointer to the directory path */ 
2722         UINT sz_path    
/* Size of path */ 
2735         res 
= chk_mounted((const TCHAR
**)&path
, &dj
.fs
, 0);     /* Get current volume */ 
2738                 i 
= sz_path
;            /* Bottom of buffer (dir stack base) */ 
2739                 dj
.sclust 
= dj
.fs
->cdir
;                        /* Start to follow upper dir from current dir */ 
2740                 while ((ccl 
= dj
.sclust
) != 0) {        /* Repeat while current dir is a sub-dir */ 
2741                         res 
= dir_sdi(&dj
, 1);                  /* Get parent dir */ 
2742                         if (res 
!= FR_OK
) break; 
2743                         res 
= dir_read(&dj
); 
2744                         if (res 
!= FR_OK
) break; 
2745                         dj
.sclust 
= LD_CLUST(dj
.dir
);   /* Goto parent dir */ 
2746                         res 
= dir_sdi(&dj
, 0); 
2747                         if (res 
!= FR_OK
) break; 
2748                         do {                                                    /* Find the entry links to the child dir */ 
2749                                 res 
= dir_read(&dj
); 
2750                                 if (res 
!= FR_OK
) break; 
2751                                 if (ccl 
== LD_CLUST(dj
.dir
)) break;     /* Found the entry */ 
2752                                 res 
= dir_next(&dj
, 0); 
2753                         } while (res 
== FR_OK
); 
2754                         if (res 
== FR_NO_FILE
) res 
= FR_INT_ERR
;/* It cannot be 'not found'. */ 
2755                         if (res 
!= FR_OK
) break; 
2760                         get_fileinfo(&dj
, &fno
);                /* Get the dir name and push it to the buffer */ 
2762                         if (_USE_LFN 
&& *path
) tp 
= path
; 
2763                         for (n 
= 0; tp
[n
]; n
++) ; 
2765                                 res 
= FR_NOT_ENOUGH_CORE
; break; 
2767                         while (n
) path
[--i
] = tp
[--n
]; 
2772                         *tp
++ = '0' + CurrVol
;                  /* Put drive number */ 
2774                         if (i 
== sz_path
) {                             /* Root-dir */ 
2776                         } else {                                                /* Sub-dir */ 
2777                                 do              /* Add stacked path str */ 
2779                                 while (i 
< sz_path
); 
2786         LEAVE_FF(dj
.fs
, res
); 
2788 #endif /* _FS_RPATH >= 2 */ 
2789 #endif /* _FS_RPATH >= 1 */ 
2793 #if _FS_MINIMIZE <= 2 
2794 /*-----------------------------------------------------------------------*/ 
2795 /* Seek File R/W Pointer                                                 */ 
2796 /*-----------------------------------------------------------------------*/ 
2799         FIL 
*fp
,                /* Pointer to the file object */ 
2800         DWORD ofs               
/* File pointer from top of file */ 
2806         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
2807         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2808         if (fp
->flag 
& FA__ERROR
)                       /* Check abort flag */ 
2809                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2812         if (fp
->cltbl
) {        /* Fast seek */ 
2813                 DWORD cl
, pcl
, ncl
, tcl
, dsc
, tlen
, ulen
, *tbl
; 
2815                 if (ofs 
== CREATE_LINKMAP
) {    /* Create CLMT */ 
2817                         tlen 
= *tbl
++; ulen 
= 2;        /* Given table size and required table size */ 
2818                         cl 
= fp
->sclust
;                        /* Top of the chain */ 
2821                                         /* Get a fragment */ 
2822                                         tcl 
= cl
; ncl 
= 0; ulen 
+= 2;   /* Top, length and used items */ 
2825                                                 cl 
= get_fat(fp
->fs
, cl
); 
2826                                                 if (cl 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2827                                                 if (cl 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2828                                         } while (cl 
== pcl 
+ 1); 
2829                                         if (ulen 
<= tlen
) {             /* Store the length and top of the fragment */ 
2830                                                 *tbl
++ = ncl
; *tbl
++ = tcl
; 
2832                                 } while (cl 
< fp
->fs
->n_fatent
);        /* Repeat until end of chain */ 
2834                         *fp
->cltbl 
= ulen
;      /* Number of items used */ 
2836                                 *tbl 
= 0;               /* Terminate table */ 
2838                                 res 
= FR_NOT_ENOUGH_CORE
;       /* Given table size is smaller than required */ 
2840                 } else {                                                /* Fast seek */ 
2841                         if (ofs 
> fp
->fsize
)            /* Clip offset at the file size */ 
2843                         fp
->fptr 
= ofs
;                         /* Set file pointer */ 
2845                                 fp
->clust 
= clmt_clust(fp
, ofs 
- 1); 
2846                                 dsc 
= clust2sect(fp
->fs
, fp
->clust
); 
2847                                 if (!dsc
) ABORT(fp
->fs
, FR_INT_ERR
); 
2848                                 dsc 
+= (ofs 
- 1) / SS(fp
->fs
) & (fp
->fs
->csize 
- 1); 
2849                                 if (fp
->fptr 
% SS(fp
->fs
) && dsc 
!= fp
->dsect
) {        /* Refill sector cache if needed */ 
2852                                         if (fp
->flag 
& FA__DIRTY
) {             /* Write-back dirty sector cache */ 
2853                                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2854                                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2855                                                 fp
->flag 
&= ~FA__DIRTY
; 
2858                                         if (disk_read(fp
->fs
->drv
, fp
->buf
, dsc
, 1) != RES_OK
)  /* Load current sector */ 
2859                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2870                 DWORD clst
, bcs
, nsect
, ifptr
; 
2872                 if (ofs 
> fp
->fsize                                     
/* In read-only mode, clip offset with the file size */ 
2874                          && !(fp
->flag 
& FA_WRITE
) 
2879                 fp
->fptr 
= nsect 
= 0; 
2881                         bcs 
= (DWORD
)fp
->fs
->csize 
* SS(fp
->fs
);        /* Cluster size (byte) */ 
2883                                 (ofs 
- 1) / bcs 
>= (ifptr 
- 1) / bcs
) { /* When seek to same or following cluster, */ 
2884                                 fp
->fptr 
= (ifptr 
- 1) & ~(bcs 
- 1);    /* start from the current cluster */ 
2887                         } else {                                                                        /* When seek to back cluster, */ 
2888                                 clst 
= fp
->sclust
;                                              /* start from the first cluster */ 
2890                                 if (clst 
== 0) {                                                /* If no cluster chain, create a new chain */ 
2891                                         clst 
= create_chain(fp
->fs
, 0); 
2892                                         if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2893                                         if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2900                                 while (ofs 
> bcs
) {                                             /* Cluster following loop */ 
2902                                         if (fp
->flag 
& FA_WRITE
) {                      /* Check if in write mode or not */ 
2903                                                 clst 
= create_chain(fp
->fs
, clst
);      /* Force stretch if in write mode */ 
2904                                                 if (clst 
== 0) {                                /* When disk gets full, clip file size */ 
2909                                                 clst 
= get_fat(fp
->fs
, clst
);   /* Follow cluster chain if not in write mode */ 
2910                                         if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2911                                         if (clst 
<= 1 || clst 
>= fp
->fs
->n_fatent
) ABORT(fp
->fs
, FR_INT_ERR
); 
2917                                 if (ofs 
% SS(fp
->fs
)) { 
2918                                         nsect 
= clust2sect(fp
->fs
, clst
);       /* Current sector */ 
2919                                         if (!nsect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2920                                         nsect 
+= ofs 
/ SS(fp
->fs
); 
2924                 if (fp
->fptr 
% SS(fp
->fs
) && nsect 
!= fp
->dsect
) {      /* Fill sector cache if needed */ 
2927                         if (fp
->flag 
& FA__DIRTY
) {                     /* Write-back dirty sector cache */ 
2928                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2929                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2930                                 fp
->flag 
&= ~FA__DIRTY
; 
2933                         if (disk_read(fp
->fs
->drv
, fp
->buf
, nsect
, 1) != RES_OK
)        /* Fill sector cache */ 
2934                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2939                 if (fp
->fptr 
> fp
->fsize
) {                     /* Set file change flag if the file size is extended */ 
2940                         fp
->fsize 
= fp
->fptr
; 
2941                         fp
->flag 
|= FA__WRITTEN
; 
2946         LEAVE_FF(fp
->fs
, res
); 
2951 #if _FS_MINIMIZE <= 1 
2952 /*-----------------------------------------------------------------------*/ 
2953 /* Create a Directroy Object                                             */ 
2954 /*-----------------------------------------------------------------------*/ 
2957         DIR *dj
,                        /* Pointer to directory object to create */ 
2958         const TCHAR 
*path       
/* Pointer to the directory path */ 
2965         res 
= chk_mounted(&path
, &dj
->fs
, 0); 
2968                 res 
= follow_path(dj
, path
);                    /* Follow the path to the directory */ 
2970                 if (res 
== FR_OK
) {                                             /* Follow completed */ 
2971                         if (dj
->dir
) {                                          /* It is not the root dir */ 
2972                                 if (dj
->dir
[DIR_Attr
] & AM_DIR
) {       /* The object is a directory */ 
2973                                         dj
->sclust 
= LD_CLUST(dj
->dir
); 
2974                                 } else {                                                /* The object is not a directory */ 
2979                                 dj
->id 
= dj
->fs
->id
; 
2980                                 res 
= dir_sdi(dj
, 0);                   /* Rewind dir */ 
2983                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
2986         LEAVE_FF(dj
->fs
, res
); 
2992 /*-----------------------------------------------------------------------*/ 
2993 /* Read Directory Entry in Sequense                                      */ 
2994 /*-----------------------------------------------------------------------*/ 
2997         DIR *dj
,                        /* Pointer to the open directory object */ 
2998         FILINFO 
*fno            
/* Pointer to file information to return */ 
3005         res 
= validate(dj
->fs
, dj
->id
);                 /* Check validity of the object */ 
3008                         res 
= dir_sdi(dj
, 0);                   /* Rewind the directory object */ 
3011                         res 
= dir_read(dj
);                             /* Read an directory item */ 
3012                         if (res 
== FR_NO_FILE
) {                /* Reached end of dir */ 
3016                         if (res 
== FR_OK
) {                             /* A valid entry is found */ 
3017                                 get_fileinfo(dj
, fno
);          /* Get the object information */ 
3018                                 res 
= dir_next(dj
, 0);          /* Increment index for next */ 
3019                                 if (res 
== FR_NO_FILE
) { 
3028         LEAVE_FF(dj
->fs
, res
); 
3033 #if _FS_MINIMIZE == 0 
3034 /*-----------------------------------------------------------------------*/ 
3035 /* Get File Status                                                       */ 
3036 /*-----------------------------------------------------------------------*/ 
3039         const TCHAR 
*path
,      /* Pointer to the file path */ 
3040         FILINFO 
*fno            
/* Pointer to file information to return */ 
3048         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
3051                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
3052                 if (res 
== FR_OK
) {                             /* Follow completed */ 
3053                         if (dj
.dir
)             /* Found an object */ 
3054                                 get_fileinfo(&dj
, fno
); 
3055                         else                    /* It is root dir */ 
3056                                 res 
= FR_INVALID_NAME
; 
3061         LEAVE_FF(dj
.fs
, res
); 
3067 /*-----------------------------------------------------------------------*/ 
3068 /* Get Number of Free Clusters                                           */ 
3069 /*-----------------------------------------------------------------------*/ 
3072         const TCHAR 
*path
,      /* Pointer to the logical drive number (root dir) */ 
3073         DWORD 
*nclst
,           /* Pointer to the variable to return number of free clusters */ 
3074         FATFS 
**fatfs           
/* Pointer to pointer to corresponding file system object to return */ 
3078         DWORD n
, clst
, sect
, stat
; 
3083         /* Get drive number */ 
3084         res 
= chk_mounted(&path
, fatfs
, 0); 
3086                 /* If free_clust is valid, return it without full cluster scan */ 
3087                 if ((*fatfs
)->free_clust 
<= (*fatfs
)->n_fatent 
- 2) { 
3088                         *nclst 
= (*fatfs
)->free_clust
; 
3090                         /* Get number of free clusters */ 
3091                         fat 
= (*fatfs
)->fs_type
; 
3093                         if (fat 
== FS_FAT12
) { 
3096                                         stat 
= get_fat(*fatfs
, clst
); 
3097                                         if (stat 
== 0xFFFFFFFF) { res 
= FR_DISK_ERR
; break; } 
3098                                         if (stat 
== 1) { res 
= FR_INT_ERR
; break; } 
3100                                 } while (++clst 
< (*fatfs
)->n_fatent
); 
3102                                 clst 
= (*fatfs
)->n_fatent
; 
3103                                 sect 
= (*fatfs
)->fatbase
; 
3107                                                 res 
= move_window(*fatfs
, sect
++); 
3108                                                 if (res 
!= FR_OK
) break; 
3112                                         if (fat 
== FS_FAT16
) { 
3113                                                 if (LD_WORD(p
) == 0) n
++; 
3116                                                 if ((LD_DWORD(p
) & 0x0FFFFFFF) == 0) n
++; 
3121                         (*fatfs
)->free_clust 
= n
; 
3122                         if (fat 
== FS_FAT32
) (*fatfs
)->fsi_flag 
= 1; 
3126         LEAVE_FF(*fatfs
, res
); 
3132 /*-----------------------------------------------------------------------*/ 
3134 /*-----------------------------------------------------------------------*/ 
3136 FRESULT 
f_truncate ( 
3137         FIL 
*fp         
/* Pointer to the file object */ 
3144         res 
= validate(fp
->fs
, fp
->id
);         /* Check validity of the object */ 
3146                 if (fp
->flag 
& FA__ERROR
) {                     /* Check abort flag */ 
3149                         if (!(fp
->flag 
& FA_WRITE
))             /* Check access mode */ 
3154                 if (fp
->fsize 
> fp
->fptr
) { 
3155                         fp
->fsize 
= fp
->fptr
;   /* Set file size to current R/W point */ 
3156                         fp
->flag 
|= FA__WRITTEN
; 
3157                         if (fp
->fptr 
== 0) {    /* When set file size to zero, remove entire cluster chain */ 
3158                                 res 
= remove_chain(fp
->fs
, fp
->sclust
); 
3160                         } else {                                /* When truncate a part of the file, remove remaining clusters */ 
3161                                 ncl 
= get_fat(fp
->fs
, fp
->clust
); 
3163                                 if (ncl 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
3164                                 if (ncl 
== 1) res 
= FR_INT_ERR
; 
3165                                 if (res 
== FR_OK 
&& ncl 
< fp
->fs
->n_fatent
) { 
3166                                         res 
= put_fat(fp
->fs
, fp
->clust
, 0x0FFFFFFF); 
3167                                         if (res 
== FR_OK
) res 
= remove_chain(fp
->fs
, ncl
); 
3171                 if (res 
!= FR_OK
) fp
->flag 
|= FA__ERROR
; 
3174         LEAVE_FF(fp
->fs
, res
); 
3180 /*-----------------------------------------------------------------------*/ 
3181 /* Delete a File or Directory                                            */ 
3182 /*-----------------------------------------------------------------------*/ 
3185         const TCHAR 
*path               
/* Pointer to the file or directory path */ 
3195         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3198                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
3199                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3200                         res 
= FR_INVALID_NAME
;                  /* Cannot remove dot entry */ 
3202                 if (res 
== FR_OK
) res 
= chk_lock(&dj
, 2);       /* Cannot remove open file */ 
3204                 if (res 
== FR_OK
) {                                     /* The object is accessible */ 
3207                                 res 
= FR_INVALID_NAME
;          /* Cannot remove the start directory */ 
3209                                 if (dir
[DIR_Attr
] & AM_RDO
) 
3210                                         res 
= FR_DENIED
;                /* Cannot remove R/O object */ 
3212                         dclst 
= LD_CLUST(dir
); 
3213                         if (res 
== FR_OK 
&& (dir
[DIR_Attr
] & AM_DIR
)) { /* Is it a sub-dir? */ 
3217                                         mem_cpy(&sdj
, &dj
, sizeof(DIR));        /* Check if the sub-dir is empty or not */ 
3219                                         res 
= dir_sdi(&sdj
, 2);         /* Exclude dot entries */ 
3221                                                 res 
= dir_read(&sdj
); 
3222                                                 if (res 
== FR_OK                        
/* Not empty dir */ 
3224                                                 || dclst 
== sdj
.fs
->cdir        
/* Current dir */ 
3227                                                 if (res 
== FR_NO_FILE
) res 
= FR_OK
;     /* Empty */ 
3232                                 res 
= dir_remove(&dj
);          /* Remove the directory entry */ 
3234                                         if (dclst
)                              /* Remove the cluster chain if exist */ 
3235                                                 res 
= remove_chain(dj
.fs
, dclst
); 
3236                                         if (res 
== FR_OK
) res 
= sync(dj
.fs
); 
3242         LEAVE_FF(dj
.fs
, res
); 
3248 /*-----------------------------------------------------------------------*/ 
3249 /* Create a Directory                                                    */ 
3250 /*-----------------------------------------------------------------------*/ 
3253         const TCHAR 
*path               
/* Pointer to the directory path */ 
3259         DWORD dsc
, dcl
, pcl
, tim 
= get_fattime(); 
3263         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3266                 res 
= follow_path(&dj
, path
);                   /* Follow the file path */ 
3267                 if (res 
== FR_OK
) res 
= FR_EXIST
;               /* Any object with same name is already existing */ 
3268                 if (_FS_RPATH 
&& res 
== FR_NO_FILE 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3269                         res 
= FR_INVALID_NAME
; 
3270                 if (res 
== FR_NO_FILE
) {                                /* Can create a new directory */ 
3271                         dcl 
= create_chain(dj
.fs
, 0);           /* Allocate a cluster for the new directory table */ 
3273                         if (dcl 
== 0) res 
= FR_DENIED
;          /* No space to allocate a new cluster */ 
3274                         if (dcl 
== 1) res 
= FR_INT_ERR
; 
3275                         if (dcl 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
3276                         if (res 
== FR_OK
)                                       /* Flush FAT */ 
3277                                 res 
= move_window(dj
.fs
, 0); 
3278                         if (res 
== FR_OK
) {                                     /* Initialize the new directory table */ 
3279                                 dsc 
= clust2sect(dj
.fs
, dcl
); 
3281                                 mem_set(dir
, 0, SS(dj
.fs
)); 
3282                                 mem_set(dir
+DIR_Name
, ' ', 8+3);        /* Create "." entry */ 
3283                                 dir
[DIR_Name
] = '.'; 
3284                                 dir
[DIR_Attr
] = AM_DIR
; 
3285                                 ST_DWORD(dir
+DIR_WrtTime
, tim
); 
3287                                 mem_cpy(dir
+SZ_DIR
, dir
, SZ_DIR
);       /* Create ".." entry */ 
3288                                 dir
[33] = '.'; pcl 
= dj
.sclust
; 
3289                                 if (dj
.fs
->fs_type 
== FS_FAT32 
&& pcl 
== dj
.fs
->dirbase
) 
3291                                 ST_CLUST(dir
+SZ_DIR
, pcl
); 
3292                                 for (n 
= dj
.fs
->csize
; n
; n
--) {        /* Write dot entries and clear following sectors */ 
3293                                         dj
.fs
->winsect 
= dsc
++; 
3295                                         res 
= move_window(dj
.fs
, 0); 
3296                                         if (res 
!= FR_OK
) break; 
3297                                         mem_set(dir
, 0, SS(dj
.fs
)); 
3300                         if (res 
== FR_OK
) res 
= dir_register(&dj
);      /* Register the object to the directoy */ 
3302                                 remove_chain(dj
.fs
, dcl
);                       /* Could not register, remove cluster chain */ 
3305                                 dir
[DIR_Attr
] = AM_DIR
;                         /* Attribute */ 
3306                                 ST_DWORD(dir
+DIR_WrtTime
, tim
);         /* Created time */ 
3307                                 ST_CLUST(dir
, dcl
);                                     /* Table start cluster */ 
3315         LEAVE_FF(dj
.fs
, res
); 
3321 /*-----------------------------------------------------------------------*/ 
3322 /* Change Attribute                                                      */ 
3323 /*-----------------------------------------------------------------------*/ 
3326         const TCHAR 
*path
,      /* Pointer to the file path */ 
3327         BYTE value
,                     /* Attribute bits */ 
3328         BYTE mask                       
/* Attribute mask to change */ 
3337         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3340                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
3342                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3343                         res 
= FR_INVALID_NAME
; 
3346                         if (!dir
) {                                             /* Is it a root directory? */ 
3347                                 res 
= FR_INVALID_NAME
; 
3348                         } else {                                                /* File or sub directory */ 
3349                                 mask 
&= AM_RDO
|AM_HID
|AM_SYS
|AM_ARC
;    /* Valid attribute mask */ 
3350                                 dir
[DIR_Attr
] = (value 
& mask
) | (dir
[DIR_Attr
] & (BYTE
)~mask
); /* Apply attribute change */ 
3357         LEAVE_FF(dj
.fs
, res
); 
3363 /*-----------------------------------------------------------------------*/ 
3364 /* Change Timestamp                                                      */ 
3365 /*-----------------------------------------------------------------------*/ 
3368         const TCHAR 
*path
,      /* Pointer to the file/directory name */ 
3369         const FILINFO 
*fno      
/* Pointer to the time stamp to be set */ 
3378         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3381                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
3383                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3384                         res 
= FR_INVALID_NAME
; 
3387                         if (!dir
) {                                     /* Root directory */ 
3388                                 res 
= FR_INVALID_NAME
; 
3389                         } else {                                        /* File or sub-directory */ 
3390                                 ST_WORD(dir
+DIR_WrtTime
, fno
->ftime
); 
3391                                 ST_WORD(dir
+DIR_WrtDate
, fno
->fdate
); 
3398         LEAVE_FF(dj
.fs
, res
); 
3404 /*-----------------------------------------------------------------------*/ 
3405 /* Rename File/Directory                                                 */ 
3406 /*-----------------------------------------------------------------------*/ 
3409         const TCHAR 
*path_old
,  /* Pointer to the old name */ 
3410         const TCHAR 
*path_new   
/* Pointer to the new name */ 
3420         res 
= chk_mounted(&path_old
, &djo
.fs
, 1); 
3424                 res 
= follow_path(&djo
, path_old
);              /* Check old object */ 
3425                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (djo
.fn
[NS
] & NS_DOT
)) 
3426                         res 
= FR_INVALID_NAME
; 
3428                 if (res 
== FR_OK
) res 
= chk_lock(&djo
, 2); 
3430                 if (res 
== FR_OK
) {                                             /* Old object is found */ 
3431                         if (!djo
.dir
) {                                         /* Is root dir? */ 
3434                                 mem_cpy(buf
, djo
.dir
+DIR_Attr
, 21);             /* Save the object information except for name */ 
3435                                 mem_cpy(&djn
, &djo
, sizeof(DIR));               /* Check new object */ 
3436                                 res 
= follow_path(&djn
, path_new
); 
3437                                 if (res 
== FR_OK
) res 
= FR_EXIST
;               /* The new object name is already existing */ 
3438                                 if (res 
== FR_NO_FILE
) {                                /* Is it a valid path and no name collision? */ 
3439 /* Start critical section that any interruption or error can cause cross-link */ 
3440                                         res 
= dir_register(&djn
);                       /* Register the new entry */ 
3442                                                 dir 
= djn
.dir
;                                  /* Copy object information except for name */ 
3443                                                 mem_cpy(dir
+13, buf
+2, 19); 
3444                                                 dir
[DIR_Attr
] = buf
[0] | AM_ARC
; 
3446                                                 if (djo
.sclust 
!= djn
.sclust 
&& (dir
[DIR_Attr
] & AM_DIR
)) {             /* Update .. entry in the directory if needed */ 
3447                                                         dw 
= clust2sect(djn
.fs
, LD_CLUST(dir
)); 
3451                                                                 res 
= move_window(djn
.fs
, dw
); 
3452                                                                 dir 
= djn
.fs
->win
+SZ_DIR
;       /* .. entry */ 
3453                                                                 if (res 
== FR_OK 
&& dir
[1] == '.') { 
3454                                                                         dw 
= (djn
.fs
->fs_type 
== FS_FAT32 
&& djn
.sclust 
== djn
.fs
->dirbase
) ? 
0 : djn
.sclust
; 
3461                                                         res 
= dir_remove(&djo
);         /* Remove old entry */ 
3466 /* End critical section */ 
3472         LEAVE_FF(djo
.fs
, res
); 
3475 #endif /* !_FS_READONLY */ 
3476 #endif /* _FS_MINIMIZE == 0 */ 
3477 #endif /* _FS_MINIMIZE <= 1 */ 
3478 #endif /* _FS_MINIMIZE <= 2 */ 
3482 /*-----------------------------------------------------------------------*/ 
3483 /* Forward data to the stream directly (available on only tiny cfg)      */ 
3484 /*-----------------------------------------------------------------------*/ 
3485 #if _USE_FORWARD && _FS_TINY 
3488         FIL 
*fp
,                                                /* Pointer to the file object */ 
3489         UINT (*func
)(const BYTE
*,UINT
), /* Pointer to the streaming function */ 
3490         UINT btr
,                                               /* Number of bytes to forward */ 
3491         UINT 
*bf                                                
/* Pointer to number of bytes forwarded */ 
3495         DWORD remain
, clst
, sect
; 
3500         *bf 
= 0;        /* Initialize byte counter */ 
3502         res 
= validate(fp
->fs
, fp
->id
);                                 /* Check validity of the object */ 
3503         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
3504         if (fp
->flag 
& FA__ERROR
)                                               /* Check error flag */ 
3505                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
3506         if (!(fp
->flag 
& FA_READ
))                                              /* Check access mode */ 
3507                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
3509         remain 
= fp
->fsize 
- fp
->fptr
; 
3510         if (btr 
> remain
) btr 
= (UINT
)remain
;                   /* Truncate btr by remaining bytes */ 
3512         for ( ;  btr 
&& (*func
)(0, 0);                                  /* Repeat until all data transferred or stream becomes busy */ 
3513                 fp
->fptr 
+= rcnt
, *bf 
+= rcnt
, btr 
-= rcnt
) { 
3514                 csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
3515                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
3516                         if (!csect
) {                                                   /* On the cluster boundary? */ 
3517                                 clst 
= (fp
->fptr 
== 0) ?                        
/* On the top of the file? */ 
3518                                         fp
->sclust 
: get_fat(fp
->fs
, fp
->clust
); 
3519                                 if (clst 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
3520                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
3521                                 fp
->clust 
= clst
;                                       /* Update current cluster */ 
3524                 sect 
= clust2sect(fp
->fs
, fp
->clust
);           /* Get current data sector */ 
3525                 if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
3527                 if (move_window(fp
->fs
, sect
))                          /* Move sector window */ 
3528                         ABORT(fp
->fs
, FR_DISK_ERR
); 
3530                 rcnt 
= SS(fp
->fs
) - (WORD
)(fp
->fptr 
% SS(fp
->fs
));      /* Forward data from sector window */ 
3531                 if (rcnt 
> btr
) rcnt 
= btr
; 
3532                 rcnt 
= (*func
)(&fp
->fs
->win
[(WORD
)fp
->fptr 
% SS(fp
->fs
)], rcnt
); 
3533                 if (!rcnt
) ABORT(fp
->fs
, FR_INT_ERR
); 
3536         LEAVE_FF(fp
->fs
, FR_OK
); 
3538 #endif /* _USE_FORWARD */ 
3542 #if _USE_MKFS && !_FS_READONLY 
3543 /*-----------------------------------------------------------------------*/ 
3544 /* Create File System on the Drive                                       */ 
3545 /*-----------------------------------------------------------------------*/ 
3546 #define N_ROOTDIR       512             /* Number of root dir entries for FAT12/16 */ 
3547 #define N_FATS          1               /* Number of FAT copies (1 or 2) */ 
3551         BYTE drv
,               /* Logical drive number */ 
3552         BYTE sfd
,               /* Partitioning rule 0:FDISK, 1:SFD */ 
3553         UINT au                 
/* Allocation unit size [bytes] */ 
3556         static const WORD vst
[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0}; 
3557         static const WORD cst
[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; 
3559         DWORD n_clst
, vs
, n
, wsect
; 
3561         DWORD b_vol
, b_fat
, b_dir
, b_data
;      /* Offset (LBA) */ 
3562         DWORD n_vol
, n_rsv
, n_fat
, n_dir
;       /* Size */ 
3567         /* Check mounted drive and clear work area */ 
3568         if (drv 
>= _VOLUMES
) return FR_INVALID_DRIVE
; 
3570         if (!fs
) return FR_NOT_ENABLED
; 
3574         /* Get disk statics */ 
3575         stat 
= disk_initialize(drv
); 
3576         if (stat 
& STA_NOINIT
) return FR_NOT_READY
; 
3577         if (stat 
& STA_PROTECT
) return FR_WRITE_PROTECTED
; 
3578 #if _MAX_SS != 512                                      /* Get disk sector size */ 
3579         if (disk_ioctl(drv
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK
) 
3582         if (disk_ioctl(drv
, GET_SECTOR_COUNT
, &n_vol
) != RES_OK 
|| n_vol 
< 128) 
3584         b_vol 
= (sfd
) ? 
0 : 63; /* Volume start sector */ 
3586         if (au 
& (au 
- 1)) au 
= 0;      /* Check validity of the AU size */ 
3587         if (!au
) {                                      /* AU auto selection */ 
3588                 vs 
= n_vol 
/ (2000 / (SS(fs
) / 512)); 
3589                 for (i 
= 0; vs 
< vst
[i
]; i
++) ; 
3592         au 
/= SS(fs
);           /* Number of sectors per cluster */ 
3593         if (au 
== 0) au 
= 1; 
3594         if (au 
> 128) au 
= 128; 
3596         /* Pre-compute number of clusters and FAT syb-type */ 
3597         n_clst 
= n_vol 
/ au
; 
3599         if (n_clst 
>= MIN_FAT16
) fmt 
= FS_FAT16
; 
3600         if (n_clst 
>= MIN_FAT32
) fmt 
= FS_FAT32
; 
3602         /* Determine offset and size of FAT structure */ 
3603         if (fmt 
== FS_FAT32
) { 
3604                 n_fat 
= ((n_clst 
* 4) + 8 + SS(fs
) - 1) / SS(fs
); 
3608                 n_fat 
= (fmt 
== FS_FAT12
) ? 
(n_clst 
* 3 + 1) / 2 + 3 : (n_clst 
* 2) + 4; 
3609                 n_fat 
= (n_fat 
+ SS(fs
) - 1) / SS(fs
); 
3611                 n_dir 
= (DWORD
)N_ROOTDIR 
* SZ_DIR 
/ SS(fs
); 
3613         b_fat 
= b_vol 
+ n_rsv
;                          /* FAT area start sector */ 
3614         b_dir 
= b_fat 
+ n_fat 
* N_FATS
;         /* Directory area start sector */ 
3615         b_data 
= b_dir 
+ n_dir
;                         /* Data area start sector */ 
3616         if (n_vol 
< b_data 
+ au
) return FR_MKFS_ABORTED
;        /* Too small volume */ 
3618         /* Align data start sector to erase block boundary (for flash memory media) */ 
3619         if (disk_ioctl(drv
, GET_BLOCK_SIZE
, &n
) != RES_OK 
|| !n 
|| n 
> 32768) n 
= 1; 
3620         n 
= (b_data 
+ n 
- 1) & ~(n 
- 1);        /* Next nearest erase block from current data start */ 
3621         n 
= (n 
- b_data
) / N_FATS
; 
3622         if (fmt 
== FS_FAT32
) {          /* FAT32: Move FAT offset */ 
3625         } else {                                        /* FAT12/16: Expand FAT size */ 
3629         /* Determine number of clusters and final check of validity of the FAT sub-type */ 
3630         n_clst 
= (n_vol 
- n_rsv 
- n_fat 
* N_FATS 
- n_dir
) / au
; 
3631         if (   (fmt 
== FS_FAT16 
&& n_clst 
< MIN_FAT16
) 
3632                 || (fmt 
== FS_FAT32 
&& n_clst 
< MIN_FAT32
)) 
3633                 return FR_MKFS_ABORTED
; 
3635         /* Create partition table if required */ 
3636         if (sfd
) {      /* No patition table (SFD) */ 
3638         } else {        /* With patition table (FDISK) */ 
3639                 DWORD n_disk 
= b_vol 
+ n_vol
; 
3641                 mem_set(fs
->win
, 0, SS(fs
)); 
3642                 tbl 
= fs
->win
+MBR_Table
; 
3643                 ST_DWORD(tbl
, 0x00010180);                      /* Partition start in CHS */ 
3644                 if (n_disk 
< 63UL * 255 * 1024) {       /* Partition end in CHS */ 
3645                         n_disk 
= n_disk 
/ 63 / 255; 
3646                         tbl
[7] = (BYTE
)n_disk
; 
3647                         tbl
[6] = (BYTE
)((n_disk 
>> 2) | 63); 
3649                         ST_WORD(&tbl
[6], 0xFFFF);       /* CHS saturated */ 
3652                 if (fmt 
!= FS_FAT32
)                            /* System ID */ 
3653                         tbl
[4] = (n_vol 
< 0x10000) ? 
0x04 : 0x06; 
3656                 ST_DWORD(tbl
+8, 63);                            /* Partition start in LBA */ 
3657                 ST_DWORD(tbl
+12, n_vol
);                        /* Partition size in LBA */ 
3658                 ST_WORD(fs
->win
+BS_55AA
, 0xAA55);       /* MBR signature */ 
3659                 if (disk_write(drv
, fs
->win
, 0, 1) != RES_OK
)   /* Put the MBR into first physical sector */ 
3664         /* Create volume boot record */ 
3665         tbl 
= fs
->win
;                                                  /* Clear sector */ 
3666         mem_set(tbl
, 0, SS(fs
)); 
3667         mem_cpy(tbl
, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ 
3668         i 
= SS(fs
);                                                             /* Sector size */ 
3669         ST_WORD(tbl
+BPB_BytsPerSec
, i
); 
3670         tbl
[BPB_SecPerClus
] = (BYTE
)au
;                 /* Sectors per cluster */ 
3671         ST_WORD(tbl
+BPB_RsvdSecCnt
, n_rsv
);             /* Reserved sectors */ 
3672         tbl
[BPB_NumFATs
] = N_FATS
;                              /* Number of FATs */ 
3673         i 
= (fmt 
== FS_FAT32
) ? 
0 : N_ROOTDIR
;  /* Number of rootdir entries */ 
3674         ST_WORD(tbl
+BPB_RootEntCnt
, i
); 
3675         if (n_vol 
< 0x10000) {                                  /* Number of total sectors */ 
3676                 ST_WORD(tbl
+BPB_TotSec16
, n_vol
); 
3678                 ST_DWORD(tbl
+BPB_TotSec32
, n_vol
); 
3680         tbl
[BPB_Media
] = md
;                                    /* Media descriptor */ 
3681         ST_WORD(tbl
+BPB_SecPerTrk
, 63);                 /* Number of sectors per track */ 
3682         ST_WORD(tbl
+BPB_NumHeads
, 255);                 /* Number of heads */ 
3683         ST_DWORD(tbl
+BPB_HiddSec
, b_vol
);               /* Hidden sectors */ 
3684         n 
= get_fattime();                                              /* Use current time as VSN */ 
3685         if (fmt 
== FS_FAT32
) { 
3686                 ST_DWORD(tbl
+BS_VolID32
, n
);            /* VSN */ 
3687                 ST_DWORD(tbl
+BPB_FATSz32
, n_fat
);       /* Number of sectors per FAT */ 
3688                 ST_DWORD(tbl
+BPB_RootClus
, 2);          /* Root directory start cluster (2) */ 
3689                 ST_WORD(tbl
+BPB_FSInfo
, 1);                     /* FSInfo record offset (VBR+1) */ 
3690                 ST_WORD(tbl
+BPB_BkBootSec
, 6);          /* Backup boot record offset (VBR+6) */ 
3691                 tbl
[BS_DrvNum32
] = 0x80;                        /* Drive number */ 
3692                 tbl
[BS_BootSig32
] = 0x29;                       /* Extended boot signature */ 
3693                 mem_cpy(tbl
+BS_VolLab32
, "NO NAME    " "FAT32   ", 19); /* Volume label, FAT signature */ 
3695                 ST_DWORD(tbl
+BS_VolID
, n
);                      /* VSN */ 
3696                 ST_WORD(tbl
+BPB_FATSz16
, n_fat
);        /* Number of sectors per FAT */ 
3697                 tbl
[BS_DrvNum
] = 0x80;                          /* Drive number */ 
3698                 tbl
[BS_BootSig
] = 0x29;                         /* Extended boot signature */ 
3699                 mem_cpy(tbl
+BS_VolLab
, "NO NAME    " "FAT     ", 19);   /* Volume label, FAT signature */ 
3701         ST_WORD(tbl
+BS_55AA
, 0xAA55);                   /* Signature (Offset is fixed here regardless of sector size) */ 
3702         if (disk_write(drv
, tbl
, b_vol
, 1) != RES_OK
)   /* Write VBR */ 
3704         if (fmt 
== FS_FAT32
)                                                    /* Write backup VBR if needed (VBR+6) */ 
3705                 disk_write(drv
, tbl
, b_vol 
+ 6, 1); 
3707         /* Initialize FAT area */ 
3709         for (i 
= 0; i 
< N_FATS
; i
++) {          /* Initialize each FAT copy */ 
3710                 mem_set(tbl
, 0, SS(fs
));                        /* 1st sector of the FAT  */ 
3711                 n 
= md
;                                                         /* Media descriptor byte */ 
3712                 if (fmt 
!= FS_FAT32
) { 
3713                         n 
|= (fmt 
== FS_FAT12
) ? 
0x00FFFF00 : 0xFFFFFF00; 
3714                         ST_DWORD(tbl
+0, n
);                             /* Reserve cluster #0-1 (FAT12/16) */ 
3717                         ST_DWORD(tbl
+0, n
);                             /* Reserve cluster #0-1 (FAT32) */ 
3718                         ST_DWORD(tbl
+4, 0xFFFFFFFF); 
3719                         ST_DWORD(tbl
+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */ 
3721                 if (disk_write(drv
, tbl
, wsect
++, 1) != RES_OK
) 
3723                 mem_set(tbl
, 0, SS(fs
));                        /* Fill following FAT entries with zero */ 
3724                 for (n 
= 1; n 
< n_fat
; n
++) {           /* This loop may take a time on FAT32 volume due to many single sector writes */ 
3725                         if (disk_write(drv
, tbl
, wsect
++, 1) != RES_OK
) 
3730         /* Initialize root directory */ 
3731         i 
= (fmt 
== FS_FAT32
) ? au 
: n_dir
; 
3733                 if (disk_write(drv
, tbl
, wsect
++, 1) != RES_OK
) 
3737 #if _USE_ERASE  /* Erase data area if needed */ 
3741                 eb
[0] = wsect
; eb
[1] = wsect 
+ (n_clst 
- ((fmt 
== FS_FAT32
) ? 
1 : 0)) * au 
- 1; 
3742                 disk_ioctl(drv
, CTRL_ERASE_SECTOR
, eb
); 
3746         /* Create FSInfo if needed */ 
3747         if (fmt 
== FS_FAT32
) { 
3748                 ST_DWORD(tbl
+FSI_LeadSig
, 0x41615252); 
3749                 ST_DWORD(tbl
+FSI_StrucSig
, 0x61417272); 
3750                 ST_DWORD(tbl
+FSI_Free_Count
, n_clst 
- 1);       /* Number of free clusters */ 
3751                 ST_DWORD(tbl
+FSI_Nxt_Free
, 2);                          /* Last allocated cluster# */ 
3752                 ST_WORD(tbl
+BS_55AA
, 0xAA55); 
3753                 disk_write(drv
, tbl
, b_vol 
+ 1, 1);     /* Write original (VBR+1) */ 
3754                 disk_write(drv
, tbl
, b_vol 
+ 7, 1);     /* Write backup (VBR+7) */ 
3757         return (disk_ioctl(drv
, CTRL_SYNC
, (void*)0) == RES_OK
) ? FR_OK 
: FR_DISK_ERR
; 
3760 #endif /* _USE_MKFS && !_FS_READONLY */ 
3766 /*-----------------------------------------------------------------------*/ 
3767 /* Get a string from the file                                            */ 
3768 /*-----------------------------------------------------------------------*/ 
3770         TCHAR
* buff
,    /* Pointer to the string buffer to read */ 
3771         int len
,                /* Size of string buffer (characters) */ 
3772         FIL
* fil                
/* Pointer to the file object */ 
3781         while (n 
< len 
- 1) {                   /* Read bytes until buffer gets filled */ 
3782                 f_read(fil
, s
, 1, &rc
); 
3783                 if (rc 
!= 1) break;                     /* Break on EOF or error */ 
3785 #if _LFN_UNICODE                                        /* Read a character in UTF-8 encoding */ 
3787                         if (c 
< 0xC0) continue; /* Skip stray trailer */ 
3788                         if (c 
< 0xE0) {                 /* Two-byte sequense */ 
3789                                 f_read(fil
, s
, 1, &rc
); 
3791                                 c 
= ((c 
& 0x1F) << 6) | (s
[0] & 0x3F); 
3792                                 if (c 
< 0x80) c 
= '?'; 
3794                                 if (c 
< 0xF0) {         /* Three-byte sequense */ 
3795                                         f_read(fil
, s
, 2, &rc
); 
3797                                         c 
= (c 
<< 12) | ((s
[0] & 0x3F) << 6) | (s
[1] & 0x3F); 
3798                                         if (c 
< 0x800) c 
= '?'; 
3799                                 } else {                        /* Reject four-byte sequense */ 
3805 #if _USE_STRFUNC >= 2 
3806                 if (c 
== '\r') continue;        /* Strip '\r' */ 
3810                 if (c 
== '\n') break;           /* Break on EOL */ 
3813         return n ? buff 
: 0;                    /* When no data read (eof or error), return with error. */ 
3820 /*-----------------------------------------------------------------------*/ 
3821 /* Put a character to the file                                           */ 
3822 /*-----------------------------------------------------------------------*/ 
3824         TCHAR c
,        /* A character to be output */ 
3825         FIL
* fil        
/* Pointer to the file object */ 
3832 #if _USE_STRFUNC >= 2 
3833         if (c 
== '\n') f_putc ('\r', fil
);      /* LF -> CRLF conversion */ 
3836 #if _LFN_UNICODE        /* Write the character in UTF-8 encoding */ 
3837         if (c 
< 0x80) {                 /* 7-bit */ 
3841                 if (c 
< 0x800) {        /* 11-bit */ 
3842                         s
[0] = (BYTE
)(0xC0 | (c 
>> 6)); 
3843                         s
[1] = (BYTE
)(0x80 | (c 
& 0x3F)); 
3845                 } else {                        /* 16-bit */ 
3846                         s
[0] = (BYTE
)(0xE0 | (c 
>> 12)); 
3847                         s
[1] = (BYTE
)(0x80 | ((c 
>> 6) & 0x3F)); 
3848                         s
[2] = (BYTE
)(0x80 | (c 
& 0x3F)); 
3852 #else                           /* Write the character without conversion */ 
3856         f_write(fil
, s
, btw
, &bw
);              /* Write the char to the file */ 
3857         return (bw 
== btw
) ? 
1 : EOF
;   /* Return the result */ 
3863 /*-----------------------------------------------------------------------*/ 
3864 /* Put a string to the file                                              */ 
3865 /*-----------------------------------------------------------------------*/ 
3867         const TCHAR
* str
,       /* Pointer to the string to be output */ 
3868         FIL
* fil                        
/* Pointer to the file object */ 
3874         for (n 
= 0; *str
; str
++, n
++) { 
3875                 if (f_putc(*str
, fil
) == EOF
) return EOF
; 
3883 /*-----------------------------------------------------------------------*/ 
3884 /* Put a formatted string to the file                                    */ 
3885 /*-----------------------------------------------------------------------*/ 
3887         FIL
* fil
,                       /* Pointer to the file object */ 
3888         const TCHAR
* str
,       /* Pointer to the format string */ 
3889         ...                                     /* Optional arguments... */ 
3896         TCHAR c
, d
, s
[16], *p
; 
3902         for (cc 
= res 
= 0; cc 
!= EOF
; res 
+= cc
) { 
3904                 if (c 
== 0) break;                      /* End of string */ 
3905                 if (c 
!= '%') {                         /* Non escape character */ 
3906                         cc 
= f_putc(c
, fil
); 
3907                         if (cc 
!= EOF
) cc 
= 1; 
3912                 if (c 
== '0') {                         /* Flag: '0' padding */ 
3915                         if (c 
== '-') {                 /* Flag: left justified */ 
3919                 while (IsDigit(c
)) {            /* Precision */ 
3920                         w 
= w 
* 10 + c 
- '0'; 
3923                 if (c 
== 'l' || c 
== 'L') {     /* Prefix: Size is long int */ 
3928                 if (IsLower(d
)) d 
-= 0x20; 
3929                 switch (d
) {                            /* Type is... */ 
3930                 case 'S' :                                      /* String */ 
3931                         p 
= va_arg(arp
, TCHAR
*); 
3932                         for (j 
= 0; p
[j
]; j
++) ; 
3934                         while (!(f 
& 2) && j
++ < w
) res 
+= (cc 
= f_putc(' ', fil
)); 
3935                         res 
+= (cc 
= f_puts(p
, fil
)); 
3936                         while (j
++ < w
) res 
+= (cc 
= f_putc(' ', fil
)); 
3937                         if (cc 
!= EOF
) cc 
= res
; 
3939                 case 'C' :                                      /* Character */ 
3940                         cc 
= f_putc((TCHAR
)va_arg(arp
, int), fil
); continue; 
3941                 case 'B' :                                      /* Binary */ 
3943                 case 'O' :                                      /* Octal */ 
3945                 case 'D' :                                      /* Signed decimal */ 
3946                 case 'U' :                                      /* Unsigned decimal */ 
3948                 case 'X' :                                      /* Hexdecimal */ 
3950                 default:                                        /* Unknown type (passthrough) */ 
3951                         cc 
= f_putc(c
, fil
); continue; 
3954                 /* Get an argument and put it in numeral */ 
3955                 v 
= (f 
& 4) ? 
va_arg(arp
, long) : ((d 
== 'D') ? 
(long)va_arg(arp
, int) : va_arg(arp
, unsigned int)); 
3956                 if (d 
== 'D' && (v 
& 0x80000000)) { 
3962                         d 
= (TCHAR
)(v 
% r
); v 
/= r
; 
3963                         if (d 
> 9) d 
+= (c 
== 'x') ? 
0x27 : 0x07; 
3965                 } while (v 
&& i 
< sizeof(s
) / sizeof(s
[0])); 
3966                 if (f 
& 8) s
[i
++] = '-'; 
3967                 j 
= i
; d 
= (f 
& 1) ? 
'0' : ' '; 
3969                 while (!(f 
& 2) && j
++ < w
) res 
+= (cc 
= f_putc(d
, fil
)); 
3970                 do res 
+= (cc 
= f_putc(s
[--i
], fil
)); while(i
); 
3971                 while (j
++ < w
) res 
+= (cc 
= f_putc(' ', fil
)); 
3972                 if (cc 
!= EOF
) cc 
= res
; 
3976         return (cc 
== EOF
) ? cc 
: res
; 
3979 #endif /* !_FS_READONLY */ 
3980 #endif /* _USE_STRFUNC */