Newer
Older
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
/****************************************************************************
* Name: fat_finddirentry
*
* Desciption: Given a path to something that may or may not be in the file
* system, return the description of the directory entry of the requested
* item.
*
* NOTE: As a side effect, this function returns with the sector containing
* the short file name directory entry in the cache.
*
****************************************************************************/
int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
const char *path)
{
off_t cluster;
uint8_t *direntry;
char terminator;
int ret;
/* 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.
*/
dirinfo->dir.fd_startcluster = cluster;
dirinfo->dir.fd_currcluster = cluster;
dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster);
}
else
{
/* For FAT12/16, the first sector of the root directory is a sector
* relative to the first sector of the fat volume.
*/
dirinfo->dir.fd_startcluster = 0;
dirinfo->dir.fd_currcluster = 0;
dirinfo->dir.fd_currsector = cluster;
}
/* fd_index is the index into the current directory table */
dirinfo->dir.fd_index = 0;
/* If no path was provided, then the root directory must be exactly what
* the caller is looking for.
*/
if (*path == '\0')
{
dirinfo->fd_root = true;
return OK;
}
/* This is not the root directory */
dirinfo->fd_root = false;
/* Now loop until the directory entry corresponding to the path is found */
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
for (;;)
{
/* 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);
if (ret < 0)
{
/* ERROR: The filename contains invalid characters or is
* too long.
*/
return ret;
}
/* Is this a path segment a long or a short file. Was a long file
* name parsed?
*/
#ifdef CONFIG_FAT_LFN
if (dirinfo->fd_lfname[0] != '\0')
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
{
/* Yes.. Search for the sequence of long file name directory
* entries. NOTE: As a side effect, this function returns with
* the sector containing the short file name directory entry
* in the cache.
*/
ret = fat_findlfnentry(fs, dirinfo);
}
else
#endif
{
/* No.. Search for the single short file name directory entry */
ret = fat_findsfnentry(fs, dirinfo);
}
/* Did we find the directory entries? */
if (ret < 0)
{
return ret;
}
/* 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 success meaning that the description the matching
* directory entry is in dirinfo.
*/
return OK;
}
/* No.. then we have found one of the intermediate directories on
* the way to the final path target. In this case, make sure
* 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 */
return -ENOTDIR;
}
/* Get the cluster number of this directory */
cluster =
((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
DIR_GETFSTCLUSTLO(direntry);
/* Then restart scanning at the new directory */
dirinfo->dir.fd_currcluster = dirinfo->dir.fd_startcluster = cluster;
dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster);
dirinfo->dir.fd_index = 2;
}
}
/****************************************************************************
* Name: fat_allocatedirentry
*
* Desciption: Find a free directory entry
*
****************************************************************************/
int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
{
int32_t cluster;
off_t sector;
int ret;
int i;
/* Re-initialize directory object */
cluster = dirinfo->dir.fd_startcluster;
/* Loop until we successfully allocate the sequence of directory entries
* or until to fail to extend the directory cluster chain.
*/
for (;;)
{
/* Can this cluster chain be extended */
dirinfo->dir.fd_currcluster = cluster;
dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster);
}
else
{
/* Fixed size FAT12/16 root directory is at fixed offset/size */
dirinfo->dir.fd_currsector = fs->fs_rootbase;
}
dirinfo->dir.fd_index = 0;
/* Is this a path segment a long or a short file. Was a long file
* name parsed?
*/
if (dirinfo->fd_lfname[0] != '\0')
/* Yes.. Allocate for the sequence of long file name directory
* entries plus a short file name directory entry.
*/
ret = fat_allocatelfnentry(fs, dirinfo);
}
/* No.. Allocate only a short file name directory entry */
else
#endif
{
ret = fat_allocatesfnentry(fs, dirinfo);
/* 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 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 */
/* Try to extend the cluster chain for this directory */
cluster = fat_extendchain(fs, dirinfo->dir.fd_currcluster);
if (cluster < 0)
{
return cluster;
}
/* Flush out any cached data in fs_buffer.. we are going to use
* it to initialize the new directory cluster.
*/
ret = fat_fscacheflush(fs);
if (ret < 0)
{
return ret;
}
/* Clear all sectors comprising the new directory cluster */
fs->fs_currentsector = fat_cluster2sector(fs, cluster);
memset(fs->fs_buffer, 0, fs->fs_hwsectorsize);
sector = fs->fs_currentsector;
ret = fat_hwwrite(fs, fs->fs_buffer, sector, 1);
if ( ret < 0)
{
return ret;
}
sector++;
}
}
}
/****************************************************************************
* Name: fat_freedirentry
*
* Desciption: Free the directory entry.
*
* NOTE: As a side effect, this function returns with the sector containing
* the deleted short file name directory entry in the cache.
*
****************************************************************************/
int fat_freedirentry(struct fat_mountpt_s *fs, struct fat_dirseq_s *seq)
struct fs_fatdir_s dir;
uint8_t *direntry;
int ret;
/* Set it to the cluster containing the "last" LFN entry (that appears
* first on the media).
*/
dir.fd_currcluster = seq->ds_lfncluster;
dir.fd_currsector = seq->ds_lfnsector;
dir.fd_index = seq->ds_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, dir.fd_currsector);
if (ret < 0)
{
return ret;
}
/* Get a pointer to the directory entry */
diroffset = (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 (dir.fd_currsector == seq->ds_sector &&
diroffset == seq->ds_offset)
{
/* Yes.. then we are finished. flush anything remaining in the
* cache and return, probably successfully.
*/
return fat_fscacheflush(fs);
}
/* There are more entries to go.. Try the next directory entry */
ret = fat_nextdirentry(fs, &dir);
if (ret < 0)
{
return ret;
}
}
#else
uint8_t *direntry;
int ret;
/* Free the single short file name entry.
*
* Make sure that the sector containing the directory entry is in the
* cache.
*/
ret = fat_fscacheread(fs, seq->ds_sector);
if (ret == OK)
{
/* Then mark the entry as deleted */
direntry = &fs->fs_buffer[seq->ds_offset];
direntry[DIR_NAME] = DIR0_EMPTY;
fs->fs_dirty = true;
}
return ret;
}
/****************************************************************************
* Name: fat_dirname2path
*
* Desciption: Convert a filename in a raw directory entry into a user
* filename. This is essentially the inverse operation of that performed
* by fat_path2dirname. See that function for more details.
*
****************************************************************************/
int fat_dirname2path(struct fat_mountpt_s *fs, struct fs_dirent_s *dir)
uint16_t diroffset;
uint8_t *direntry;
uint8_t attribute;
#endif
/* Get a reference to the current directory entry */
diroffset = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
direntry = &fs->fs_buffer[diroffset];
/* Does this entry refer to the last entry of a long file name? */
#ifdef CONFIG_FAT_LFN
attribute = DIR_GETATTRIBUTES(direntry);
if (((*direntry & LDIR0_LAST) != 0 && attribute == LDDIR_LFNATTR))
{
/* Yes.. Get the name from a sequence of long file name directory
* entries.
*/
return fat_getlfname(fs, dir);
{
/* No.. Get the name from a short file name directory entries */
return fat_getsfname(direntry, dir->fd_dir.d_name, NAME_MAX+1);
}
}
/****************************************************************************
* Name: fat_dirnamewrite
*
* Desciption: Write the (possibly long) directory entry name. This function
* is called only from fat_rename to write the new file name.
* Assumption: The directory sector containing the short file name entry
* is in the cache. *NOT* the sector containing the last long file name
* entry!
*
****************************************************************************/
int fat_dirnamewrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
{
#ifdef CONFIG_FAT_LFN
int ret;
/* Is this a long file name? */
if (dirinfo->fd_lfname[0] != '\0')
{
/* 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)
{
return ret;
}
}
/* On return, fat_lfsfname() will leave the short file name entry in the
* cache. So we can just fall throught to write that directory entry, perhaps
* using the short file name alias for the long file name.
*/
return fat_putsfname(fs, dirinfo);
}
/****************************************************************************
* Name: fat_dirwrite
*
* Desciption: Write a directory entry, possibly with a long file name.
* Called from:
*
* fat_mkdir() to write the new FAT directory entry.
* fat_dircreate() to create any new directory entry.
*
* Assumption: The directory sector is in the cache. The caller will write
* sector information.
*
****************************************************************************/
int fat_dirwrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
uint8_t attributes, uint32_t fattime)
{
#ifdef CONFIG_FAT_LFN
int ret;
/* Does this directory entry have a long file name? */
if (dirinfo->fd_lfname[0] != '\0')
/* 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)
{
return ret;
}
}
/* On return, fat_lfsfname() will leave the short file name entry in the
* cache. So we can just fall throught to write that directory entry, perhaps
* using the short file name alias for the long file name.
*/
#endif
/* Put the short file name entry data */
return fat_putsfdirentry(fs, dirinfo, attributes, fattime);
}
/****************************************************************************
* Name: fat_dircreate
*
* Desciption: Create a directory entry for a new file
*
****************************************************************************/
int fat_dircreate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
{
uint32_t fattime;
int ret;
/* Allocate a directory entry. If long file name support is enabled, then
* this might, in fact, allocate a sequence of directory entries.
*/
ret = fat_allocatedirentry(fs, dirinfo);
if (ret != OK)
{
/* Failed to allocate the required directory entry or entries. */
return ret;
}
/* Write the directory entry (or entries) with the current time and the
* ARCHIVE attribute.
*/
fattime = fat_systime2fattime();
return fat_dirwrite(fs, dirinfo, FATATTR_ARCHIVE, fattime);
}
/****************************************************************************
* Name: fat_remove
*
* Desciption: Remove a directory or file from the file system. This
* implements both rmdir() and unlink().
*
****************************************************************************/
int fat_remove(struct fat_mountpt_s *fs, const char *relpath, bool directory)
{
struct fat_dirinfo_s dirinfo;
uint32_t dircluster;
uint8_t *direntry;
int ret;
/* Find the directory entry referring to the entry to be deleted */
ret = fat_finddirentry(fs, &dirinfo, relpath);
if (ret != OK)
{
/* No such path */
return -ENOENT;
}
/* Check if this is a FAT12/16 root directory */
if (dirinfo.fd_root)
{
/* The root directory cannot be removed */
return -EPERM;
}
/* The object has to have write access to be deleted */
direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
if ((DIR_GETATTRIBUTES(direntry) & FATATTR_READONLY) != 0)
{
/* It is a read-only entry */
return -EACCES;
}
/* Get the directory sector and cluster containing the entry to be deleted. */
((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
DIR_GETFSTCLUSTLO(direntry);
/* Is this entry a directory? */
if (DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY)
{
/* It is a sub-directory. Check if we are be asked to remove
* a directory or a file.
*/
if (!directory)
{
/* We are asked to delete a file */
return -EISDIR;
}
/* We are asked to delete a directory. Check if this
* sub-directory is empty
*/
dirinfo.dir.fd_currcluster = dircluster;
dirinfo.dir.fd_currsector = fat_cluster2sector(fs, dircluster);
dirinfo.dir.fd_index = 2;
/* Loop until either (1) an entry is found in the directory (error),
* (2) the directory is found to be empty, or (3) some error occurs.
*/
for (;;)
{
unsigned int subdirindex;
uint8_t *subdirentry;
/* Make sure that the sector containing the of the subdirectory
* sector is in the cache
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
*/
ret = fat_fscacheread(fs, dirinfo.dir.fd_currsector);
if (ret < 0)
{
return ret;
}
/* Get a reference to the next entry in the directory */
subdirindex = (dirinfo.dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
subdirentry = &fs->fs_buffer[subdirindex];
/* Is this the last entry in the direcory? */
if (subdirentry[DIR_NAME] == DIR0_ALLEMPTY)
{
/* Yes then the directory is empty. Break out of the
* loop and delete the directory.
*/
break;
}
/* Check if the next entry refers to a file or directory */
if (subdirentry[DIR_NAME] != DIR0_EMPTY &&
!(DIR_GETATTRIBUTES(subdirentry) & FATATTR_VOLUMEID))
{
/* The directory is not empty */
return -ENOTEMPTY;
}
/* Get the next directory entry */
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
ret = fat_nextdirentry(fs, &dirinfo.dir);
if (ret < 0)
{
return ret;
}
}
}
else
{
/* It is a file. Check if we are be asked to remove a directory
* or a file.
*/
if (directory)
{
/* We are asked to remove a directory */
return -ENOTDIR;
}
}
/* Mark the directory entry 'deleted'. If long file name support is
* enabled, then multiple directory entries may be freed.
*/
ret = fat_freedirentry(fs, &dirinfo.fd_seq);
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
if (ret < 0)
{
return ret;
}
/* And remove the cluster chain making up the subdirectory */
ret = fat_removechain(fs, dircluster);
if (ret < 0)
{
return ret;
}
/* Update the FSINFO sector (FAT32) */
ret = fat_updatefsinfo(fs);
if (ret < 0)
{
return ret;
}
return OK;
}