From bc247a45a0bfa550241573b19db9c36fb6f48411 Mon Sep 17 00:00:00 2001 From: patacongo Date: Sun, 31 Mar 2013 13:06:22 +0000 Subject: [PATCH] FAT fixes for extending directory entries with long file names. From RonenV git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5802 42af7a65-404d-4744-a932-0658087f49c3 --- ChangeLog | 7 +- arch/arm/src/lpc17xx/lpc17_sdcard.c | 4 +- fs/fat/fs_fat32dirent.c | 59 +++++++++++----- fs/fat/fs_fat32util.c | 103 +++++++++++++++++----------- 4 files changed, 112 insertions(+), 61 deletions(-) diff --git a/ChangeLog b/ChangeLog index 58d078a4ea..878555bb52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4472,4 +4472,9 @@ implementation of the LPC17 DMA and integration into the SDCARD driver (2013-3-29). * arch/arm/src/lpc17xx/lpc17_gdma.c: LPC17 DMA is code complete and - under test. Does not yet work (2013-3-30). \ No newline at end of file + under test. Does not yet work (2013-3-30). + * fs/fat/fs_fat32dirent.c and fs_fat32util.c: Several fixes to the + FAT file system from Ronen Vainish. These fixes mostly involve the + logic to extend directory clusters for the case of long file names + but also include a few important general fixes (such as for storing + 32 bit FAT values) (2013-03-31). diff --git a/arch/arm/src/lpc17xx/lpc17_sdcard.c b/arch/arm/src/lpc17xx/lpc17_sdcard.c index c776395a7e..d8485351b6 100644 --- a/arch/arm/src/lpc17xx/lpc17_sdcard.c +++ b/arch/arm/src/lpc17xx/lpc17_sdcard.c @@ -2466,7 +2466,7 @@ static int lpc17_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, lpc17_configxfrints(priv, SDCARD_DMARECV_MASK); - regval = getreg32(LPC17_SDCARD_DCTRL); + regval = getreg32(LPC17_SDCARD_DCTRL); regval |= SDCARD_DCTRL_DMAEN; putreg32(regval, LPC17_SDCARD_DCTRL); @@ -2549,7 +2549,7 @@ static int lpc17_dmasendsetup(FAR struct sdio_dev_s *dev, { lpc17_sample(priv, SAMPLENDX_BEFORE_ENABLE); - regval = getreg32(LPC17_SDCARD_DCTRL); + regval = getreg32(LPC17_SDCARD_DCTRL); regval |= SDCARD_DCTRL_DMAEN; putreg32(regval, LPC17_SDCARD_DCTRL); diff --git a/fs/fat/fs_fat32dirent.c b/fs/fat/fs_fat32dirent.c index dd10a3f62b..b11900d83c 100644 --- a/fs/fat/fs_fat32dirent.c +++ b/fs/fat/fs_fat32dirent.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/fat/fs_fat32dirent.c * - * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -264,7 +264,7 @@ static inline int fat_parsesfname(const char **path, /* Initialized the name with all spaces */ memset(dirinfo->fd_name, ' ', DIR_MAXFNAME); - + /* Loop until the name is successfully parsed or an error occurs */ endndx = 8; @@ -626,7 +626,7 @@ static inline int fat_createalias(struct fat_dirinfo_s *dirinfo) /* Initialized the short name with all spaces */ memset(dirinfo->fd_name, ' ', DIR_MAXFNAME); - + /* Handle a special case where there is no name. Windows seems to use * the extension plus random stuff then ~1 to pat to 8 bytes. Some * examples: @@ -705,7 +705,7 @@ static inline int fat_createalias(struct fat_dirinfo_s *dirinfo) /* Handle lower case characters */ ch = toupper(ch); - + /* We now have a valid character to add to the name or extension. */ dirinfo->fd_name[ndx++] = ch; @@ -1363,7 +1363,7 @@ static inline int fat_findlfnentry(struct fat_mountpt_s *fs, } /* Continue at the next directory entry */ - + next_entry: if (fat_nextdirentry(fs, &dirinfo->dir) != OK) { @@ -1553,7 +1553,7 @@ static inline int fat_allocatelfnentry(struct fat_mountpt_s *fs, /* 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 @@ -1920,7 +1920,7 @@ static inline int fat_getlfname(struct fat_mountpt_s *fs, struct fs_dirent_s *di 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); @@ -2011,6 +2011,7 @@ static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo uint8_t offset; uint8_t seqno; uint8_t checksum; + off_t startsector; int namelen; int ret; @@ -2051,6 +2052,13 @@ static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo dirinfo->dir.fd_currsector = dirinfo->fd_seq.ds_lfnsector; dirinfo->dir.fd_index = dirinfo->fd_seq.ds_lfnoffset / DIR_SIZE; + /* ds_lfnoffset is the offset in the sector. However fd_index is used as + * index for the entire cluster. We need to add that offset + */ + + startsector = fat_cluster2sector(fs, dirinfo->dir.fd_currcluster); + dirinfo->dir.fd_index += (dirinfo->dir.fd_currsector - startsector) * DIRSEC_NDIRS(fs); + /* Make sure that the alias is unique in this directory*/ ret = fat_uniquealias(fs, dirinfo); @@ -2070,7 +2078,7 @@ static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo /* Make sure that the sector containing the "last" long file name entry * is in the sector cache (it probably is not). */ - + ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector); if (ret < 0) { @@ -2126,7 +2134,7 @@ static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo } /* The remainder should now be zero */ - + DEBUGASSERT(remainder == 0); } else @@ -2328,7 +2336,7 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, * the sector containing the short file name directory entry * in the cache. */ - + ret = fat_findlfnentry(fs, dirinfo); } else @@ -2405,13 +2413,15 @@ int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, /**************************************************************************** * Name: fat_allocatedirentry * - * Desciption: Find a free directory entry + * Desciption: + * Find (or allocate) all needed directory entries to contain the file name * ****************************************************************************/ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo) { int32_t cluster; + int32_t prevcluster; off_t sector; int ret; int i; @@ -2445,7 +2455,7 @@ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo /* Start at the first entry in the root directory. */ dirinfo->dir.fd_index = 0; - + /* Is this a path segment a long or a short file. Was a long file * name parsed? */ @@ -2456,7 +2466,7 @@ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo /* Yes.. Allocate for the sequence of long file name directory * entries plus a short file name directory entry. */ - + ret = fat_allocatelfnentry(fs, dirinfo); } @@ -2495,7 +2505,9 @@ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo /* Try to extend the cluster chain for this directory */ - cluster = fat_extendchain(fs, dirinfo->dir.fd_currcluster); + prevcluster = cluster; + cluster = fat_extendchain(fs, dirinfo->dir.fd_currcluster); + if (cluster < 0) { return cluster; @@ -2524,8 +2536,13 @@ int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo { return ret; } + sector++; } + + /* Start the search again */ + + cluster = prevcluster; } } @@ -2545,6 +2562,7 @@ int fat_freedirentry(struct fat_mountpt_s *fs, struct fat_dirseq_s *seq) struct fs_fatdir_s dir; uint16_t diroffset; uint8_t *direntry; + off_t startsector; int ret; /* Set it to the cluster containing the "last" LFN entry (that appears @@ -2555,6 +2573,13 @@ int fat_freedirentry(struct fat_mountpt_s *fs, struct fat_dirseq_s *seq) dir.fd_currsector = seq->ds_lfnsector; dir.fd_index = seq->ds_lfnoffset / DIR_SIZE; + /* Remember that ds_lfnoffset is the offset in the sector and not the + * cluster. + */ + + startsector = fat_cluster2sector(fs, dir.fd_currcluster); + dir.fd_index += (dir.fd_currsector - startsector) * DIRSEC_NDIRS(fs); + /* Free all of the directory entries used for the sequence of long file name * and for the single short file name entry. */ @@ -2655,7 +2680,7 @@ int fat_dirname2path(struct fat_mountpt_s *fs, struct fs_dirent_s *dir) /* Yes.. Get the name from a sequence of long file name directory * entries. */ - + return fat_getlfname(fs, dir); } else @@ -2691,7 +2716,7 @@ int fat_dirnamewrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo) /* Write the sequence of long file name directory entries (this function * also creates the short file name alias). */ - + ret = fat_putlfname(fs, dirinfo); if (ret != OK) { @@ -2735,7 +2760,7 @@ int fat_dirwrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo, /* Write the sequence of long file name directory entries (this function * also creates the short file name alias). */ - + ret = fat_putlfname(fs, dirinfo); if (ret != OK) { diff --git a/fs/fat/fs_fat32util.c b/fs/fat/fs_fat32util.c index 8edef7735c..d2c74fe14f 100644 --- a/fs/fat/fs_fat32util.c +++ b/fs/fat/fs_fat32util.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/fat/fs_fat32util.c * - * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: @@ -275,7 +275,7 @@ static int fat_checkbootrecord(struct fat_mountpt_s *fs) } else { - fs->fs_rootbase = fs->fs_fatbase + ntotalfatsects; + fs->fs_rootbase = fs->fs_fatbase + ntotalfatsects; } fs->fs_database = fs->fs_fatbase + ntotalfatsects + fs->fs_rootentcnt / DIRSEC_NDIRS(fs); @@ -349,16 +349,17 @@ void fat_putuint16(uint8_t *ptr, uint16_t value16) void fat_putuint32(uint8_t *ptr, uint32_t value32) { uint16_t *val = (uint16_t*)&value32; + #ifdef CONFIG_ENDIAN_BIG /* The bytes always have to be swapped if the target is big-endian */ - fat_putuint16(&ptr[0], val[2]); + fat_putuint16(&ptr[0], val[1]); fat_putuint16(&ptr[2], val[0]); #else /* Byte-by-byte transfer is still necessary if the address is un-aligned */ fat_putuint16(&ptr[0], val[0]); - fat_putuint16(&ptr[2], val[2]); + fat_putuint16(&ptr[2], val[1]); #endif } @@ -579,7 +580,7 @@ int fat_mount(struct fat_mountpt_s *fs, bool writeable) /* Check if the partition exists and, if so, get the bootsector for that * partition and see if we can find the boot record there. */ - + uint8_t part = PART_GETTYPE(i, fs->fs_buffer); fvdbg("Partition %d, offset %d, type %d\n", i, PART_ENTRY(i), part); @@ -670,6 +671,7 @@ int fat_mount(struct fat_mountpt_s *fs, bool writeable) errout_with_buffer: fat_io_free(fs->fs_buffer, fs->fs_hwsectorsize); fs->fs_buffer = 0; + errout: fs->fs_mounted = false; return ret; @@ -886,6 +888,7 @@ off_t fat_getcluster(struct fat_mountpt_s *fs, uint32_t clusterno) cluster &= 0x0fff; } + return cluster; } @@ -898,8 +901,10 @@ off_t fat_getcluster(struct fat_mountpt_s *fs, uint32_t clusterno) if (fat_fscacheread(fs, fatsector) < 0) { /* Read error */ + break; } + return FAT_GETFAT16(fs->fs_buffer, fatindex); } @@ -912,10 +917,13 @@ off_t fat_getcluster(struct fat_mountpt_s *fs, uint32_t clusterno) if (fat_fscacheread(fs, fatsector) < 0) { /* Read error */ + break; } + return FAT_GETFAT32(fs->fs_buffer, fatindex) & 0x0fffffff; } + default: break; } @@ -1046,6 +1054,7 @@ int fat_putcluster(struct fat_mountpt_s *fs, uint32_t clusterno, off_t nextclust break; } + FAT_PUTFAT16(fs->fs_buffer, fatindex, nextcluster & 0xffff); } break; @@ -1055,6 +1064,7 @@ int fat_putcluster(struct fat_mountpt_s *fs, uint32_t clusterno, off_t nextclust unsigned int fatoffset = 4 * clusterno; off_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset); unsigned int fatindex = fatoffset & SEC_NDXMASK(fs); + uint32_t val; if (fat_fscacheread(fs, fatsector) < 0) { @@ -1062,7 +1072,11 @@ int fat_putcluster(struct fat_mountpt_s *fs, uint32_t clusterno, off_t nextclust break; } - FAT_PUTFAT32(fs->fs_buffer, fatindex, nextcluster & 0x0fffffff); + + /* Keep the top 4 bits */ + + val = FAT_GETFAT32(fs->fs_buffer, fatindex) & 0xf0000000; + FAT_PUTFAT32(fs->fs_buffer, fatindex, val | (nextcluster & 0x0fffffff)); } break; @@ -1228,12 +1242,14 @@ int32_t fat_extendchain(struct fat_mountpt_s *fs, uint32_t cluster) startsector = fat_getcluster(fs, newcluster); if (startsector == 0) { - /* Found have found a free cluster break out*/ + /* Found have found a free cluster break out */ + break; } else if (startsector < 0) { /* Some error occurred, return the error number */ + return startsector; } @@ -1254,7 +1270,8 @@ int32_t fat_extendchain(struct fat_mountpt_s *fs, uint32_t cluster) ret = fat_putcluster(fs, newcluster, 0x0fffffff); if (ret < 0) { - /* An error occurred */ + /* An error occurred */ + return ret; } @@ -1290,7 +1307,7 @@ int32_t fat_extendchain(struct fat_mountpt_s *fs, uint32_t cluster) * * Desciption: Read the next directory entry from the sector in cache, * reading the next sector(s) in the cluster as necessary. This function - * must return -ENOSPC if if fails because there are no further entries + * must return -ENOSPC if it fails because there are no further entries * available in the directory. * ****************************************************************************/ @@ -1368,6 +1385,7 @@ int fat_nextdirentry(struct fat_mountpt_s *fs, struct fs_fatdir_s *dir) dir->fd_currcluster = cluster; dir->fd_currsector = fat_cluster2sector(fs, cluster); + ndx = 0; } } } @@ -1416,7 +1434,7 @@ int fat_dirtruncate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo) /* Set the ARCHIVE attribute and update the write time */ DIR_PUTATTRIBUTES(direntry, FATATTR_ARCHIVE); - + writetime = fat_systime2fattime(); DIR_PUTWRTTIME(direntry, writetime & 0xffff); DIR_PUTWRTDATE(direntry, writetime > 16); @@ -1477,7 +1495,7 @@ int fat_fscacheflush(struct fat_mountpt_s *fs) int i; for (i = fs->fs_fatnumfats; i >= 2; i--) - { + { fs->fs_currentsector += fs->fs_nfatsects; ret = fat_hwwrite(fs, fs->fs_buffer, fs->fs_currentsector, 1); if (ret < 0) @@ -1591,31 +1609,32 @@ int fat_ffcacheread(struct fat_mountpt_s *fs, struct fat_file_s *ff, off_t secto */ if (ff->ff_cachesector != sector || (ff->ff_bflags & FFBUFF_VALID) == 0) - { - /* We will need to read the new sector. First, flush the cached - * sector if it is dirty. - */ + { + /* We will need to read the new sector. First, flush the cached + * sector if it is dirty. + */ - ret = fat_ffcacheflush(fs, ff); - if (ret < 0) - { - return ret; - } + ret = fat_ffcacheflush(fs, ff); + if (ret < 0) + { + return ret; + } - /* Then read the specified sector into the cache */ + /* Then read the specified sector into the cache */ - ret = fat_hwread(fs, ff->ff_buffer, sector, 1); - if (ret < 0) - { - return ret; - } + ret = fat_hwread(fs, ff->ff_buffer, sector, 1); + if (ret < 0) + { + return ret; + } - /* Update the cached sector number */ + /* Update the cached sector number */ - ff->ff_cachesector = sector; - ff->ff_bflags |= FFBUFF_VALID; + ff->ff_cachesector = sector; + ff->ff_bflags |= FFBUFF_VALID; } - return OK; + + return OK; } /**************************************************************************** @@ -1632,21 +1651,22 @@ int fat_ffcacheinvalidate(struct fat_mountpt_s *fs, struct fat_file_s *ff) /* Is there anything valid in the buffer now? */ if ((ff->ff_bflags & FFBUFF_VALID) != 0) - { - /* We will invalidate the buffered sector */ + { + /* We will invalidate the buffered sector */ - ret = fat_ffcacheflush(fs, ff); - if (ret < 0) - { - return ret; - } + ret = fat_ffcacheflush(fs, ff); + if (ret < 0) + { + return ret; + } - /* Then discard the current cache contents */ + /* Then discard the current cache contents */ - ff->ff_bflags &= ~FFBUFF_VALID; - ff->ff_cachesector = 0; + ff->ff_bflags &= ~FFBUFF_VALID; + ff->ff_cachesector = 0; } - return OK; + + return OK; } /**************************************************************************** @@ -1694,6 +1714,7 @@ int fat_updatefsinfo(struct fat_mountpt_s *fs) fs->fs_fsidirty = false; } } + return ret; }