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