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