Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers FATFileSystem.cpp Source File

FATFileSystem.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2012 ARM Limited
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to deal
00006  * in the Software without restriction, including without limitation the rights
00007  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  * copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00019  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00020  * SOFTWARE.
00021  */
00022 #include "mbed.h"
00023 
00024 #include "diskio.h"
00025 #include "ffconf.h"
00026 #include "mbed_debug.h"
00027 #include "mbed_critical.h"
00028 #include <errno.h>
00029 
00030 #include "FATFileSystem.h"
00031 
00032 
00033 ////// Error handling /////
00034 
00035 static int fat_error_remap(FRESULT res)
00036 {
00037     switch(res) {
00038         case FR_OK:                     /* (0) Succeeded */
00039             return 0;                   /* no error */
00040         case FR_DISK_ERR:               /* (1) A hard error occurred in the low level disk I/O layer */
00041         case FR_NOT_READY:              /* (3) The physical drive cannot work */
00042             return -EIO;                /* I/O error */
00043         case FR_NO_FILE:                /* (4) Could not find the file */
00044         case FR_NO_PATH:                /* (5) Could not find the path */
00045         case FR_INVALID_NAME:           /* (6) The path name format is invalid */
00046         case FR_INVALID_DRIVE:          /* (11) The logical drive number is invalid */
00047         case FR_NO_FILESYSTEM:          /* (13) There is no valid FAT volume */
00048             return -ENOENT;             /* No such file or directory */
00049         case FR_DENIED:                 /* (7) Access denied due to prohibited access or directory full */
00050             return -EACCES;             /* Permission denied */
00051         case FR_EXIST:                  /* (8) Access denied due to prohibited access */
00052             return -EEXIST;             /* File exists */
00053         case FR_WRITE_PROTECTED:        /* (10) The physical drive is write protected */
00054         case FR_LOCKED:                 /* (16) The operation is rejected according to the file sharing policy */
00055             return -EACCES;             /* Permission denied */
00056         case FR_INVALID_OBJECT:         /* (9) The file/directory object is invalid */
00057             return -EFAULT;             /* Bad address */
00058         case FR_NOT_ENABLED:            /* (12) The volume has no work area */
00059             return -ENXIO;              /* No such device or address */
00060         case FR_NOT_ENOUGH_CORE:        /* (17) LFN working buffer could not be allocated */
00061             return -ENOMEM;             /* Not enough space */
00062         case FR_TOO_MANY_OPEN_FILES:    /* (18) Number of open files > _FS_LOCK */
00063             return -ENFILE;             /* Too many open files in system */
00064         case FR_INVALID_PARAMETER:      /* (19) Given parameter is invalid */
00065             return -ENOEXEC;            /* Exec format error */
00066         case FR_INT_ERR:                /* (2) Assertion failed */
00067         case FR_MKFS_ABORTED:           /* (14) The f_mkfs() aborted due to any parameter error */
00068         case FR_TIMEOUT:                /* (15) Could not get a grant to access the volume within defined period */
00069         default:                        /* Bad file number */
00070             return -EBADF;
00071     }
00072 }
00073 
00074 // Helper class for deferring operations when variable falls out of scope
00075 template <typename T>
00076 class Deferred {
00077 public:
00078     T _t;
00079     Callback<void(T)> _ondefer;
00080 
00081     Deferred(const Deferred&);
00082     Deferred &operator=(const Deferred&);
00083 
00084 public:
00085     Deferred(T t, Callback<void(T)> ondefer = NULL)
00086         : _t(t), _ondefer(ondefer)
00087     {
00088     }
00089 
00090     operator T()
00091     {
00092         return _t;
00093     }
00094 
00095     ~Deferred()
00096     {
00097         if (_ondefer) {
00098             _ondefer(_t);
00099         }
00100     }
00101 };
00102 
00103 static void dodelete(const char *data)
00104 {
00105     delete[] data;
00106 }
00107 
00108 // Adds prefix needed internally by fatfs, this can be avoided for the first fatfs
00109 // (id 0) otherwise a prefix of "id:/" is inserted in front of the string.
00110 static Deferred<const char*> fat_path_prefix(int id, const char *path)
00111 {
00112     // We can avoid dynamic allocation when only on fatfs is in use
00113     if (id == 0) {
00114         return path;
00115     }
00116 
00117     // Prefix path with id, will look something like 2:/hi/hello/filehere.txt
00118     char *buffer = new char[strlen("0:/") + strlen(path) + 1];
00119     if (!buffer) {
00120         return NULL;
00121     }
00122 
00123     buffer[0] = '0' + id;
00124     buffer[1] = ':';
00125     buffer[2] = '/';
00126     strcpy(buffer + strlen("0:/"), path);
00127     return Deferred<const char*>(buffer, dodelete);
00128 }
00129 
00130 
00131 ////// Disk operations //////
00132 
00133 // Global access to block device from FAT driver
00134 static BlockDevice *_ffs[_VOLUMES] = {0};
00135 static SingletonPtr<PlatformMutex>  _ffs_mutex;
00136 
00137 
00138 // FAT driver functions
00139 DWORD get_fattime(void)
00140 {
00141     time_t rawtime;
00142     time(&rawtime);
00143     struct tm *ptm = localtime(&rawtime);
00144     return (DWORD)(ptm->tm_year - 80) << 25
00145            | (DWORD)(ptm->tm_mon + 1  ) << 21
00146            | (DWORD)(ptm->tm_mday     ) << 16
00147            | (DWORD)(ptm->tm_hour     ) << 11
00148            | (DWORD)(ptm->tm_min      ) << 5
00149            | (DWORD)(ptm->tm_sec/2    );
00150 }
00151 
00152 void *ff_memalloc(UINT size)
00153 {
00154     return malloc(size);
00155 }
00156 
00157 void ff_memfree(void *p)
00158 {
00159     free(p);
00160 }
00161 
00162 // Implementation of diskio functions (see ChaN/diskio.h)
00163 static WORD disk_get_sector_size(BYTE pdrv)
00164 {
00165     WORD ssize = _ffs[pdrv]->get_erase_size();
00166     if (ssize < 512) {
00167         ssize = 512;
00168     }
00169 
00170     MBED_ASSERT(ssize >= _MIN_SS && ssize <= _MAX_SS);
00171     MBED_ASSERT(_ffs[pdrv]->get_read_size() <= _ffs[pdrv]->get_erase_size());
00172     MBED_ASSERT(_ffs[pdrv]->get_program_size() <= _ffs[pdrv]->get_erase_size());
00173     return ssize;
00174 }
00175 
00176 static DWORD disk_get_sector_count(BYTE pdrv)
00177 {
00178     DWORD scount = _ffs[pdrv]->size() / disk_get_sector_size(pdrv);
00179     MBED_ASSERT(scount >= 64);
00180     return scount;
00181 }
00182 
00183 DSTATUS disk_status(BYTE pdrv)
00184 {
00185     debug_if(FFS_DBG, "disk_status on pdrv [%d]\n", pdrv);
00186     return RES_OK;
00187 }
00188 
00189 DSTATUS disk_initialize(BYTE pdrv)
00190 {
00191     debug_if(FFS_DBG, "disk_initialize on pdrv [%d]\n", pdrv);
00192     return (DSTATUS)_ffs[pdrv]->init();
00193 }
00194 
00195 DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
00196 {
00197     debug_if(FFS_DBG, "disk_read(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv);
00198     DWORD ssize = disk_get_sector_size(pdrv);
00199     int err = _ffs[pdrv]->read(buff, sector*ssize, count*ssize);
00200     return err ? RES_PARERR : RES_OK;
00201 }
00202 
00203 DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
00204 {
00205     debug_if(FFS_DBG, "disk_write(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv);
00206     DWORD ssize = disk_get_sector_size(pdrv);
00207     int err = _ffs[pdrv]->erase(sector*ssize, count*ssize);
00208     if (err) {
00209         return RES_PARERR;
00210     }
00211 
00212     err = _ffs[pdrv]->program(buff, sector*ssize, count*ssize);
00213     if (err) {
00214         return RES_PARERR;
00215     }
00216 
00217     return RES_OK;
00218 }
00219 
00220 DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
00221 {
00222     debug_if(FFS_DBG, "disk_ioctl(%d)\n", cmd);
00223     switch (cmd) {
00224         case CTRL_SYNC:
00225             if (_ffs[pdrv] == NULL) {
00226                 return RES_NOTRDY;
00227             } else {
00228                 return RES_OK;
00229             }
00230         case GET_SECTOR_COUNT:
00231             if (_ffs[pdrv] == NULL) {
00232                 return RES_NOTRDY;
00233             } else {
00234                 *((DWORD*)buff) = disk_get_sector_count(pdrv);
00235                 return RES_OK;
00236             }
00237         case GET_SECTOR_SIZE:
00238             if (_ffs[pdrv] == NULL) {
00239                 return RES_NOTRDY;
00240             } else {
00241                 *((WORD*)buff) = disk_get_sector_size(pdrv);
00242                 return RES_OK;
00243             }
00244         case GET_BLOCK_SIZE:
00245             *((DWORD*)buff) = 1; // default when not known
00246             return RES_OK;
00247         case CTRL_TRIM:
00248             if (_ffs[pdrv] == NULL) {
00249                 return RES_NOTRDY;
00250             } else {
00251                 DWORD *sectors = (DWORD*)buff;
00252                 DWORD ssize = disk_get_sector_size(pdrv);
00253                 int err = _ffs[pdrv]->trim(sectors[0]*ssize, (sectors[1]-sectors[0]+1)*ssize);
00254                 return err ? RES_PARERR : RES_OK;
00255             }
00256     }
00257 
00258     return RES_PARERR;
00259 }
00260 
00261 
00262 ////// Generic filesystem operations //////
00263 
00264 // Filesystem implementation (See FATFilySystem.h)
00265 FATFileSystem::FATFileSystem(const char *name, BlockDevice *bd)
00266         : FileSystem(name), _id(-1) {
00267     if (bd) {
00268         mount(bd);
00269     }
00270 }
00271 
00272 FATFileSystem::~FATFileSystem()
00273 {
00274     // nop if unmounted
00275     unmount();
00276 }
00277 
00278 int FATFileSystem::mount(BlockDevice *bd) {
00279     // requires duplicate definition to allow virtual overload to work
00280     return mount(bd, true);
00281 }
00282 
00283 int FATFileSystem::mount(BlockDevice *bd, bool mount) {
00284     lock();
00285     if (_id != -1) {
00286         unlock();
00287         return -EINVAL;
00288     }
00289 
00290     for (int i = 0; i < _VOLUMES; i++) {
00291         if (!_ffs[i]) {
00292             _id = i;
00293             _ffs[_id] = bd;
00294             _fsid[0] = '0' + _id;
00295             _fsid[1] = ':';
00296             _fsid[2] = '\0';
00297             debug_if(FFS_DBG, "Mounting [%s] on ffs drive [%s]\n", getName(), _fsid);
00298             FRESULT res = f_mount(&_fs, _fsid, mount);
00299             unlock();
00300             return fat_error_remap(res);
00301         }
00302     }
00303 
00304     unlock();
00305     return -ENOMEM;
00306 }
00307 
00308 int FATFileSystem::unmount()
00309 {
00310     lock();
00311     if (_id == -1) {
00312         unlock();
00313         return -EINVAL;
00314     }
00315 
00316     FRESULT res = f_mount(NULL, _fsid, 0);
00317     _ffs[_id] = NULL;
00318     _id = -1;
00319     unlock();
00320     return fat_error_remap(res);
00321 }
00322 
00323 /* See http://elm-chan.org/fsw/ff/en/mkfs.html for details of f_mkfs() and
00324  * associated arguments. */
00325 int FATFileSystem::format(BlockDevice *bd, bd_size_t cluster_size) {
00326     FATFileSystem fs;
00327     int err = fs.mount(bd, false);
00328     if (err) {
00329         return err;
00330     }
00331 
00332     // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster)
00333     fs.lock();
00334     FRESULT res = f_mkfs(fs._fsid, 1, cluster_size);
00335     fs.unlock();
00336     if (res != FR_OK) {
00337         return fat_error_remap(res);
00338     }
00339 
00340     err = fs.unmount();
00341     if (err) {
00342         return err;
00343     }
00344 
00345     return 0;
00346 }
00347 
00348 int FATFileSystem::reformat(BlockDevice *bd, int allocation_unit) {
00349     lock();
00350     if (_id != -1) {
00351         if (!bd) {
00352             bd = _ffs[_id];
00353         }
00354 
00355         int err = unmount();
00356         if (err) {
00357             unlock();
00358             return err;
00359         }
00360     }
00361 
00362     if (!bd) {
00363         unlock();
00364         return -ENODEV;
00365     }
00366 
00367     int err = FATFileSystem::format(bd, allocation_unit);
00368     if (err) {
00369         unlock();
00370         return err;
00371     }
00372 
00373     err = mount(bd);
00374     unlock();
00375     return err;
00376 }
00377 
00378 int FATFileSystem::remove(const char *path) {
00379     Deferred<const char*> fpath = fat_path_prefix(_id, path);
00380 
00381     lock();
00382     FRESULT res = f_unlink(fpath);
00383     unlock();
00384 
00385     if (res != FR_OK) {
00386         debug_if(FFS_DBG, "f_unlink() failed: %d\n", res);
00387     }
00388     return fat_error_remap(res);
00389 }
00390 
00391 int FATFileSystem::rename(const char *oldpath, const char *newpath) {
00392     Deferred<const char*> oldfpath = fat_path_prefix(_id, oldpath);
00393     Deferred<const char*> newfpath = fat_path_prefix(_id, newpath);
00394 
00395     lock();
00396     FRESULT res = f_rename(oldfpath, newfpath);
00397     unlock();
00398 
00399     if (res != FR_OK) {
00400         debug_if(FFS_DBG, "f_rename() failed: %d\n", res);
00401     }
00402     return fat_error_remap(res);
00403 }
00404 
00405 int FATFileSystem::mkdir(const char *path, mode_t mode) {
00406     Deferred<const char*> fpath = fat_path_prefix(_id, path);
00407 
00408     lock();
00409     FRESULT res = f_mkdir(fpath);
00410     unlock();
00411 
00412     if (res != FR_OK) {
00413         debug_if(FFS_DBG, "f_mkdir() failed: %d\n", res);
00414     }
00415     return fat_error_remap(res);
00416 }
00417 
00418 int FATFileSystem::stat(const char *path, struct stat *st) {
00419     Deferred<const char*> fpath = fat_path_prefix(_id, path);
00420 
00421     lock();
00422     FILINFO f;
00423     memset(&f, 0, sizeof(f));
00424 
00425     FRESULT res = f_stat(fpath, &f);
00426     if (res != FR_OK) {
00427         unlock();
00428         return fat_error_remap(res);
00429     }
00430 
00431     /* ARMCC doesnt support stat(), and these symbols are not defined by the toolchain. */
00432 #ifdef TOOLCHAIN_GCC
00433     st->st_size = f.fsize;
00434     st->st_mode = 0;
00435     st->st_mode |= (f.fattrib & AM_DIR) ? S_IFDIR : S_IFREG;
00436     st->st_mode |= (f.fattrib & AM_RDO) ?
00437         (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) :
00438         (S_IRWXU | S_IRWXG | S_IRWXO);
00439 #endif /* TOOLCHAIN_GCC */
00440     unlock();
00441 
00442     return 0;
00443 }
00444 
00445 void FATFileSystem::lock() {
00446     _ffs_mutex->lock();
00447 }
00448 
00449 void FATFileSystem::unlock() {
00450     _ffs_mutex->unlock();
00451 }
00452 
00453 
00454 ////// File operations //////
00455 int FATFileSystem::file_open(fs_file_t *file, const char *path, int flags) {
00456     debug_if(FFS_DBG, "open(%s) on filesystem [%s], drv [%s]\n", path, getName(), _id);
00457 
00458     FIL *fh = new FIL;
00459     Deferred<const char*> fpath = fat_path_prefix(_id, path);
00460 
00461     /* POSIX flags -> FatFS open mode */
00462     BYTE openmode;
00463     if (flags & O_RDWR) {
00464         openmode = FA_READ | FA_WRITE;
00465     } else if (flags & O_WRONLY) {
00466         openmode = FA_WRITE;
00467     } else {
00468         openmode = FA_READ;
00469     }
00470     if (flags & O_CREAT) {
00471         if (flags & O_TRUNC) {
00472             openmode |= FA_CREATE_ALWAYS;
00473         } else {
00474             openmode |= FA_OPEN_ALWAYS;
00475         }
00476     }
00477 
00478     lock();
00479     FRESULT res = f_open(fh, fpath, openmode);
00480 
00481     if (res != FR_OK) {
00482         unlock();
00483         debug_if(FFS_DBG, "f_open('w') failed: %d\n", res);
00484         delete fh;
00485         return fat_error_remap(res);
00486     }
00487 
00488     if (flags & O_APPEND) {
00489         f_lseek(fh, fh->fsize);
00490     }
00491     unlock();
00492 
00493     *file = fh;
00494     return 0;
00495 }
00496 
00497 int FATFileSystem::file_close(fs_file_t file) {
00498     FIL *fh = static_cast<FIL*>(file);
00499 
00500     lock();
00501     FRESULT res = f_close(fh);
00502     unlock();
00503 
00504     delete fh;
00505     return fat_error_remap(res);
00506 }
00507 
00508 ssize_t FATFileSystem::file_read(fs_file_t file, void *buffer, size_t len) {
00509     FIL *fh = static_cast<FIL*>(file);
00510 
00511     lock();
00512     UINT n;
00513     FRESULT res = f_read(fh, buffer, len, &n);
00514     unlock();
00515 
00516     if (res != FR_OK) {
00517         debug_if(FFS_DBG, "f_read() failed: %d\n", res);
00518         return fat_error_remap(res);
00519     } else {
00520         return n;
00521     }
00522 }
00523 
00524 ssize_t FATFileSystem::file_write(fs_file_t file, const void *buffer, size_t len) {
00525     FIL *fh = static_cast<FIL*>(file);
00526 
00527     lock();
00528     UINT n;
00529     FRESULT res = f_write(fh, buffer, len, &n);
00530     unlock();
00531 
00532     if (res != FR_OK) {
00533         debug_if(FFS_DBG, "f_write() failed: %d", res);
00534         return fat_error_remap(res);
00535     } else {
00536         return n;
00537     }
00538 }
00539 
00540 int FATFileSystem::file_sync(fs_file_t file) {
00541     FIL *fh = static_cast<FIL*>(file);
00542 
00543     lock();
00544     FRESULT res = f_sync(fh);
00545     unlock();
00546 
00547     if (res != FR_OK) {
00548         debug_if(FFS_DBG, "f_sync() failed: %d\n", res);
00549     }
00550     return fat_error_remap(res);
00551 }
00552 
00553 off_t FATFileSystem::file_seek(fs_file_t file, off_t offset, int whence) {
00554     FIL *fh = static_cast<FIL*>(file);
00555 
00556     lock();
00557     if (whence == SEEK_END) {
00558         offset += fh->fsize;
00559     } else if(whence==SEEK_CUR) {
00560         offset += fh->fptr;
00561     }
00562 
00563     FRESULT res = f_lseek(fh, offset);
00564     off_t noffset = fh->fptr;
00565     unlock();
00566 
00567     if (res != FR_OK) {
00568         debug_if(FFS_DBG, "lseek failed: %d\n", res);
00569         return fat_error_remap(res);
00570     } else {
00571         return noffset;
00572     }
00573 }
00574 
00575 off_t FATFileSystem::file_tell(fs_file_t file) {
00576     FIL *fh = static_cast<FIL*>(file);
00577 
00578     lock();
00579     off_t res = fh->fptr;
00580     unlock();
00581 
00582     return res;
00583 }
00584 
00585 off_t FATFileSystem::file_size(fs_file_t file) {
00586     FIL *fh = static_cast<FIL*>(file);
00587 
00588     lock();
00589     off_t res = fh->fsize;
00590     unlock();
00591 
00592     return res;
00593 }
00594 
00595 
00596 ////// Dir operations //////
00597 int FATFileSystem::dir_open(fs_dir_t *dir, const char *path) {
00598     FATFS_DIR *dh = new FATFS_DIR;
00599     Deferred<const char*> fpath = fat_path_prefix(_id, path);
00600 
00601     lock();
00602     FRESULT res = f_opendir(dh, fpath);
00603     unlock();
00604 
00605     if (res != FR_OK) {
00606         debug_if(FFS_DBG, "f_opendir() failed: %d\n", res);
00607         delete dh;
00608         return fat_error_remap(res);
00609     }
00610 
00611     *dir = dh;
00612     return 0;
00613 }
00614 
00615 int FATFileSystem::dir_close(fs_dir_t dir) {
00616     FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir);
00617 
00618     lock();
00619     FRESULT res = f_closedir(dh);
00620     unlock();
00621 
00622     delete dh;
00623     return fat_error_remap(res);
00624 }
00625 
00626 ssize_t FATFileSystem::dir_read(fs_dir_t dir, struct dirent *ent) {
00627     FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir);
00628     FILINFO finfo;
00629 
00630 #if _USE_LFN
00631     finfo.lfname = ent->d_name;
00632     finfo.lfsize = NAME_MAX;
00633 #endif // _USE_LFN
00634 
00635     lock();
00636     FRESULT res = f_readdir(dh, &finfo);
00637     unlock();
00638 
00639     if (res != FR_OK) {
00640         return fat_error_remap(res);
00641     } else if (finfo.fname[0] == 0) {
00642         return 0;
00643     }
00644 
00645     ent->d_type = (finfo.fattrib & AM_DIR) ? DT_DIR : DT_REG;
00646 
00647 #if _USE_LFN
00648     if (ent->d_name[0] == 0) {
00649         // No long filename so use short filename.
00650         strncpy(ent->d_name, finfo.fname, NAME_MAX);
00651     }
00652 #else
00653     strncpy(end->d_name, finfo.fname, len);
00654 #endif
00655 
00656     return 1;
00657 }
00658 
00659 void FATFileSystem::dir_seek(fs_dir_t dir, off_t offset) {
00660     FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir);
00661 
00662     lock();
00663     if (offset < dh->index) {
00664         f_rewinddir(dh);
00665     }
00666     while (dh->index < offset) {
00667         FILINFO finfo;
00668         FRESULT res;
00669 #if _USE_LFN
00670         char lfname[NAME_MAX];
00671         finfo.lfname = lfname;
00672         finfo.lfsize = NAME_MAX;
00673 #endif // _USE_LFN
00674         res = f_readdir(dh, &finfo);
00675         if (res != FR_OK) {
00676             break;
00677         } else if (finfo.fname[0] == 0) {
00678             break;
00679         }
00680     }
00681     unlock();
00682 }
00683 
00684 off_t FATFileSystem::dir_tell(fs_dir_t dir) {
00685     FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir);
00686 
00687     lock();
00688     off_t offset = dh->index;
00689     unlock();
00690 
00691     return offset;
00692 }
00693 
00694 void FATFileSystem::dir_rewind(fs_dir_t dir) {
00695     FATFS_DIR *dh = static_cast<FATFS_DIR*>(dir);
00696 
00697     lock();
00698     f_rewinddir(dh);
00699     unlock();
00700 }
00701