SAMV71 Xplained Ultra Software Package 1.0

ff.c

00001 /*----------------------------------------------------------------------------/
00002 /  FatFs - FAT file system module  R0.10b                (C)ChaN, 2014
00003 /-----------------------------------------------------------------------------/
00004 / FatFs module is a generic FAT file system module for small embedded systems.
00005 / This is a free software that opened for education, research and commercial
00006 / developments under license policy of following terms.
00007 /
00008 /  Copyright (C) 2014, ChaN, all right reserved.
00009 /
00010 / * The FatFs module is a free software and there is NO WARRANTY.
00011 / * No restriction on use. You can use, modify and redistribute it for
00012 /   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
00013 / * Redistributions of source code must retain the above copyright notice.
00014 /
00015 /-----------------------------------------------------------------------------/
00016 / Feb 26,'06 R0.00  Prototype.
00017 /
00018 / Apr 29,'06 R0.01  First stable version.
00019 /
00020 / Jun 01,'06 R0.02  Added FAT12 support.
00021 /                   Removed unbuffered mode.
00022 /                   Fixed a problem on small (<32M) partition.
00023 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
00024 /
00025 / Sep 22,'06 R0.03  Added f_rename().
00026 /                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
00027 / Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
00028 /                   Fixed f_mkdir() creates incorrect directory on FAT32.
00029 /
00030 / Feb 04,'07 R0.04  Supported multiple drive system.
00031 /                   Changed some interfaces for multiple drive system.
00032 /                   Changed f_mountdrv() to f_mount().
00033 /                   Added f_mkfs().
00034 / Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
00035 /                   Added a capability of extending file size to f_lseek().
00036 /                   Added minimization level 3.
00037 /                   Fixed an endian sensitive code in f_mkfs().
00038 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
00039 /                   Added FSINFO support.
00040 /                   Fixed DBCS name can result FR_INVALID_NAME.
00041 /                   Fixed short seek (<= csize) collapses the file object.
00042 /
00043 / Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
00044 /                   Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
00045 /                   Fixed f_mkdir() on FAT32 creates incorrect directory.
00046 / Feb 03,'08 R0.05a Added f_truncate() and f_utime().
00047 /                   Fixed off by one error at FAT sub-type determination.
00048 /                   Fixed btr in f_read() can be mistruncated.
00049 /                   Fixed cached sector is not flushed when create and close without write.
00050 /
00051 / Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
00052 /                   Improved performance of f_lseek() on moving to the same or following cluster.
00053 /
00054 / Apr 01,'09 R0.07  Merged Tiny-FatFs as a configuration option. (_FS_TINY)
00055 /                   Added long file name feature.
00056 /                   Added multiple code page feature.
00057 /                   Added re-entrancy for multi-task operation.
00058 /                   Added auto cluster size selection to f_mkfs().
00059 /                   Added rewind option to f_readdir().
00060 /                   Changed result code of critical errors.
00061 /                   Renamed string functions to avoid name collision.
00062 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
00063 /                   Added multiple sector size feature.
00064 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
00065 /                   Fixed wrong cache control in f_lseek().
00066 /                   Added relative path feature.
00067 /                   Added f_chdir() and f_chdrive().
00068 /                   Added proper case conversion to extended character.
00069 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
00070 /                   Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
00071 /                   Fixed name matching error on the 13 character boundary.
00072 /                   Added a configuration option, _LFN_UNICODE.
00073 /                   Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
00074 /
00075 / May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN = 3)
00076 /                   Added file lock feature. (_FS_SHARE)
00077 /                   Added fast seek feature. (_USE_FASTSEEK)
00078 /                   Changed some types on the API, XCHAR->TCHAR.
00079 /                   Changed .fname in the FILINFO structure on Unicode cfg.
00080 /                   String functions support UTF-8 encoding files on Unicode cfg.
00081 / Aug 16,'10 R0.08a Added f_getcwd().
00082 /                   Added sector erase feature. (_USE_ERASE)
00083 /                   Moved file lock semaphore table from fs object to the bss.
00084 /                   Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.
00085 /                   Fixed f_mkfs() creates wrong FAT32 volume.
00086 / Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write().
00087 /                   f_lseek() reports required table size on creating CLMP.
00088 /                   Extended format syntax of f_printf().
00089 /                   Ignores duplicated directory separators in given path name.
00090 /
00091 / Sep 06,'11 R0.09  f_mkfs() supports multiple partition to complete the multiple partition feature.
00092 /                   Added f_fdisk().
00093 / Aug 27,'12 R0.09a Changed f_open() and f_opendir() reject null object pointer to avoid crash.
00094 /                   Changed option name _FS_SHARE to _FS_LOCK.
00095 /                   Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
00096 / Jan 24,'13 R0.09b Added f_setlabel() and f_getlabel().
00097 /
00098 / Oct 02,'13 R0.10  Added selection of character encoding on the file. (_STRF_ENCODE)
00099 /                   Added f_closedir().
00100 /                   Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
00101 /                   Added forced mount feature with changes of f_mount().
00102 /                   Improved behavior of volume auto detection.
00103 /                   Improved write throughput of f_puts() and f_printf().
00104 /                   Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
00105 /                   Fixed f_write() can be truncated when the file size is close to 4GB.
00106 /                   Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.
00107 / Jan 15,'14 R0.10a Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
00108 /                   Added a configuration option of minimum sector size. (_MIN_SS)
00109 /                   2nd argument of f_rename() can have a drive number and it will be ignored.
00110 /                   Fixed f_mount() with forced mount fails when drive number is >= 1.
00111 /                   Fixed f_close() invalidates the file object without volume lock.
00112 /                   Fixed f_closedir() returns but the volume lock is left acquired.
00113 /                   Fixed creation of an entry with LFN fails on too many SFN collisions.
00114 / May 19,'14 R0.10b Fixed a hard error in the disk I/O layer can collapse the directory entry.
00115 /                   Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN.
00116 /---------------------------------------------------------------------------*/
00117 
00118 #include "ff.h"         /* Declarations of FatFs API */
00119 #include "diskio.h"     /* Declarations of disk I/O functions */
00120 
00121 
00122 
00123 
00124 /*--------------------------------------------------------------------------
00125 
00126    Module Private Definitions
00127 
00128 ---------------------------------------------------------------------------*/
00129 
00130 #if _FATFS != 8051   /* Revision ID */
00131 #error Wrong include file (ff.h).
00132 #endif
00133 
00134 
00135 /* Reentrancy related */
00136 #if _FS_REENTRANT
00137 #if _USE_LFN == 1
00138 #error Static LFN work area cannot be used at thread-safe configuration.
00139 #endif
00140 #define ENTER_FF(fs)        { if (!lock_fs(fs)) return FR_TIMEOUT; }
00141 #define LEAVE_FF(fs, res)   { unlock_fs(fs, res); return res; }
00142 #else
00143 #define ENTER_FF(fs)
00144 #define LEAVE_FF(fs, res)    return res
00145 #endif
00146 
00147 #define ABORT(fs, res)      { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
00148 
00149 
00150 /* Definitions of sector size */
00151 #if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096)
00152 #error Wrong sector size configuration.
00153 #endif
00154 #if _MAX_SS == _MIN_SS
00155 #define SS(fs)  ((UINT)_MAX_SS) /* Fixed sector size */
00156 #else
00157 #define SS(fs)  ((fs)->ssize)   /* Variable sector size */
00158 #endif
00159 
00160 
00161 /* File access control feature */
00162 #if _FS_LOCK
00163 #if _FS_READONLY
00164 #error _FS_LOCK must be 0 at read-only cfg.
00165 #endif
00166 typedef struct {
00167     FATFS *fs;       /* Object ID 1, volume (NULL:blank entry) */
00168     DWORD clu;       /* Object ID 2, directory (0:root) */
00169     WORD idx;        /* Object ID 3, directory index */
00170     WORD ctr;        /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
00171 } FILESEM;
00172 #endif
00173 
00174 
00175 
00176 /* DBCS code ranges and SBCS extend character conversion table */
00177 
00178 #if _CODE_PAGE == 932    /* Japanese Shift-JIS */
00179 #define _DF1S    0x81    /* DBC 1st byte range 1 start */
00180 #define _DF1E    0x9F    /* DBC 1st byte range 1 end */
00181 #define _DF2S    0xE0    /* DBC 1st byte range 2 start */
00182 #define _DF2E    0xFC    /* DBC 1st byte range 2 end */
00183 #define _DS1S    0x40    /* DBC 2nd byte range 1 start */
00184 #define _DS1E    0x7E    /* DBC 2nd byte range 1 end */
00185 #define _DS2S    0x80    /* DBC 2nd byte range 2 start */
00186 #define _DS2E    0xFC    /* DBC 2nd byte range 2 end */
00187 
00188 #elif _CODE_PAGE == 936    /* Simplified Chinese GBK */
00189 #define _DF1S    0x81
00190 #define _DF1E    0xFE
00191 #define _DS1S    0x40
00192 #define _DS1E    0x7E
00193 #define _DS2S    0x80
00194 #define _DS2E    0xFE
00195 
00196 #elif _CODE_PAGE == 949    /* Korean */
00197 #define _DF1S    0x81
00198 #define _DF1E    0xFE
00199 #define _DS1S    0x41
00200 #define _DS1E    0x5A
00201 #define _DS2S    0x61
00202 #define _DS2E    0x7A
00203 #define _DS3S    0x81
00204 #define _DS3E    0xFE
00205 
00206 #elif _CODE_PAGE == 950    /* Traditional Chinese Big5 */
00207 #define _DF1S    0x81
00208 #define _DF1E    0xFE
00209 #define _DS1S    0x40
00210 #define _DS1E    0x7E
00211 #define _DS2S    0xA1
00212 #define _DS2E    0xFE
00213 
00214 #elif _CODE_PAGE == 437    /* U.S. (OEM) */
00215 #define _DF1S    0
00216 #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, \
00217                 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, \
00218                 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, \
00219                 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}
00220 
00221 #elif _CODE_PAGE == 720    /* Arabic (OEM) */
00222 #define _DF1S    0
00223 #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, \
00224                 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, \
00225                 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, \
00226                 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}
00227 
00228 #elif _CODE_PAGE == 737    /* Greek (OEM) */
00229 #define _DF1S    0
00230 #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, \
00231                 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, \
00232                 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, \
00233                 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}
00234 
00235 #elif _CODE_PAGE == 775    /* Baltic (OEM) */
00236 #define _DF1S    0
00237 #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, \
00238                 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, \
00239                 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, \
00240                 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}
00241 
00242 #elif _CODE_PAGE == 850    /* Multilingual Latin 1 (OEM) */
00243 #define _DF1S    0
00244 #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, \
00245                 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, \
00246                 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, \
00247                 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}
00248 
00249 #elif _CODE_PAGE == 852    /* Latin 2 (OEM) */
00250 #define _DF1S    0
00251 #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, \
00252                 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, \
00253                 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, \
00254                 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}
00255 
00256 #elif _CODE_PAGE == 855    /* Cyrillic (OEM) */
00257 #define _DF1S    0
00258 #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, \
00259                 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, \
00260                 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, \
00261                 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}
00262 
00263 #elif _CODE_PAGE == 857    /* Turkish (OEM) */
00264 #define _DF1S    0
00265 #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, \
00266                 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, \
00267                 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, \
00268                 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}
00269 
00270 #elif _CODE_PAGE == 858    /* Multilingual Latin 1 + Euro (OEM) */
00271 #define _DF1S    0
00272 #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, \
00273                 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, \
00274                 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, \
00275                 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}
00276 
00277 #elif _CODE_PAGE == 862    /* Hebrew (OEM) */
00278 #define _DF1S    0
00279 #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, \
00280                 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, \
00281                 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, \
00282                 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}
00283 
00284 #elif _CODE_PAGE == 866    /* Russian (OEM) */
00285 #define _DF1S    0
00286 #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, \
00287                 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, \
00288                 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, \
00289                 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}
00290 
00291 #elif _CODE_PAGE == 874    /* Thai (OEM, Windows) */
00292 #define _DF1S    0
00293 #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, \
00294                 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, \
00295                 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, \
00296                 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}
00297 
00298 #elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
00299 #define _DF1S    0
00300 #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, \
00301                 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, \
00302                 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, \
00303                 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}
00304 
00305 #elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
00306 #define _DF1S    0
00307 #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, \
00308                 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, \
00309                 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, \
00310                 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}
00311 
00312 #elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
00313 #define _DF1S    0
00314 #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, \
00315                 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, \
00316                 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, \
00317                 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}
00318 
00319 #elif _CODE_PAGE == 1253 /* Greek (Windows) */
00320 #define _DF1S    0
00321 #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, \
00322                 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, \
00323                 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, \
00324                 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}
00325 
00326 #elif _CODE_PAGE == 1254 /* Turkish (Windows) */
00327 #define _DF1S    0
00328 #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, \
00329                 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, \
00330                 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, \
00331                 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}
00332 
00333 #elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
00334 #define _DF1S    0
00335 #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, \
00336                 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, \
00337                 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, \
00338                 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}
00339 
00340 #elif _CODE_PAGE == 1256 /* Arabic (Windows) */
00341 #define _DF1S    0
00342 #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, \
00343                 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, \
00344                 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, \
00345                 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}
00346 
00347 #elif _CODE_PAGE == 1257 /* Baltic (Windows) */
00348 #define _DF1S    0
00349 #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, \
00350                 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, \
00351                 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, \
00352                 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}
00353 
00354 #elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
00355 #define _DF1S    0
00356 #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, \
00357                 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, \
00358                 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, \
00359                 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}
00360 
00361 #elif _CODE_PAGE == 1    /* ASCII (for only non-LFN cfg) */
00362 #if _USE_LFN
00363 #error Cannot use LFN feature without valid code page.
00364 #endif
00365 #define _DF1S    0
00366 
00367 #else
00368 #error Unknown code page
00369 
00370 #endif
00371 
00372 
00373 /* Character code support macros */
00374 #define IsUpper(c)    (((c)>='A')&&((c)<='Z'))
00375 #define IsLower(c)    (((c)>='a')&&((c)<='z'))
00376 #define IsDigit(c)    (((c)>='0')&&((c)<='9'))
00377 
00378 #if _DF1S        /* Code page is DBCS */
00379 
00380 #ifdef _DF2S    /* Two 1st byte areas */
00381 #define IsDBCS1(c)    (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
00382 #else            /* One 1st byte area */
00383 #define IsDBCS1(c)    ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
00384 #endif
00385 
00386 #ifdef _DS3S    /* Three 2nd byte areas */
00387 #define IsDBCS2(c)    (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
00388 #else            /* Two 2nd byte areas */
00389 #define IsDBCS2(c)    (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
00390 #endif
00391 
00392 #else            /* Code page is SBCS */
00393 
00394 #define IsDBCS1(c)    0
00395 #define IsDBCS2(c)    0
00396 
00397 #endif /* _DF1S */
00398 
00399 
00400 /* Name status flags */
00401 #define NS            11      /* Index of name status byte in fn[] */
00402 #define NS_LOSS       0x01    /* Out of 8.3 format */
00403 #define NS_LFN        0x02    /* Force to create LFN entry */
00404 #define NS_LAST       0x04    /* Last segment */
00405 #define NS_BODY       0x08    /* Lower case flag (body) */
00406 #define NS_EXT        0x10    /* Lower case flag (ext) */
00407 #define NS_DOT        0x20    /* Dot entry */
00408 
00409 
00410 /* FAT sub-type boundaries */
00411 #define MIN_FAT16    4086U    /* Minimum number of clusters for FAT16 */
00412 #define    MIN_FAT32    65526U    /* Minimum number of clusters for FAT32 */
00413 
00414 
00415 /* FatFs refers the members in the FAT structures as byte array instead of
00416 / structure member because the structure is not binary compatible between
00417 / different platforms */
00418 
00419 #define BS_jmpBoot            0         /* Jump instruction (3) */
00420 #define BS_OEMName            3         /* OEM name (8) */
00421 #define BPB_BytsPerSec        11        /* Sector size [byte] (2) */
00422 #define BPB_SecPerClus        13        /* Cluster size [sector] (1) */
00423 #define BPB_RsvdSecCnt        14        /* Size of reserved area [sector] (2) */
00424 #define BPB_NumFATs           16        /* Number of FAT copies (1) */
00425 #define BPB_RootEntCnt        17        /* Number of root directory entries for FAT12/16 (2) */
00426 #define BPB_TotSec16          19        /* Volume size [sector] (2) */
00427 #define BPB_Media             21        /* Media descriptor (1) */
00428 #define BPB_FATSz16           22        /* FAT size [sector] (2) */
00429 #define BPB_SecPerTrk         24        /* Track size [sector] (2) */
00430 #define BPB_NumHeads          26        /* Number of heads (2) */
00431 #define BPB_HiddSec           28        /* Number of special hidden sectors (4) */
00432 #define BPB_TotSec32          32        /* Volume size [sector] (4) */
00433 #define BS_DrvNum             36        /* Physical drive number (2) */
00434 #define BS_BootSig            38        /* Extended boot signature (1) */
00435 #define BS_VolID              39        /* Volume serial number (4) */
00436 #define BS_VolLab             43        /* Volume label (8) */
00437 #define BS_FilSysType         54        /* File system type (1) */
00438 #define BPB_FATSz32           36        /* FAT size [sector] (4) */
00439 #define BPB_ExtFlags          40        /* Extended flags (2) */
00440 #define BPB_FSVer             42        /* File system version (2) */
00441 #define BPB_RootClus          44        /* Root directory first cluster (4) */
00442 #define BPB_FSInfo            48        /* Offset of FSINFO sector (2) */
00443 #define BPB_BkBootSec         50        /* Offset of backup boot sector (2) */
00444 #define BS_DrvNum32           64        /* Physical drive number (2) */
00445 #define BS_BootSig32          66        /* Extended boot signature (1) */
00446 #define BS_VolID32            67        /* Volume serial number (4) */
00447 #define BS_VolLab32           71        /* Volume label (8) */
00448 #define BS_FilSysType32       82        /* File system type (1) */
00449 #define FSI_LeadSig            0        /* FSI: Leading signature (4) */
00450 #define FSI_StrucSig          484       /* FSI: Structure signature (4) */
00451 #define FSI_Free_Count        488       /* FSI: Number of free clusters (4) */
00452 #define FSI_Nxt_Free          492       /* FSI: Last allocated cluster (4) */
00453 #define MBR_Table             446       /* MBR: Partition table offset (2) */
00454 #define SZ_PTE                16        /* MBR: Size of a partition table entry */
00455 #define BS_55AA               510       /* Signature word (2) */
00456 
00457 #define DIR_Name              0         /* Short file name (11) */
00458 #define DIR_Attr              11        /* Attribute (1) */
00459 #define DIR_NTres             12        /* NT flag (1) */
00460 #define DIR_CrtTimeTenth      13        /* Created time sub-second (1) */
00461 #define DIR_CrtTime           14        /* Created time (2) */
00462 #define DIR_CrtDate           16        /* Created date (2) */
00463 #define DIR_LstAccDate        18        /* Last accessed date (2) */
00464 #define DIR_FstClusHI         20        /* Higher 16-bit of first cluster (2) */
00465 #define DIR_WrtTime           22        /* Modified time (2) */
00466 #define DIR_WrtDate           24        /* Modified date (2) */
00467 #define DIR_FstClusLO         26        /* Lower 16-bit of first cluster (2) */
00468 #define DIR_FileSize          28        /* File size (4) */
00469 #define LDIR_Ord              0         /* LFN entry order and LLE flag (1) */
00470 #define LDIR_Attr             11        /* LFN attribute (1) */
00471 #define LDIR_Type             12        /* LFN type (1) */
00472 #define LDIR_Chksum           13        /* Sum of corresponding SFN entry */
00473 #define LDIR_FstClusLO        26        /* Filled by zero (0) */
00474 #define SZ_DIR                32        /* Size of a directory entry */
00475 #define LLE                   0x40      /* Last long entry flag in LDIR_Ord */
00476 #define DDE                   0xE5      /* Deleted directory entry mark in DIR_Name[0] */
00477 #define NDDE                  0x05      /* Replacement of the character collides with DDE */
00478 
00479 
00480 
00481 
00482 /*------------------------------------------------------------*/
00483 /* Module private work area                                   */
00484 /*------------------------------------------------------------*/
00485 /* Note that uninitialized variables with static duration are
00486 /  guaranteed zero/null as initial value. If not, either the
00487 /  linker or start-up routine is out of ANSI-C standard.
00488 */
00489 
00490 #if _VOLUMES >= 1 || _VOLUMES <= 10
00491 static
00492 FATFS *FatFs[_VOLUMES];        /* Pointer to the file system objects (logical drives) */
00493 #else
00494 #error Number of volumes must be 1 to 10.
00495 #endif
00496 
00497 static
00498 WORD Fsid;                    /* File system mount ID */
00499 
00500 #if _FS_RPATH && _VOLUMES >= 2
00501 static
00502 BYTE CurrVol;                /* Current drive */
00503 #endif
00504 
00505 #if _FS_LOCK
00506 static
00507 FILESEM    Files[_FS_LOCK];    /* Open object lock semaphores */
00508 #endif
00509 
00510 #if _USE_LFN == 0            /* No LFN feature */
00511 #define    DEF_NAMEBUF            BYTE sfn[12]
00512 #define INIT_BUF(dobj)        (dobj).fn = sfn
00513 #define    FREE_BUF()
00514 
00515 #elif _USE_LFN == 1            /* LFN feature with static working buffer */
00516 static
00517 WCHAR LfnBuf[_MAX_LFN+1];
00518 #define    DEF_NAMEBUF            BYTE sfn[12]
00519 #define INIT_BUF(dobj)        { (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
00520 #define    FREE_BUF()
00521 
00522 #elif _USE_LFN == 2         /* LFN feature with dynamic working buffer on the stack */
00523 #define    DEF_NAMEBUF            BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]
00524 #define INIT_BUF(dobj)        { (dobj).fn = sfn; (dobj).lfn = lbuf; }
00525 #define    FREE_BUF()
00526 
00527 #elif _USE_LFN == 3         /* LFN feature with dynamic working buffer on the heap */
00528 #define    DEF_NAMEBUF            BYTE sfn[12]; WCHAR *lfn
00529 #define INIT_BUF(dobj)        { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
00530                               if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
00531                               (dobj).lfn = lfn;    (dobj).fn = sfn; }
00532 #define    FREE_BUF()            ff_memfree(lfn)
00533 
00534 #else
00535 #error Wrong LFN configuration.
00536 #endif
00537 
00538 
00539 #ifdef _EXCVT
00540 static
00541 const BYTE ExCvt[] = _EXCVT;    /* Upper conversion table for extended characters */
00542 #endif
00543 
00544 
00545 
00546 
00547 
00548 
00549 /*--------------------------------------------------------------------------
00550 
00551    Module Private Functions
00552 
00553 ---------------------------------------------------------------------------*/
00554 
00555 
00556 /*-----------------------------------------------------------------------*/
00557 /* String functions                                                      */
00558 /*-----------------------------------------------------------------------*/
00559 
00560 /* Copy memory to memory */
00561 static
00562 void mem_cpy (void* dst, const void* src, UINT cnt) {
00563     BYTE *d = (BYTE*)dst;
00564     const BYTE *s = (const BYTE*)src;
00565 
00566 #if _WORD_ACCESS == 1
00567     while (cnt >= sizeof (int)) {
00568         *(int*)d = *(int*)s;
00569         d += sizeof (int); s += sizeof (int);
00570         cnt -= sizeof (int);
00571     }
00572 #endif
00573     while (cnt--)
00574         *d++ = *s++;
00575 }
00576 
00577 /* Fill memory */
00578 static
00579 void mem_set (void* dst, int val, UINT cnt) {
00580     BYTE *d = (BYTE*)dst;
00581 
00582     while (cnt--)
00583         *d++ = (BYTE)val;
00584 }
00585 
00586 /* Compare memory to memory */
00587 static
00588 int mem_cmp (const void* dst, const void* src, UINT cnt) {
00589     const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
00590     int r = 0;
00591 
00592     while (cnt-- && (r = *d++ - *s++) == 0) ;
00593     return r;
00594 }
00595 
00596 /* Check if chr is contained in the string */
00597 static
00598 int chk_chr (const char* str, int chr) {
00599     while (*str && *str != chr) str++;
00600     return *str;
00601 }
00602 
00603 
00604 
00605 
00606 /*-----------------------------------------------------------------------*/
00607 /* Request/Release grant to access the volume                            */
00608 /*-----------------------------------------------------------------------*/
00609 #if _FS_REENTRANT
00610 static
00611 int lock_fs (
00612     FATFS* fs        /* File system object */
00613 )
00614 {
00615     return ff_req_grant(fs->sobj);
00616 }
00617 
00618 
00619 static
00620 void unlock_fs (
00621     FATFS* fs,        /* File system object */
00622     FRESULT res        /* Result code to be returned */
00623 )
00624 {
00625     if (fs &&
00626         res != FR_NOT_ENABLED &&
00627         res != FR_INVALID_DRIVE &&
00628         res != FR_INVALID_OBJECT &&
00629         res != FR_TIMEOUT) {
00630         ff_rel_grant(fs->sobj);
00631     }
00632 }
00633 #endif
00634 
00635 
00636 
00637 
00638 /*-----------------------------------------------------------------------*/
00639 /* File lock control functions                                           */
00640 /*-----------------------------------------------------------------------*/
00641 #if _FS_LOCK
00642 
00643 static
00644 FRESULT chk_lock (    /* Check if the file can be accessed */
00645     DIR* dp,        /* Directory object pointing the file to be checked */
00646     int acc            /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */
00647 )
00648 {
00649     UINT i, be;
00650 
00651     /* Search file semaphore table */
00652     for (i = be = 0; i < _FS_LOCK; i++) {
00653         if (Files[i].fs) {    /* Existing entry */
00654             if (Files[i].fs == dp->fs &&         /* Check if the object matched with an open object */
00655                 Files[i].clu == dp->sclust &&
00656                 Files[i].idx == dp->index) break;
00657         } else {            /* Blank entry */
00658             be = 1;
00659         }
00660     }
00661     if (i == _FS_LOCK)    /* The object is not opened */
00662         return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;    /* Is there a blank entry for new object? */
00663 
00664     /* The object has been opened. Reject any open against writing file and all write mode open */
00665     return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
00666 }
00667 
00668 
00669 static
00670 int enq_lock (void)    /* Check if an entry is available for a new object */
00671 {
00672     UINT i;
00673 
00674     for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
00675     return (i == _FS_LOCK) ? 0 : 1;
00676 }
00677 
00678 
00679 static
00680 UINT inc_lock (    /* Increment object open counter and returns its index (0:Internal error) */
00681     DIR* dp,    /* Directory object pointing the file to register or increment */
00682     int acc        /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
00683 )
00684 {
00685     UINT i;
00686 
00687 
00688     for (i = 0; i < _FS_LOCK; i++) {    /* Find the object */
00689         if (Files[i].fs == dp->fs &&
00690             Files[i].clu == dp->sclust &&
00691             Files[i].idx == dp->index) break;
00692     }
00693 
00694     if (i == _FS_LOCK) {                /* Not opened. Register it as new. */
00695         for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
00696         if (i == _FS_LOCK) return 0;    /* No free entry to register (int err) */
00697         Files[i].fs = dp->fs;
00698         Files[i].clu = dp->sclust;
00699         Files[i].idx = dp->index;
00700         Files[i].ctr = 0;
00701     }
00702 
00703     if (acc && Files[i].ctr) return 0;    /* Access violation (int err) */
00704 
00705     Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;    /* Set semaphore value */
00706 
00707     return i + 1;
00708 }
00709 
00710 
00711 static
00712 FRESULT dec_lock (    /* Decrement object open counter */
00713     UINT i            /* Semaphore index (1..) */
00714 )
00715 {
00716     WORD n;
00717     FRESULT res;
00718 
00719 
00720     if (--i < _FS_LOCK) {           /* Shift index number origin from 0 */
00721         n = Files[i].ctr;
00722         if (n == 0x100) n = 0;      /* If write mode open, delete the entry */
00723         if (n) n--;                 /* Decrement read mode open count */
00724         Files[i].ctr = n;
00725         if (!n) Files[i].fs = 0;    /* Delete the entry if open count gets zero */
00726         res = FR_OK;
00727     } else {
00728         res = FR_INT_ERR;            /* Invalid index number */
00729     }
00730     return res;
00731 }
00732 
00733 
00734 static
00735 void clear_lock (    /* Clear lock entries of the volume */
00736     FATFS *fs
00737 )
00738 {
00739     UINT i;
00740 
00741     for (i = 0; i < _FS_LOCK; i++) {
00742         if (Files[i].fs == fs) Files[i].fs = 0;
00743     }
00744 }
00745 #endif
00746 
00747 
00748 
00749 
00750 /*-----------------------------------------------------------------------*/
00751 /* Move/Flush disk access window in the file system object               */
00752 /*-----------------------------------------------------------------------*/
00753 #if !_FS_READONLY
00754 static
00755 FRESULT sync_window (
00756     FATFS* fs        /* File system object */
00757 )
00758 {
00759     DWORD wsect;
00760     UINT nf;
00761 
00762 
00763     if (fs->wflag) {    /* Write back the sector if it is dirty */
00764         wsect = fs->winsect;    /* Current sector number */
00765         if (disk_write(fs->drv, fs->win, wsect, 1))
00766             return FR_DISK_ERR;
00767         fs->wflag = 0;
00768         if (wsect - fs->fatbase < fs->fsize) {        /* Is it in the FAT area? */
00769             for (nf = fs->n_fats; nf >= 2; nf--) {    /* Reflect the change to all FAT copies */
00770                 wsect += fs->fsize;
00771                 disk_write(fs->drv, fs->win, wsect, 1);
00772             }
00773         }
00774     }
00775     return FR_OK;
00776 }
00777 #endif
00778 
00779 
00780 static
00781 FRESULT move_window (
00782     FATFS* fs,        /* File system object */
00783     DWORD sector    /* Sector number to make appearance in the fs->win[] */
00784 )
00785 {
00786     if (sector != fs->winsect) {    /* Changed current window */
00787 #if !_FS_READONLY
00788         if (sync_window(fs) != FR_OK)
00789             return FR_DISK_ERR;
00790 #endif
00791         if (disk_read(fs->drv, fs->win, sector, 1))
00792             return FR_DISK_ERR;
00793         fs->winsect = sector;
00794     }
00795 
00796     return FR_OK;
00797 }
00798 
00799 
00800 
00801 
00802 /*-----------------------------------------------------------------------*/
00803 /* Synchronize file system and storage device                             */
00804 /*-----------------------------------------------------------------------*/
00805 #if !_FS_READONLY
00806 static
00807 FRESULT sync_fs (    /* FR_OK: successful, FR_DISK_ERR: failed */
00808     FATFS* fs        /* File system object */
00809 )
00810 {
00811     FRESULT res;
00812 
00813 
00814     res = sync_window(fs);
00815     if (res == FR_OK) {
00816         /* Update FSINFO sector if needed */
00817         if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {
00818             /* Create FSINFO structure */
00819             mem_set(fs->win, 0, SS(fs));
00820             ST_WORD(fs->win+BS_55AA, 0xAA55);
00821             ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
00822             ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
00823             ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
00824             ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
00825             /* Write it into the FSINFO sector */
00826             fs->winsect = fs->volbase + 1;
00827             disk_write(fs->drv, fs->win, fs->winsect, 1);
00828             fs->fsi_flag = 0;
00829         }
00830         /* Make sure that no pending write process in the physical drive */
00831         if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK)
00832             res = FR_DISK_ERR;
00833     }
00834 
00835     return res;
00836 }
00837 #endif
00838 
00839 
00840 
00841 
00842 /*-----------------------------------------------------------------------*/
00843 /* Get sector# from cluster#                                             */
00844 /*-----------------------------------------------------------------------*/
00845 
00846 
00847 static DWORD clust2sect (    /* !=0: Sector number, 0: Failed - invalid cluster# */
00848     FATFS* fs,        /* File system object */
00849     DWORD clst        /* Cluster# to be converted */
00850 )
00851 {
00852     clst -= 2;
00853     if (clst >= (fs->n_fatent - 2)) return 0;        /* Invalid cluster# */
00854     return clst * fs->csize + fs->database;
00855 }
00856 
00857 
00858 
00859 
00860 /*-----------------------------------------------------------------------*/
00861 /* FAT access - Read value of a FAT entry                                */
00862 /*-----------------------------------------------------------------------*/
00863 
00864 
00865 static DWORD get_fat (    /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
00866     FATFS* fs,    /* File system object */
00867     DWORD clst    /* Cluster# to get the link information */
00868 )
00869 {
00870     UINT wc, bc;
00871     BYTE *p;
00872 
00873 
00874     if (clst < 2 || clst >= fs->n_fatent)    /* Check range */
00875         return 1;
00876 
00877     switch (fs->fs_type) {
00878     case FS_FAT12 :
00879         bc = (UINT)clst; bc += bc / 2;
00880         if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
00881         wc = fs->win[bc % SS(fs)]; bc++;
00882         if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
00883         wc |= fs->win[bc % SS(fs)] << 8;
00884         return clst & 1 ? wc >> 4 : (wc & 0xFFF);
00885 
00886     case FS_FAT16 :
00887         if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break;
00888         p = &fs->win[clst * 2 % SS(fs)];
00889         return LD_WORD(p);
00890 
00891     case FS_FAT32 :
00892         if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break;
00893         p = &fs->win[clst * 4 % SS(fs)];
00894         return LD_DWORD(p) & 0x0FFFFFFF;
00895 
00896     default:
00897         return 1;
00898     }
00899 
00900     return 0xFFFFFFFF;    /* An error occurred at the disk I/O layer */
00901 }
00902 
00903 
00904 
00905 
00906 /*-----------------------------------------------------------------------*/
00907 /* FAT access - Change value of a FAT entry                              */
00908 /*-----------------------------------------------------------------------*/
00909 #if !_FS_READONLY
00910 
00911 static FRESULT put_fat (
00912     FATFS* fs,    /* File system object */
00913     DWORD clst,    /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
00914     DWORD val    /* New value to mark the cluster */
00915 )
00916 {
00917     UINT bc;
00918     BYTE *p;
00919     FRESULT res;
00920 
00921 
00922     if (clst < 2 || clst >= fs->n_fatent) {    /* Check range */
00923         res = FR_INT_ERR;
00924 
00925     } else {
00926         switch (fs->fs_type) {
00927         case FS_FAT12 :
00928             bc = (UINT)clst; bc += bc / 2;
00929             res = move_window(fs, fs->fatbase + (bc / SS(fs)));
00930             if (res != FR_OK) break;
00931             p = &fs->win[bc % SS(fs)];
00932             *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
00933             bc++;
00934             fs->wflag = 1;
00935             res = move_window(fs, fs->fatbase + (bc / SS(fs)));
00936             if (res != FR_OK) break;
00937             p = &fs->win[bc % SS(fs)];
00938             *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
00939             break;
00940 
00941         case FS_FAT16 :
00942             res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
00943             if (res != FR_OK) break;
00944             p = &fs->win[clst * 2 % SS(fs)];
00945             ST_WORD(p, (WORD)val);
00946             break;
00947 
00948         case FS_FAT32 :
00949             res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
00950             if (res != FR_OK) break;
00951             p = &fs->win[clst * 4 % SS(fs)];
00952             val |= LD_DWORD(p) & 0xF0000000;
00953             ST_DWORD(p, val);
00954             break;
00955 
00956         default :
00957             res = FR_INT_ERR;
00958         }
00959         fs->wflag = 1;
00960     }
00961 
00962     return res;
00963 }
00964 #endif /* !_FS_READONLY */
00965 
00966 
00967 
00968 
00969 /*-----------------------------------------------------------------------*/
00970 /* FAT handling - Remove a cluster chain                                 */
00971 /*-----------------------------------------------------------------------*/
00972 #if !_FS_READONLY
00973 static
00974 FRESULT remove_chain (
00975     FATFS* fs,            /* File system object */
00976     DWORD clst            /* Cluster# to remove a chain from */
00977 )
00978 {
00979     FRESULT res;
00980     DWORD nxt;
00981 #if _USE_ERASE
00982     DWORD scl = clst, ecl = clst, rt[2];
00983 #endif
00984 
00985     if (clst < 2 || clst >= fs->n_fatent) {    /* Check range */
00986         res = FR_INT_ERR;
00987 
00988     } else {
00989         res = FR_OK;
00990         while (clst < fs->n_fatent) {            /* Not a last link? */
00991             nxt = get_fat(fs, clst);            /* Get cluster status */
00992             if (nxt == 0) break;                /* Empty cluster? */
00993             if (nxt == 1) { res = FR_INT_ERR; break; }    /* Internal error? */
00994             if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
00995             res = put_fat(fs, clst, 0);            /* Mark the cluster "empty" */
00996             if (res != FR_OK) break;
00997             if (fs->free_clust != 0xFFFFFFFF) {    /* Update FSINFO */
00998                 fs->free_clust++;
00999                 fs->fsi_flag |= 1;
01000             }
01001 #if _USE_ERASE
01002             if (ecl + 1 == nxt) {    /* Is next cluster contiguous? */
01003                 ecl = nxt;
01004             } else {                /* End of contiguous clusters */ 
01005                 rt[0] = clust2sect(fs, scl);                    /* Start sector */
01006                 rt[1] = clust2sect(fs, ecl) + fs->csize - 1;    /* End sector */
01007                 disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, rt);        /* Erase the block */
01008                 scl = ecl = nxt;
01009             }
01010 #endif
01011             clst = nxt;    /* Next cluster */
01012         }
01013     }
01014 
01015     return res;
01016 }
01017 #endif
01018 
01019 
01020 
01021 
01022 /*-----------------------------------------------------------------------*/
01023 /* FAT handling - Stretch or Create a cluster chain                      */
01024 /*-----------------------------------------------------------------------*/
01025 #if !_FS_READONLY
01026 static
01027 DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
01028     FATFS* fs,            /* File system object */
01029     DWORD clst            /* Cluster# to stretch. 0 means create a new chain. */
01030 )
01031 {
01032     DWORD cs, ncl, scl;
01033     FRESULT res;
01034 
01035 
01036     if (clst == 0) {        /* Create a new chain */
01037         scl = fs->last_clust;            /* Get suggested start point */
01038         if (!scl || scl >= fs->n_fatent) scl = 1;
01039     }
01040     else {                    /* Stretch the current chain */
01041         cs = get_fat(fs, clst);            /* Check the cluster status */
01042         if (cs < 2) return 1;            /* Invalid value */
01043         if (cs == 0xFFFFFFFF) return cs;    /* A disk error occurred */
01044         if (cs < fs->n_fatent) return cs;    /* It is already followed by next cluster */
01045         scl = clst;
01046     }
01047 
01048     ncl = scl;                /* Start cluster */
01049     for (;;) {
01050         ncl++;                            /* Next cluster */
01051         if (ncl >= fs->n_fatent) {        /* Check wrap around */
01052             ncl = 2;
01053             if (ncl > scl) return 0;    /* No free cluster */
01054         }
01055         cs = get_fat(fs, ncl);            /* Get the cluster status */
01056         if (cs == 0) break;                /* Found a free cluster */
01057         if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
01058             return cs;
01059         if (ncl == scl) return 0;        /* No free cluster */
01060     }
01061 
01062     res = put_fat(fs, ncl, 0x0FFFFFFF);    /* Mark the new cluster "last link" */
01063     if (res == FR_OK && clst != 0) {
01064         res = put_fat(fs, clst, ncl);    /* Link it to the previous one if needed */
01065     }
01066     if (res == FR_OK) {
01067         fs->last_clust = ncl;            /* Update FSINFO */
01068         if (fs->free_clust != 0xFFFFFFFF) {
01069             fs->free_clust--;
01070             fs->fsi_flag |= 1;
01071         }
01072     } else {
01073         ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;
01074     }
01075 
01076     return ncl;        /* Return new cluster number or error code */
01077 }
01078 #endif /* !_FS_READONLY */
01079 
01080 
01081 
01082 
01083 /*-----------------------------------------------------------------------*/
01084 /* FAT handling - Convert offset into cluster with link map table        */
01085 /*-----------------------------------------------------------------------*/
01086 
01087 #if _USE_FASTSEEK
01088 static
01089 DWORD clmt_clust (    /* <2:Error, >=2:Cluster number */
01090     FIL* fp,        /* Pointer to the file object */
01091     DWORD ofs        /* File offset to be converted to cluster# */
01092 )
01093 {
01094     DWORD cl, ncl, *tbl;
01095 
01096 
01097     tbl = fp->cltbl + 1;    /* Top of CLMT */
01098     cl = ofs / SS(fp->fs) / fp->fs->csize;    /* Cluster order from top of the file */
01099     for (;;) {
01100         ncl = *tbl++;            /* Number of clusters in the fragment */
01101         if (!ncl) return 0;        /* End of table? (error) */
01102         if (cl < ncl) break;    /* In this fragment? */
01103         cl -= ncl; tbl++;        /* Next fragment */
01104     }
01105     return cl + *tbl;    /* Return the cluster number */
01106 }
01107 #endif    /* _USE_FASTSEEK */
01108 
01109 
01110 
01111 
01112 /*-----------------------------------------------------------------------*/
01113 /* Directory handling - Set directory index                              */
01114 /*-----------------------------------------------------------------------*/
01115 
01116 static
01117 FRESULT dir_sdi (
01118     DIR* dp,        /* Pointer to directory object */
01119     UINT idx        /* Index of directory table */
01120 )
01121 {
01122     DWORD clst, sect;
01123     UINT ic;
01124 
01125 
01126     dp->index = (WORD)idx;    /* Current index */
01127     clst = dp->sclust;        /* Table start cluster (0:root) */
01128     if (clst == 1 || clst >= dp->fs->n_fatent)    /* Check start cluster range */
01129         return FR_INT_ERR;
01130     if (!clst && dp->fs->fs_type == FS_FAT32)    /* Replace cluster# 0 with root cluster# if in FAT32 */
01131         clst = dp->fs->dirbase;
01132 
01133     if (clst == 0) {    /* Static table (root-directory in FAT12/16) */
01134         if (idx >= dp->fs->n_rootdir)    /* Is index out of range? */
01135             return FR_INT_ERR;
01136         sect = dp->fs->dirbase;
01137     }
01138     else {                /* Dynamic table (root-directory in FAT32 or sub-directory) */
01139         ic = SS(dp->fs) / SZ_DIR * dp->fs->csize;    /* Entries per cluster */
01140         while (idx >= ic) {    /* Follow cluster chain */
01141             clst = get_fat(dp->fs, clst);                /* Get next cluster */
01142             if (clst == 0xFFFFFFFF) return FR_DISK_ERR;    /* Disk error */
01143             if (clst < 2 || clst >= dp->fs->n_fatent)    /* Reached to end of table or internal error */
01144                 return FR_INT_ERR;
01145             idx -= ic;
01146         }
01147         sect = clust2sect(dp->fs, clst);
01148     }
01149     dp->clust = clst;    /* Current cluster# */
01150     if (!sect) return FR_INT_ERR;
01151     dp->sect = sect + idx / (SS(dp->fs) / SZ_DIR);                    /* Sector# of the directory entry */
01152     dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIR)) * SZ_DIR;    /* Ptr to the entry in the sector */
01153 
01154     return FR_OK;
01155 }
01156 
01157 
01158 
01159 
01160 /*-----------------------------------------------------------------------*/
01161 /* Directory handling - Move directory table index next                  */
01162 /*-----------------------------------------------------------------------*/
01163 
01164 static
01165 FRESULT dir_next (    /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
01166     DIR* dp,        /* Pointer to the directory object */
01167     int stretch        /* 0: Do not stretch table, 1: Stretch table if needed */
01168 )
01169 {
01170     DWORD clst;
01171     UINT i;
01172 
01173 
01174     i = dp->index + 1;
01175     if (!(i & 0xFFFF) || !dp->sect)    /* Report EOT when index has reached 65535 */
01176         return FR_NO_FILE;
01177 
01178     if (!(i % (SS(dp->fs) / SZ_DIR))) {    /* Sector changed? */
01179         dp->sect++;                    /* Next sector */
01180 
01181         if (!dp->clust) {        /* Static table */
01182             if (i >= dp->fs->n_rootdir)    /* Report EOT if it reached end of static table */
01183                 return FR_NO_FILE;
01184         }
01185         else {                    /* Dynamic table */
01186             if (((i / (SS(dp->fs) / SZ_DIR)) & (dp->fs->csize - 1)) == 0) {    /* Cluster changed? */
01187                 clst = get_fat(dp->fs, dp->clust);                /* Get next cluster */
01188                 if (clst <= 1) return FR_INT_ERR;
01189                 if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
01190                 if (clst >= dp->fs->n_fatent) {                    /* If it reached end of dynamic table, */
01191 #if !_FS_READONLY
01192                     UINT c;
01193                     if (!stretch) return FR_NO_FILE;            /* If do not stretch, report EOT */
01194                     clst = create_chain(dp->fs, dp->clust);        /* Stretch cluster chain */
01195                     if (clst == 0) return FR_DENIED;            /* No free cluster */
01196                     if (clst == 1) return FR_INT_ERR;
01197                     if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
01198                     /* Clean-up stretched table */
01199                     if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */
01200                     mem_set(dp->fs->win, 0, SS(dp->fs));        /* Clear window buffer */
01201                     dp->fs->winsect = clust2sect(dp->fs, clst);    /* Cluster start sector */
01202                     for (c = 0; c < dp->fs->csize; c++) {        /* Fill the new cluster with 0 */
01203                         dp->fs->wflag = 1;
01204                         if (sync_window(dp->fs)) return FR_DISK_ERR;
01205                         dp->fs->winsect++;
01206                     }
01207                     dp->fs->winsect -= c;                        /* Rewind window offset */
01208 #else
01209                     if (!stretch) return FR_NO_FILE;            /* If do not stretch, report EOT (this is to suppress warning) */
01210                     return FR_NO_FILE;                            /* Report EOT */
01211 #endif
01212                 }
01213                 dp->clust = clst;                /* Initialize data for new cluster */
01214                 dp->sect = clust2sect(dp->fs, clst);
01215             }
01216         }
01217     }
01218 
01219     dp->index = (WORD)i;    /* Current index */
01220     dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIR)) * SZ_DIR;    /* Current entry in the window */
01221 
01222     return FR_OK;
01223 }
01224 
01225 
01226 
01227 
01228 /*-----------------------------------------------------------------------*/
01229 /* Directory handling - Reserve directory entry                          */
01230 /*-----------------------------------------------------------------------*/
01231 
01232 #if !_FS_READONLY
01233 static
01234 FRESULT dir_alloc (
01235     DIR* dp,    /* Pointer to the directory object */
01236     UINT nent    /* Number of contiguous entries to allocate (1-21) */
01237 )
01238 {
01239     FRESULT res;
01240     UINT n;
01241 
01242 
01243     res = dir_sdi(dp, 0);
01244     if (res == FR_OK) {
01245         n = 0;
01246         do {
01247             res = move_window(dp->fs, dp->sect);
01248             if (res != FR_OK) break;
01249             if (dp->dir[0] == DDE || dp->dir[0] == 0) {    /* Is it a blank entry? */
01250                 if (++n == nent) break;    /* A block of contiguous entries is found */
01251             } else {
01252                 n = 0;                    /* Not a blank entry. Restart to search */
01253             }
01254             res = dir_next(dp, 1);        /* Next entry with table stretch enabled */
01255         } while (res == FR_OK);
01256     }
01257     if (res == FR_NO_FILE) res = FR_DENIED;    /* No directory entry to allocate */
01258     return res;
01259 }
01260 #endif
01261 
01262 
01263 
01264 
01265 /*-----------------------------------------------------------------------*/
01266 /* Directory handling - Load/Store start cluster number                  */
01267 /*-----------------------------------------------------------------------*/
01268 
01269 static
01270 DWORD ld_clust (
01271     FATFS* fs,    /* Pointer to the fs object */
01272     BYTE* dir    /* Pointer to the directory entry */
01273 )
01274 {
01275     DWORD cl;
01276 
01277     cl = LD_WORD(dir+DIR_FstClusLO);
01278     if (fs->fs_type == FS_FAT32)
01279         cl |= (DWORD)LD_WORD(dir+DIR_FstClusHI) << 16;
01280 
01281     return cl;
01282 }
01283 
01284 
01285 #if !_FS_READONLY
01286 static
01287 void st_clust (
01288     BYTE* dir,    /* Pointer to the directory entry */
01289     DWORD cl    /* Value to be set */
01290 )
01291 {
01292     ST_WORD(dir+DIR_FstClusLO, cl);
01293     ST_WORD(dir+DIR_FstClusHI, cl >> 16);
01294 }
01295 #endif
01296 
01297 
01298 
01299 
01300 /*-----------------------------------------------------------------------*/
01301 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
01302 /*-----------------------------------------------------------------------*/
01303 #if _USE_LFN
01304 static
01305 const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};    /* Offset of LFN characters in the directory entry */
01306 
01307 
01308 static
01309 int cmp_lfn (            /* 1:Matched, 0:Not matched */
01310     WCHAR* lfnbuf,        /* Pointer to the LFN to be compared */
01311     BYTE* dir            /* Pointer to the directory entry containing a part of LFN */
01312 )
01313 {
01314     UINT i, s;
01315     WCHAR wc, uc;
01316 
01317 
01318     i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13;    /* Get offset in the LFN buffer */
01319     s = 0; wc = 1;
01320     do {
01321         uc = LD_WORD(dir+LfnOfs[s]);    /* Pick an LFN character from the entry */
01322         if (wc) {    /* Last character has not been processed */
01323             wc = ff_wtoupper(uc);        /* Convert it to upper case */
01324             if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))    /* Compare it */
01325                 return 0;                /* Not matched */
01326         } else {
01327             if (uc != 0xFFFF) return 0;    /* Check filler */
01328         }
01329     } while (++s < 13);                /* Repeat until all characters in the entry are checked */
01330 
01331     if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i])    /* Last segment matched but different length */
01332         return 0;
01333 
01334     return 1;                        /* The part of LFN matched */
01335 }
01336 
01337 
01338 
01339 static
01340 int pick_lfn (            /* 1:Succeeded, 0:Buffer overflow */
01341     WCHAR* lfnbuf,        /* Pointer to the Unicode-LFN buffer */
01342     BYTE* dir             /* Pointer to the directory entry */
01343 )
01344 {
01345     UINT i, s;
01346     WCHAR wc, uc;
01347 
01348 
01349     i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;    /* Offset in the LFN buffer */
01350 
01351     s = 0; wc = 1;
01352     do {
01353         uc = LD_WORD(dir+LfnOfs[s]);        /* Pick an LFN character from the entry */
01354         if (wc) {    /* Last character has not been processed */
01355             if (i >= _MAX_LFN) return 0;    /* Buffer overflow? */
01356             lfnbuf[i++] = wc = uc;            /* Store it */
01357         } else {
01358             if (uc != 0xFFFF) return 0;        /* Check filler */
01359         }
01360     } while (++s < 13);                        /* Read all character in the entry */
01361 
01362     if (dir[LDIR_Ord] & LLE) {                /* Put terminator if it is the last LFN part */
01363         if (i >= _MAX_LFN) return 0;        /* Buffer overflow? */
01364         lfnbuf[i] = 0;
01365     }
01366 
01367     return 1;
01368 }
01369 
01370 
01371 #if !_FS_READONLY
01372 static
01373 void fit_lfn (
01374     const WCHAR* lfnbuf,    /* Pointer to the LFN buffer */
01375     BYTE* dir,                /* Pointer to the directory entry */
01376     BYTE ord,                /* LFN order (1-20) */
01377     BYTE sum                /* SFN sum */
01378 )
01379 {
01380     UINT i, s;
01381     WCHAR wc;
01382 
01383 
01384     dir[LDIR_Chksum] = sum;            /* Set check sum */
01385     dir[LDIR_Attr] = AM_LFN;        /* Set attribute. LFN entry */
01386     dir[LDIR_Type] = 0;
01387     ST_WORD(dir+LDIR_FstClusLO, 0);
01388 
01389     i = (ord - 1) * 13;                /* Get offset in the LFN buffer */
01390     s = wc = 0;
01391     do {
01392         if (wc != 0xFFFF) wc = lfnbuf[i++];    /* Get an effective character */
01393         ST_WORD(dir+LfnOfs[s], wc);    /* Put it */
01394         if (!wc) wc = 0xFFFF;        /* Padding characters following last character */
01395     } while (++s < 13);
01396     if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE;    /* Bottom LFN part is the start of LFN sequence */
01397     dir[LDIR_Ord] = ord;            /* Set the LFN order */
01398 }
01399 
01400 #endif
01401 #endif
01402 
01403 
01404 
01405 
01406 /*-----------------------------------------------------------------------*/
01407 /* Create numbered name                                                  */
01408 /*-----------------------------------------------------------------------*/
01409 #if _USE_LFN
01410 static
01411 void gen_numname (
01412     BYTE* dst,            /* Pointer to the buffer to store numbered SFN */
01413     const BYTE* src,    /* Pointer to SFN */
01414     const WCHAR* lfn,    /* Pointer to LFN */
01415     UINT seq            /* Sequence number */
01416 )
01417 {
01418     BYTE ns[8], c;
01419     UINT i, j;
01420 
01421 
01422     mem_cpy(dst, src, 11);
01423 
01424     if (seq > 5) {    /* On many collisions, generate a hash number instead of sequential number */
01425         WCHAR wc;
01426         DWORD sr = seq;
01427 
01428         while (*lfn) {    /* Create a CRC */
01429             wc = *lfn++;
01430             for (i = 0; i < 16; i++) {
01431                 sr = (sr << 1) + (wc & 1);
01432                 wc >>= 1;
01433                 if (sr & 0x10000) sr ^= 0x11021;
01434             }
01435         }
01436         seq = (UINT)sr;
01437     }
01438 
01439     /* itoa (hexadecimal) */
01440     i = 7;
01441     do {
01442         c = (seq % 16) + '0';
01443         if (c > '9') c += 7;
01444         ns[i--] = c;
01445         seq /= 16;
01446     } while (seq);
01447     ns[i] = '~';
01448 
01449     /* Append the number */
01450     for (j = 0; j < i && dst[j] != ' '; j++) {
01451         if (IsDBCS1(dst[j])) {
01452             if (j == i - 1) break;
01453             j++;
01454         }
01455     }
01456     do {
01457         dst[j++] = (i < 8) ? ns[i++] : ' ';
01458     } while (j < 8);
01459 }
01460 #endif
01461 
01462 
01463 
01464 
01465 /*-----------------------------------------------------------------------*/
01466 /* Calculate sum of an SFN                                               */
01467 /*-----------------------------------------------------------------------*/
01468 #if _USE_LFN
01469 static
01470 BYTE sum_sfn (
01471     const BYTE* dir        /* Pointer to the SFN entry */
01472 )
01473 {
01474     BYTE sum = 0;
01475     UINT n = 11;
01476 
01477     do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
01478     return sum;
01479 }
01480 #endif
01481 
01482 
01483 
01484 
01485 /*-----------------------------------------------------------------------*/
01486 /* Directory handling - Find an object in the directory                  */
01487 /*-----------------------------------------------------------------------*/
01488 
01489 static
01490 FRESULT dir_find (
01491     DIR* dp            /* Pointer to the directory object linked to the file name */
01492 )
01493 {
01494     FRESULT res;
01495     BYTE c, *dir;
01496 #if _USE_LFN
01497     BYTE a, ord, sum;
01498 #endif
01499 
01500     res = dir_sdi(dp, 0);            /* Rewind directory object */
01501     if (res != FR_OK) return res;
01502 
01503 #if _USE_LFN
01504     ord = sum = 0xFF; dp->lfn_idx = 0xFFFF;    /* Reset LFN sequence */
01505 #endif
01506     do {
01507         res = move_window(dp->fs, dp->sect);
01508         if (res != FR_OK) break;
01509         dir = dp->dir;                    /* Ptr to the directory entry of current index */
01510         c = dir[DIR_Name];
01511         if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
01512 #if _USE_LFN    /* LFN configuration */
01513         a = dir[DIR_Attr] & AM_MASK;
01514         if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
01515             ord = 0xFF; dp->lfn_idx = 0xFFFF;    /* Reset LFN sequence */
01516         } else {
01517             if (a == AM_LFN) {            /* An LFN entry is found */
01518                 if (dp->lfn) {
01519                     if (c & LLE) {        /* Is it start of LFN sequence? */
01520                         sum = dir[LDIR_Chksum];
01521                         c &= ~LLE; ord = c;    /* LFN start order */
01522                         dp->lfn_idx = dp->index;    /* Start index of LFN */
01523                     }
01524                     /* Check validity of the LFN entry and compare it with given name */
01525                     ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;
01526                 }
01527             } else {                    /* An SFN entry is found */
01528                 if (!ord && sum == sum_sfn(dir)) break;    /* LFN matched? */
01529                 if (!(dp->fn[NS] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break;    /* SFN matched? */
01530                 ord = 0xFF; dp->lfn_idx = 0xFFFF;    /* Reset LFN sequence */
01531             }
01532         }
01533 #else        /* Non LFN configuration */
01534         if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */
01535             break;
01536 #endif
01537         res = dir_next(dp, 0);        /* Next entry */
01538     } while (res == FR_OK);
01539 
01540     return res;
01541 }
01542 
01543 
01544 
01545 
01546 /*-----------------------------------------------------------------------*/
01547 /* Read an object from the directory                                     */
01548 /*-----------------------------------------------------------------------*/
01549 #if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2
01550 static
01551 FRESULT dir_read (
01552     DIR* dp,        /* Pointer to the directory object */
01553     int vol            /* Filtered by 0:file/directory or 1:volume label */
01554 )
01555 {
01556     FRESULT res;
01557     BYTE a, c, *dir;
01558 #if _USE_LFN
01559     BYTE ord = 0xFF, sum = 0xFF;
01560 #endif
01561 
01562     res = FR_NO_FILE;
01563     while (dp->sect) {
01564         res = move_window(dp->fs, dp->sect);
01565         if (res != FR_OK) break;
01566         dir = dp->dir;                    /* Ptr to the directory entry of current index */
01567         c = dir[DIR_Name];
01568         if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
01569         a = dir[DIR_Attr] & AM_MASK;
01570 #if _USE_LFN    /* LFN configuration */
01571         if (c == DDE || (!_FS_RPATH && c == '.') || (int)(a == AM_VOL) != vol) {    /* An entry without valid data */
01572             ord = 0xFF;
01573         } else {
01574             if (a == AM_LFN) {            /* An LFN entry is found */
01575                 if (c & LLE) {            /* Is it start of LFN sequence? */
01576                     sum = dir[LDIR_Chksum];
01577                     c &= ~LLE; ord = c;
01578                     dp->lfn_idx = dp->index;
01579                 }
01580                 /* Check LFN validity and capture it */
01581                 ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;
01582             } else {                    /* An SFN entry is found */
01583                 if (ord || sum != sum_sfn(dir))    /* Is there a valid LFN? */
01584                     dp->lfn_idx = 0xFFFF;        /* It has no LFN. */
01585                 break;
01586             }
01587         }
01588 #else        /* Non LFN configuration */
01589         if (c != DDE && (_FS_RPATH || c != '.') && a != AM_LFN && (int)(a == AM_VOL) == vol)    /* Is it a valid entry? */
01590             break;
01591 #endif
01592         res = dir_next(dp, 0);                /* Next entry */
01593         if (res != FR_OK) break;
01594     }
01595 
01596     if (res != FR_OK) dp->sect = 0;
01597 
01598     return res;
01599 }
01600 #endif    /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */
01601 
01602 
01603 
01604 
01605 /*-----------------------------------------------------------------------*/
01606 /* Register an object to the directory                                   */
01607 /*-----------------------------------------------------------------------*/
01608 #if !_FS_READONLY
01609 static
01610 FRESULT dir_register (    /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
01611     DIR* dp                /* Target directory with object name to be created */
01612 )
01613 {
01614     FRESULT res;
01615 #if _USE_LFN    /* LFN configuration */
01616     UINT n, nent;
01617     BYTE sn[12], *fn, sum;
01618     WCHAR *lfn;
01619 
01620 
01621     fn = dp->fn; lfn = dp->lfn;
01622     mem_cpy(sn, fn, 12);
01623 
01624     if (_FS_RPATH && (sn[NS] & NS_DOT))        /* Cannot create dot entry */
01625         return FR_INVALID_NAME;
01626 
01627     if (sn[NS] & NS_LOSS) {            /* When LFN is out of 8.3 format, generate a numbered name */
01628         fn[NS] = 0; dp->lfn = 0;            /* Find only SFN */
01629         for (n = 1; n < 100; n++) {
01630             gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
01631             res = dir_find(dp);                /* Check if the name collides with existing SFN */
01632             if (res != FR_OK) break;
01633         }
01634         if (n == 100) return FR_DENIED;        /* Abort if too many collisions */
01635         if (res != FR_NO_FILE) return res;    /* Abort if the result is other than 'not collided' */
01636         fn[NS] = sn[NS]; dp->lfn = lfn;
01637     }
01638 
01639     if (sn[NS] & NS_LFN) {            /* When LFN is to be created, allocate entries for an SFN + LFNs. */
01640         for (n = 0; lfn[n]; n++) ;
01641         nent = (n + 25) / 13;
01642     } else {                        /* Otherwise allocate an entry for an SFN  */
01643         nent = 1;
01644     }
01645     res = dir_alloc(dp, nent);        /* Allocate entries */
01646 
01647     if (res == FR_OK && --nent) {    /* Set LFN entry if needed */
01648         res = dir_sdi(dp, dp->index - nent);
01649         if (res == FR_OK) {
01650             sum = sum_sfn(dp->fn);    /* Sum value of the SFN tied to the LFN */
01651             do {                    /* Store LFN entries in bottom first */
01652                 res = move_window(dp->fs, dp->sect);
01653                 if (res != FR_OK) break;
01654                 fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum);
01655                 dp->fs->wflag = 1;
01656                 res = dir_next(dp, 0);    /* Next entry */
01657             } while (res == FR_OK && --nent);
01658         }
01659     }
01660 #else    /* Non LFN configuration */
01661     res = dir_alloc(dp, 1);        /* Allocate an entry for SFN */
01662 #endif
01663 
01664     if (res == FR_OK) {                /* Set SFN entry */
01665         res = move_window(dp->fs, dp->sect);
01666         if (res == FR_OK) {
01667             mem_set(dp->dir, 0, SZ_DIR);    /* Clean the entry */
01668             mem_cpy(dp->dir, dp->fn, 11);    /* Put SFN */
01669 #if _USE_LFN
01670             dp->dir[DIR_NTres] = dp->fn[NS] & (NS_BODY | NS_EXT);    /* Put NT flag */
01671 #endif
01672             dp->fs->wflag = 1;
01673         }
01674     }
01675 
01676     return res;
01677 }
01678 #endif /* !_FS_READONLY */
01679 
01680 
01681 
01682 
01683 /*-----------------------------------------------------------------------*/
01684 /* Remove an object from the directory                                   */
01685 /*-----------------------------------------------------------------------*/
01686 #if !_FS_READONLY && !_FS_MINIMIZE
01687 static
01688 FRESULT dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */
01689     DIR* dp                /* Directory object pointing the entry to be removed */
01690 )
01691 {
01692     FRESULT res;
01693 #if _USE_LFN    /* LFN configuration */
01694     UINT i;
01695 
01696     i = dp->index;    /* SFN index */
01697     res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx);    /* Goto the SFN or top of the LFN entries */
01698     if (res == FR_OK) {
01699         do {
01700             res = move_window(dp->fs, dp->sect);
01701             if (res != FR_OK) break;
01702             mem_set(dp->dir, 0, SZ_DIR);    /* Clear and mark the entry "deleted" */
01703             *dp->dir = DDE;
01704             dp->fs->wflag = 1;
01705             if (dp->index >= i) break;    /* When reached SFN, all entries of the object has been deleted. */
01706             res = dir_next(dp, 0);        /* Next entry */
01707         } while (res == FR_OK);
01708         if (res == FR_NO_FILE) res = FR_INT_ERR;
01709     }
01710 
01711 #else            /* Non LFN configuration */
01712     res = dir_sdi(dp, dp->index);
01713     if (res == FR_OK) {
01714         res = move_window(dp->fs, dp->sect);
01715         if (res == FR_OK) {
01716             mem_set(dp->dir, 0, SZ_DIR);    /* Clear and mark the entry "deleted" */
01717             *dp->dir = DDE;
01718             dp->fs->wflag = 1;
01719         }
01720     }
01721 #endif
01722 
01723     return res;
01724 }
01725 #endif /* !_FS_READONLY */
01726 
01727 
01728 
01729 
01730 /*-----------------------------------------------------------------------*/
01731 /* Get file information from directory entry                             */
01732 /*-----------------------------------------------------------------------*/
01733 #if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2
01734 static
01735 void get_fileinfo (        /* No return code */
01736     DIR* dp,            /* Pointer to the directory object */
01737     FILINFO* fno         /* Pointer to the file information to be filled */
01738 )
01739 {
01740     UINT i;
01741     TCHAR *p, c;
01742 
01743 
01744     p = fno->fname;
01745     if (dp->sect) {        /* Get SFN */
01746         BYTE *dir = dp->dir;
01747 
01748         i = 0;
01749         while (i < 11) {        /* Copy name body and extension */
01750             c = (TCHAR)dir[i++];
01751             if (c == ' ') continue;            /* Skip padding spaces */
01752             if (c == NDDE) c = (TCHAR)DDE;    /* Restore replaced DDE character */
01753             if (i == 9) *p++ = '.';            /* Insert a . if extension is exist */
01754 #if _USE_LFN
01755             if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY)))
01756                 c += 0x20;            /* To lower */
01757 #if _LFN_UNICODE
01758             if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i]))
01759                 c = c << 8 | dir[i++];
01760             c = ff_convert(c, 1);    /* OEM -> Unicode */
01761             if (!c) c = '?';
01762 #endif
01763 #endif
01764             *p++ = c;
01765         }
01766         fno->fattrib = dir[DIR_Attr];                /* Attribute */
01767         fno->fsize = LD_DWORD(dir+DIR_FileSize);    /* Size */
01768         fno->fdate = LD_WORD(dir+DIR_WrtDate);        /* Date */
01769         fno->ftime = LD_WORD(dir+DIR_WrtTime);        /* Time */
01770     }
01771     *p = 0;        /* Terminate SFN string by a \0 */
01772 
01773 #if _USE_LFN
01774     if (fno->lfname) {
01775         WCHAR w, *lfn;
01776 
01777         i = 0; p = fno->lfname;
01778         if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) {    /* Get LFN if available */
01779             lfn = dp->lfn;
01780             while ((w = *lfn++) != 0) {        /* Get an LFN character */
01781 #if !_LFN_UNICODE
01782                 w = ff_convert(w, 0);        /* Unicode -> OEM */
01783                 if (!w) { i = 0; break; }    /* No LFN if it could not be converted */
01784                 if (_DF1S && w >= 0x100)    /* Put 1st byte if it is a DBC (always false on SBCS cfg) */
01785                     p[i++] = (TCHAR)(w >> 8);
01786 #endif
01787                 if (i >= fno->lfsize - 1) { i = 0; break; }    /* No LFN if buffer overflow */
01788                 p[i++] = (TCHAR)w;
01789             }
01790         }
01791         p[i] = 0;    /* Terminate LFN string by a \0 */
01792     }
01793 #endif
01794 }
01795 #endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2*/
01796 
01797 
01798 
01799 
01800 /*-----------------------------------------------------------------------*/
01801 /* Pick a segment and create the object name in directory form           */
01802 /*-----------------------------------------------------------------------*/
01803 
01804 static
01805 FRESULT create_name (
01806     DIR* dp,            /* Pointer to the directory object */
01807     const TCHAR** path    /* Pointer to pointer to the segment in the path string */
01808 )
01809 {
01810 #if _USE_LFN    /* LFN configuration */
01811     BYTE b, cf;
01812     WCHAR w, *lfn;
01813     UINT i, ni, si, di;
01814     const TCHAR *p;
01815 
01816     /* Create LFN in Unicode */
01817     for (p = *path; *p == '/' || *p == '\\'; p++) ;    /* Strip duplicated separator */
01818     lfn = dp->lfn;
01819     si = di = 0;
01820     for (;;) {
01821         w = p[si++];                    /* Get a character */
01822         if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
01823         if (di >= _MAX_LFN)                /* Reject too long name */
01824             return FR_INVALID_NAME;
01825 #if !_LFN_UNICODE
01826         w &= 0xFF;
01827         if (IsDBCS1(w)) {                /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
01828             b = (BYTE)p[si++];            /* Get 2nd byte */
01829             if (!IsDBCS2(b))
01830                 return FR_INVALID_NAME;    /* Reject invalid sequence */
01831             w = (w << 8) + b;            /* Create a DBC */
01832         }
01833         w = ff_convert(w, 1);            /* Convert ANSI/OEM to Unicode */
01834         if (!w) return FR_INVALID_NAME;    /* Reject invalid code */
01835 #endif
01836         if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */
01837             return FR_INVALID_NAME;
01838         lfn[di++] = w;                    /* Store the Unicode character */
01839     }
01840     *path = &p[si];                        /* Return pointer to the next segment */
01841     cf = (w < ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
01842 #if _FS_RPATH
01843     if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */
01844         (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) {
01845         lfn[di] = 0;
01846         for (i = 0; i < 11; i++)
01847             dp->fn[i] = (i < di) ? '.' : ' ';
01848         dp->fn[i] = cf | NS_DOT;        /* This is a dot entry */
01849         return FR_OK;
01850     }
01851 #endif
01852     while (di) {                        /* Strip trailing spaces and dots */
01853         w = lfn[di-1];
01854         if (w != ' ' && w != '.') break;
01855         di--;
01856     }
01857     if (!di) return FR_INVALID_NAME;    /* Reject nul string */
01858 
01859     lfn[di] = 0;                        /* LFN is created */
01860 
01861     /* Create SFN in directory form */
01862     mem_set(dp->fn, ' ', 11);
01863     for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;    /* Strip leading spaces and dots */
01864     if (si) cf |= NS_LOSS | NS_LFN;
01865     while (di && lfn[di - 1] != '.') di--;    /* Find extension (di<=si: no extension) */
01866 
01867     b = i = 0; ni = 8;
01868     for (;;) {
01869         w = lfn[si++];                    /* Get an LFN character */
01870         if (!w) break;                    /* Break on end of the LFN */
01871         if (w == ' ' || (w == '.' && si != di)) {    /* Remove spaces and dots */
01872             cf |= NS_LOSS | NS_LFN; continue;
01873         }
01874 
01875         if (i >= ni || si == di) {        /* Extension or end of SFN */
01876             if (ni == 11) {                /* Long extension */
01877                 cf |= NS_LOSS | NS_LFN; break;
01878             }
01879             if (si != di) cf |= NS_LOSS | NS_LFN;    /* Out of 8.3 format */
01880             if (si > di) break;            /* No extension */
01881             si = di; i = 8; ni = 11;    /* Enter extension section */
01882             b <<= 2; continue;
01883         }
01884 
01885         if (w >= 0x80) {                /* Non ASCII character */
01886 #ifdef _EXCVT
01887             w = ff_convert(w, 0);        /* Unicode -> OEM code */
01888             if (w) w = ExCvt[w - 0x80];    /* Convert extended character to upper (SBCS) */
01889 #else
01890             w = ff_convert(ff_wtoupper(w), 0);    /* Upper converted Unicode -> OEM code */
01891 #endif
01892             cf |= NS_LFN;                /* Force create LFN entry */
01893         }
01894 
01895         if (_DF1S && w >= 0x100) {        /* Double byte character (always false on SBCS cfg) */
01896             if (i >= ni - 1) {
01897                 cf |= NS_LOSS | NS_LFN; i = ni; continue;
01898             }
01899             dp->fn[i++] = (BYTE)(w >> 8);
01900         } else {                        /* Single byte character */
01901             if (!w || chk_chr("+,;=[]", w)) {    /* Replace illegal characters for SFN */
01902                 w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
01903             } else {
01904                 if (IsUpper(w)) {        /* ASCII large capital */
01905                     b |= 2;
01906                 } else {
01907                     if (IsLower(w)) {    /* ASCII small capital */
01908                         b |= 1; w -= 0x20;
01909                     }
01910                 }
01911             }
01912         }
01913         dp->fn[i++] = (BYTE)w;
01914     }
01915 
01916     if (dp->fn[0] == DDE) dp->fn[0] = NDDE;    /* If the first character collides with deleted mark, replace it with 0x05 */
01917 
01918     if (ni == 8) b <<= 2;
01919     if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)    /* Create LFN entry when there are composite capitals */
01920         cf |= NS_LFN;
01921     if (!(cf & NS_LFN)) {                        /* When LFN is in 8.3 format without extended character, NT flags are created */
01922         if ((b & 0x03) == 0x01) cf |= NS_EXT;    /* NT flag (Extension has only small capital) */
01923         if ((b & 0x0C) == 0x04) cf |= NS_BODY;    /* NT flag (Filename has only small capital) */
01924     }
01925 
01926     dp->fn[NS] = cf;    /* SFN is created */
01927 
01928     return FR_OK;
01929 
01930 
01931 #else    /* Non-LFN configuration */
01932     BYTE b, c, d, *sfn;
01933     UINT ni, si, i;
01934     const char *p;
01935 
01936     /* Create file name in directory form */
01937     for (p = *path; *p == '/' || *p == '\\'; p++) ;    /* Strip duplicated separator */
01938     sfn = dp->fn;
01939     mem_set(sfn, ' ', 11);
01940     si = i = b = 0; ni = 8;
01941 #if _FS_RPATH
01942     if (p[si] == '.') { /* Is this a dot entry? */
01943         for (;;) {
01944             c = (BYTE)p[si++];
01945             if (c != '.' || si >= 3) break;
01946             sfn[i++] = c;
01947         }
01948         if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
01949         *path = &p[si];                                    /* Return pointer to the next segment */
01950         sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;    /* Set last segment flag if end of path */
01951         return FR_OK;
01952     }
01953 #endif
01954     for (;;) {
01955         c = (BYTE)p[si++];
01956         if (c <= ' ' || c == '/' || c == '\\') break;    /* Break on end of segment */
01957         if (c == '.' || i >= ni) {
01958             if (ni != 8 || c != '.') return FR_INVALID_NAME;
01959             i = 8; ni = 11;
01960             b <<= 2; continue;
01961         }
01962         if (c >= 0x80) {                /* Extended character? */
01963             b |= 3;                        /* Eliminate NT flag */
01964 #ifdef _EXCVT
01965             c = ExCvt[c - 0x80];        /* To upper extended characters (SBCS cfg) */
01966 #else
01967 #if !_DF1S
01968             return FR_INVALID_NAME;        /* Reject extended characters (ASCII cfg) */
01969 #endif
01970 #endif
01971         }
01972         if (IsDBCS1(c)) {                /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
01973             d = (BYTE)p[si++];            /* Get 2nd byte */
01974             if (!IsDBCS2(d) || i >= ni - 1)    /* Reject invalid DBC */
01975                 return FR_INVALID_NAME;
01976             sfn[i++] = c;
01977             sfn[i++] = d;
01978         } else {                        /* Single byte code */
01979             if (chk_chr("\"*+,:;<=>\?[]|\x7F", c))    /* Reject illegal chrs for SFN */
01980                 return FR_INVALID_NAME;
01981             if (IsUpper(c)) {            /* ASCII large capital? */
01982                 b |= 2;
01983             } else {
01984                 if (IsLower(c)) {        /* ASCII small capital? */
01985                     b |= 1; c -= 0x20;
01986                 }
01987             }
01988             sfn[i++] = c;
01989         }
01990     }
01991     *path = &p[si];                        /* Return pointer to the next segment */
01992     c = (c <= ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
01993 
01994     if (!i) return FR_INVALID_NAME;        /* Reject nul string */
01995     if (sfn[0] == DDE) sfn[0] = NDDE;    /* When first character collides with DDE, replace it with 0x05 */
01996 
01997     if (ni == 8) b <<= 2;
01998     if ((b & 0x03) == 0x01) c |= NS_EXT;    /* NT flag (Name extension has only small capital) */
01999     if ((b & 0x0C) == 0x04) c |= NS_BODY;    /* NT flag (Name body has only small capital) */
02000 
02001     sfn[NS] = c;        /* Store NT flag, File name is created */
02002 
02003     return FR_OK;
02004 #endif
02005 }
02006 
02007 
02008 
02009 
02010 /*-----------------------------------------------------------------------*/
02011 /* Follow a file path                                                    */
02012 /*-----------------------------------------------------------------------*/
02013 
02014 static
02015 FRESULT follow_path (    /* FR_OK(0): successful, !=0: error code */
02016     DIR* dp,            /* Directory object to return last directory and found object */
02017     const TCHAR* path    /* Full-path string to find a file or directory */
02018 )
02019 {
02020     FRESULT res;
02021     BYTE *dir, ns;
02022 
02023 
02024 #if _FS_RPATH
02025     if (*path == '/' || *path == '\\') {    /* There is a heading separator */
02026         path++;    dp->sclust = 0;                /* Strip it and start from the root directory */
02027     } else {                                /* No heading separator */
02028         dp->sclust = dp->fs->cdir;            /* Start from the current directory */
02029     }
02030 #else
02031     if (*path == '/' || *path == '\\')        /* Strip heading separator if exist */
02032         path++;
02033     dp->sclust = 0;                            /* Always start from the root directory */
02034 #endif
02035 
02036     if ((UINT)*path < ' ') {                /* Null path name is the origin directory itself */
02037         res = dir_sdi(dp, 0);
02038         dp->dir = 0;
02039     } else {                                /* Follow path */
02040         for (;;) {
02041             res = create_name(dp, &path);    /* Get a segment name of the path */
02042             if (res != FR_OK) break;
02043             res = dir_find(dp);                /* Find an object with the segment name */
02044             ns = dp->fn[NS];
02045             if (res != FR_OK) {                /* Failed to find the object */
02046                 if (res == FR_NO_FILE) {    /* Object is not found */
02047                     if (_FS_RPATH && (ns & NS_DOT)) {    /* If dot entry is not exist, */
02048                         dp->sclust = 0; dp->dir = 0;    /* it is the root directory and stay there */
02049                         if (!(ns & NS_LAST)) continue;    /* Continue to follow if not last segment */
02050                         res = FR_OK;                    /* Ended at the root directory. Function completed. */
02051                     } else {                            /* Could not find the object */
02052                         if (!(ns & NS_LAST)) res = FR_NO_PATH;    /* Adjust error code if not last segment */
02053                     }
02054                 }
02055                 break;
02056             }
02057             if (ns & NS_LAST) break;            /* Last segment matched. Function completed. */
02058             dir = dp->dir;                        /* Follow the sub-directory */
02059             if (!(dir[DIR_Attr] & AM_DIR)) {    /* It is not a sub-directory and cannot follow */
02060                 res = FR_NO_PATH; break;
02061             }
02062             dp->sclust = ld_clust(dp->fs, dir);
02063         }
02064     }
02065 
02066     return res;
02067 }
02068 
02069 
02070 
02071 
02072 /*-----------------------------------------------------------------------*/
02073 /* Get logical drive number from path name                               */
02074 /*-----------------------------------------------------------------------*/
02075 
02076 static
02077 int get_ldnumber (        /* Returns logical drive number (-1:invalid drive) */
02078     const TCHAR** path    /* Pointer to pointer to the path name */
02079 )
02080 {
02081     const TCHAR *tp, *tt;
02082     UINT i;
02083     int vol = -1;
02084 
02085 
02086     if (*path) {    /* If the pointer is not a null */
02087         for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ;    /* Find ':' in the path */
02088         if (*tt == ':') {    /* If a ':' is exist in the path name */
02089             tp = *path;
02090             i = *tp++ - '0'; 
02091             if (i < 10 && tp == tt) {    /* Is there a numeric drive id? */
02092                 if (i < _VOLUMES) {    /* If a drive id is found, get the value and strip it */
02093                     vol = (int)i;
02094                     *path = ++tt;
02095                 }
02096             } else {    /* No numeric drive number */
02097 #if _STR_VOLUME_ID        /* Find string drive id */
02098                 static const char* const str[] = {_VOLUME_STRS};
02099                 const char *sp;
02100                 char c;
02101                 TCHAR tc;
02102 
02103                 i = 0; tt++;
02104                 do {
02105                     sp = str[i]; tp = *path;
02106                     do {    /* Compare a string drive id with path name */
02107                         c = *sp++; tc = *tp++;
02108                         if (IsLower(tc)) tc -= 0x20;
02109                     } while (c && (TCHAR)c == tc);
02110                 } while ((c || tp != tt) && ++i < _VOLUMES);    /* Repeat for each id until pattern match */
02111                 if (i < _VOLUMES) {    /* If a drive id is found, get the value and strip it */
02112                     vol = (int)i;
02113                     *path = tt;
02114                 }
02115 #endif
02116             }
02117             return vol;
02118         }
02119 #if _FS_RPATH && _VOLUMES >= 2
02120         vol = CurrVol;    /* Current drive */
02121 #else
02122         vol = 0;        /* Drive 0 */
02123 #endif
02124     }
02125     return vol;
02126 }
02127 
02128 
02129 
02130 
02131 /*-----------------------------------------------------------------------*/
02132 /* Load a sector and check if it is an FAT boot sector                   */
02133 /*-----------------------------------------------------------------------*/
02134 
02135 static
02136 BYTE check_fs (    /* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a boot sector, 3:Disk error */
02137     FATFS* fs,    /* File system object */
02138     DWORD sect    /* Sector# (lba) to check if it is an FAT boot record or not */
02139 )
02140 {
02141     fs->wflag = 0; fs->winsect = 0xFFFFFFFF;    /* Invalidate window */
02142     if (move_window(fs, sect) != FR_OK)            /* Load boot record */
02143         return 3;
02144 
02145     if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)    /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */
02146         return 2;
02147 
02148     if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)        /* Check "FAT" string */
02149         return 0;
02150     if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)    /* Check "FAT" string */
02151         return 0;
02152 
02153     return 1;
02154 }
02155 
02156 
02157 
02158 
02159 /*-----------------------------------------------------------------------*/
02160 /* Find logical drive and check if the volume is mounted                 */
02161 /*-----------------------------------------------------------------------*/
02162 
02163 static
02164 FRESULT find_volume (    /* FR_OK(0): successful, !=0: any error occurred */
02165     FATFS** rfs,        /* Pointer to pointer to the found file system object */
02166     const TCHAR** path,    /* Pointer to pointer to the path name (drive number) */
02167     BYTE wmode            /* !=0: Check write protection for write access */
02168 )
02169 {
02170     BYTE fmt;
02171     int vol;
02172     DSTATUS stat;
02173     DWORD bsect, fasize, tsect, sysect, nclst, szbfat;
02174     WORD nrsv;
02175     FATFS *fs;
02176 
02177 
02178     /* Get logical drive number from the path name */
02179     *rfs = 0;
02180     vol = get_ldnumber(path);
02181     if (vol < 0) return FR_INVALID_DRIVE;
02182 
02183     /* Check if the file system object is valid or not */
02184     fs = FatFs[vol];                    /* Get pointer to the file system object */
02185     if (!fs) return FR_NOT_ENABLED;        /* Is the file system object available? */
02186 
02187     ENTER_FF(fs);                        /* Lock the volume */
02188     *rfs = fs;                            /* Return pointer to the file system object */
02189 
02190     if (fs->fs_type) {                    /* If the volume has been mounted */
02191         stat = disk_status(fs->drv);
02192         if (!(stat & STA_NOINIT)) {        /* and the physical drive is kept initialized */
02193             if (!_FS_READONLY && wmode && (stat & STA_PROTECT))    /* Check write protection if needed */
02194                 return FR_WRITE_PROTECTED;
02195             return FR_OK;                /* The file system object is valid */
02196         }
02197     }
02198 
02199     /* The file system object is not valid. */
02200     /* Following code attempts to mount the volume. (analyse BPB and initialize the fs object) */
02201 
02202     fs->fs_type = 0;                    /* Clear the file system object */
02203     fs->drv = LD2PD(vol);                /* Bind the logical drive and a physical drive */
02204     stat = disk_initialize(fs->drv);    /* Initialize the physical drive */
02205     if (stat & STA_NOINIT)                /* Check if the initialization succeeded */
02206         return FR_NOT_READY;            /* Failed to initialize due to no medium or hard error */
02207     if (!_FS_READONLY && wmode && (stat & STA_PROTECT))    /* Check disk write protection if needed */
02208         return FR_WRITE_PROTECTED;
02209 #if _MAX_SS != _MIN_SS                        /* Get sector size (multiple sector size cfg only) */
02210     if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
02211         || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR;
02212 #endif
02213     /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */
02214     bsect = 0;
02215     fmt = check_fs(fs, bsect);                    /* Load sector 0 and check if it is an FAT boot sector as SFD */
02216     if (fmt == 1 || (!fmt && (LD2PT(vol)))) {    /* Not an FAT boot sector or forced partition number */
02217         UINT i;
02218         DWORD br[4];
02219 
02220         for (i = 0; i < 4; i++) {            /* Get partition offset */
02221             BYTE *pt = fs->win+MBR_Table + i * SZ_PTE;
02222             br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0;
02223         }
02224         i = LD2PT(vol);                        /* Partition number: 0:auto, 1-4:forced */
02225         if (i) i--;
02226         do {                                /* Find an FAT volume */
02227             bsect = br[i];
02228             fmt = bsect ? check_fs(fs, bsect) : 2;    /* Check the partition */
02229         } while (!LD2PT(vol) && fmt && ++i < 4);
02230     }
02231     if (fmt == 3) return FR_DISK_ERR;        /* An error occurred in the disk I/O layer */
02232     if (fmt) return FR_NO_FILESYSTEM;        /* No FAT volume is found */
02233 
02234     /* An FAT volume is found. Following code initializes the file system object */
02235 
02236     if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))        /* (BPB_BytsPerSec must be equal to the physical sector size) */
02237         return FR_NO_FILESYSTEM;
02238 
02239     fasize = LD_WORD(fs->win+BPB_FATSz16);                /* Number of sectors per FAT */
02240     if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32);
02241     fs->fsize = fasize;
02242 
02243     fs->n_fats = fs->win[BPB_NumFATs];                    /* Number of FAT copies */
02244     if (fs->n_fats != 1 && fs->n_fats != 2)                /* (Must be 1 or 2) */
02245         return FR_NO_FILESYSTEM;
02246     fasize *= fs->n_fats;                                /* Number of sectors for FAT area */
02247 
02248     fs->csize = fs->win[BPB_SecPerClus];                /* Number of sectors per cluster */
02249     if (!fs->csize || (fs->csize & (fs->csize - 1)))    /* (Must be power of 2) */
02250         return FR_NO_FILESYSTEM;
02251 
02252     fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);    /* Number of root directory entries */
02253     if (fs->n_rootdir % (SS(fs) / SZ_DIR))                /* (Must be sector aligned) */
02254         return FR_NO_FILESYSTEM;
02255 
02256     tsect = LD_WORD(fs->win+BPB_TotSec16);                /* Number of sectors on the volume */
02257     if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
02258 
02259     nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt);                /* Number of reserved sectors */
02260     if (!nrsv) return FR_NO_FILESYSTEM;                    /* (Must not be 0) */
02261 
02262     /* Determine the FAT sub type */
02263     sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR);    /* RSV+FAT+DIR */
02264     if (tsect < sysect) return FR_NO_FILESYSTEM;        /* (Invalid volume size) */
02265     nclst = (tsect - sysect) / fs->csize;                /* Number of clusters */
02266     if (!nclst) return FR_NO_FILESYSTEM;                /* (Invalid volume size) */
02267     fmt = FS_FAT12;
02268     if (nclst >= MIN_FAT16) fmt = FS_FAT16;
02269     if (nclst >= MIN_FAT32) fmt = FS_FAT32;
02270 
02271     /* Boundaries and Limits */
02272     fs->n_fatent = nclst + 2;                            /* Number of FAT entries */
02273     fs->volbase = bsect;                                /* Volume start sector */
02274     fs->fatbase = bsect + nrsv;                         /* FAT start sector */
02275     fs->database = bsect + sysect;                        /* Data start sector */
02276     if (fmt == FS_FAT32) {
02277         if (fs->n_rootdir) return FR_NO_FILESYSTEM;        /* (BPB_RootEntCnt must be 0) */
02278         fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);    /* Root directory start cluster */
02279         szbfat = fs->n_fatent * 4;                        /* (Needed FAT size) */
02280     } else {
02281         if (!fs->n_rootdir)    return FR_NO_FILESYSTEM;    /* (BPB_RootEntCnt must not be 0) */
02282         fs->dirbase = fs->fatbase + fasize;                /* Root directory start sector */
02283         szbfat = (fmt == FS_FAT16) ?                    /* (Needed FAT size) */
02284             fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
02285     }
02286     if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs))    /* (BPB_FATSz must not be less than needed) */
02287         return FR_NO_FILESYSTEM;
02288 
02289 #if !_FS_READONLY
02290     /* Initialize cluster allocation information */
02291     fs->last_clust = fs->free_clust = 0xFFFFFFFF;
02292 
02293     /* Get fsinfo if available */
02294     fs->fsi_flag = 0x80;
02295 #if (_FS_NOFSINFO & 3) != 3
02296     if (fmt == FS_FAT32                /* Enable FSINFO only if FAT32 and BPB_FSInfo is 1 */
02297         && LD_WORD(fs->win+BPB_FSInfo) == 1
02298         && move_window(fs, bsect + 1) == FR_OK)
02299     {
02300         fs->fsi_flag = 0;
02301         if (LD_WORD(fs->win+BS_55AA) == 0xAA55    /* Load FSINFO data if available */
02302             && LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252
02303             && LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272)
02304         {
02305 #if (_FS_NOFSINFO & 1) == 0
02306             fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
02307 #endif
02308 #if (_FS_NOFSINFO & 2) == 0
02309             fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
02310 #endif
02311         }
02312     }
02313 #endif
02314 #endif
02315     fs->fs_type = fmt;    /* FAT sub-type */
02316     fs->id = ++Fsid;    /* File system mount ID */
02317 #if _FS_RPATH
02318     fs->cdir = 0;        /* Set current directory to root */
02319 #endif
02320 #if _FS_LOCK            /* Clear file lock semaphores */
02321     clear_lock(fs);
02322 #endif
02323 
02324     return FR_OK;
02325 }
02326 
02327 
02328 
02329 
02330 /*-----------------------------------------------------------------------*/
02331 /* Check if the file/directory object is valid or not                    */
02332 /*-----------------------------------------------------------------------*/
02333 
02334 static
02335 FRESULT validate (    /* FR_OK(0): The object is valid, !=0: Invalid */
02336     void* obj        /* Pointer to the object FIL/DIR to check validity */
02337 )
02338 {
02339     FIL *fil = (FIL*)obj;    /* Assuming offset of .fs and .id in the FIL/DIR structure is identical */
02340 
02341 
02342     if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id)
02343         return FR_INVALID_OBJECT;
02344 
02345     ENTER_FF(fil->fs);        /* Lock file system */
02346 
02347     if (disk_status(fil->fs->drv) & STA_NOINIT)
02348         return FR_NOT_READY;
02349 
02350     return FR_OK;
02351 }
02352 
02353 
02354 
02355 
02356 /*--------------------------------------------------------------------------
02357 
02358    Public Functions
02359 
02360 --------------------------------------------------------------------------*/
02361 
02362 
02363 
02364 /*-----------------------------------------------------------------------*/
02365 /* Mount/Unmount a Logical Drive                                         */
02366 /*-----------------------------------------------------------------------*/
02367 
02368 FRESULT f_mount (
02369     FATFS* fs,            /* Pointer to the file system object (NULL:unmount)*/
02370     const TCHAR* path,    /* Logical drive number to be mounted/unmounted */
02371     BYTE opt            /* 0:Do not mount (delayed mount), 1:Mount immediately */
02372 )
02373 {
02374     FATFS *cfs;
02375     int vol;
02376     FRESULT res;
02377     const TCHAR *rp = path;
02378 
02379 
02380     vol = get_ldnumber(&rp);
02381     if (vol < 0) return FR_INVALID_DRIVE;
02382     cfs = FatFs[vol];                    /* Pointer to fs object */
02383 
02384     if (cfs) {
02385 #if _FS_LOCK
02386         clear_lock(cfs);
02387 #endif
02388 #if _FS_REENTRANT                        /* Discard sync object of the current volume */
02389         if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
02390 #endif
02391         cfs->fs_type = 0;                /* Clear old fs object */
02392     }
02393 
02394     if (fs) {
02395         fs->fs_type = 0;                /* Clear new fs object */
02396 #if _FS_REENTRANT                        /* Create sync object for the new volume */
02397         if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
02398 #endif
02399     }
02400     FatFs[vol] = fs;                    /* Register new fs object */
02401 
02402     if (!fs || opt != 1) return FR_OK;    /* Do not mount now, it will be mounted later */
02403 
02404     res = find_volume(&fs, &path, 0);    /* Force mounted the volume */
02405     LEAVE_FF(fs, res);
02406 }
02407 
02408 
02409 
02410 
02411 /*-----------------------------------------------------------------------*/
02412 /* Open or Create a File                                                 */
02413 /*-----------------------------------------------------------------------*/
02414 
02415 FRESULT f_open (
02416     FIL* fp,            /* Pointer to the blank file object */
02417     const TCHAR* path,    /* Pointer to the file name */
02418     BYTE mode            /* Access mode and file open mode flags */
02419 )
02420 {
02421     FRESULT res;
02422     DIR dj;
02423     BYTE *dir;
02424     DEF_NAMEBUF;
02425 
02426 
02427     if (!fp) return FR_INVALID_OBJECT;
02428     fp->fs = 0;            /* Clear file object */
02429 
02430     /* Get logical drive number */
02431 #if !_FS_READONLY
02432     mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
02433     res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ));
02434 #else
02435     mode &= FA_READ;
02436     res = find_volume(&dj.fs, &path, 0);
02437 #endif
02438     if (res == FR_OK) {
02439         INIT_BUF(dj);
02440         res = follow_path(&dj, path);    /* Follow the file path */
02441         dir = dj.dir;
02442 #if !_FS_READONLY    /* R/W configuration */
02443         if (res == FR_OK) {
02444             if (!dir)    /* Default directory itself */
02445                 res = FR_INVALID_NAME;
02446 #if _FS_LOCK
02447             else
02448                 res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
02449 #endif
02450         }
02451         /* Create or Open a file */
02452         if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
02453             DWORD dw, cl;
02454 
02455             if (res != FR_OK) {                    /* No file, create new */
02456                 if (res == FR_NO_FILE)            /* There is no file to open, create a new entry */
02457 #if _FS_LOCK
02458                     res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
02459 #else
02460                     res = dir_register(&dj);
02461 #endif
02462                 mode |= FA_CREATE_ALWAYS;        /* File is created */
02463                 dir = dj.dir;                    /* New entry */
02464             }
02465             else {                                /* Any object is already existing */
02466                 if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) {    /* Cannot overwrite it (R/O or DIR) */
02467                     res = FR_DENIED;
02468                 } else {
02469                     if (mode & FA_CREATE_NEW)    /* Cannot create as new file */
02470                         res = FR_EXIST;
02471                 }
02472             }
02473             if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {    /* Truncate it if overwrite mode */
02474                 dw = get_fattime();                /* Created time */
02475                 ST_DWORD(dir+DIR_CrtTime, dw);
02476                 dir[DIR_Attr] = 0;                /* Reset attribute */
02477                 ST_DWORD(dir+DIR_FileSize, 0);    /* size = 0 */
02478                 cl = ld_clust(dj.fs, dir);        /* Get start cluster */
02479                 st_clust(dir, 0);                /* cluster = 0 */
02480                 dj.fs->wflag = 1;
02481                 if (cl) {                        /* Remove the cluster chain if exist */
02482                     dw = dj.fs->winsect;
02483                     res = remove_chain(dj.fs, cl);
02484                     if (res == FR_OK) {
02485                         dj.fs->last_clust = cl - 1;    /* Reuse the cluster hole */
02486                         res = move_window(dj.fs, dw);
02487                     }
02488                 }
02489             }
02490         }
02491         else {    /* Open an existing file */
02492             if (res == FR_OK) {                    /* Follow succeeded */
02493                 if (dir[DIR_Attr] & AM_DIR) {    /* It is a directory */
02494                     res = FR_NO_FILE;
02495                 } else {
02496                     if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
02497                         res = FR_DENIED;
02498                 }
02499             }
02500         }
02501         if (res == FR_OK) {
02502             if (mode & FA_CREATE_ALWAYS)        /* Set file change flag if created or overwritten */
02503                 mode |= FA__WRITTEN;
02504             fp->dir_sect = dj.fs->winsect;        /* Pointer to the directory entry */
02505             fp->dir_ptr = dir;
02506 #if _FS_LOCK
02507             fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
02508             if (!fp->lockid) res = FR_INT_ERR;
02509 #endif
02510         }
02511 
02512 #else                /* R/O configuration */
02513         if (res == FR_OK) {                    /* Follow succeeded */
02514             dir = dj.dir;
02515             if (!dir) {                        /* Current directory itself */
02516                 res = FR_INVALID_NAME;
02517             } else {
02518                 if (dir[DIR_Attr] & AM_DIR)    /* It is a directory */
02519                     res = FR_NO_FILE;
02520             }
02521         }
02522 #endif
02523         FREE_BUF();
02524 
02525         if (res == FR_OK) {
02526             fp->flag = mode;                    /* File access mode */
02527             fp->err = 0;                        /* Clear error flag */
02528             fp->sclust = ld_clust(dj.fs, dir);    /* File start cluster */
02529             fp->fsize = LD_DWORD(dir+DIR_FileSize);    /* File size */
02530             fp->fptr = 0;                        /* File pointer */
02531             fp->dsect = 0;
02532 #if _USE_FASTSEEK
02533             fp->cltbl = 0;                        /* Normal seek mode */
02534 #endif
02535             fp->fs = dj.fs;                         /* Validate file object */
02536             fp->id = fp->fs->id;
02537         }
02538     }
02539 
02540     LEAVE_FF(dj.fs, res);
02541 }
02542 
02543 
02544 
02545 
02546 /*-----------------------------------------------------------------------*/
02547 /* Read File                                                             */
02548 /*-----------------------------------------------------------------------*/
02549 
02550 FRESULT f_read (
02551     FIL* fp,         /* Pointer to the file object */
02552     void* buff,        /* Pointer to data buffer */
02553     UINT btr,        /* Number of bytes to read */
02554     UINT* br        /* Pointer to number of bytes read */
02555 )
02556 {
02557     FRESULT res;
02558     DWORD clst, sect, remain;
02559     UINT rcnt, cc;
02560     BYTE csect, *rbuff = (BYTE*)buff;
02561 
02562 
02563     *br = 0;    /* Clear read byte counter */
02564 
02565     res = validate(fp);                            /* Check validity */
02566     if (res != FR_OK) LEAVE_FF(fp->fs, res);
02567     if (fp->err)                                /* Check error */
02568         LEAVE_FF(fp->fs, (FRESULT)fp->err);
02569     if (!(fp->flag & FA_READ))                     /* Check access mode */
02570         LEAVE_FF(fp->fs, FR_DENIED);
02571     remain = fp->fsize - fp->fptr;
02572     if (btr > remain) btr = (UINT)remain;        /* Truncate btr by remaining bytes */
02573 
02574     for ( ;  btr;                                /* Repeat until all data read */
02575         rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
02576         if ((fp->fptr % SS(fp->fs)) == 0) {        /* On the sector boundary? */
02577             csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
02578             if (!csect) {                        /* On the cluster boundary? */
02579                 if (fp->fptr == 0) {            /* On the top of the file? */
02580                     clst = fp->sclust;            /* Follow from the origin */
02581                 } else {                        /* Middle or end of the file */
02582 #if _USE_FASTSEEK
02583                     if (fp->cltbl)
02584                         clst = clmt_clust(fp, fp->fptr);    /* Get cluster# from the CLMT */
02585                     else
02586 #endif
02587                         clst = get_fat(fp->fs, fp->clust);    /* Follow cluster chain on the FAT */
02588                 }
02589                 if (clst < 2) ABORT(fp->fs, FR_INT_ERR);
02590                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
02591                 fp->clust = clst;                /* Update current cluster */
02592             }
02593             sect = clust2sect(fp->fs, fp->clust);    /* Get current sector */
02594             if (!sect) ABORT(fp->fs, FR_INT_ERR);
02595             sect += csect;
02596             cc = btr / SS(fp->fs);                /* When remaining bytes >= sector size, */
02597             if (cc) {                            /* Read maximum contiguous sectors directly */
02598                 if (csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
02599                     cc = fp->fs->csize - csect;
02600                 if (disk_read(fp->fs->drv, rbuff, sect, cc))
02601                     ABORT(fp->fs, FR_DISK_ERR);
02602 #if !_FS_READONLY && _FS_MINIMIZE <= 2            /* Replace one of the read sectors with cached data if it contains a dirty sector */
02603 #if _FS_TINY
02604                 if (fp->fs->wflag && fp->fs->winsect - sect < cc)
02605                     mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
02606 #else
02607                 if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)
02608                     mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
02609 #endif
02610 #endif
02611                 rcnt = SS(fp->fs) * cc;            /* Number of bytes transferred */
02612                 continue;
02613             }
02614 #if !_FS_TINY
02615             if (fp->dsect != sect) {            /* Load data sector if not in cache */
02616 #if !_FS_READONLY
02617                 if (fp->flag & FA__DIRTY) {        /* Write-back dirty sector cache */
02618                     if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
02619                         ABORT(fp->fs, FR_DISK_ERR);
02620                     fp->flag &= ~FA__DIRTY;
02621                 }
02622 #endif
02623                 if (disk_read(fp->fs->drv, fp->buf, sect, 1))    /* Fill sector cache */
02624                     ABORT(fp->fs, FR_DISK_ERR);
02625             }
02626 #endif
02627             fp->dsect = sect;
02628         }
02629         rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));    /* Get partial sector data from sector buffer */
02630         if (rcnt > btr) rcnt = btr;
02631 #if _FS_TINY
02632         if (move_window(fp->fs, fp->dsect))        /* Move sector window */
02633             ABORT(fp->fs, FR_DISK_ERR);
02634         mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
02635 #else
02636         mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
02637 #endif
02638     }
02639 
02640     LEAVE_FF(fp->fs, FR_OK);
02641 }
02642 
02643 
02644 
02645 
02646 #if !_FS_READONLY
02647 /*-----------------------------------------------------------------------*/
02648 /* Write File                                                            */
02649 /*-----------------------------------------------------------------------*/
02650 
02651 FRESULT f_write (
02652     FIL* fp,            /* Pointer to the file object */
02653     const void *buff,    /* Pointer to the data to be written */
02654     UINT btw,            /* Number of bytes to write */
02655     UINT* bw            /* Pointer to number of bytes written */
02656 )
02657 {
02658     FRESULT res;
02659     DWORD clst, sect;
02660     UINT wcnt, cc;
02661     const BYTE *wbuff = (const BYTE*)buff;
02662     BYTE csect;
02663 
02664 
02665     *bw = 0;    /* Clear write byte counter */
02666 
02667     res = validate(fp);                        /* Check validity */
02668     if (res != FR_OK) LEAVE_FF(fp->fs, res);
02669     if (fp->err)                            /* Check error */
02670         LEAVE_FF(fp->fs, (FRESULT)fp->err);
02671     if (!(fp->flag & FA_WRITE))                /* Check access mode */
02672         LEAVE_FF(fp->fs, FR_DENIED);
02673     if (fp->fptr + btw < fp->fptr) btw = 0;    /* File size cannot reach 4GB */
02674 
02675     for ( ;  btw;                            /* Repeat until all data written */
02676         wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
02677         if ((fp->fptr % SS(fp->fs)) == 0) {    /* On the sector boundary? */
02678             csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
02679             if (!csect) {                    /* On the cluster boundary? */
02680                 if (fp->fptr == 0) {        /* On the top of the file? */
02681                     clst = fp->sclust;        /* Follow from the origin */
02682                     if (clst == 0)            /* When no cluster is allocated, */
02683                         clst = create_chain(fp->fs, 0);    /* Create a new cluster chain */
02684                 } else {                    /* Middle or end of the file */
02685 #if _USE_FASTSEEK
02686                     if (fp->cltbl)
02687                         clst = clmt_clust(fp, fp->fptr);    /* Get cluster# from the CLMT */
02688                     else
02689 #endif
02690                         clst = create_chain(fp->fs, fp->clust);    /* Follow or stretch cluster chain on the FAT */
02691                 }
02692                 if (clst == 0) break;        /* Could not allocate a new cluster (disk full) */
02693                 if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
02694                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
02695                 fp->clust = clst;            /* Update current cluster */
02696                 if (fp->sclust == 0) fp->sclust = clst;    /* Set start cluster if the first write */
02697             }
02698 #if _FS_TINY
02699             if (fp->fs->winsect == fp->dsect && sync_window(fp->fs))    /* Write-back sector cache */
02700                 ABORT(fp->fs, FR_DISK_ERR);
02701 #else
02702             if (fp->flag & FA__DIRTY) {        /* Write-back sector cache */
02703                 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
02704                     ABORT(fp->fs, FR_DISK_ERR);
02705                 fp->flag &= ~FA__DIRTY;
02706             }
02707 #endif
02708             sect = clust2sect(fp->fs, fp->clust);    /* Get current sector */
02709             if (!sect) ABORT(fp->fs, FR_INT_ERR);
02710             sect += csect;
02711             cc = btw / SS(fp->fs);            /* When remaining bytes >= sector size, */
02712             if (cc) {                        /* Write maximum contiguous sectors directly */
02713                 if (csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
02714                     cc = fp->fs->csize - csect;
02715                 if (disk_write(fp->fs->drv, wbuff, sect, cc))
02716                     ABORT(fp->fs, FR_DISK_ERR);
02717 #if _FS_MINIMIZE <= 2
02718 #if _FS_TINY
02719                 if (fp->fs->winsect - sect < cc) {    /* Refill sector cache if it gets invalidated by the direct write */
02720                     mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
02721                     fp->fs->wflag = 0;
02722                 }
02723 #else
02724                 if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
02725                     mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
02726                     fp->flag &= ~FA__DIRTY;
02727                 }
02728 #endif
02729 #endif
02730                 wcnt = SS(fp->fs) * cc;        /* Number of bytes transferred */
02731                 continue;
02732             }
02733 #if _FS_TINY
02734             if (fp->fptr >= fp->fsize) {    /* Avoid silly cache filling at growing edge */
02735                 if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR);
02736                 fp->fs->winsect = sect;
02737             }
02738 #else
02739             if (fp->dsect != sect) {        /* Fill sector cache with file data */
02740                 if (fp->fptr < fp->fsize &&
02741                     disk_read(fp->fs->drv, fp->buf, sect, 1))
02742                         ABORT(fp->fs, FR_DISK_ERR);
02743             }
02744 #endif
02745             fp->dsect = sect;
02746         }
02747         wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */
02748         if (wcnt > btw) wcnt = btw;
02749 #if _FS_TINY
02750         if (move_window(fp->fs, fp->dsect))    /* Move sector window */
02751             ABORT(fp->fs, FR_DISK_ERR);
02752         mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
02753         fp->fs->wflag = 1;
02754 #else
02755         mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
02756         fp->flag |= FA__DIRTY;
02757 #endif
02758     }
02759 
02760     if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;    /* Update file size if needed */
02761     fp->flag |= FA__WRITTEN;                        /* Set file change flag */
02762 
02763     LEAVE_FF(fp->fs, FR_OK);
02764 }
02765 
02766 
02767 
02768 
02769 /*-----------------------------------------------------------------------*/
02770 /* Synchronize the File                                                  */
02771 /*-----------------------------------------------------------------------*/
02772 
02773 FRESULT f_sync (
02774     FIL* fp        /* Pointer to the file object */
02775 )
02776 {
02777     FRESULT res;
02778     DWORD tm;
02779     BYTE *dir;
02780 
02781 
02782     res = validate(fp);                    /* Check validity of the object */
02783     if (res == FR_OK) {
02784         if (fp->flag & FA__WRITTEN) {    /* Has the file been written? */
02785             /* Write-back dirty buffer */
02786 #if !_FS_TINY
02787             if (fp->flag & FA__DIRTY) {
02788                 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
02789                     LEAVE_FF(fp->fs, FR_DISK_ERR);
02790                 fp->flag &= ~FA__DIRTY;
02791             }
02792 #endif
02793             /* Update the directory entry */
02794             res = move_window(fp->fs, fp->dir_sect);
02795             if (res == FR_OK) {
02796                 dir = fp->dir_ptr;
02797                 dir[DIR_Attr] |= AM_ARC;                    /* Set archive bit */
02798                 ST_DWORD(dir+DIR_FileSize, fp->fsize);        /* Update file size */
02799                 st_clust(dir, fp->sclust);                    /* Update start cluster */
02800                 tm = get_fattime();                            /* Update updated time */
02801                 ST_DWORD(dir+DIR_WrtTime, tm);
02802                 ST_WORD(dir+DIR_LstAccDate, 0);
02803                 fp->flag &= ~FA__WRITTEN;
02804                 fp->fs->wflag = 1;
02805                 res = sync_fs(fp->fs);
02806             }
02807         }
02808     }
02809 
02810     LEAVE_FF(fp->fs, res);
02811 }
02812 
02813 #endif /* !_FS_READONLY */
02814 
02815 
02816 
02817 
02818 /*-----------------------------------------------------------------------*/
02819 /* Close File                                                            */
02820 /*-----------------------------------------------------------------------*/
02821 
02822 FRESULT f_close (
02823     FIL *fp        /* Pointer to the file object to be closed */
02824 )
02825 {
02826     FRESULT res;
02827 
02828 
02829 #if !_FS_READONLY
02830     res = f_sync(fp);                    /* Flush cached data */
02831     if (res == FR_OK)
02832 #endif
02833     {
02834         res = validate(fp);                /* Lock volume */
02835         if (res == FR_OK) {
02836 #if _FS_REENTRANT
02837             FATFS *fs = fp->fs;
02838 #endif
02839 #if _FS_LOCK
02840             res = dec_lock(fp->lockid);    /* Decrement file open counter */
02841             if (res == FR_OK)
02842 #endif
02843                 fp->fs = 0;                /* Invalidate file object */
02844 #if _FS_REENTRANT
02845             unlock_fs(fs, FR_OK);        /* Unlock volume */
02846 #endif
02847         }
02848     }
02849     return res;
02850 }
02851 
02852 
02853 
02854 
02855 /*-----------------------------------------------------------------------*/
02856 /* Change Current Directory or Current Drive, Get Current Directory      */
02857 /*-----------------------------------------------------------------------*/
02858 
02859 #if _FS_RPATH >= 1
02860 #if _VOLUMES >= 2
02861 FRESULT f_chdrive (
02862     const TCHAR* path        /* Drive number */
02863 )
02864 {
02865     int vol;
02866 
02867 
02868     vol = get_ldnumber(&path);
02869     if (vol < 0) return FR_INVALID_DRIVE;
02870 
02871     CurrVol = (BYTE)vol;
02872 
02873     return FR_OK;
02874 }
02875 #endif
02876 
02877 
02878 FRESULT f_chdir (
02879     const TCHAR* path    /* Pointer to the directory path */
02880 )
02881 {
02882     FRESULT res;
02883     DIR dj;
02884     DEF_NAMEBUF;
02885 
02886 
02887     /* Get logical drive number */
02888     res = find_volume(&dj.fs, &path, 0);
02889     if (res == FR_OK) {
02890         INIT_BUF(dj);
02891         res = follow_path(&dj, path);        /* Follow the path */
02892         FREE_BUF();
02893         if (res == FR_OK) {                    /* Follow completed */
02894             if (!dj.dir) {
02895                 dj.fs->cdir = dj.sclust;    /* Start directory itself */
02896             } else {
02897                 if (dj.dir[DIR_Attr] & AM_DIR)    /* Reached to the directory */
02898                     dj.fs->cdir = ld_clust(dj.fs, dj.dir);
02899                 else
02900                     res = FR_NO_PATH;        /* Reached but a file */
02901             }
02902         }
02903         if (res == FR_NO_FILE) res = FR_NO_PATH;
02904     }
02905 
02906     LEAVE_FF(dj.fs, res);
02907 }
02908 
02909 
02910 #if _FS_RPATH >= 2
02911 FRESULT f_getcwd (
02912     TCHAR* buff,    /* Pointer to the directory path */
02913     UINT len        /* Size of path */
02914 )
02915 {
02916     FRESULT res;
02917     DIR dj;
02918     UINT i, n;
02919     DWORD ccl;
02920     TCHAR *tp;
02921     FILINFO fno;
02922     DEF_NAMEBUF;
02923 
02924 
02925     *buff = 0;
02926     /* Get logical drive number */
02927     res = find_volume(&dj.fs, (const TCHAR**)&buff, 0);    /* Get current volume */
02928     if (res == FR_OK) {
02929         INIT_BUF(dj);
02930         i = len;            /* Bottom of buffer (directory stack base) */
02931         dj.sclust = dj.fs->cdir;            /* Start to follow upper directory from current directory */
02932         while ((ccl = dj.sclust) != 0) {    /* Repeat while current directory is a sub-directory */
02933             res = dir_sdi(&dj, 1);            /* Get parent directory */
02934             if (res != FR_OK) break;
02935             res = dir_read(&dj, 0);
02936             if (res != FR_OK) break;
02937             dj.sclust = ld_clust(dj.fs, dj.dir);    /* Goto parent directory */
02938             res = dir_sdi(&dj, 0);
02939             if (res != FR_OK) break;
02940             do {                            /* Find the entry links to the child directory */
02941                 res = dir_read(&dj, 0);
02942                 if (res != FR_OK) break;
02943                 if (ccl == ld_clust(dj.fs, dj.dir)) break;    /* Found the entry */
02944                 res = dir_next(&dj, 0);    
02945             } while (res == FR_OK);
02946             if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
02947             if (res != FR_OK) break;
02948 #if _USE_LFN
02949             fno.lfname = buff;
02950             fno.lfsize = i;
02951 #endif
02952             get_fileinfo(&dj, &fno);        /* Get the directory name and push it to the buffer */
02953             tp = fno.fname;
02954 #if _USE_LFN
02955             if (*buff) tp = buff;
02956 #endif
02957             for (n = 0; tp[n]; n++) ;
02958             if (i < n + 3) {
02959                 res = FR_NOT_ENOUGH_CORE; break;
02960             }
02961             while (n) buff[--i] = tp[--n];
02962             buff[--i] = '/';
02963         }
02964         tp = buff;
02965         if (res == FR_OK) {
02966 #if _VOLUMES >= 2
02967             *tp++ = '0' + CurrVol;            /* Put drive number */
02968             *tp++ = ':';
02969 #endif
02970             if (i == len) {                    /* Root-directory */
02971                 *tp++ = '/';
02972             } else {                        /* Sub-directory */
02973                 do        /* Add stacked path str */
02974                     *tp++ = buff[i++];
02975                 while (i < len);
02976             }
02977         }
02978         *tp = 0;
02979         FREE_BUF();
02980     }
02981 
02982     LEAVE_FF(dj.fs, res);
02983 }
02984 #endif /* _FS_RPATH >= 2 */
02985 #endif /* _FS_RPATH >= 1 */
02986 
02987 
02988 
02989 #if _FS_MINIMIZE <= 2
02990 /*-----------------------------------------------------------------------*/
02991 /* Seek File R/W Pointer                                                 */
02992 /*-----------------------------------------------------------------------*/
02993 
02994 FRESULT f_lseek (
02995     FIL* fp,        /* Pointer to the file object */
02996     DWORD ofs        /* File pointer from top of file */
02997 )
02998 {
02999     FRESULT res;
03000 
03001 
03002     res = validate(fp);                    /* Check validity of the object */
03003     if (res != FR_OK) LEAVE_FF(fp->fs, res);
03004     if (fp->err)                        /* Check error */
03005         LEAVE_FF(fp->fs, (FRESULT)fp->err);
03006 
03007 #if _USE_FASTSEEK
03008     if (fp->cltbl) {    /* Fast seek */
03009         DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
03010 
03011         if (ofs == CREATE_LINKMAP) {    /* Create CLMT */
03012             tbl = fp->cltbl;
03013             tlen = *tbl++; ulen = 2;    /* Given table size and required table size */
03014             cl = fp->sclust;            /* Top of the chain */
03015             if (cl) {
03016                 do {
03017                     /* Get a fragment */
03018                     tcl = cl; ncl = 0; ulen += 2;    /* Top, length and used items */
03019                     do {
03020                         pcl = cl; ncl++;
03021                         cl = get_fat(fp->fs, cl);
03022                         if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);
03023                         if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
03024                     } while (cl == pcl + 1);
03025                     if (ulen <= tlen) {        /* Store the length and top of the fragment */
03026                         *tbl++ = ncl; *tbl++ = tcl;
03027                     }
03028                 } while (cl < fp->fs->n_fatent);    /* Repeat until end of chain */
03029             }
03030             *fp->cltbl = ulen;    /* Number of items used */
03031             if (ulen <= tlen)
03032                 *tbl = 0;        /* Terminate table */
03033             else
03034                 res = FR_NOT_ENOUGH_CORE;    /* Given table size is smaller than required */
03035 
03036         } else {                        /* Fast seek */
03037             if (ofs > fp->fsize)        /* Clip offset at the file size */
03038                 ofs = fp->fsize;
03039             fp->fptr = ofs;                /* Set file pointer */
03040             if (ofs) {
03041                 fp->clust = clmt_clust(fp, ofs - 1);
03042                 dsc = clust2sect(fp->fs, fp->clust);
03043                 if (!dsc) ABORT(fp->fs, FR_INT_ERR);
03044                 dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);
03045                 if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) {    /* Refill sector cache if needed */
03046 #if !_FS_TINY
03047 #if !_FS_READONLY
03048                     if (fp->flag & FA__DIRTY) {        /* Write-back dirty sector cache */
03049                         if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
03050                             ABORT(fp->fs, FR_DISK_ERR);
03051                         fp->flag &= ~FA__DIRTY;
03052                     }
03053 #endif
03054                     if (disk_read(fp->fs->drv, fp->buf, dsc, 1))    /* Load current sector */
03055                         ABORT(fp->fs, FR_DISK_ERR);
03056 #endif
03057                     fp->dsect = dsc;
03058                 }
03059             }
03060         }
03061     } else
03062 #endif
03063 
03064     /* Normal Seek */
03065     {
03066         DWORD clst, bcs, nsect, ifptr;
03067 
03068         if (ofs > fp->fsize                    /* In read-only mode, clip offset with the file size */
03069 #if !_FS_READONLY
03070              && !(fp->flag & FA_WRITE)
03071 #endif
03072             ) ofs = fp->fsize;
03073 
03074         ifptr = fp->fptr;
03075         fp->fptr = nsect = 0;
03076         if (ofs) {
03077             bcs = (DWORD)fp->fs->csize * SS(fp->fs);    /* Cluster size (byte) */
03078             if (ifptr > 0 &&
03079                 (ofs - 1) / bcs >= (ifptr - 1) / bcs) {    /* When seek to same or following cluster, */
03080                 fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
03081                 ofs -= fp->fptr;
03082                 clst = fp->clust;
03083             } else {                                    /* When seek to back cluster, */
03084                 clst = fp->sclust;                        /* start from the first cluster */
03085 #if !_FS_READONLY
03086                 if (clst == 0) {                        /* If no cluster chain, create a new chain */
03087                     clst = create_chain(fp->fs, 0);
03088                     if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
03089                     if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
03090                     fp->sclust = clst;
03091                 }
03092 #endif
03093                 fp->clust = clst;
03094             }
03095             if (clst != 0) {
03096                 while (ofs > bcs) {                        /* Cluster following loop */
03097 #if !_FS_READONLY
03098                     if (fp->flag & FA_WRITE) {            /* Check if in write mode or not */
03099                         clst = create_chain(fp->fs, clst);    /* Force stretch if in write mode */
03100                         if (clst == 0) {                /* When disk gets full, clip file size */
03101                             ofs = bcs; break;
03102                         }
03103                     } else
03104 #endif
03105                         clst = get_fat(fp->fs, clst);    /* Follow cluster chain if not in write mode */
03106                     if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
03107                     if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);
03108                     fp->clust = clst;
03109                     fp->fptr += bcs;
03110                     ofs -= bcs;
03111                 }
03112                 fp->fptr += ofs;
03113                 if (ofs % SS(fp->fs)) {
03114                     nsect = clust2sect(fp->fs, clst);    /* Current sector */
03115                     if (!nsect) ABORT(fp->fs, FR_INT_ERR);
03116                     nsect += ofs / SS(fp->fs);
03117                 }
03118             }
03119         }
03120         if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {    /* Fill sector cache if needed */
03121 #if !_FS_TINY
03122 #if !_FS_READONLY
03123             if (fp->flag & FA__DIRTY) {            /* Write-back dirty sector cache */
03124                 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
03125                     ABORT(fp->fs, FR_DISK_ERR);
03126                 fp->flag &= ~FA__DIRTY;
03127             }
03128 #endif
03129             if (disk_read(fp->fs->drv, fp->buf, nsect, 1))    /* Fill sector cache */
03130                 ABORT(fp->fs, FR_DISK_ERR);
03131 #endif
03132             fp->dsect = nsect;
03133         }
03134 #if !_FS_READONLY
03135         if (fp->fptr > fp->fsize) {            /* Set file change flag if the file size is extended */
03136             fp->fsize = fp->fptr;
03137             fp->flag |= FA__WRITTEN;
03138         }
03139 #endif
03140     }
03141 
03142     LEAVE_FF(fp->fs, res);
03143 }
03144 
03145 
03146 
03147 #if _FS_MINIMIZE <= 1
03148 /*-----------------------------------------------------------------------*/
03149 /* Create a Directory Object                                             */
03150 /*-----------------------------------------------------------------------*/
03151 
03152 FRESULT f_opendir (
03153     DIR* dp,            /* Pointer to directory object to create */
03154     const TCHAR* path    /* Pointer to the directory path */
03155 )
03156 {
03157     FRESULT res;
03158     FATFS* fs;
03159     DEF_NAMEBUF;
03160 
03161 
03162     if (!dp) return FR_INVALID_OBJECT;
03163 
03164     /* Get logical drive number */
03165     res = find_volume(&fs, &path, 0);
03166     if (res == FR_OK) {
03167         dp->fs = fs;
03168         INIT_BUF(*dp);
03169         res = follow_path(dp, path);            /* Follow the path to the directory */
03170         FREE_BUF();
03171         if (res == FR_OK) {                        /* Follow completed */
03172             if (dp->dir) {                        /* It is not the origin directory itself */
03173                 if (dp->dir[DIR_Attr] & AM_DIR)    /* The object is a sub directory */
03174                     dp->sclust = ld_clust(fs, dp->dir);
03175                 else                            /* The object is a file */
03176                     res = FR_NO_PATH;
03177             }
03178             if (res == FR_OK) {
03179                 dp->id = fs->id;
03180                 res = dir_sdi(dp, 0);            /* Rewind directory */
03181 #if _FS_LOCK
03182                 if (res == FR_OK) {
03183                     if (dp->sclust) {
03184                         dp->lockid = inc_lock(dp, 0);    /* Lock the sub directory */
03185                         if (!dp->lockid)
03186                             res = FR_TOO_MANY_OPEN_FILES;
03187                     } else {
03188                         dp->lockid = 0;    /* Root directory need not to be locked */
03189                     }
03190                 }
03191 #endif
03192             }
03193         }
03194         if (res == FR_NO_FILE) res = FR_NO_PATH;
03195     }
03196     if (res != FR_OK) dp->fs = 0;        /* Invalidate the directory object if function faild */
03197 
03198     LEAVE_FF(fs, res);
03199 }
03200 
03201 
03202 
03203 
03204 /*-----------------------------------------------------------------------*/
03205 /* Close Directory                                                       */
03206 /*-----------------------------------------------------------------------*/
03207 
03208 FRESULT f_closedir (
03209     DIR *dp        /* Pointer to the directory object to be closed */
03210 )
03211 {
03212     FRESULT res;
03213 
03214 
03215     res = validate(dp);
03216     if (res == FR_OK) {
03217 #if _FS_REENTRANT
03218         FATFS *fs = dp->fs;
03219 #endif
03220 #if _FS_LOCK
03221         if (dp->lockid)                /* Decrement sub-directory open counter */
03222             res = dec_lock(dp->lockid);
03223         if (res == FR_OK)
03224 #endif
03225             dp->fs = 0;                /* Invalidate directory object */
03226 #if _FS_REENTRANT
03227         unlock_fs(fs, FR_OK);        /* Unlock volume */
03228 #endif
03229     }
03230     return res;
03231 }
03232 
03233 
03234 
03235 
03236 /*-----------------------------------------------------------------------*/
03237 /* Read Directory Entries in Sequence                                    */
03238 /*-----------------------------------------------------------------------*/
03239 
03240 FRESULT f_readdir (
03241     DIR* dp,            /* Pointer to the open directory object */
03242     FILINFO* fno        /* Pointer to file information to return */
03243 )
03244 {
03245     FRESULT res;
03246     DEF_NAMEBUF;
03247 
03248 
03249     res = validate(dp);                        /* Check validity of the object */
03250     if (res == FR_OK) {
03251         if (!fno) {
03252             res = dir_sdi(dp, 0);            /* Rewind the directory object */
03253         } else {
03254             INIT_BUF(*dp);
03255             res = dir_read(dp, 0);            /* Read an item */
03256             if (res == FR_NO_FILE) {        /* Reached end of directory */
03257                 dp->sect = 0;
03258                 res = FR_OK;
03259             }
03260             if (res == FR_OK) {                /* A valid entry is found */
03261                 get_fileinfo(dp, fno);        /* Get the object information */
03262                 res = dir_next(dp, 0);        /* Increment index for next */
03263                 if (res == FR_NO_FILE) {
03264                     dp->sect = 0;
03265                     res = FR_OK;
03266                 }
03267             }
03268             FREE_BUF();
03269         }
03270     }
03271 
03272     LEAVE_FF(dp->fs, res);
03273 }
03274 
03275 
03276 
03277 #if _FS_MINIMIZE == 0
03278 /*-----------------------------------------------------------------------*/
03279 /* Get File Status                                                       */
03280 /*-----------------------------------------------------------------------*/
03281 
03282 FRESULT f_stat (
03283     const TCHAR* path,    /* Pointer to the file path */
03284     FILINFO* fno        /* Pointer to file information to return */
03285 )
03286 {
03287     FRESULT res;
03288     DIR dj;
03289     DEF_NAMEBUF;
03290 
03291 
03292     /* Get logical drive number */
03293     res = find_volume(&dj.fs, &path, 0);
03294     if (res == FR_OK) {
03295         INIT_BUF(dj);
03296         res = follow_path(&dj, path);    /* Follow the file path */
03297         if (res == FR_OK) {                /* Follow completed */
03298             if (dj.dir) {        /* Found an object */
03299                 if (fno) get_fileinfo(&dj, fno);
03300             } else {            /* It is root directory */
03301                 res = FR_INVALID_NAME;
03302             }
03303         }
03304         FREE_BUF();
03305     }
03306 
03307     LEAVE_FF(dj.fs, res);
03308 }
03309 
03310 
03311 
03312 #if !_FS_READONLY
03313 /*-----------------------------------------------------------------------*/
03314 /* Get Number of Free Clusters                                           */
03315 /*-----------------------------------------------------------------------*/
03316 
03317 FRESULT f_getfree (
03318     const TCHAR* path,    /* Path name of the logical drive number */
03319     DWORD* nclst,        /* Pointer to a variable to return number of free clusters */
03320     FATFS** fatfs        /* Pointer to return pointer to corresponding file system object */
03321 )
03322 {
03323     FRESULT res;
03324     FATFS *fs;
03325     DWORD n, clst, sect, stat;
03326     UINT i;
03327     BYTE fat, *p;
03328 
03329 
03330     /* Get logical drive number */
03331     res = find_volume(fatfs, &path, 0);
03332     fs = *fatfs;
03333     if (res == FR_OK) {
03334         /* If free_clust is valid, return it without full cluster scan */
03335         if (fs->free_clust <= fs->n_fatent - 2) {
03336             *nclst = fs->free_clust;
03337         } else {
03338             /* Get number of free clusters */
03339             fat = fs->fs_type;
03340             n = 0;
03341             if (fat == FS_FAT12) {
03342                 clst = 2;
03343                 do {
03344                     stat = get_fat(fs, clst);
03345                     if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
03346                     if (stat == 1) { res = FR_INT_ERR; break; }
03347                     if (stat == 0) n++;
03348                 } while (++clst < fs->n_fatent);
03349             } else {
03350                 clst = fs->n_fatent;
03351                 sect = fs->fatbase;
03352                 i = 0; p = 0;
03353                 do {
03354                     if (!i) {
03355                         res = move_window(fs, sect++);
03356                         if (res != FR_OK) break;
03357                         p = fs->win;
03358                         i = SS(fs);
03359                     }
03360                     if (fat == FS_FAT16) {
03361                         if (LD_WORD(p) == 0) n++;
03362                         p += 2; i -= 2;
03363                     } else {
03364                         if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
03365                         p += 4; i -= 4;
03366                     }
03367                 } while (--clst);
03368             }
03369             fs->free_clust = n;
03370             fs->fsi_flag |= 1;
03371             *nclst = n;
03372         }
03373     }
03374     LEAVE_FF(fs, res);
03375 }
03376 
03377 
03378 
03379 
03380 /*-----------------------------------------------------------------------*/
03381 /* Truncate File                                                         */
03382 /*-----------------------------------------------------------------------*/
03383 
03384 FRESULT f_truncate (
03385     FIL* fp        /* Pointer to the file object */
03386 )
03387 {
03388     FRESULT res;
03389     DWORD ncl;
03390 
03391 
03392     res = validate(fp);                        /* Check validity of the object */
03393     if (res == FR_OK) {
03394         if (fp->err) {                        /* Check error */
03395             res = (FRESULT)fp->err;
03396         } else {
03397             if (!(fp->flag & FA_WRITE))        /* Check access mode */
03398                 res = FR_DENIED;
03399         }
03400     }
03401     if (res == FR_OK) {
03402         if (fp->fsize > fp->fptr) {
03403             fp->fsize = fp->fptr;    /* Set file size to current R/W point */
03404             fp->flag |= FA__WRITTEN;
03405             if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
03406                 res = remove_chain(fp->fs, fp->sclust);
03407                 fp->sclust = 0;
03408             } else {                /* When truncate a part of the file, remove remaining clusters */
03409                 ncl = get_fat(fp->fs, fp->clust);
03410                 res = FR_OK;
03411                 if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
03412                 if (ncl == 1) res = FR_INT_ERR;
03413                 if (res == FR_OK && ncl < fp->fs->n_fatent) {
03414                     res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF);
03415                     if (res == FR_OK) res = remove_chain(fp->fs, ncl);
03416                 }
03417             }
03418 #if !_FS_TINY
03419             if (res == FR_OK && (fp->flag & FA__DIRTY)) {
03420                 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
03421                     res = FR_DISK_ERR;
03422                 else
03423                     fp->flag &= ~FA__DIRTY;
03424             }
03425 #endif
03426         }
03427         if (res != FR_OK) fp->err = (FRESULT)res;
03428     }
03429 
03430     LEAVE_FF(fp->fs, res);
03431 }
03432 
03433 
03434 
03435 
03436 /*-----------------------------------------------------------------------*/
03437 /* Delete a File or Directory                                            */
03438 /*-----------------------------------------------------------------------*/
03439 
03440 FRESULT f_unlink (
03441     const TCHAR* path        /* Pointer to the file or directory path */
03442 )
03443 {
03444     FRESULT res;
03445     DIR dj, sdj;
03446     BYTE *dir;
03447     DWORD dclst;
03448     DEF_NAMEBUF;
03449 
03450 
03451     /* Get logical drive number */
03452     res = find_volume(&dj.fs, &path, 1);
03453     if (res == FR_OK) {
03454         INIT_BUF(dj);
03455         res = follow_path(&dj, path);        /* Follow the file path */
03456         if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
03457             res = FR_INVALID_NAME;            /* Cannot remove dot entry */
03458 #if _FS_LOCK
03459         if (res == FR_OK) res = chk_lock(&dj, 2);    /* Cannot remove open file */
03460 #endif
03461         if (res == FR_OK) {                    /* The object is accessible */
03462             dir = dj.dir;
03463             if (!dir) {
03464                 res = FR_INVALID_NAME;        /* Cannot remove the start directory */
03465             } else {
03466                 if (dir[DIR_Attr] & AM_RDO)
03467                     res = FR_DENIED;        /* Cannot remove R/O object */
03468             }
03469             dclst = ld_clust(dj.fs, dir);
03470             if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) {    /* Is it a sub-dir? */
03471                 if (dclst < 2) {
03472                     res = FR_INT_ERR;
03473                 } else {
03474                     mem_cpy(&sdj, &dj, sizeof (DIR));    /* Check if the sub-directory is empty or not */
03475                     sdj.sclust = dclst;
03476                     res = dir_sdi(&sdj, 2);        /* Exclude dot entries */
03477                     if (res == FR_OK) {
03478                         res = dir_read(&sdj, 0);    /* Read an item */
03479                         if (res == FR_OK        /* Not empty directory */
03480 #if _FS_RPATH
03481                         || dclst == dj.fs->cdir    /* Current directory */
03482 #endif
03483                         ) res = FR_DENIED;
03484                         if (res == FR_NO_FILE) res = FR_OK;    /* Empty */
03485                     }
03486                 }
03487             }
03488             if (res == FR_OK) {
03489                 res = dir_remove(&dj);        /* Remove the directory entry */
03490                 if (res == FR_OK) {
03491                     if (dclst)                /* Remove the cluster chain if exist */
03492                         res = remove_chain(dj.fs, dclst);
03493                     if (res == FR_OK) res = sync_fs(dj.fs);
03494                 }
03495             }
03496         }
03497         FREE_BUF();
03498     }
03499 
03500     LEAVE_FF(dj.fs, res);
03501 }
03502 
03503 
03504 
03505 
03506 /*-----------------------------------------------------------------------*/
03507 /* Create a Directory                                                    */
03508 /*-----------------------------------------------------------------------*/
03509 
03510 FRESULT f_mkdir (
03511     const TCHAR* path        /* Pointer to the directory path */
03512 )
03513 {
03514     FRESULT res;
03515     DIR dj;
03516     BYTE *dir, n;
03517     DWORD dsc, dcl, pcl, tm = get_fattime();
03518     DEF_NAMEBUF;
03519 
03520 
03521     /* Get logical drive number */
03522     res = find_volume(&dj.fs, &path, 1);
03523     if (res == FR_OK) {
03524         INIT_BUF(dj);
03525         res = follow_path(&dj, path);            /* Follow the file path */
03526         if (res == FR_OK) res = FR_EXIST;        /* Any object with same name is already existing */
03527         if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
03528             res = FR_INVALID_NAME;
03529         if (res == FR_NO_FILE) {                /* Can create a new directory */
03530             dcl = create_chain(dj.fs, 0);        /* Allocate a cluster for the new directory table */
03531             res = FR_OK;
03532             if (dcl == 0) res = FR_DENIED;        /* No space to allocate a new cluster */
03533             if (dcl == 1) res = FR_INT_ERR;
03534             if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
03535             if (res == FR_OK)                    /* Flush FAT */
03536                 res = sync_window(dj.fs);
03537             if (res == FR_OK) {                    /* Initialize the new directory table */
03538                 dsc = clust2sect(dj.fs, dcl);
03539                 dir = dj.fs->win;
03540                 mem_set(dir, 0, SS(dj.fs));
03541                 mem_set(dir+DIR_Name, ' ', 11);    /* Create "." entry */
03542                 dir[DIR_Name] = '.';
03543                 dir[DIR_Attr] = AM_DIR;
03544                 ST_DWORD(dir+DIR_WrtTime, tm);
03545                 st_clust(dir, dcl);
03546                 mem_cpy(dir+SZ_DIR, dir, SZ_DIR);      /* Create ".." entry */
03547                 dir[SZ_DIR+1] = '.'; pcl = dj.sclust;
03548                 if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)
03549                     pcl = 0;
03550                 st_clust(dir+SZ_DIR, pcl);
03551                 for (n = dj.fs->csize; n; n--) {       /* Write dot entries and clear following sectors */
03552                     dj.fs->winsect = dsc++;
03553                     dj.fs->wflag = 1;
03554                     res = sync_window(dj.fs);
03555                     if (res != FR_OK) break;
03556                     mem_set(dir, 0, SS(dj.fs));
03557                 }
03558             }
03559             if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directory */
03560             if (res != FR_OK) {
03561                 remove_chain(dj.fs, dcl);              /* Could not register, remove cluster chain */
03562             } else {
03563                 dir = dj.dir;
03564                 dir[DIR_Attr] = AM_DIR;                /* Attribute */
03565                 ST_DWORD(dir+DIR_WrtTime, tm);         /* Created time */
03566                 st_clust(dir, dcl);                    /* Table start cluster */
03567                 dj.fs->wflag = 1;
03568                 res = sync_fs(dj.fs);
03569             }
03570         }
03571         FREE_BUF();
03572     }
03573 
03574     LEAVE_FF(dj.fs, res);
03575 }
03576 
03577 
03578 
03579 
03580 /*-----------------------------------------------------------------------*/
03581 /* Change Attribute                                                      */
03582 /*-----------------------------------------------------------------------*/
03583 
03584 FRESULT f_chmod (
03585     const TCHAR* path,    /* Pointer to the file path */
03586     BYTE value,            /* Attribute bits */
03587     BYTE mask            /* Attribute mask to change */
03588 )
03589 {
03590     FRESULT res;
03591     DIR dj;
03592     BYTE *dir;
03593     DEF_NAMEBUF;
03594 
03595 
03596     /* Get logical drive number */
03597     res = find_volume(&dj.fs, &path, 1);
03598     if (res == FR_OK) {
03599         INIT_BUF(dj);
03600         res = follow_path(&dj, path);        /* Follow the file path */
03601         FREE_BUF();
03602         if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
03603             res = FR_INVALID_NAME;
03604         if (res == FR_OK) {
03605             dir = dj.dir;
03606             if (!dir) {                        /* Is it a root directory? */
03607                 res = FR_INVALID_NAME;
03608             } else {                           /* File or sub directory */
03609                 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
03610                 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask);    /* Apply attribute change */
03611                 dj.fs->wflag = 1;
03612                 res = sync_fs(dj.fs);
03613             }
03614         }
03615     }
03616 
03617     LEAVE_FF(dj.fs, res);
03618 }
03619 
03620 
03621 
03622 
03623 /*-----------------------------------------------------------------------*/
03624 /* Change Timestamp                                                      */
03625 /*-----------------------------------------------------------------------*/
03626 
03627 FRESULT f_utime (
03628     const TCHAR* path,    /* Pointer to the file/directory name */
03629     const FILINFO* fno    /* Pointer to the time stamp to be set */
03630 )
03631 {
03632     FRESULT res;
03633     DIR dj;
03634     BYTE *dir;
03635     DEF_NAMEBUF;
03636 
03637 
03638     /* Get logical drive number */
03639     res = find_volume(&dj.fs, &path, 1);
03640     if (res == FR_OK) {
03641         INIT_BUF(dj);
03642         res = follow_path(&dj, path);    /* Follow the file path */
03643         FREE_BUF();
03644         if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
03645             res = FR_INVALID_NAME;
03646         if (res == FR_OK) {
03647             dir = dj.dir;
03648             if (!dir) {                    /* Root directory */
03649                 res = FR_INVALID_NAME;
03650             } else {                    /* File or sub-directory */
03651                 ST_WORD(dir+DIR_WrtTime, fno->ftime);
03652                 ST_WORD(dir+DIR_WrtDate, fno->fdate);
03653                 dj.fs->wflag = 1;
03654                 res = sync_fs(dj.fs);
03655             }
03656         }
03657     }
03658 
03659     LEAVE_FF(dj.fs, res);
03660 }
03661 
03662 
03663 
03664 
03665 /*-----------------------------------------------------------------------*/
03666 /* Rename File/Directory                                                 */
03667 /*-----------------------------------------------------------------------*/
03668 
03669 FRESULT f_rename (
03670     const TCHAR* path_old,    /* Pointer to the object to be renamed */
03671     const TCHAR* path_new    /* Pointer to the new name */
03672 )
03673 {
03674     FRESULT res;
03675     DIR djo, djn;
03676     BYTE buf[21], *dir;
03677     DWORD dw;
03678     DEF_NAMEBUF;
03679 
03680 
03681     /* Get logical drive number of the source object */
03682     res = find_volume(&djo.fs, &path_old, 1);
03683     if (res == FR_OK) {
03684         djn.fs = djo.fs;
03685         INIT_BUF(djo);
03686         res = follow_path(&djo, path_old);        /* Check old object */
03687         if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT))
03688             res = FR_INVALID_NAME;
03689 #if _FS_LOCK
03690         if (res == FR_OK) res = chk_lock(&djo, 2);
03691 #endif
03692         if (res == FR_OK) {                        /* Old object is found */
03693             if (!djo.dir) {                        /* Is root dir? */
03694                 res = FR_NO_FILE;
03695             } else {
03696                 mem_cpy(buf, djo.dir+DIR_Attr, 21);        /* Save the object information except name */
03697                 mem_cpy(&djn, &djo, sizeof (DIR));        /* Duplicate the directory object */
03698                 if (get_ldnumber(&path_new) >= 0)        /* Snip drive number off and ignore it */
03699                     res = follow_path(&djn, path_new);    /* and check if new object is exist */
03700                 else
03701                     res = FR_INVALID_DRIVE;
03702                 if (res == FR_OK) res = FR_EXIST;        /* The new object name is already existing */
03703                 if (res == FR_NO_FILE) {                 /* Is it a valid path and no name collision? */
03704 /* Start critical section that any interruption can cause a cross-link */
03705                     res = dir_register(&djn);            /* Register the new entry */
03706                     if (res == FR_OK) {
03707                         dir = djn.dir;                    /* Copy object information except name */
03708                         mem_cpy(dir+13, buf+2, 19);
03709                         dir[DIR_Attr] = buf[0] | AM_ARC;
03710                         djo.fs->wflag = 1;
03711                         if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) {        /* Update .. entry in the directory if needed */
03712                             dw = clust2sect(djo.fs, ld_clust(djo.fs, dir));
03713                             if (!dw) {
03714                                 res = FR_INT_ERR;
03715                             } else {
03716                                 res = move_window(djo.fs, dw);
03717                                 dir = djo.fs->win+SZ_DIR;    /* .. entry */
03718                                 if (res == FR_OK && dir[1] == '.') {
03719                                     dw = (djo.fs->fs_type == FS_FAT32 && djn.sclust == djo.fs->dirbase) ? 0 : djn.sclust;
03720                                     st_clust(dir, dw);
03721                                     djo.fs->wflag = 1;
03722                                 }
03723                             }
03724                         }
03725                         if (res == FR_OK) {
03726                             res = dir_remove(&djo);        /* Remove old entry */
03727                             if (res == FR_OK)
03728                                 res = sync_fs(djo.fs);
03729                         }
03730                     }
03731 /* End critical section */
03732                 }
03733             }
03734         }
03735         FREE_BUF();
03736     }
03737 
03738     LEAVE_FF(djo.fs, res);
03739 }
03740 
03741 #endif /* !_FS_READONLY */
03742 #endif /* _FS_MINIMIZE == 0 */
03743 #endif /* _FS_MINIMIZE <= 1 */
03744 #endif /* _FS_MINIMIZE <= 2 */
03745 
03746 
03747 
03748 #if _USE_LABEL
03749 /*-----------------------------------------------------------------------*/
03750 /* Get volume label                                                      */
03751 /*-----------------------------------------------------------------------*/
03752 
03753 FRESULT f_getlabel (
03754     const TCHAR* path,    /* Path name of the logical drive number */
03755     TCHAR* label,        /* Pointer to a buffer to return the volume label */
03756     DWORD* vsn            /* Pointer to a variable to return the volume serial number */
03757 )
03758 {
03759     FRESULT res;
03760     DIR dj;
03761     UINT i, j;
03762 
03763 
03764     /* Get logical drive number */
03765     res = find_volume(&dj.fs, &path, 0);
03766 
03767     /* Get volume label */
03768     if (res == FR_OK && label) {
03769         dj.sclust = 0;                    /* Open root directory */
03770         res = dir_sdi(&dj, 0);
03771         if (res == FR_OK) {
03772             res = dir_read(&dj, 1);        /* Get an entry with AM_VOL */
03773             if (res == FR_OK) {            /* A volume label is exist */
03774 #if _USE_LFN && _LFN_UNICODE
03775                 WCHAR w;
03776                 i = j = 0;
03777                 do {
03778                     w = (i < 11) ? dj.dir[i++] : ' ';
03779                     if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i]))
03780                         w = w << 8 | dj.dir[i++];
03781                     label[j++] = ff_convert(w, 1);    /* OEM -> Unicode */
03782                 } while (j < 11);
03783 #else
03784                 mem_cpy(label, dj.dir, 11);
03785 #endif
03786                 j = 11;
03787                 do {
03788                     label[j] = 0;
03789                     if (!j) break;
03790                 } while (label[--j] == ' ');
03791             }
03792             if (res == FR_NO_FILE) {    /* No label, return nul string */
03793                 label[0] = 0;
03794                 res = FR_OK;
03795             }
03796         }
03797     }
03798 
03799     /* Get volume serial number */
03800     if (res == FR_OK && vsn) {
03801         res = move_window(dj.fs, dj.fs->volbase);
03802         if (res == FR_OK) {
03803             i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID;
03804             *vsn = LD_DWORD(&dj.fs->win[i]);
03805         }
03806     }
03807 
03808     LEAVE_FF(dj.fs, res);
03809 }
03810 
03811 
03812 
03813 #if !_FS_READONLY
03814 /*-----------------------------------------------------------------------*/
03815 /* Set volume label                                                      */
03816 /*-----------------------------------------------------------------------*/
03817 
03818 FRESULT f_setlabel (
03819     const TCHAR* label    /* Pointer to the volume label to set */
03820 )
03821 {
03822     FRESULT res;
03823     DIR dj;
03824     BYTE vn[11];
03825     UINT i, j, sl;
03826     WCHAR w;
03827     DWORD tm;
03828 
03829 
03830     /* Get logical drive number */
03831     res = find_volume(&dj.fs, &label, 1);
03832     if (res) LEAVE_FF(dj.fs, res);
03833 
03834     /* Create a volume label in directory form */
03835     vn[0] = 0;
03836     for (sl = 0; label[sl]; sl++) ;                /* Get name length */
03837     for ( ; sl && label[sl-1] == ' '; sl--) ;    /* Remove trailing spaces */
03838     if (sl) {    /* Create volume label in directory form */
03839         i = j = 0;
03840         do {
03841 #if _USE_LFN && _LFN_UNICODE
03842             w = ff_convert(ff_wtoupper(label[i++]), 0);
03843 #else
03844             w = (BYTE)label[i++];
03845             if (IsDBCS1(w))
03846                 w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;
03847 #if _USE_LFN
03848             w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0);
03849 #else
03850             if (IsLower(w)) w -= 0x20;            /* To upper ASCII characters */
03851 #ifdef _EXCVT
03852             if (w >= 0x80) w = ExCvt[w - 0x80];    /* To upper extended characters (SBCS cfg) */
03853 #else
03854             if (!_DF1S && w >= 0x80) w = 0;        /* Reject extended characters (ASCII cfg) */
03855 #endif
03856 #endif
03857 #endif
03858             if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */
03859                 LEAVE_FF(dj.fs, FR_INVALID_NAME);
03860             if (w >= 0x100) vn[j++] = (BYTE)(w >> 8);
03861             vn[j++] = (BYTE)w;
03862         } while (i < sl);
03863         while (j < 11) vn[j++] = ' ';
03864     }
03865 
03866     /* Set volume label */
03867     dj.sclust = 0;                    /* Open root directory */
03868     res = dir_sdi(&dj, 0);
03869     if (res == FR_OK) {
03870         res = dir_read(&dj, 1);        /* Get an entry with AM_VOL */
03871         if (res == FR_OK) {            /* A volume label is found */
03872             if (vn[0]) {
03873                 mem_cpy(dj.dir, vn, 11);    /* Change the volume label name */
03874                 tm = get_fattime();
03875                 ST_DWORD(dj.dir+DIR_WrtTime, tm);
03876             } else {
03877                 dj.dir[0] = DDE;            /* Remove the volume label */
03878             }
03879             dj.fs->wflag = 1;
03880             res = sync_fs(dj.fs);
03881         } else {                    /* No volume label is found or error */
03882             if (res == FR_NO_FILE) {
03883                 res = FR_OK;
03884                 if (vn[0]) {                /* Create volume label as new */
03885                     res = dir_alloc(&dj, 1);    /* Allocate an entry for volume label */
03886                     if (res == FR_OK) {
03887                         mem_set(dj.dir, 0, SZ_DIR);    /* Set volume label */
03888                         mem_cpy(dj.dir, vn, 11);
03889                         dj.dir[DIR_Attr] = AM_VOL;
03890                         tm = get_fattime();
03891                         ST_DWORD(dj.dir+DIR_WrtTime, tm);
03892                         dj.fs->wflag = 1;
03893                         res = sync_fs(dj.fs);
03894                     }
03895                 }
03896             }
03897         }
03898     }
03899 
03900     LEAVE_FF(dj.fs, res);
03901 }
03902 
03903 #endif /* !_FS_READONLY */
03904 #endif /* _USE_LABEL */
03905 
03906 
03907 
03908 /*-----------------------------------------------------------------------*/
03909 /* Forward data to the stream directly (available on only tiny cfg)      */
03910 /*-----------------------------------------------------------------------*/
03911 #if _USE_FORWARD && _FS_TINY
03912 
03913 FRESULT f_forward (
03914     FIL* fp,                         /* Pointer to the file object */
03915     UINT (*func)(const BYTE*,UINT),    /* Pointer to the streaming function */
03916     UINT btf,                        /* Number of bytes to forward */
03917     UINT* bf                        /* Pointer to number of bytes forwarded */
03918 )
03919 {
03920     FRESULT res;
03921     DWORD remain, clst, sect;
03922     UINT rcnt;
03923     BYTE csect;
03924 
03925 
03926     *bf = 0;    /* Clear transfer byte counter */
03927 
03928     res = validate(fp);                                /* Check validity of the object */
03929     if (res != FR_OK) LEAVE_FF(fp->fs, res);
03930     if (fp->err)                                    /* Check error */
03931         LEAVE_FF(fp->fs, (FRESULT)fp->err);
03932     if (!(fp->flag & FA_READ))                        /* Check access mode */
03933         LEAVE_FF(fp->fs, FR_DENIED);
03934 
03935     remain = fp->fsize - fp->fptr;
03936     if (btf > remain) btf = (UINT)remain;            /* Truncate btf by remaining bytes */
03937 
03938     for ( ;  btf && (*func)(0, 0);                    /* Repeat until all data transferred or stream becomes busy */
03939         fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {
03940         csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
03941         if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
03942             if (!csect) {                            /* On the cluster boundary? */
03943                 clst = (fp->fptr == 0) ?            /* On the top of the file? */
03944                     fp->sclust : get_fat(fp->fs, fp->clust);
03945                 if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
03946                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
03947                 fp->clust = clst;                    /* Update current cluster */
03948             }
03949         }
03950         sect = clust2sect(fp->fs, fp->clust);        /* Get current data sector */
03951         if (!sect) ABORT(fp->fs, FR_INT_ERR);
03952         sect += csect;
03953         if (move_window(fp->fs, sect))                /* Move sector window */
03954             ABORT(fp->fs, FR_DISK_ERR);
03955         fp->dsect = sect;
03956         rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));    /* Forward data from sector window */
03957         if (rcnt > btf) rcnt = btf;
03958         rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
03959         if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
03960     }
03961 
03962     LEAVE_FF(fp->fs, FR_OK);
03963 }
03964 #endif /* _USE_FORWARD */
03965 
03966 
03967 
03968 #if _USE_MKFS && !_FS_READONLY
03969 /*-----------------------------------------------------------------------*/
03970 /* Create File System on the Drive                                       */
03971 /*-----------------------------------------------------------------------*/
03972 #define N_ROOTDIR    512        /* Number of root directory entries for FAT12/16 */
03973 #define N_FATS        1        /* Number of FAT copies (1 or 2) */
03974 
03975 
03976 FRESULT f_mkfs (
03977     const TCHAR* path,    /* Logical drive number */
03978     BYTE sfd,            /* Partitioning rule 0:FDISK, 1:SFD */
03979     UINT au                /* Allocation unit [bytes] */
03980 )
03981 {
03982     static const WORD vst[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0};
03983     static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
03984     int vol;
03985     BYTE fmt, md, sys, *tbl, pdrv, part;
03986     DWORD n_clst, vs, n, wsect;
03987     UINT i;
03988     DWORD b_vol, b_fat, b_dir, b_data;    /* LBA */
03989     DWORD n_vol, n_rsv, n_fat, n_dir;    /* Size */
03990     FATFS *fs;
03991     DSTATUS stat;
03992 
03993 
03994     /* Check mounted drive and clear work area */
03995     vol = get_ldnumber(&path);
03996     if (vol < 0) return FR_INVALID_DRIVE;
03997     if (sfd > 1) return FR_INVALID_PARAMETER;
03998     if (au & (au - 1)) return FR_INVALID_PARAMETER;
03999     fs = FatFs[vol];
04000     if (!fs) return FR_NOT_ENABLED;
04001     fs->fs_type = 0;
04002     pdrv = LD2PD(vol);    /* Physical drive */
04003     part = LD2PT(vol);    /* Partition (0:auto detect, 1-4:get from partition table)*/
04004 
04005     /* Get disk statics */
04006     stat = disk_initialize(pdrv);
04007     if (stat & STA_NOINIT) return FR_NOT_READY;
04008     if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
04009 #if _MAX_SS != _MIN_SS        /* Get disk sector size */
04010     if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS)
04011         return FR_DISK_ERR;
04012 #endif
04013     if (_MULTI_PARTITION && part) {
04014         /* Get partition information from partition table in the MBR */
04015         if (disk_read(pdrv, fs->win, 0, 1)) return FR_DISK_ERR;
04016         if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;
04017         tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
04018         if (!tbl[4]) return FR_MKFS_ABORTED;    /* No partition? */
04019         b_vol = LD_DWORD(tbl+8);    /* Volume start sector */
04020         n_vol = LD_DWORD(tbl+12);    /* Volume size */
04021     } else {
04022         /* Create a partition in this function */
04023         if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)
04024             return FR_DISK_ERR;
04025         b_vol = (sfd) ? 0 : 63;        /* Volume start sector */
04026         n_vol -= b_vol;                /* Volume size */
04027     }
04028 
04029     if (!au) {                /* AU auto selection */
04030         vs = n_vol / (2000 / (SS(fs) / 512));
04031         for (i = 0; vs < vst[i]; i++) ;
04032         au = cst[i];
04033     }
04034     au /= SS(fs);        /* Number of sectors per cluster */
04035     if (au == 0) au = 1;
04036     if (au > 128) au = 128;
04037 
04038     /* Pre-compute number of clusters and FAT sub-type */
04039     n_clst = n_vol / au;
04040     fmt = FS_FAT12;
04041     if (n_clst >= MIN_FAT16) fmt = FS_FAT16;
04042     if (n_clst >= MIN_FAT32) fmt = FS_FAT32;
04043 
04044     /* Determine offset and size of FAT structure */
04045     if (fmt == FS_FAT32) {
04046         n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
04047         n_rsv = 32;
04048         n_dir = 0;
04049     } else {
04050         n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;
04051         n_fat = (n_fat + SS(fs) - 1) / SS(fs);
04052         n_rsv = 1;
04053         n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs);
04054     }
04055     b_fat = b_vol + n_rsv;                /* FAT area start sector */
04056     b_dir = b_fat + n_fat * N_FATS;        /* Directory area start sector */
04057     b_data = b_dir + n_dir;                /* Data area start sector */
04058     if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED;    /* Too small volume */
04059 
04060     /* Align data start sector to erase block boundary (for flash memory media) */
04061     if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;
04062     n = (b_data + n - 1) & ~(n - 1);    /* Next nearest erase block from current data start */
04063     n = (n - b_data) / N_FATS;
04064     if (fmt == FS_FAT32) {        /* FAT32: Move FAT offset */
04065         n_rsv += n;
04066         b_fat += n;
04067     } else {                    /* FAT12/16: Expand FAT size */
04068         n_fat += n;
04069     }
04070 
04071     /* Determine number of clusters and final check of validity of the FAT sub-type */
04072     n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;
04073     if (   (fmt == FS_FAT16 && n_clst < MIN_FAT16)
04074         || (fmt == FS_FAT32 && n_clst < MIN_FAT32))
04075         return FR_MKFS_ABORTED;
04076 
04077     /* Determine system ID in the partition table */
04078     if (fmt == FS_FAT32) {
04079         sys = 0x0C;        /* FAT32X */
04080     } else {
04081         if (fmt == FS_FAT12 && n_vol < 0x10000) {
04082             sys = 0x01;    /* FAT12(<65536) */
04083         } else {
04084             sys = (n_vol < 0x10000) ? 0x04 : 0x06;    /* FAT16(<65536) : FAT12/16(>=65536) */
04085         }
04086     }
04087 
04088     if (_MULTI_PARTITION && part) {
04089         /* Update system ID in the partition table */
04090         tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
04091         tbl[4] = sys;
04092         if (disk_write(pdrv, fs->win, 0, 1))    /* Write it to teh MBR */
04093             return FR_DISK_ERR;
04094         md = 0xF8;
04095     } else {
04096         if (sfd) {    /* No partition table (SFD) */
04097             md = 0xF0;
04098         } else {    /* Create partition table (FDISK) */
04099             mem_set(fs->win, 0, SS(fs));
04100             tbl = fs->win+MBR_Table;    /* Create partition table for single partition in the drive */
04101             tbl[1] = 1;                        /* Partition start head */
04102             tbl[2] = 1;                        /* Partition start sector */
04103             tbl[3] = 0;                        /* Partition start cylinder */
04104             tbl[4] = sys;                    /* System type */
04105             tbl[5] = 254;                    /* Partition end head */
04106             n = (b_vol + n_vol) / 63 / 255;
04107             tbl[6] = (BYTE)(n >> 2 | 63);    /* Partition end sector */
04108             tbl[7] = (BYTE)n;                /* End cylinder */
04109             ST_DWORD(tbl+8, 63);            /* Partition start in LBA */
04110             ST_DWORD(tbl+12, n_vol);        /* Partition size in LBA */
04111             ST_WORD(fs->win+BS_55AA, 0xAA55);    /* MBR signature */
04112             if (disk_write(pdrv, fs->win, 0, 1))    /* Write it to the MBR */
04113                 return FR_DISK_ERR;
04114             md = 0xF8;
04115         }
04116     }
04117 
04118     /* Create BPB in the VBR */
04119     tbl = fs->win;                            /* Clear sector */
04120     mem_set(tbl, 0, SS(fs));
04121     mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */
04122     i = SS(fs);                                /* Sector size */
04123     ST_WORD(tbl+BPB_BytsPerSec, i);
04124     tbl[BPB_SecPerClus] = (BYTE)au;            /* Sectors per cluster */
04125     ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);        /* Reserved sectors */
04126     tbl[BPB_NumFATs] = N_FATS;                /* Number of FATs */
04127     i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR;    /* Number of root directory entries */
04128     ST_WORD(tbl+BPB_RootEntCnt, i);
04129     if (n_vol < 0x10000) {                    /* Number of total sectors */
04130         ST_WORD(tbl+BPB_TotSec16, n_vol);
04131     } else {
04132         ST_DWORD(tbl+BPB_TotSec32, n_vol);
04133     }
04134     tbl[BPB_Media] = md;                    /* Media descriptor */
04135     ST_WORD(tbl+BPB_SecPerTrk, 63);            /* Number of sectors per track */
04136     ST_WORD(tbl+BPB_NumHeads, 255);            /* Number of heads */
04137     ST_DWORD(tbl+BPB_HiddSec, b_vol);        /* Hidden sectors */
04138     n = get_fattime();                        /* Use current time as VSN */
04139     if (fmt == FS_FAT32) {
04140         ST_DWORD(tbl+BS_VolID32, n);        /* VSN */
04141         ST_DWORD(tbl+BPB_FATSz32, n_fat);    /* Number of sectors per FAT */
04142         ST_DWORD(tbl+BPB_RootClus, 2);        /* Root directory start cluster (2) */
04143         ST_WORD(tbl+BPB_FSInfo, 1);            /* FSINFO record offset (VBR+1) */
04144         ST_WORD(tbl+BPB_BkBootSec, 6);        /* Backup boot record offset (VBR+6) */
04145         tbl[BS_DrvNum32] = 0x80;            /* Drive number */
04146         tbl[BS_BootSig32] = 0x29;            /* Extended boot signature */
04147         mem_cpy(tbl+BS_VolLab32, "NO NAME    " "FAT32   ", 19);    /* Volume label, FAT signature */
04148     } else {
04149         ST_DWORD(tbl+BS_VolID, n);            /* VSN */
04150         ST_WORD(tbl+BPB_FATSz16, n_fat);    /* Number of sectors per FAT */
04151         tbl[BS_DrvNum] = 0x80;                /* Drive number */
04152         tbl[BS_BootSig] = 0x29;                /* Extended boot signature */
04153         mem_cpy(tbl+BS_VolLab, "NO NAME    " "FAT     ", 19);    /* Volume label, FAT signature */
04154     }
04155     ST_WORD(tbl+BS_55AA, 0xAA55);            /* Signature (Offset is fixed here regardless of sector size) */
04156     if (disk_write(pdrv, tbl, b_vol, 1))    /* Write it to the VBR sector */
04157         return FR_DISK_ERR;
04158     if (fmt == FS_FAT32)                    /* Write backup VBR if needed (VBR+6) */
04159         disk_write(pdrv, tbl, b_vol + 6, 1);
04160 
04161     /* Initialize FAT area */
04162     wsect = b_fat;
04163     for (i = 0; i < N_FATS; i++) {        /* Initialize each FAT copy */
04164         mem_set(tbl, 0, SS(fs));            /* 1st sector of the FAT  */
04165         n = md;                                /* Media descriptor byte */
04166         if (fmt != FS_FAT32) {
04167             n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
04168             ST_DWORD(tbl+0, n);                /* Reserve cluster #0-1 (FAT12/16) */
04169         } else {
04170             n |= 0xFFFFFF00;
04171             ST_DWORD(tbl+0, n);                /* Reserve cluster #0-1 (FAT32) */
04172             ST_DWORD(tbl+4, 0xFFFFFFFF);
04173             ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root directory */
04174         }
04175         if (disk_write(pdrv, tbl, wsect++, 1))
04176             return FR_DISK_ERR;
04177         mem_set(tbl, 0, SS(fs));            /* Fill following FAT entries with zero */
04178         for (n = 1; n < n_fat; n++) {        /* This loop may take a time on FAT32 volume due to many single sector writes */
04179             if (disk_write(pdrv, tbl, wsect++, 1))
04180                 return FR_DISK_ERR;
04181         }
04182     }
04183 
04184     /* Initialize root directory */
04185     i = (fmt == FS_FAT32) ? au : (UINT)n_dir;
04186     do {
04187         if (disk_write(pdrv, tbl, wsect++, 1))
04188             return FR_DISK_ERR;
04189     } while (--i);
04190 
04191 #if _USE_ERASE    /* Erase data area if needed */
04192     {
04193         DWORD eb[2];
04194 
04195         eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;
04196         disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb);
04197     }
04198 #endif
04199 
04200     /* Create FSINFO if needed */
04201     if (fmt == FS_FAT32) {
04202         ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
04203         ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
04204         ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);    /* Number of free clusters */
04205         ST_DWORD(tbl+FSI_Nxt_Free, 2);                /* Last allocated cluster# */
04206         ST_WORD(tbl+BS_55AA, 0xAA55);
04207         disk_write(pdrv, tbl, b_vol + 1, 1);    /* Write original (VBR+1) */
04208         disk_write(pdrv, tbl, b_vol + 7, 1);    /* Write backup (VBR+7) */
04209     }
04210 
04211     return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
04212 }
04213 
04214 
04215 
04216 #if _MULTI_PARTITION
04217 /*-----------------------------------------------------------------------*/
04218 /* Divide Physical Drive                                                 */
04219 /*-----------------------------------------------------------------------*/
04220 
04221 FRESULT f_fdisk (
04222     BYTE pdrv,            /* Physical drive number */
04223     const DWORD szt[],    /* Pointer to the size table for each partitions */
04224     void* work            /* Pointer to the working buffer */
04225 )
04226 {
04227     UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
04228     BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
04229     DSTATUS stat;
04230     DWORD sz_disk, sz_part, s_part;
04231 
04232 
04233     stat = disk_initialize(pdrv);
04234     if (stat & STA_NOINIT) return FR_NOT_READY;
04235     if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
04236     if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
04237 
04238     /* Determine CHS in the table regardless of the drive geometry */
04239     for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
04240     if (n == 256) n--;
04241     e_hd = n - 1;
04242     sz_cyl = 63 * n;
04243     tot_cyl = sz_disk / sz_cyl;
04244 
04245     /* Create partition table */
04246     mem_set(buf, 0, _MAX_SS);
04247     p = buf + MBR_Table; b_cyl = 0;
04248     for (i = 0; i < 4; i++, p += SZ_PTE) {
04249         p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;
04250         if (!p_cyl) continue;
04251         s_part = (DWORD)sz_cyl * b_cyl;
04252         sz_part = (DWORD)sz_cyl * p_cyl;
04253         if (i == 0) {    /* Exclude first track of cylinder 0 */
04254             s_hd = 1;
04255             s_part += 63; sz_part -= 63;
04256         } else {
04257             s_hd = 0;
04258         }
04259         e_cyl = b_cyl + p_cyl - 1;
04260         if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;
04261 
04262         /* Set partition table */
04263         p[1] = s_hd;                        /* Start head */
04264         p[2] = (BYTE)((b_cyl >> 2) + 1);    /* Start sector */
04265         p[3] = (BYTE)b_cyl;                    /* Start cylinder */
04266         p[4] = 0x06;                        /* System type (temporary setting) */
04267         p[5] = e_hd;                        /* End head */
04268         p[6] = (BYTE)((e_cyl >> 2) + 63);    /* End sector */
04269         p[7] = (BYTE)e_cyl;                    /* End cylinder */
04270         ST_DWORD(p + 8, s_part);            /* Start sector in LBA */
04271         ST_DWORD(p + 12, sz_part);            /* Partition size */
04272 
04273         /* Next partition */
04274         b_cyl += p_cyl;
04275     }
04276     ST_WORD(p, 0xAA55);
04277 
04278     /* Write it to the MBR */
04279     return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK;
04280 }
04281 
04282 
04283 #endif /* _MULTI_PARTITION */
04284 #endif /* _USE_MKFS && !_FS_READONLY */
04285 
04286 
04287 
04288 
04289 #if _USE_STRFUNC
04290 /*-----------------------------------------------------------------------*/
04291 /* Get a string from the file                                            */
04292 /*-----------------------------------------------------------------------*/
04293 
04294 TCHAR* f_gets (
04295     TCHAR* buff,    /* Pointer to the string buffer to read */
04296     int len,        /* Size of string buffer (characters) */
04297     FIL* fp            /* Pointer to the file object */
04298 )
04299 {
04300     int n = 0;
04301     TCHAR c, *p = buff;
04302     BYTE s[2];
04303     UINT rc;
04304 
04305 
04306     while (n < len - 1) {    /* Read characters until buffer gets filled */
04307 #if _USE_LFN && _LFN_UNICODE
04308 #if _STRF_ENCODE == 3        /* Read a character in UTF-8 */
04309         f_read(fp, s, 1, &rc);
04310         if (rc != 1) break;
04311         c = s[0];
04312         if (c >= 0x80) {
04313             if (c < 0xC0) continue;    /* Skip stray trailer */
04314             if (c < 0xE0) {            /* Two-byte sequence */
04315                 f_read(fp, s, 1, &rc);
04316                 if (rc != 1) break;
04317                 c = (c & 0x1F) << 6 | (s[0] & 0x3F);
04318                 if (c < 0x80) c = '?';
04319             } else {
04320                 if (c < 0xF0) {        /* Three-byte sequence */
04321                     f_read(fp, s, 2, &rc);
04322                     if (rc != 2) break;
04323                     c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F);
04324                     if (c < 0x800) c = '?';
04325                 } else {            /* Reject four-byte sequence */
04326                     c = '?';
04327                 }
04328             }
04329         }
04330 #elif _STRF_ENCODE == 2        /* Read a character in UTF-16BE */
04331         f_read(fp, s, 2, &rc);
04332         if (rc != 2) break;
04333         c = s[1] + (s[0] << 8);
04334 #elif _STRF_ENCODE == 1        /* Read a character in UTF-16LE */
04335         f_read(fp, s, 2, &rc);
04336         if (rc != 2) break;
04337         c = s[0] + (s[1] << 8);
04338 #else                        /* Read a character in ANSI/OEM */
04339         f_read(fp, s, 1, &rc);
04340         if (rc != 1) break;
04341         c = s[0];
04342         if (IsDBCS1(c)) {
04343             f_read(fp, s, 1, &rc);
04344             if (rc != 1) break;
04345             c = (c << 8) + s[0];
04346         }
04347         c = ff_convert(c, 1);    /* OEM -> Unicode */
04348         if (!c) c = '?';
04349 #endif
04350 #else                        /* Read a character without conversion */
04351         f_read(fp, s, 1, &rc);
04352         if (rc != 1) break;
04353         c = s[0];
04354 #endif
04355         if (_USE_STRFUNC == 2 && c == '\r') continue;    /* Strip '\r' */
04356         *p++ = c;
04357         n++;
04358         if (c == '\n') break;        /* Break on EOL */
04359     }
04360     *p = 0;
04361     return n ? buff : 0;            /* When no data read (eof or error), return with error. */
04362 }
04363 
04364 
04365 
04366 #if !_FS_READONLY
04367 #include <stdarg.h>
04368 /*-----------------------------------------------------------------------*/
04369 /* Put a character to the file                                           */
04370 /*-----------------------------------------------------------------------*/
04371 
04372 typedef struct {
04373     FIL* fp;
04374     int idx, nchr;
04375     BYTE buf[64];
04376 } putbuff;
04377 
04378 
04379 static
04380 void putc_bfd (
04381     putbuff* pb,
04382     TCHAR c
04383 )
04384 {
04385     UINT bw;
04386     int i;
04387 
04388 
04389     if (_USE_STRFUNC == 2 && c == '\n')     /* LF -> CRLF conversion */
04390         putc_bfd(pb, '\r');
04391 
04392     i = pb->idx;    /* Buffer write index (-1:error) */
04393     if (i < 0) return;
04394 
04395 #if _USE_LFN && _LFN_UNICODE
04396 #if _STRF_ENCODE == 3            /* Write a character in UTF-8 */
04397     if (c < 0x80) {                /* 7-bit */
04398         pb->buf[i++] = (BYTE)c;
04399     } else {
04400         if (c < 0x800) {        /* 11-bit */
04401             pb->buf[i++] = (BYTE)(0xC0 | c >> 6);
04402         } else {                /* 16-bit */
04403             pb->buf[i++] = (BYTE)(0xE0 | c >> 12);
04404             pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F));
04405         }
04406         pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F));
04407     }
04408 #elif _STRF_ENCODE == 2            /* Write a character in UTF-16BE */
04409     pb->buf[i++] = (BYTE)(c >> 8);
04410     pb->buf[i++] = (BYTE)c;
04411 #elif _STRF_ENCODE == 1            /* Write a character in UTF-16LE */
04412     pb->buf[i++] = (BYTE)c;
04413     pb->buf[i++] = (BYTE)(c >> 8);
04414 #else                            /* Write a character in ANSI/OEM */
04415     c = ff_convert(c, 0);    /* Unicode -> OEM */
04416     if (!c) c = '?';
04417     if (c >= 0x100)
04418         pb->buf[i++] = (BYTE)(c >> 8);
04419     pb->buf[i++] = (BYTE)c;
04420 #endif
04421 #else                            /* Write a character without conversion */
04422     pb->buf[i++] = (BYTE)c;
04423 #endif
04424 
04425     if (i >= (int)(sizeof pb->buf) - 3) {    /* Write buffered characters to the file */
04426         f_write(pb->fp, pb->buf, (UINT)i, &bw);
04427         i = (bw == (UINT)i) ? 0 : -1;
04428     }
04429     pb->idx = i;
04430     pb->nchr++;
04431 }
04432 
04433 
04434 
04435 int f_putc (
04436     TCHAR c,    /* A character to be output */
04437     FIL* fp        /* Pointer to the file object */
04438 )
04439 {
04440     putbuff pb;
04441     UINT nw;
04442 
04443 
04444     pb.fp = fp;            /* Initialize output buffer */
04445     pb.nchr = pb.idx = 0;
04446 
04447     putc_bfd(&pb, c);    /* Put a character */
04448 
04449     if (   pb.idx >= 0    /* Flush buffered characters to the file */
04450         && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
04451         && (UINT)pb.idx == nw) return pb.nchr;
04452     return EOF;
04453 }
04454 
04455 
04456 
04457 
04458 /*-----------------------------------------------------------------------*/
04459 /* Put a string to the file                                              */
04460 /*-----------------------------------------------------------------------*/
04461 
04462 int f_puts (
04463     const TCHAR* str,    /* Pointer to the string to be output */
04464     FIL* fp                /* Pointer to the file object */
04465 )
04466 {
04467     putbuff pb;
04468     UINT nw;
04469 
04470 
04471     pb.fp = fp;                /* Initialize output buffer */
04472     pb.nchr = pb.idx = 0;
04473 
04474     while (*str)            /* Put the string */
04475         putc_bfd(&pb, *str++);
04476 
04477     if (   pb.idx >= 0        /* Flush buffered characters to the file */
04478         && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
04479         && (UINT)pb.idx == nw) return pb.nchr;
04480     return EOF;
04481 }
04482 
04483 
04484 
04485 
04486 /*-----------------------------------------------------------------------*/
04487 /* Put a formatted string to the file                                    */
04488 /*-----------------------------------------------------------------------*/
04489 
04490 int f_printf (
04491     FIL* fp,            /* Pointer to the file object */
04492     const TCHAR* fmt,    /* Pointer to the format string */
04493     ...                    /* Optional arguments... */
04494 )
04495 {
04496     va_list arp;
04497     BYTE f, r;
04498     UINT nw, i, j, w;
04499     DWORD v;
04500     TCHAR c, d, s[16], *p;
04501     putbuff pb;
04502 
04503 
04504     pb.fp = fp;                /* Initialize output buffer */
04505     pb.nchr = pb.idx = 0;
04506 
04507     va_start(arp, fmt);
04508 
04509     for (;;) {
04510         c = *fmt++;
04511         if (c == 0) break;            /* End of string */
04512         if (c != '%') {                /* Non escape character */
04513             putc_bfd(&pb, c);
04514             continue;
04515         }
04516         w = f = 0;
04517         c = *fmt++;
04518         if (c == '0') {                /* Flag: '0' padding */
04519             f = 1; c = *fmt++;
04520         } else {
04521             if (c == '-') {            /* Flag: left justified */
04522                 f = 2; c = *fmt++;
04523             }
04524         }
04525         while (IsDigit(c)) {        /* Precision */
04526             w = w * 10 + c - '0';
04527             c = *fmt++;
04528         }
04529         if (c == 'l' || c == 'L') {    /* Prefix: Size is long int */
04530             f |= 4; c = *fmt++;
04531         }
04532         if (!c) break;
04533         d = c;
04534         if (IsLower(d)) d -= 0x20;
04535         switch (d) {                /* Type is... */
04536         case 'S' :                    /* String */
04537             p = va_arg(arp, TCHAR*);
04538             for (j = 0; p[j]; j++) ;
04539             if (!(f & 2)) {
04540                 while (j++ < w) putc_bfd(&pb, ' ');
04541             }
04542             while (*p) putc_bfd(&pb, *p++);
04543             while (j++ < w) putc_bfd(&pb, ' ');
04544             continue;
04545         case 'C' :                    /* Character */
04546             putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
04547         case 'B' :                    /* Binary */
04548             r = 2; break;
04549         case 'O' :                    /* Octal */
04550             r = 8; break;
04551         case 'D' :                    /* Signed decimal */
04552         case 'U' :                    /* Unsigned decimal */
04553             r = 10; break;
04554         case 'X' :                    /* Hexadecimal */
04555             r = 16; break;
04556         default:                    /* Unknown type (pass-through) */
04557             putc_bfd(&pb, c); continue;
04558         }
04559 
04560         /* Get an argument and put it in numeral */
04561         v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));
04562         if (d == 'D' && (v & 0x80000000)) {
04563             v = 0 - v;
04564             f |= 8;
04565         }
04566         i = 0;
04567         do {
04568             d = (TCHAR)(v % r); v /= r;
04569             if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
04570             s[i++] = d + '0';
04571         } while (v && i < sizeof s / sizeof s[0]);
04572         if (f & 8) s[i++] = '-';
04573         j = i; d = (f & 1) ? '0' : ' ';
04574         while (!(f & 2) && j++ < w) putc_bfd(&pb, d);
04575         do putc_bfd(&pb, s[--i]); while (i);
04576         while (j++ < w) putc_bfd(&pb, d);
04577     }
04578 
04579     va_end(arp);
04580 
04581     if (   pb.idx >= 0        /* Flush buffered characters to the file */
04582         && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
04583         && (UINT)pb.idx == nw) return pb.nchr;
04584     return EOF;
04585 }
04586 
04587 #endif /* !_FS_READONLY */
04588 #endif /* _USE_STRFUNC */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines