From 6e1ea39b8e41e8600506bc5325c25969de091ff3 Mon Sep 17 00:00:00 2001 From: patacongo Date: Tue, 15 Nov 2011 22:37:14 +0000 Subject: Fix an error in FAT needed for interoperability with Windows when access first entry in root directory git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4093 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/fs/fat/fs_fat32.c | 15 ++++-- nuttx/fs/fat/fs_fat32dirent.c | 106 +++++++++++++++++++++++++++++++++--------- 2 files changed, 95 insertions(+), 26 deletions(-) (limited to 'nuttx/fs') diff --git a/nuttx/fs/fat/fs_fat32.c b/nuttx/fs/fat/fs_fat32.c index f7818d551..b0ee57a18 100644 --- a/nuttx/fs/fat/fs_fat32.c +++ b/nuttx/fs/fat/fs_fat32.c @@ -1295,12 +1295,13 @@ static int fat_opendir(struct inode *mountpt, const char *relpath, struct fs_dir else if ((DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY) == 0) { /* The entry is not a directory */ + ret = -ENOTDIR; goto errout_with_semaphore; } else { - /* The entry is a directory */ + /* The entry is a directory (but not the root directory) */ dir->u.fat.fd_startcluster = ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) | @@ -1492,7 +1493,9 @@ static int fat_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) goto errout_with_semaphore; } - /* Check if this is the root directory */ + /* Check if this is the root directory. If it is the root directory, we + * reset the fd_index to 1, skipping over the initial, unused entry. + */ if (fs->fs_type != FSTYPE_FAT32 && dir->u.fat.fd_startcluster == 0) @@ -1501,7 +1504,7 @@ static int fat_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) dir->u.fat.fd_currcluster = 0; dir->u.fat.fd_currsector = fs->fs_rootbase; - dir->u.fat.fd_index = 0; + dir->u.fat.fd_index = 1; } else if (fs->fs_type == FSTYPE_FAT32 && dir->u.fat.fd_startcluster == fs->fs_rootbase) @@ -1510,10 +1513,12 @@ static int fat_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) dir->u.fat.fd_currcluster = dir->u.fat.fd_startcluster; dir->u.fat.fd_currsector = fat_cluster2sector(fs, fs->fs_rootbase); - dir->u.fat.fd_index = 0; + dir->u.fat.fd_index = 1; } - /* This is not the root directory */ + /* This is not the root directory. Here the fd_index is set to 2, skipping over + * both the "." and ".." entries. + */ else { diff --git a/nuttx/fs/fat/fs_fat32dirent.c b/nuttx/fs/fat/fs_fat32dirent.c index a8c65b8de..54633004f 100644 --- a/nuttx/fs/fat/fs_fat32dirent.c +++ b/nuttx/fs/fat/fs_fat32dirent.c @@ -144,6 +144,8 @@ static inline int fat_getsfname(uint8_t *direntry, char *buffer, static void fat_getlfnchunk(uint8_t *chunk, uint8_t *dest, int nchunk); static inline int fat_getlfname(struct fat_mountpt_s *fs, struct fs_dirent_s *dir); #endif +static int fat_dirverify(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, + uint16_t offset); static int fat_putsfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo); #ifdef CONFIG_FAT_LFN static void fat_initlfname(uint8_t *chunk, int nchunk); @@ -792,13 +794,14 @@ static inline int fat_findalias(struct fat_mountpt_s *fs, memcpy(&tmpinfo, dirinfo, sizeof(struct fat_dirinfo_s)); - /* Then re-initialize to the beginning of the current directory (skipping - * '.' and '..'). + /* Then re-initialize to the beginning of the current directory, skipping + * over the first entry (unused in the root directory and '.' entry in other + * directories). */ tmpinfo.dir.fd_startcluster = tmpinfo.dir.fd_currcluster; tmpinfo.dir.fd_currsector = tmpinfo.fd_seq.ds_startsector; - tmpinfo.dir.fd_index = 2; + tmpinfo.dir.fd_index = 1; /* Search for the single short file name directory entry in this directory */ @@ -1908,6 +1911,56 @@ static inline int fat_getlfname(struct fat_mountpt_s *fs, struct fs_dirent_s *di } #endif +/**************************************************************************** + * Name: fat_dirverify + * + * Desciption: + * Verify that every entry preceding this one is marked with something + * other than DIR0_ALLEMPTY. This is necessary only in the root directory + * of freshly formatted volumes. In that case, all entries are set to + * zero. + * + * This function also assures that the sector containing the entry is in + * the sector cache. + * + ****************************************************************************/ + +static int fat_dirverify(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, + uint16_t offset) +{ + uint8_t *direntry; + uint16_t i; + int ret; + + /* Make sure that the sector containing the directory entry is in the sector + * cache. + */ + + ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); + if (ret < 0) + { + return ret; + } + + /* Check every entry preceding this one */ + + for (i = 0; i < offset; i += DIR_SIZE) + { + /* Is the rest of the directory marked empty? */ + + direntry = &fs->fs_buffer[i]; + if (direntry[DIR_NAME] == DIR0_ALLEMPTY) + { + /* Then mark the just the entry as empty */ + + fs->fs_dirty = true; + direntry[DIR_NAME] = DIR0_EMPTY; + } + } + + return OK; +} + /**************************************************************************** * Name: fat_putsfname * @@ -1921,6 +1974,8 @@ static int fat_putsfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo { uint8_t *direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset]; + /* Write the short directory entry */ + memcpy(&direntry[DIR_NAME], dirinfo->fd_name, DIR_MAXFNAME); #ifdef CONFIG_FAT_LCNAMES DIR_PUTNTRES(direntry, dirinfo->fd_ntflags); @@ -2012,6 +2067,12 @@ static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo int namelen; int ret; + /* Some special handling in case we are writing the first entry of the + * root directory in a freshly formatted volume. + */ + + (void)fat_dirverify(fs, dirinfo, dirinfo->fd_seq.ds_lfnoffset); + /* Get the length of the long file name (size of the fd_lfname array is * LDIR_MAXFNAME+1 we do not have to check the length of the string). * NOTE that remainder is conditionally incremented to include the NUL @@ -2065,18 +2126,11 @@ static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo seqno = LDIR0_LAST | nentries; - /* Make sure that the sector containing the "last" long file name entry - * is in the sector cache (it probably is not). + /* Now loop, writing each long file name entry. We know that the sector + * is in the sector cache because fat_dirverify() assures us that that is + * so. */ - ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); - if (ret < 0) - { - return ret; - } - - /* Now loop, writing each long file name entry */ - for (;;) { /* Get the string offset associated with the directory entry. */ @@ -2201,6 +2255,12 @@ static int fat_putsfdirentry(struct fat_mountpt_s *fs, { uint8_t *direntry; + /* Some special handling in case we are writing the first entry of the + * root directory in a freshly formatted volume. + */ + + (void)fat_dirverify(fs, dirinfo, dirinfo->fd_seq.ds_offset); + /* Initialize the 32-byte directory entry */ direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset]; @@ -2276,11 +2336,11 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, dirinfo->dir.fd_currsector = cluster; } - /* fd_index is the index into the current directory table. Skipping over - * the '.' and '..' entries. + /* fd_index is the index into the current directory table. It is set to one + * to skip over the first, unused entry in the root directory. */ - dirinfo->dir.fd_index = 2; + dirinfo->dir.fd_index = 1; /* If no path was provided, then the root directory must be exactly what * the caller is looking for. @@ -2377,7 +2437,10 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) | DIR_GETFSTCLUSTLO(direntry); - /* Then restart scanning at the new directory */ + /* Then restart scanning at the new directory, skipping over both the + * '.' and '..' entries that exist in all directories EXCEPT the root + * directory. + */ dirinfo->dir.fd_startcluster = cluster; dirinfo->dir.fd_currcluster = cluster; @@ -2426,9 +2489,9 @@ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo dirinfo->dir.fd_currsector = fs->fs_rootbase; } - /* Skip over the '.' and '..' entries */ + /* Skip over the first, unused entry in the root directory. */ - dirinfo->dir.fd_index = 2; + dirinfo->dir.fd_index = 1; /* Is this a path segment a long or a short file. Was a long file * name parsed? @@ -2835,8 +2898,9 @@ int fat_remove(struct fat_mountpt_s *fs, const char *relpath, bool directory) return -EISDIR; } - /* We are asked to delete a directory. Check if this - * sub-directory is empty + /* We are asked to delete a directory. Check if this sub-directory is + * empty (i.e., that there are no valid entries other than the initial + * '.' and '..' entries). */ dirinfo.dir.fd_currcluster = dircluster; -- cgit v1.2.3