From 2e1aaa37db1e7d9aad331b99296edc9e5fd8c89a Mon Sep 17 00:00:00 2001 From: patacongo Date: Wed, 13 Jul 2011 21:53:06 +0000 Subject: More FAT long file name logic git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3782 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/fs/fat/fs_fat32.h | 18 +- nuttx/fs/fat/fs_fat32dirent.c | 897 +++++++++++++++++++++++++++++++++++------- nuttx/fs/fat/fs_fat32util.c | 9 +- 3 files changed, 772 insertions(+), 152 deletions(-) (limited to 'nuttx/fs') diff --git a/nuttx/fs/fat/fs_fat32.h b/nuttx/fs/fat/fs_fat32.h index 5c10de000..e45d011d8 100644 --- a/nuttx/fs/fat/fs_fat32.h +++ b/nuttx/fs/fat/fs_fat32.h @@ -217,7 +217,7 @@ # define LDIR_MAXLFNCHARS 13 /* Max unicode characters in one LFN entry */ # define LDIR_MAXLFNS 20 /* Max number of LFN entries */ -/* LFN directory enty offsets */ +/* LFN directory entry offsets */ # define LDIR_SEQ 0 /* 1@ 0: Sequence number */ # define LDIR_WCHAR1_5 1 /* 10@ 1: File name characters 1-5 (5 Unicode characters) */ @@ -235,6 +235,10 @@ # define LDIR0_E5 DIR0_E5 /* The actual value is 0xe5 */ # define LDIR0_LAST 0x40 /* Last LFN in file name (appears first) */ # define LDIR0_SEQ_MASK 0x1f /* Mask for sequence number (1-20) */ + +/* The LFN entry attribute */ + +# define LDDIR_LFNATTR 0x0f #endif /**************************************************************************** @@ -333,10 +337,10 @@ #define DIR_GETCRTTIMETENTH(p) UBYTE_VAL(p,DIR_CRTTIMETENTH) #ifdef CONFIG_FAT_LFN -# define LDIR_GETSEQ(p) UBYTE_VAL(*p, LDIR_SEQ); -# define LDIR_GETATTRIBUTES(p) UBYTE_VAL(*p, LDIR_ATTRIBUTES); -# define LDIR_GETNTRES(p) UBYTE_VAL(*p, LDIR_NTRES); -# define LDIR_GETCHECKSUM(p) UBYTE_VAL(*p, LDIR_CHECKSUM); +# define LDIR_GETSEQ(p) UBYTE_VAL(*p,LDIR_SEQ); +# define LDIR_GETATTRIBUTES(p) UBYTE_VAL(*p,LDIR_ATTRIBUTES); +# define LDIR_GETNTRES(p) UBYTE_VAL(*p,LDIR_NTRES); +# define LDIR_GETCHECKSUM(p) UBYTE_VAL(*p,LDIR_CHECKSUM); #endif #define MBR_PUTSECPERCLUS(p,v) UBYTE_PUT(p,BS_SECPERCLUS,v) @@ -717,11 +721,13 @@ struct fat_dirseq_s uint16_t ds_lfnoffset; /* Sector offset to last long file name entry */ #endif - /* Sector numbers */ + /* Sector and cluster numbers */ off_t ds_sector; /* Sector of the short file name entry */ #ifdef CONFIG_FAT_LFN + off_t ds_cluster; /* Cluster containing the short file name entry */ off_t ds_lfnsector; /* Sector of the last long name entry */ + off_t ds_lfncluster; /* Cluster containing the long file name entry */ #endif }; diff --git a/nuttx/fs/fat/fs_fat32dirent.c b/nuttx/fs/fat/fs_fat32dirent.c index 8703ae47a..f0db53e47 100644 --- a/nuttx/fs/fat/fs_fat32dirent.c +++ b/nuttx/fs/fat/fs_fat32dirent.c @@ -700,6 +700,32 @@ static inline int fat_uniquealias(const char **path, } #endif +/**************************************************************************** + * Name: fat_lfnchecksum + * + * Desciption: Caculate the checksum of . + * + * Returned value: + * OK - The alias is unique. + * <0 - Otherwise an negated error is returned. + * + ****************************************************************************/ + +#ifdef CONFIG_FAT_LFN +static uint8_t fat_lfnchecksum(const uint8_t *sfname) +{ + uint8_t sum = 0; + int i; + + for (i = DIR_MAXFNAME; i; i--) + { + sum = ((sum & 1) << 7) + (sum >> 1) + *sfname++; + } + + return sum; +} +#endif + /**************************************************************************** * Name: fat_path2dirname * @@ -728,41 +754,581 @@ static int fat_path2dirname(const char **path, struct fat_dirinfo_s *dirinfo, */ ret = fat_parselfname(path, dirinfo, terminator); + } + + return ret; +#else + /* Only short, 8+3 filenames supported */ + + return fat_parsesfname(path, dirinfo, terminator); +#endif +} + +/**************************************************************************** + * Name: fat_findsfnentry + * + * Desciption: Find a short file name directory entry. + * + ****************************************************************************/ + +static inline int fat_findsfnentry(struct fat_mountpt_s *fs, + struct fat_dirinfo_s *dirinfo) +{ + uint16_t diroffset; + uint8_t *direntry; + int ret; + + /* Search, beginning with the current sector, for a directory entry this + * the match shore name + */ + + for (;;) + { + /* Read the next sector into memory */ + + ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); if (ret < 0) { - /* Not a valid long file name */ + return ret; + } + + /* Get a pointer to the directory entry */ + + diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index); + direntry = &fs->fs_buffer[diroffset]; + + /* Check if we are at the end of the directory */ + + if (direntry[DIR_NAME] == DIR0_ALLEMPTY) + { + return -ENOENT; + } + + /* Check if we have found the directory entry that we are looking for */ + + if (direntry[DIR_NAME] != DIR0_EMPTY && + !(DIR_GETATTRIBUTES(direntry) & FATATTR_VOLUMEID) && + !memcmp(&direntry[DIR_NAME], dirinfo->fd_name, DIR_MAXFNAME) ) + { + /* Yes.. Return success */ + + dirinfo->fd_seq.ds_sector = fs->fs_currentsector; + dirinfo->fd_seq.ds_offset = diroffset; +#ifdef CONFIG_FAT_LFN + dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster; +#endif + + /* Position the last long file name directory entry at the same + * position. + */ + +#ifdef CONFIG_FAT_LFN + dirinfo->fd_seq.ds_lfnsector = dirinfo->fd_seq.ds_sector; + dirinfo->fd_seq.ds_lfnoffset = dirinfo->fd_seq.ds_offset; + dirinfo->fd_seq.ds_lfncluster = dirinfo->fd_seq.ds_cluster; +#endif + return OK; + } + + /* No... get the next directory index and try again */ + + if (fat_nextdirentry(fs, &dirinfo->dir) != OK) + { + return -ENOENT; + } + } +} + +/**************************************************************************** + * Name: fat_cmplfnchunk + * + * Desciption: There are 13 characters per LFN entry, broken up into three + * chunks for characts 1-5, 6-11, and 12-13. This function will perform + * the comparison of a single chunk. + * + ****************************************************************************/ + +#ifdef CONFIG_FAT_LFN +static bool fat_cmplfnchunk(wchar_t *chunk, uint8_t *substr, int nchunk) +{ + uint8_t *ptr; + int i; + + /* Check bytes 1-nchunk */ + + for (i = 0; i < nchunk; i++) + { + wchar_t wch; + + /* If we encounter the NUL terminator in the name string, then it is + * a match -- return TRUE. + */ + + if (*substr == 0) + { + return true; + } + + /* Get the next unicode character from the chunk. We only handle ASCII. + * For ASCII, the upper byte should be zero and the lower should match + * the ASCII code. + */ + + wch = fat_getuint16(chunk); + if (wch & 0xff != (wchar_t)*substr) + { + return false; + } + + /* Yes.. the characters match. Try the next */ + + chunk++; + substr++; + } + + /* All of the characters in the chunk match.. Return success */ + + return true; +} +#endif + +/**************************************************************************** + * Name: fat_cmplfname + * + * Desciption: Given an LFN directory entry, compare a substring of the name + * to a portion in the directory entry. + * + ****************************************************************************/ + +#ifdef CONFIG_FAT_LFN +static bool fat_cmplfname(uint8_t *direntry, uint8_t *substr) +{ + uint8_t *chunk; + int i; + + /* Check bytes 1-5 */ + + chunk = LDIR_PTRWCHAR1_5(direntry); + if (fat_cmplfnchunk(chunk, substr, 5)) + { + /* Check bytes 6-11 */ + + chunk = LDIR_PTRWCHAR6_11(direntry); + if (fat_cmplfnchunk(chunk, &substr[5], 6)) + { + /* Check bytes 6-11 */ + + chunk = efine LDIR_WCHAR12_13; + return fat_cmplfnchunk(chunk, &substr[11], 2); + } + } + + return false; +} +#endif + +/**************************************************************************** + * Name: fat_findlfnentry + * + * Desciption: Find a sequence of long file name directory entries. + * + ****************************************************************************/ +#ifdef CONFIG_FAT_LFN +static inline int fat_findlfnentry(struct fat_mountpt_s *fs, + struct fat_dirinfo_s *dirinfo) +{ + uint16_t diroffset; + uint8_t *direntry; + uint8_t lastseq; + uint8_t seqno; + uint8_t nfullentries; + uint8_t nentries; + uint8_t remainder; + uint8_t cksum; + int offset; + int namelen; + int ret; + + /* Get the length of the long file name and make sure that it does + * not exceed the maximum. + */ + + namelen = strlen(dirinfo.fd_lfname); + if (namelen > LDIR_MAXFNAME) + { + return -EINVAL; + } + + /* How many LFN directory entries are we expecting? */ + + nfullentries = namelen / LDIR_MAXLFNCHARS; + remainder = namelen - nfullentries * LDIR_MAXLFNCHARS; + nentries = nfullentries; + if (remainder > 0) + { + nentries++; + } + DEBUGASSERT(nentries > 0 && nentries <= LDIR_MAXLFNS); + + /* This is the first sequency number we are looking for, the sequence + * number of the last LFN entry (remember that they appear in reverse + * order.. from last to first). + */ + + lastseq = LDIR_SEQ | nentries; + seqno = lastseq; + + /* Search, beginning with the current sector, for a directory entry this + * the match shore name + */ + + for (;;) + { + /* Read the next sector into memory */ + + ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); + if (ret < 0) + { return ret; } - /* It is a valid long file name, create a quick short file name - * alias. + /* Get a pointer to the directory entry */ + + diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index); + direntry = &fs->fs_buffer[diroffset]; + + /* Check if we are at the end of the directory */ + + if (direntry[DIR_NAME] == DIR0_ALLEMPTY) + { + return -ENOENT; + } + + /* Is this an LFN entry? Does it have the sequence number we are + * looking for? + */ + + if (LDIR_GETATTRIBUTES(direntry) != LDDIR_LFNATTR) || + LDIR_GETSEQ(direntry) != seqno); + { + /* No, restart the search at the next entry */ + + seqno = lastseq; + goto nextentry; + } + + /* Yes.. If this is not the "last" LFN entry, then the checksum must + * also be the same. + */ + + if (seqno == lastseq) + { + /* Just save the checksum for subsequent checks */ + + cksum = LDIR_GETCHECKSUM(direntry); + } + + /* Not the first entry in the sequence. Does the checksum match the + * previous sequences? + */ + + else if (cksum != LDIR_GETCHECKSUM(direntry)) + { + /* No, restart the search at the next entry */ + + seqno = lastseq; + goto nextentry; + } + + /* Check if the name substring in this LFN matches the corresponding + * substring of the name we are looking for. */ - ret = fat_createalias(path, dirinfo); - DEBUGASSERT(ret == OK); /* This should never fail */ + offset = ((seqno & LDIR0_SEQ_MASK) - 1) * LDIR_MAXLFNCHARS; + if (fat_cmplfname(direntry, &dirinfo.fd_lfname[offset])) + { + /* Yes.. it matches. Check the sequence number. Is this the + * "last" LFN entry (i.e., the one that appears first)? + */ + + if (seqno == lastseq) + { + /* Yes.. Save information about this LFN entry position */ + + dirinfo->fd_seq.ds_lfnsector = fs->fs_currentsector; + dirinfo->fd_seq.ds_lfnoffset = diroffset; + dirinfo->fd_seq.ds_lfncluster = dirinfo->dir.fd_currcluster; + seqno &= LDIR0_SEQ_MASK; + } + + /* Is this the first sequence number (i.e., the LFN entry that + * will appear last)? + */ + + if (seqno == 1) + { + /* We have found all of the LFN entries. The next directory + * entry should be the one containing the short file name + * alias and all of the meat about the file or directory. + */ + + if (fat_nextdirentry(fs, &dirinfo->dir) != OK) + { + return -ENOENT; + } + + /* Verify the checksum */ + + if (fat_lfnchecksum(dirinfo->fd_name) == cksum) + { + /* Success! Save the position of the directory entry and + * return success. + */ + + dirinfo->fd_seq.ds_sector = fs->fs_currentsector; + dirinfo->fd_seq.ds_offset = diroffset; + dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster; + return OK; + } + + /* Bad news.. reset and continue with this entry (which is + * probably not an LFN entry unless the file systen is + * seriously corrupted. + */ + + seqno = lastseq; + continue; + } - /* Make sure that the alias is unique */ + /* No.. there are more LFN entries to go. Decrement the sequence + * number and check the next directory entry. + */ - ret = fat_uniquealias(path, dirinfo); + seqno--; + } + else + { + /* No.. the names do not match. Restart the search at the next + * entry. + */ + + seqno = lastseq; + } + + /* Continue at the next directory entry */ + +next_entry: + if (fat_nextdirentry(fs, &dirinfo->dir) != OK) + { + return -ENOENT; + } } +} +#endif - return ret; -#else - /* Only short, 8+3 filenames supported */ +/**************************************************************************** + * Name: fat_allocatesfnentry + * + * Desciption: Find a free directory entry for a short file name entry. + * + ****************************************************************************/ - return fat_parsesfname(path, dirinfo, terminator); +static inline int fat_allocatesfnentry(struct fat_mountpt_s *fs, + struct fat_dirinfo_s *dirinfo) +{ + uint16_t diroffset; + uint8_t *direntry; + uint8_t ch; + int ret; + + for (;;) + { + /* Read the directory sector into fs_buffer */ + + ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); + if (ret < 0) + { + /* Make sure that the return value is NOT -ENOSPC */ + + return -EIO; + } + + /* Get a pointer to the entry at fd_index */ + + diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE; + direntry = &fs->fs_buffer[diroffset]; + + /* Check if this directory entry is empty */ + + ch = direntry[DIR_NAME]; + if (ch == DIR0_ALLEMPTY || ch == DIR0_EMPTY) + { + /* It is empty -- we have found a directory entry */ + + dirinfo->fd_seq.ds_sector = fs->fs_currentsector; + dirinfo->fd_seq.ds_offset = diroffset; +#ifdef CONFIG_FAT_LFN + dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster; +#endif + + /* Set the "last" long file name offset to the same entry */ + +#ifdef CONFIG_FAT_LFN + dirinfo->fd_seq.ds_lfnsector = dirinfo->fd_seq.ds_sector; + dirinfo->fd_seq.ds_lfnoffset = dirinfo->fd_seq.ds_offset; + dirinfo->fd_seq.ds_lfncluster = dirinfo->fd_seq.ds_cluster; #endif + return OK; + } + + /* It is not empty try the next one */ + + ret = fat_nextdirentry(fs, &dirinfo->dir); + if (ret < 0) + { + /* This will return -ENOSPC if we have examined all of the + * directory entries without finding a free entry. + */ + + return ret; + } + } } /**************************************************************************** - * Name: fat_checkname + * Name: fat_allocatelfnentry * - * Desciption: Given a path to something that may or may not be in the file - * system, return the directory entry of the item. + * Desciption: Find a sequence of free directory entries for a several long + * and one short file name entry. * ****************************************************************************/ +#ifdef CONFIG_FAT_LFN +static inline int fat_allocatelfnentry(struct fat_mountpt_s *fs, + struct fat_dirinfo_s *dirinfo) +{ + uint16_t diroffset; + uint8_t *direntry; + uint8_t nentries; + uint8_t remainder; + uint8_t needed; + int namelen; + int ret; + + /* Get the length of the long file name and make sure that it does + * not exceed the maximum. + */ + + namelen = strlen(dirinfo->fd_lfname); + if (namelen > LDIR_MAXFNAME) + { + return -EINVAL; + } + + /* How many LFN directory entries are we expecting? */ + + nentries = namelen / LDIR_MAXLFNCHARS; + remainder = namelen - nfullentries * LDIR_MAXLFNCHARS; + if (remainder > 0) + { + nentries++; + } + DEBUGASSERT(nentries > 0 && nentries <= LDIR_MAXLFNS); + + /* Plus another for short file name entry that follows the sequence of LFN + * entries. + */ + + nentries++; + + /* Now, search the directory looking for a sequence for free entries that + * long. + */ + + needed = nentries; + for (;;) + { + /* Read the directory sector into fs_buffer */ + + ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); + if (ret < 0) + { + /* Make sure that the return value is NOT -ENOSPC */ + + return -EIO; + } + + /* Get a pointer to the entry at fd_index */ + + diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE; + direntry = &fs->fs_buffer[diroffset]; + + /* Check if this directory entry is empty */ + + ch = direntry[DIR_NAME]; + if (ch == DIR0_ALLEMPTY || ch == DIR0_EMPTY) + { + /* It is empty -- we have found a directory entry. Is this the + * "last" LFN entry (i.e., the one that occurs first)? + */ + + if (needed == nentries) + { + /* Yes.. remember the position of this entry */ + + dirinfo->fd_seq.ds_lfnsector = fs->fs_currentsector; + dirinfo->fd_seq.ds_lfnoffset = diroffset; + dirinfo->fd_seq.ds_lfncluster = dirinfo->dir.fd_currcluster; + } + + /* Is this last entry we need (i.e., the entry for the short + * file name entry)? + */ + + if (needed <= 1) + { + /* Yes.. remember the position of this entry and return + * success. + */ + + dirinfo->fd_seq.ds_sector = fs->fs_currentsector; + dirinfo->fd_seq.ds_offset = diroffset; + dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster; + return OK; + } + + /* Otherwise, just decrement the number of directory entries + * needed and continue looking. + */ + + needed--; + } + + /* The directory entry is not available */ + + else + { + /* Reset the search and continue looking */ + + needed = nentries; + } + + /* Try the next directory entry */ + + ret = fat_nextdirentry(fs, &dirinfo->dir); + if (ret < 0) + { + /* This will return -ENOSPC if we have examined all of the + * directory entries without finding a free entry. + */ + + return ret; + } + } +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -779,21 +1345,20 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, const char *path) { off_t cluster; - uint16_t diroffset; - uint8_t *direntry = NULL; + uint8_t *direntry; char terminator; int ret; - /* Initialize to traverse the chain. Set it to the cluster of - * the root directory + /* Initialize to traverse the chain. Set it to the cluster of the root + * directory */ cluster = fs->fs_rootbase; if (fs->fs_type == FSTYPE_FAT32) { - /* For FAT32, the root directory is variable sized and is a - * cluster chain like any other directory. fs_rootbase holds - * the first cluster of the root directory. + /* For FAT32, the root directory is variable sized and is a cluster + * chain like any other directory. fs_rootbase holds the first + * cluster of the root directory. */ dirinfo->dir.fd_startcluster = cluster; @@ -815,8 +1380,8 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, dirinfo->dir.fd_index = 0; - /* If no path was provided, then the root directory must be exactly - * what the caller is looking for. + /* If no path was provided, then the root directory must be exactly what + * the caller is looking for. */ if (*path == '\0') @@ -829,8 +1394,8 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, for (;;) { - /* Convert the next the path segment name into the kind of - * name that we would see in the directory entry. + /* Convert the next the path segment name into the kind of name that + * we would see in the directory entry. */ ret = fat_path2dirname(&path, dirinfo, &terminator); @@ -843,65 +1408,46 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, return ret; } - /* Now search the current directory entry for an entry with this - * matching name. + /* Is this a path segment a long or a short file. Was a long file + * name parsed? */ - for (;;) +#ifdef CONFIG_FAT_LFN + if (dirinfo.fd_lfname[0] != '\0') { - /* Read the next sector into memory */ - - ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); - if (ret < 0) - { - return ret; - } - - /* Get a pointer to the directory entry */ - - diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index); - direntry = &fs->fs_buffer[diroffset]; - - /* Check if we are at the end of the directory */ - - if (direntry[DIR_NAME] == DIR0_ALLEMPTY) - { - return -ENOENT; - } - - /* Check if we have found the directory entry that we are looking for */ + /* Yes.. Search for the sequence of long file name directory + * entries. + */ + + ret = fat_findlfnentry(fs, dirinfo); + } - if (direntry[DIR_NAME] != DIR0_EMPTY && - !(DIR_GETATTRIBUTES(direntry) & FATATTR_VOLUMEID) && - !memcmp(&direntry[DIR_NAME], dirinfo->fd_name, DIR_MAXFNAME) ) - { - /* Yes.. break out of the loop */ + /* No.. Search for the single short file name directory entry */ - break; - } + else +#endif + { + ret = fat_findsfnentry(fs, dirinfo); + } - /* No... get the next directory index and try again */ + /* Did we find the directory entries? */ - if (fat_nextdirentry(fs, &dirinfo->dir) != OK) - { - return -ENOENT; - } + if (ret < 0) + { + return ret; } - /* We get here only if we have found a directory entry that matches - * the path element that we are looking for. - * - * If the terminator character in the path was the end of the string + /* If the terminator character in the path was the end of the string * then we have successfully found the directory entry that describes * the path. */ if (!terminator) { - /* Return the sector and offset to the matching directory entry */ + /* Return success meaning that the description the matching + * directory entry is in dirinfo. + */ - dirinfo->fd_seq.ds_sector = fs->fs_currentsector; - dirinfo->fd_seq.ds_offset = diroffset; return OK; } @@ -910,6 +1456,7 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, * the thing that we found is, indeed, a directory. */ + direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset]; if (!(DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY)) { /* Ooops.. we found something else */ @@ -942,115 +1489,117 @@ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo { int32_t cluster; off_t sector; - uint16_t diroffset; - uint8_t *direntry; - uint8_t ch; int ret; int i; /* Re-initialize directory object */ cluster = dirinfo->dir.fd_startcluster; - if (cluster) - { - /* Cluster chain can be extended */ - - dirinfo->dir.fd_currcluster = cluster; - dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster); - } - else - { - /* Fixed size FAT12/16 root directory is at fixxed offset/size */ - dirinfo->dir.fd_currsector = fs->fs_rootbase; - } - dirinfo->dir.fd_index = 0; + /* Loop until we successfully allocate the sequence of directory entries + * or until to fail to extend the directory cluster chain. + */ for (;;) { - /* Read the directory sector into fs_buffer */ + /* Can this cluster chain be extended */ - ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); - if (ret < 0) + if (cluster) { - return ret; - } + /* Cluster chain can be extended */ - /* Get a pointer to the entry at fd_index */ + dirinfo->dir.fd_currcluster = cluster; + dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster); + } + else + { + /* Fixed size FAT12/16 root directory is at fixxed offset/size */ - diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE; - direntry = &fs->fs_buffer[diroffset]; + dirinfo->dir.fd_currsector = fs->fs_rootbase; + } + dirinfo->dir.fd_index = 0; - /* Check if this directory entry is empty */ + /* Is this a path segment a long or a short file. Was a long file + * name parsed? + */ - ch = direntry[DIR_NAME]; - if (ch == DIR0_ALLEMPTY || ch == DIR0_EMPTY) +#ifdef CONFIG_FAT_LFN + if (dirinfo.fd_lfname[0] != '\0') { - /* It is empty -- we have found a directory entry */ + /* Yes.. Allocate for the sequence of long file name directory + * entries plus a short file name directory entry. + */ + + ret = fat_allocatelfnentry(fs, dirinfo); + } - dirinfo->fd_seq.ds_sector = fs->fs_currentsector; - dirinfo->fd_seq.ds_offset = diroffset; - return OK; + /* No.. Allocate only a short file name directory entry */ + + else +#endif + { + ret = fat_allocatesfnentry(fs, dirinfo); } - ret = fat_nextdirentry(fs, &dirinfo->dir); - if (ret < 0) + /* Did we successfully allocate the directory entries? If the error + * value is -ENOSPC, then we can try to extend the directory cluster + * (we can't handle other return values) + */ + + if (ret == OK || ret != -ENOSPC) { return ret; } - } - /* If we get here, then we have reached the end of the directory table - * in this sector without finding a free directory enty. - * - * It this is a fixed size dirctory entry, then this is an error. - * Otherwise, we can try to extend the directory cluster chain to - * make space for the new directory entry. - */ + /* If we get here, then we have reached the end of the directory table + * in this sector without finding a free directory entry. + * + * It this is a fixed size dirctory entry, then this is an error. + * Otherwise, we can try to extend the directory cluster chain to + * make space for the new directory entry. + */ - if (!cluster) - { - /* The size is fixed */ - return -ENOSPC; - } + if (!cluster) + { + /* The size is fixed */ - /* Try to extend the cluster chain for this directory */ + return -ENOSPC; + } - cluster = fat_extendchain(fs, dirinfo->dir.fd_currcluster); - if (cluster < 0) - { - return cluster; - } + /* Try to extend the cluster chain for this directory */ - /* Flush out any cached data in fs_buffer.. we are going to use - * it to initialize the new directory cluster. - */ + cluster = fat_extendchain(fs, dirinfo->dir.fd_currcluster); + if (cluster < 0) + { + return cluster; + } - ret = fat_fscacheflush(fs); - if (ret < 0) - { - return ret; - } + /* Flush out any cached data in fs_buffer.. we are going to use + * it to initialize the new directory cluster. + */ - /* Clear all sectors comprising the new directory cluster */ + ret = fat_fscacheflush(fs); + if (ret < 0) + { + return ret; + } - fs->fs_currentsector = fat_cluster2sector(fs, cluster); - memset(fs->fs_buffer, 0, fs->fs_hwsectorsize); + /* Clear all sectors comprising the new directory cluster */ - sector = sector; - for (i = fs->fs_fatsecperclus; i; i--) - { - ret = fat_hwwrite(fs, fs->fs_buffer, sector, 1); - if ( ret < 0) + fs->fs_currentsector = fat_cluster2sector(fs, cluster); + memset(fs->fs_buffer, 0, fs->fs_hwsectorsize); + + sector = sector; + for (i = fs->fs_fatsecperclus; i; i--) { - return ret; + ret = fat_hwwrite(fs, fs->fs_buffer, sector, 1); + if ( ret < 0) + { + return ret; + } + sector++; } - sector++; } - - dirinfo->fd_seq.ds_sector = fs->fs_currentsector; - dirinfo->fd_seq.ds_offset = 0; - return OK; } /**************************************************************************** @@ -1062,10 +1611,71 @@ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo int fat_freedirentry(struct fat_mountpt_s *fs, struct fat_dirseq_s *seq) { +#ifdef CONFIG_FAT_LFN + uint16_t diroffset; + uint8_t *direntry; + int ret; + + /* Set it to the cluster containing the "last" LFN entry (that appears + * first on the media). + */ + + dirinfo->dir.fd_startcluster = dirinfo->fd_seq.fd_lfncluster; + dirinfo->dir.fd_currcluster = dirinfo->fd_seq.fd_lfnsector; + dirinfo->dir.fd_index = dirinfo->fd_seq.fd_lfnoffset / DIR_SIZE; + + /* Free all of the directory entries used for the sequence of long file name + * and for the single short file name entry. + */ + + for (;;) + { + /* Read the directory sector into the sector cache */ + + ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); + if (ret < 0) + { + return ret; + } + + /* Get a pointer to the directory entry */ + + diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE; + direntry = &fs->fs_buffer[diroffset]; + + /* Then mark the entry as deleted */ + + direntry[DIR_NAME] = DIR0_EMPTY; + fs->fs_dirty = true; + + /* Did we just free the single short file name entry? */ + + if (dirinfo->dir.fd_currsector == dirinfo->fd_seq.fd_sector && + diroffset == dirinfo->fd_seq.fd_sector) + { + /* Yes.. then we are finished. flush anything remaining in the + * cache and return, probably successfully. + */ + + return fat_fscacheflush(fs); + } + + /* There are moe entries to go.. Try the next directory entry */ + + ret = fat_nextdirentry(fs, &dirinfo->dir); + if (ret < 0) + { + return ret; + } + } + +#else uint8_t *direntry; int ret; - /* Make sure that the sector containing the directory entry is in the + /* Free the single short file name entry. + * + * Make sure that the sector containing the directory entry is in the * cache. */ @@ -1080,6 +1690,7 @@ int fat_freedirentry(struct fat_mountpt_s *fs, struct fat_dirseq_s *seq) } return ret; +#endif } /**************************************************************************** diff --git a/nuttx/fs/fat/fs_fat32util.c b/nuttx/fs/fat/fs_fat32util.c index a8c745e71..ca4137a5a 100644 --- a/nuttx/fs/fat/fs_fat32util.c +++ b/nuttx/fs/fat/fs_fat32util.c @@ -1181,7 +1181,9 @@ int32_t fat_extendchain(struct fat_mountpt_s *fs, uint32_t cluster) * Name: fat_nextdirentry * * Desciption: Read the next directory entry from the sector in cache, - * reading the next sector(s) in the cluster as necessary. + * reading the next sector(s) in the cluster as necessary. This function + * must return -ENOSPC if if fails because there are no further entries + * available in the directory. * ****************************************************************************/ @@ -1221,7 +1223,7 @@ int fat_nextdirentry(struct fat_mountpt_s *fs, struct fs_fatdir_s *dir) * the root directory. */ - return ERROR; + return -ENOSPC; } } else @@ -1250,7 +1252,8 @@ int fat_nextdirentry(struct fat_mountpt_s *fs, struct fs_fatdir_s *dir) if (cluster < 2 || cluster >= fs->fs_nclusters) { /* No, we have probably reached the end of the cluster list */ - return ERROR; + + return -ENOSPC; } /* Initialize for new cluster */ -- cgit v1.2.3