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