Code for autonomous ground vehicle, Data Bus, 3rd place winner in 2012 Sparkfun AVC.
Dependencies: Watchdog mbed Schedule SimpleFilter LSM303DLM PinDetect DebounceIn Servo
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 Tue Jul 12 2022 14:09:25 by 1.7.2