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