Revised to support ability to have both SD and USB drives mounted.

Dependents:   Multi-FileSystem Multi-FileSystem

Fork of FATFileSystem by mbed official

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ff.cpp Source File

ff.cpp

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