diff options
Diffstat (limited to 'nuttx/fs/fat/fs_fat32.c')
-rw-r--r-- | nuttx/fs/fat/fs_fat32.c | 2412 |
1 files changed, 0 insertions, 2412 deletions
diff --git a/nuttx/fs/fat/fs_fat32.c b/nuttx/fs/fat/fs_fat32.c deleted file mode 100644 index df8962b51..000000000 --- a/nuttx/fs/fat/fs_fat32.c +++ /dev/null @@ -1,2412 +0,0 @@ -/**************************************************************************** - * fs/fat/fs_fat32.c - * - * Copyright (C) 2007-2009, 2011-2013 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt <gnutt@nuttx.org> - * - * References: - * Microsoft FAT documentation - * Some good ideas were leveraged from the FAT implementation: - * 'Copyright (C) 2007, ChaN, all right reserved.' - * which has an unrestricted license. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include <nuttx/config.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/statfs.h> - -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <semaphore.h> -#include <assert.h> -#include <fcntl.h> -#include <errno.h> -#include <debug.h> - -#include <nuttx/kmalloc.h> -#include <nuttx/fs/fs.h> -#include <nuttx/fs/fat.h> -#include <nuttx/fs/dirent.h> - -#include "fs_internal.h" -#include "fs_fat32.h" - -/**************************************************************************** - * Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static int fat_open(FAR struct file *filep, const char *relpath, - int oflags, mode_t mode); -static int fat_close(FAR struct file *filep); -static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen); -static ssize_t fat_write(FAR struct file *filep, const char *buffer, - size_t buflen); -static off_t fat_seek(FAR struct file *filep, off_t offset, int whence); -static int fat_ioctl(FAR struct file *filep, int cmd, unsigned long arg); - -static int fat_sync(FAR struct file *filep); -static int fat_dup(FAR const struct file *oldp, FAR struct file *newp); - -static int fat_opendir(struct inode *mountpt, const char *relpath, - struct fs_dirent_s *dir); -static int fat_readdir(struct inode *mountpt, struct fs_dirent_s *dir); -static int fat_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir); - -static int fat_bind(FAR struct inode *blkdriver, const void *data, - void **handle); -static int fat_unbind(void *handle, FAR struct inode **blkdriver); -static int fat_statfs(struct inode *mountpt, struct statfs *buf); - -static int fat_unlink(struct inode *mountpt, const char *relpath); -static int fat_mkdir(struct inode *mountpt, const char *relpath, - mode_t mode); -static int fat_rmdir(struct inode *mountpt, const char *relpath); -static int fat_rename(struct inode *mountpt, const char *oldrelpath, - const char *newrelpath); -static int fat_stat(struct inode *mountpt, const char *relpath, struct stat *buf); - -/**************************************************************************** - * Private Variables - ****************************************************************************/ - -/**************************************************************************** - * Public Variables - ****************************************************************************/ - -/* See fs_mount.c -- this structure is explicitly externed there. - * We use the old-fashioned kind of initializers so that this will compile - * with any compiler. - */ - -const struct mountpt_operations fat_operations = -{ - fat_open, /* open */ - fat_close, /* close */ - fat_read, /* read */ - fat_write, /* write */ - fat_seek, /* seek */ - fat_ioctl, /* ioctl */ - - fat_sync, /* sync */ - fat_dup, /* dup */ - - fat_opendir, /* opendir */ - NULL, /* closedir */ - fat_readdir, /* readdir */ - fat_rewinddir, /* rewinddir */ - - fat_bind, /* bind */ - fat_unbind, /* unbind */ - fat_statfs, /* statfs */ - - fat_unlink, /* unlinke */ - fat_mkdir, /* mkdir */ - fat_rmdir, /* rmdir */ - fat_rename, /* rename */ - fat_stat /* stat */ -}; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: fat_open - ****************************************************************************/ - -static int fat_open(FAR struct file *filep, const char *relpath, - int oflags, mode_t mode) -{ - struct fat_dirinfo_s dirinfo; - struct inode *inode; - struct fat_mountpt_s *fs; - struct fat_file_s *ff; - uint8_t *direntry; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv == NULL && filep->f_inode != NULL); - - /* Get the mountpoint inode reference from the file structure and the - * mountpoint private data from the inode structure - */ - - inode = filep->f_inode; - fs = inode->i_private; - - DEBUGASSERT(fs != NULL); - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Initialize the directory info structure */ - - memset(&dirinfo, 0, sizeof(struct fat_dirinfo_s)); - - /* Locate the directory entry for this path */ - - ret = fat_finddirentry(fs, &dirinfo, relpath); - - /* Three possibililities: (1) a node exists for the relpath and - * dirinfo describes the directory entry of the entity, (2) the - * node does not exist, or (3) some error occurred. - */ - - if (ret == OK) - { - bool readonly; - - /* The name exists -- but is it a file or a directory? */ - - direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset]; - if (dirinfo.fd_root || - (DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY)) - { - /* It is a directory */ - - ret = -EISDIR; - goto errout_with_semaphore; - } - - /* It would be an error if we are asked to create it exclusively */ - - if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) - { - /* Already exists -- can't create it exclusively */ - - ret = -EEXIST; - goto errout_with_semaphore; - } - -#ifdef CONFIG_FILE_MODE -# warning "Missing check for privileges based on inode->i_mode" -#endif - - /* Check if the caller has sufficient privileges to open the file */ - - readonly = ((DIR_GETATTRIBUTES(direntry) & FATATTR_READONLY) != 0); - if (((oflags & O_WRONLY) != 0) && readonly) - { - ret = -EACCES; - goto errout_with_semaphore; - } - - /* If O_TRUNC is specified and the file is opened for writing, - * then truncate the file. This operation requires that the file is - * writable, but we have already checked that. O_TRUNC without write - * access is ignored. - */ - - if ((oflags & (O_TRUNC|O_WRONLY)) == (O_TRUNC|O_WRONLY)) - { - /* Truncate the file to zero length */ - - ret = fat_dirtruncate(fs, &dirinfo); - if (ret < 0) - { - goto errout_with_semaphore; - } - } - - /* fall through to finish the file open operations */ - } - else if (ret == -ENOENT) - { - /* The file does not exist. Were we asked to create it? */ - - if ((oflags & O_CREAT) == 0) - { - /* No.. then we fail with -ENOENT */ - - ret = -ENOENT; - goto errout_with_semaphore; - } - - /* Yes.. create the file */ - - ret = fat_dircreate(fs, &dirinfo); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Fall through to finish the file open operation */ - - direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset]; - } - else - { - /* An error occurred while checking for file existence -- - * such as if an invalid path were provided. - */ - - goto errout_with_semaphore; - } - - /* Create an instance of the file private date to describe the opened - * file. - */ - - ff = (struct fat_file_s *)kzalloc(sizeof(struct fat_file_s)); - if (!ff) - { - ret = -ENOMEM; - goto errout_with_semaphore; - } - - /* Create a file buffer to support partial sector accesses */ - - ff->ff_buffer = (uint8_t*)fat_io_alloc(fs->fs_hwsectorsize); - if (!ff->ff_buffer) - { - ret = -ENOMEM; - goto errout_with_struct; - } - - /* Initialize the file private data (only need to initialize non-zero elements) */ - - ff->ff_oflags = oflags; - - /* Save information that can be used later to recover the directory entry */ - - ff->ff_dirsector = fs->fs_currentsector; - ff->ff_dirindex = dirinfo.dir.fd_index; - - /* File cluster/size info */ - - ff->ff_startcluster = - ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) | - DIR_GETFSTCLUSTLO(direntry); - - ff->ff_currentcluster = ff->ff_startcluster; - ff->ff_sectorsincluster = fs->fs_fatsecperclus; - ff->ff_size = DIR_GETFILESIZE(direntry); - - /* Attach the private date to the struct file instance */ - - filep->f_priv = ff; - - /* Then insert the new instance into the mountpoint structure. - * It needs to be there (1) to handle error conditions that effect - * all files, and (2) to inform the umount logic that we are busy - * (but a simple reference count could have done that). - */ - - ff->ff_next = fs->fs_head; - fs->fs_head = ff->ff_next; - - fat_semgive(fs); - - /* In write/append mode, we need to set the file pointer to the end of the file */ - - if ((oflags & (O_APPEND|O_WRONLY)) == (O_APPEND|O_WRONLY)) - { - off_t offset = fat_seek(filep, ff->ff_size, SEEK_SET); - if (offset < 0) - { - kfree(ff); - return (int)offset; - } - } - - return OK; - - /* Error exits -- goto's are nasty things, but they sure can make error - * handling a lot simpler. - */ - -errout_with_struct: - kfree(ff); - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_close - ****************************************************************************/ - -static int fat_close(FAR struct file *filep) -{ - struct inode *inode; - struct fat_file_s *ff; - struct fat_mountpt_s *fs; - int ret = OK; - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); - - /* Recover our private data from the struct file instance */ - - ff = filep->f_priv; - inode = filep->f_inode; - fs = inode->i_private; - - /* Do not check if the mount is healthy. We must support closing of - * the file even when there is healthy mount. - */ - - /* Synchronize the file buffers and disk content; update times */ - - ret = fat_sync(filep); - - /* Then deallocate the memory structures created when the open method - * was called. - * - * Free the sector buffer that was used to manage partial sector accesses. - */ - - if (ff->ff_buffer) - { - (void)fs; /* Unused if fat_io_free == free(). */ - fat_io_free(ff->ff_buffer, fs->fs_hwsectorsize); - } - - /* Then free the file structure itself. */ - - kfree(ff); - filep->f_priv = NULL; - return ret; -} - -/**************************************************************************** - * Name: fat_read - ****************************************************************************/ - -static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen) -{ - struct inode *inode; - struct fat_mountpt_s *fs; - struct fat_file_s *ff; - unsigned int bytesread; - unsigned int readsize; - unsigned int nsectors; - size_t bytesleft; - int32_t cluster; - uint8_t *userbuffer = (uint8_t*)buffer; - int sectorindex; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); - - /* Recover our private data from the struct file instance */ - - ff = filep->f_priv; - inode = filep->f_inode; - fs = inode->i_private; - - DEBUGASSERT(fs != NULL); - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Check if the file was opened with read access */ - - if ((ff->ff_oflags & O_RDOK) == 0) - { - ret = -EACCES; - goto errout_with_semaphore; - } - - /* Get the number of bytes left in the file */ - - bytesleft = ff->ff_size - filep->f_pos; - - /* Truncate read count so that it does not exceed the number - * of bytes left in the file. - */ - - if (buflen > bytesleft) - { - buflen = bytesleft; - } - - /* Get the first sector to read from. */ - - if (!ff->ff_currentsector) - { - /* The current sector can be determined from the current cluster - * and the file offset. - */ - - ret = fat_currentsector(fs, ff, filep->f_pos); - if (ret < 0) - { - return ret; - } - } - - /* Loop until either (1) all data has been transferred, or (2) an - * error occurs. We assume we start with the current sector - * (ff_currentsector) which may be uninitialized. - */ - - readsize = 0; - sectorindex = filep->f_pos & SEC_NDXMASK(fs); - - while (buflen > 0) - { - bytesread = 0; - - /* Check if the user has provided a buffer large enough to - * hold one or more complete sectors -AND- the read is - * aligned to a sector boundary. - */ - - nsectors = buflen / fs->fs_hwsectorsize; - if (nsectors > 0 && sectorindex == 0) - { - /* Read maximum contiguous sectors directly to the user's - * buffer without using our tiny read buffer. - * - * Limit the number of sectors that we read on this time - * through the loop to the remaining contiguous sectors - * in this cluster - */ - - if (nsectors > ff->ff_sectorsincluster) - { - nsectors = ff->ff_sectorsincluster; - } - - /* We are not sure of the state of the file buffer so - * the safest thing to do is just invalidate it - */ - - (void)fat_ffcacheinvalidate(fs, ff); - - /* Read all of the sectors directly into user memory */ - - ret = fat_hwread(fs, userbuffer, ff->ff_currentsector, nsectors); - if (ret < 0) - { - goto errout_with_semaphore; - } - - ff->ff_sectorsincluster -= nsectors; - ff->ff_currentsector += nsectors; - bytesread = nsectors * fs->fs_hwsectorsize; - } - else - { - /* We are reading a partial sector. First, read the whole sector - * into the file data buffer. This is a caching buffer so if - * it is already there then all is well. - */ - - ret = fat_ffcacheread(fs, ff, ff->ff_currentsector); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Copy the partial sector into the user buffer */ - - bytesread = fs->fs_hwsectorsize - sectorindex; - if (bytesread > buflen) - { - /* We will not read to the end of the buffer */ - - bytesread = buflen; - } - else - { - /* We will read to the end of the buffer (or beyond) */ - - ff->ff_sectorsincluster--; - ff->ff_currentsector++; - } - - memcpy(userbuffer, &ff->ff_buffer[sectorindex], bytesread); - } - - /* Set up for the next sector read */ - - userbuffer += bytesread; - filep->f_pos += bytesread; - readsize += bytesread; - buflen -= bytesread; - sectorindex = filep->f_pos & SEC_NDXMASK(fs); - - /* Check if the current read stream has incremented to the next - * cluster boundary - */ - - if (ff->ff_sectorsincluster < 1) - { - /* Find the next cluster in the FAT. */ - - cluster = fat_getcluster(fs, ff->ff_currentcluster); - if (cluster < 2 || cluster >= fs->fs_nclusters) - { - ret = -EINVAL; /* Not the right error */ - goto errout_with_semaphore; - } - - /* Setup to read the first sector from the new cluster */ - - ff->ff_currentcluster = cluster; - ff->ff_currentsector = fat_cluster2sector(fs, cluster); - ff->ff_sectorsincluster = fs->fs_fatsecperclus; - } - } - - fat_semgive(fs); - return readsize; - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_write - ****************************************************************************/ - -static ssize_t fat_write(FAR struct file *filep, const char *buffer, - size_t buflen) -{ - struct inode *inode; - struct fat_mountpt_s *fs; - struct fat_file_s *ff; - int32_t cluster; - unsigned int byteswritten; - unsigned int writesize; - unsigned int nsectors; - uint8_t *userbuffer = (uint8_t*)buffer; - int sectorindex; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); - - /* Recover our private data from the struct file instance */ - - ff = filep->f_priv; - inode = filep->f_inode; - fs = inode->i_private; - - DEBUGASSERT(fs != NULL); - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Check if the file was opened for write access */ - - if ((ff->ff_oflags & O_WROK) == 0) - { - ret = -EACCES; - goto errout_with_semaphore; - } - - /* Check if the file size would exceed the range of off_t */ - - if (ff->ff_size + buflen < ff->ff_size) - { - ret = -EFBIG; - goto errout_with_semaphore; - } - - /* Get the first sector to write to. */ - - if (!ff->ff_currentsector) - { - /* Has the starting cluster been defined? */ - - if (ff->ff_startcluster == 0) - { - /* No.. we have to create a new cluster chain */ - - ff->ff_startcluster = fat_createchain(fs); - ff->ff_currentcluster = ff->ff_startcluster; - ff->ff_sectorsincluster = fs->fs_fatsecperclus; - } - - /* The current sector can then be determined from the currentcluster - * and the file offset. - */ - - ret = fat_currentsector(fs, ff, filep->f_pos); - if (ret < 0) - { - return ret; - } - } - - /* Loop until either (1) all data has been transferred, or (2) an - * error occurs. We assume we start with the current sector in - * cache (ff_currentsector) - */ - - byteswritten = 0; - sectorindex = filep->f_pos & SEC_NDXMASK(fs); - - while (buflen > 0) - { - /* Check if the user has provided a buffer large enough to - * hold one or more complete sectors. - */ - - nsectors = buflen / fs->fs_hwsectorsize; - if (nsectors > 0 && sectorindex == 0) - { - /* Write maximum contiguous sectors directly from the user's - * buffer without using our tiny read buffer. - * - * Limit the number of sectors that we write on this time - * through the loop to the remaining contiguous sectors - * in this cluster - */ - - if (nsectors > ff->ff_sectorsincluster) - { - nsectors = ff->ff_sectorsincluster; - } - - /* We are not sure of the state of the sector cache so the - * safest thing to do is write back any dirty, cached sector - * and invalidate the current cache content. - */ - - (void)fat_ffcacheinvalidate(fs, ff); - - /* Write all of the sectors directly from user memory */ - - ret = fat_hwwrite(fs, userbuffer, ff->ff_currentsector, nsectors); - if (ret < 0) - { - goto errout_with_semaphore; - } - - ff->ff_sectorsincluster -= nsectors; - ff->ff_currentsector += nsectors; - writesize = nsectors * fs->fs_hwsectorsize; - ff->ff_bflags |= FFBUFF_MODIFIED; - } - else - { - /* We are writing a partial sector -OR- the current sector - * has not yet been filled. - * - * We will first have to read the full sector in memory as - * part of a read-modify-write operation. NOTE we don't - * have to read the data on a rare case: When we are extending - * the file (filep->f_pos == ff->ff_size) -AND- the new data - * happens to be aligned at the beginning of the sector - * (sectorindex == 0). - */ - - if (filep->f_pos < ff->ff_size || sectorindex != 0) - { - /* Read the current sector into memory (perhaps first flushing - * the old, dirty sector to disk). - */ - - ret = fat_ffcacheread(fs, ff, ff->ff_currentsector); - if (ret < 0) - { - goto errout_with_semaphore; - } - } - else - { - /* Flush unwritten data in the sector cache. */ - - ret = fat_ffcacheflush(fs, ff); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Now mark the clean cache buffer as the current sector. */ - - ff->ff_cachesector = ff->ff_currentsector; - } - - /* Copy the partial sector from the user buffer */ - - writesize = fs->fs_hwsectorsize - sectorindex; - if (writesize > buflen) - { - /* We will not write to the end of the buffer. Set - * write size to the size of the user buffer. - */ - - writesize = buflen; - } - else - { - /* We will write to the end of the buffer (or beyond). Bump - * up the current sector number (actually the next sector number). - */ - - ff->ff_sectorsincluster--; - ff->ff_currentsector++; - } - - /* Copy the data into the cached sector and make sure that the - * cached sector is marked "dirty" - */ - - memcpy(&ff->ff_buffer[sectorindex], userbuffer, writesize); - ff->ff_bflags |= (FFBUFF_DIRTY|FFBUFF_VALID|FFBUFF_MODIFIED); - } - - /* Set up for the next write */ - - userbuffer += writesize; - filep->f_pos += writesize; - byteswritten += writesize; - buflen -= writesize; - sectorindex = filep->f_pos & SEC_NDXMASK(fs); - - /* Check if the current read stream has incremented to the next - * cluster boundary - */ - - if (ff->ff_sectorsincluster < 1) - { - /* Extend the current cluster by one (unless lseek was used to - * move the file position back from the end of the file) - */ - - cluster = fat_extendchain(fs, ff->ff_currentcluster); - - /* Verify the cluster number */ - - if (cluster < 0) - { - ret = cluster; - goto errout_with_semaphore; - } - else if (cluster < 2 || cluster >= fs->fs_nclusters) - { - ret = -ENOSPC; - goto errout_with_semaphore; - } - - /* Setup to write the first sector from the new cluster */ - - ff->ff_currentcluster = cluster; - ff->ff_sectorsincluster = fs->fs_fatsecperclus; - ff->ff_currentsector = fat_cluster2sector(fs, cluster); - } - } - - /* The transfer has completed without error. Update the file size */ - - if (filep->f_pos > ff->ff_size) - { - ff->ff_size = filep->f_pos; - } - - fat_semgive(fs); - return byteswritten; - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_seek - ****************************************************************************/ - -static off_t fat_seek(FAR struct file *filep, off_t offset, int whence) -{ - struct inode *inode; - struct fat_mountpt_s *fs; - struct fat_file_s *ff; - int32_t cluster; - off_t position; - unsigned int clustersize; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); - - /* Recover our private data from the struct file instance */ - - ff = filep->f_priv; - inode = filep->f_inode; - fs = inode->i_private; - - DEBUGASSERT(fs != NULL); - - /* Map the offset according to the whence option */ - - switch (whence) - { - case SEEK_SET: /* The offset is set to offset bytes. */ - position = offset; - break; - - case SEEK_CUR: /* The offset is set to its current location plus - * offset bytes. */ - - position = offset + filep->f_pos; - break; - - case SEEK_END: /* The offset is set to the size of the file plus - * offset bytes. */ - - position = offset + ff->ff_size; - break; - - default: - return -EINVAL; - } - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Check if there is unwritten data in the file buffer */ - - ret = fat_ffcacheflush(fs, ff); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Attempts to set the position beyound the end of file will - * work if the file is open for write access. - */ - - if (position > ff->ff_size && (ff->ff_oflags & O_WROK) == 0) - { - /* Otherwise, the position is limited to the file size */ - - position = ff->ff_size; - } - - /* Set file position to the beginning of the file (first cluster, - * first sector in cluster) - */ - - filep->f_pos = 0; - ff->ff_sectorsincluster = fs->fs_fatsecperclus; - - /* Get the start cluster of the file */ - - cluster = ff->ff_startcluster; - - /* Create a new cluster chain if the file does not have one (and - * if we are seeking beyond zero - */ - - if (!cluster && position > 0) - { - cluster = fat_createchain(fs); - if (cluster < 0) - { - ret = cluster; - goto errout_with_semaphore; - } - - ff->ff_startcluster = cluster; - } - - /* Move file position if necessary */ - - if (cluster) - { - /* If the file has a cluster chain, follow it to the - * requested position. - */ - - clustersize = fs->fs_fatsecperclus * fs->fs_hwsectorsize; - for (;;) - { - /* Skip over clusters prior to the one containing - * the requested position. - */ - - ff->ff_currentcluster = cluster; - if (position < clustersize) - { - break; - } - - /* Extend the cluster chain if write in enabled. NOTE: - * this is not consistent with the lseek description: - * "The lseek() function allows the file offset to be - * set beyond the end of the file (but this does not - * change the size of the file). If data is later written - * at this point, subsequent reads of the data in the - * gap (a "hole") return null bytes ('\0') until data - * is actually written into the gap." - */ - - if ((ff->ff_oflags & O_WROK) != 0) - { - /* Extend the cluster chain (fat_extendchain - * will follow the existing chain or add new - * clusters as needed. - */ - - cluster = fat_extendchain(fs, cluster); - } - else - { - /* Otherwise we can only follong the existing chain */ - - cluster = fat_getcluster(fs, cluster); - } - - if (cluster < 0) - { - /* An error occurred getting the cluster */ - - ret = cluster; - goto errout_with_semaphore; - } - - /* Zero means that there is no further clusters available - * in the chain. - */ - - if (cluster == 0) - { - /* At the position to the current locaiton and - * break out. - */ - - position = clustersize; - break; - } - - if (cluster >= fs->fs_nclusters) - { - ret = -ENOSPC; - goto errout_with_semaphore; - } - - /* Otherwise, update the position and continue looking */ - - filep->f_pos += clustersize; - position -= clustersize; - } - - /* We get here after we have found the sector containing - * the requested position. - * - * Save the new file position - */ - - filep->f_pos += position; - - /* Then get the current sector from the cluster and the offset - * into the cluster from the position - */ - - (void)fat_currentsector(fs, ff, filep->f_pos); - - /* Load the sector corresponding to the position */ - - if ((position & SEC_NDXMASK(fs)) != 0) - { - ret = fat_ffcacheread(fs, ff, ff->ff_currentsector); - if (ret < 0) - { - goto errout_with_semaphore; - } - } - } - - /* If we extended the size of the file, then mark the file as modified. */ - - if ((ff->ff_oflags & O_WROK) != 0 && filep->f_pos > ff->ff_size) - { - ff->ff_size = filep->f_pos; - ff->ff_bflags |= FFBUFF_MODIFIED; - } - - fat_semgive(fs); - return OK; - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_ioctl - ****************************************************************************/ - -static int fat_ioctl(FAR struct file *filep, int cmd, unsigned long arg) -{ - struct inode *inode; - struct fat_mountpt_s *fs; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); - - /* Recover our private data from the struct file instance */ - - inode = filep->f_inode; - fs = inode->i_private; - - DEBUGASSERT(fs != NULL); - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - fat_semgive(fs); - return ret; - } - - /* ioctl calls are just passed through to the contained block driver */ - - fat_semgive(fs); - return -ENOSYS; -} - -/**************************************************************************** - * Name: fat_sync - * - * Description: Synchronize the file state on disk to match internal, in- - * memory state. - * - ****************************************************************************/ - -static int fat_sync(FAR struct file *filep) -{ - struct inode *inode; - struct fat_mountpt_s *fs; - struct fat_file_s *ff; - uint32_t wrttime; - uint8_t *direntry; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); - - /* Recover our private data from the struct file instance */ - - ff = filep->f_priv; - inode = filep->f_inode; - fs = inode->i_private; - - DEBUGASSERT(fs != NULL); - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Check if the has been modified in any way */ - - if ((ff->ff_bflags & FFBUFF_MODIFIED) != 0) - { - /* Flush any unwritten data in the file buffer */ - - ret = fat_ffcacheflush(fs, ff); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Update the directory entry. First read the directory - * entry into the fs_buffer (preserving the ff_buffer) - */ - - ret = fat_fscacheread(fs, ff->ff_dirsector); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Recover a pointer to the specific directory entry - * in the sector using the saved directory index. - */ - - direntry = &fs->fs_buffer[(ff->ff_dirindex & DIRSEC_NDXMASK(fs)) * DIR_SIZE]; - - /* Set the archive bit, set the write time, and update - * anything that may have* changed in the directory - * entry: the file size, and the start cluster - */ - - direntry[DIR_ATTRIBUTES] |= FATATTR_ARCHIVE; - - DIR_PUTFILESIZE(direntry, ff->ff_size); - DIR_PUTFSTCLUSTLO(direntry, ff->ff_startcluster); - DIR_PUTFSTCLUSTHI(direntry, ff->ff_startcluster >> 16); - - wrttime = fat_systime2fattime(); - DIR_PUTWRTTIME(direntry, wrttime & 0xffff); - DIR_PUTWRTDATE(direntry, wrttime >> 16); - - /* Clear the modified bit in the flags */ - - ff->ff_bflags &= ~FFBUFF_MODIFIED; - - /* Flush these change to disk and update FSINFO (if - * appropriate. - */ - - fs->fs_dirty = true; - ret = fat_updatefsinfo(fs); - } - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_dup - * - * Description: Duplicate open file data in the new file structure. - * - ****************************************************************************/ - -static int fat_dup(FAR const struct file *oldp, FAR struct file *newp) -{ - FAR struct fat_mountpt_s *fs; - FAR struct fat_file_s *oldff; - FAR struct fat_file_s *newff; - int ret; - - fvdbg("Dup %p->%p\n", oldp, newp); - - /* Sanity checks */ - - DEBUGASSERT(oldp->f_priv != NULL && - newp->f_priv == NULL && - newp->f_inode != NULL); - - /* Recover our private data from the struct file instance */ - - fs = (struct fat_mountpt_s *)oldp->f_inode->i_private; - DEBUGASSERT(fs != NULL); - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Recover the old private data from the old struct file instance */ - - oldff = oldp->f_priv; - - /* Create a new instance of the file private date to describe the - * dup'ed file. - */ - - newff = (struct fat_file_s *)kmalloc(sizeof(struct fat_file_s)); - if (!newff) - { - ret = -ENOMEM; - goto errout_with_semaphore; - } - - /* Create a file buffer to support partial sector accesses */ - - newff->ff_buffer = (uint8_t*)fat_io_alloc(fs->fs_hwsectorsize); - if (!newff->ff_buffer) - { - ret = -ENOMEM; - goto errout_with_struct; - } - - /* Copy the rest of the open open file state from the old file structure. - * There are some assumptions and potential issues here: - * - * 1) We assume that the higher level logic has copied the elements of - * the file structure, in particular, the file position. - * 2) There is a problem with ff_size if there are multiple opened - * file structures, each believing they know the size of the file. - * If one instance modifies the file length, then the new size of - * the opened file will be unknown to the other. That is a lurking - * bug! - * - * One good solution to this might be to add a refernce count to the - * file structure. Then, instead of dup'ing the whole structure - * as is done here, just increment the reference count on the - * structure. The would have to be integrated with open logic as - * well, however, so that the same file structure is re-used if the - * file is re-opened. - */ - - newff->ff_bflags = 0; /* File buffer flags */ - newff->ff_oflags = oldff->ff_oflags; /* File open flags */ - newff->ff_sectorsincluster = oldff->ff_sectorsincluster; /* Sectors remaining in cluster */ - newff->ff_dirindex = oldff->ff_dirindex; /* Index to directory entry */ - newff->ff_currentcluster = oldff->ff_currentcluster; /* Current cluster */ - newff->ff_dirsector = oldff->ff_dirsector; /* Sector containing directory entry */ - newff->ff_size = oldff->ff_size; /* Size of the file */ - newff->ff_startcluster = oldff->ff_startcluster; /* Start cluster of file on media */ - newff->ff_currentsector = oldff->ff_currentsector; /* Current sector */ - newff->ff_cachesector = 0; /* Sector in file buffer */ - - /* Attach the private date to the struct file instance */ - - newp->f_priv = newff; - - /* Then insert the new instance into the mountpoint structure. - * It needs to be there (1) to handle error conditions that effect - * all files, and (2) to inform the umount logic that we are busy - * (but a simple reference count could have done that). - */ - - newff->ff_next = fs->fs_head; - fs->fs_head = newff->ff_next; - - fat_semgive(fs); - return OK; - - /* Error exits -- goto's are nasty things, but they sure can make error - * handling a lot simpler. - */ - -errout_with_struct: - kfree(newff); - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_opendir - * - * Description: Open a directory for read access - * - ****************************************************************************/ - -static int fat_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir) -{ - struct fat_mountpt_s *fs; - struct fat_dirinfo_s dirinfo; - uint8_t *direntry; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); - - /* Recover our private data from the inode instance */ - - fs = mountpt->i_private; - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Find the requested directory */ - - ret = fat_finddirentry(fs, &dirinfo, relpath); - if (ret < 0) - { - goto errout_with_semaphore; - } - direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset]; - - /* Check if this is the root directory */ - - if (dirinfo.fd_root) - { - /* Handle the FAT12/16/32 root directory using the values setup by - * fat_finddirentry() above. - */ - - dir->u.fat.fd_startcluster = dirinfo.dir.fd_startcluster; - dir->u.fat.fd_currcluster = dirinfo.dir.fd_currcluster; - dir->u.fat.fd_currsector = dirinfo.dir.fd_currsector; - dir->u.fat.fd_index = dirinfo.dir.fd_index; - } - - /* This is not the root directory. Verify that it is some kind of directory */ - - 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 (but not the root directory) */ - - dir->u.fat.fd_startcluster = - ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) | - DIR_GETFSTCLUSTLO(direntry); - dir->u.fat.fd_currcluster = dir->u.fat.fd_startcluster; - dir->u.fat.fd_currsector = fat_cluster2sector(fs, dir->u.fat.fd_currcluster); - dir->u.fat.fd_index = 2; - } - - fat_semgive(fs); - return OK; - -errout_with_semaphore: - fat_semgive(fs); - return ERROR; -} - -/**************************************************************************** - * Name: fat_readdir - * - * Description: Read the next directory entry - * - ****************************************************************************/ - -static int fat_readdir(struct inode *mountpt, struct fs_dirent_s *dir) -{ - struct fat_mountpt_s *fs; - unsigned int dirindex; - uint8_t *direntry; - uint8_t ch; - uint8_t attribute; - bool found; - int ret = OK; - - /* Sanity checks */ - - DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); - - /* Recover our private data from the inode instance */ - - fs = mountpt->i_private; - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Read the next directory entry */ - - dir->fd_dir.d_name[0] = '\0'; - found = false; - - while (dir->u.fat.fd_currsector && !found) - { - ret = fat_fscacheread(fs, dir->u.fat.fd_currsector); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Get a reference to the current directory entry */ - - dirindex = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE; - direntry = &fs->fs_buffer[dirindex]; - - /* Has it reached to end of the directory */ - - ch = *direntry; - if (ch == DIR0_ALLEMPTY) - { - /* We signal the end of the directory by returning the - * special error -ENOENT - */ - - ret = -ENOENT; - goto errout_with_semaphore; - } - - /* No, is the current entry a valid entry? */ - - attribute = DIR_GETATTRIBUTES(direntry); - -#ifdef CONFIG_FAT_LFN - if (ch != DIR0_EMPTY && - ((attribute & FATATTR_VOLUMEID) == 0 || - ((ch & LDIR0_LAST) != 0 && attribute == LDDIR_LFNATTR))) -#else - if (ch != DIR0_EMPTY && (attribute & FATATTR_VOLUMEID) == 0) -#endif - { - /* Yes.. get the name from the directory entry. NOTE: For the case - * of the long file name entry, this will advance the several - * several directory entries. - */ - - ret = fat_dirname2path(fs, dir); - if (ret == OK) - { - /* The name was successfully extracted. Re-read the - * attributes: If this is long directory entry, then the - * attributes that we need will be the the final, short file - * name entry and not in the directory entry where we started - * looking for the file name. We can be assured that, on - * success, fat_dirname2path() will leave the short file name - * entry in the cache regardless of the kind of directory - * entry. We simply have to re-read it to cover the the long - * file name case. - */ - -#ifdef CONFIG_FAT_LFN - - /* Get a reference to the current, short file name directory - * entry. - */ - - dirindex = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE; - direntry = &fs->fs_buffer[dirindex]; - - /* Then re-read the attributes from the short file name entry */ - - attribute = DIR_GETATTRIBUTES(direntry); -#endif - /* Now get the file type from the directory attributes. */ - - if ((attribute & FATATTR_DIRECTORY) == 0) - { - dir->fd_dir.d_type = DTYPE_FILE; - } - else - { - dir->fd_dir.d_type = DTYPE_DIRECTORY; - } - - /* Mark the entry found. We will set up the next directory index, - * and then exit with success. - */ - - found = true; - } - } - - /* Set up the next directory index */ - - if (fat_nextdirentry(fs, &dir->u.fat) != OK) - { - ret = -ENOENT; - goto errout_with_semaphore; - } - } - - fat_semgive(fs); - return OK; - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_rewindir - * - * Description: Reset directory read to the first entry - * - ****************************************************************************/ - -static int fat_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) -{ - struct fat_mountpt_s *fs; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); - - /* Recover our private data from the inode instance */ - - fs = mountpt->i_private; - - /* Make sure that the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Check if this is the root directory. If it is the root directory, we - * reset the fd_index to 0, starting with the initial, entry. - */ - - if (fs->fs_type != FSTYPE_FAT32 && - dir->u.fat.fd_startcluster == 0) - { - /* Handle the FAT12/16 root directory */ - - dir->u.fat.fd_currcluster = 0; - dir->u.fat.fd_currsector = fs->fs_rootbase; - dir->u.fat.fd_index = 0; - } - else if (fs->fs_type == FSTYPE_FAT32 && - dir->u.fat.fd_startcluster == fs->fs_rootbase) - { - /* Handle the FAT32 root directory */ - - 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; - } - - /* This is not the root directory. Here the fd_index is set to 2, skipping over - * both the "." and ".." entries. - */ - - else - { - dir->u.fat.fd_currcluster = dir->u.fat.fd_startcluster; - dir->u.fat.fd_currsector = fat_cluster2sector(fs, dir->u.fat.fd_currcluster); - dir->u.fat.fd_index = 2; - } - - fat_semgive(fs); - return OK; - -errout_with_semaphore: - fat_semgive(fs); - return ERROR; -} - -/**************************************************************************** - * Name: fat_bind - * - * Description: This implements a portion of the mount operation. This - * function allocates and initializes the mountpoint private data and - * binds the blockdriver inode to the filesystem private data. The final - * binding of the private data (containing the blockdriver) to the - * mountpoint is performed by mount(). - * - ****************************************************************************/ - -static int fat_bind(FAR struct inode *blkdriver, const void *data, - void **handle) -{ - struct fat_mountpt_s *fs; - int ret; - - /* Open the block driver */ - - if (!blkdriver || !blkdriver->u.i_bops) - { - return -ENODEV; - } - - if (blkdriver->u.i_bops->open && - blkdriver->u.i_bops->open(blkdriver) != OK) - { - return -ENODEV; - } - - /* Create an instance of the mountpt state structure */ - - fs = (struct fat_mountpt_s *)kzalloc(sizeof(struct fat_mountpt_s)); - if (!fs) - { - return -ENOMEM; - } - - /* Initialize the allocated mountpt state structure. The filesystem is - * responsible for one reference ont the blkdriver inode and does not - * have to addref() here (but does have to release in ubind(). - */ - - fs->fs_blkdriver = blkdriver; /* Save the block driver reference */ - sem_init(&fs->fs_sem, 0, 0); /* Initialize the semaphore that controls access */ - - /* Then get information about the FAT32 filesystem on the devices managed - * by this block driver. - */ - - ret = fat_mount(fs, true); - if (ret != 0) - { - sem_destroy(&fs->fs_sem); - kfree(fs); - return ret; - } - - *handle = (void*)fs; - fat_semgive(fs); - return OK; -} - -/**************************************************************************** - * Name: fat_unbind - * - * Description: This implements the filesystem portion of the umount - * operation. - * - ****************************************************************************/ - -static int fat_unbind(void *handle, FAR struct inode **blkdriver) -{ - struct fat_mountpt_s *fs = (struct fat_mountpt_s*)handle; - int ret; - - if (!fs) - { - return -EINVAL; - } - - /* Check if there are sill any files opened on the filesystem. */ - - ret = OK; /* Assume success */ - fat_semtake(fs); - if (fs->fs_head) - { - /* We cannot unmount now.. there are open files */ - - ret = -EBUSY; - } - else - { - /* Unmount ... close the block driver */ - - if (fs->fs_blkdriver) - { - struct inode *inode = fs->fs_blkdriver; - if (inode) - { - if (inode->u.i_bops && inode->u.i_bops->close) - { - (void)inode->u.i_bops->close(inode); - } - - /* We hold a reference to the block driver but should - * not but mucking with inodes in this context. So, we will just return - * our contained reference to the block driver inode and let the umount - * logic dispose of it. - */ - - if (blkdriver) - { - *blkdriver = inode; - } - } - } - - /* Release the mountpoint private data */ - - if (fs->fs_buffer) - { - fat_io_free(fs->fs_buffer, fs->fs_hwsectorsize); - } - - kfree(fs); - } - - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_statfs - * - * Description: Return filesystem statistics - * - ****************************************************************************/ - -static int fat_statfs(struct inode *mountpt, struct statfs *buf) -{ - struct fat_mountpt_s *fs; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt && mountpt->i_private); - - /* Get the mountpoint private data from the inode structure */ - - fs = mountpt->i_private; - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Fill in the statfs info */ - - memset(buf, 0, sizeof(struct statfs)); - buf->f_type = MSDOS_SUPER_MAGIC; - - /* We will claim that the optimal transfer size is the size of a cluster in bytes */ - - buf->f_bsize = fs->fs_fatsecperclus * fs->fs_hwsectorsize; - - /* Everything else follows in units of clusters */ - - ret = fat_nfreeclusters(fs, &buf->f_bfree); /* Free blocks in the file system */ - if (ret >= 0) - { - buf->f_blocks = fs->fs_nclusters; /* Total data blocks in the file system */ - buf->f_bavail = buf->f_bfree; /* Free blocks avail to non-superuser */ -#ifdef CONFIG_FAT_LFN - buf->f_namelen = LDIR_MAXFNAME; /* Maximum length of filenames */ -#else - buf->f_namelen = (8+1+3); /* Maximum length of filenames */ -#endif - } - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_unlink - * - * Description: Remove a file - * - ****************************************************************************/ - -static int fat_unlink(struct inode *mountpt, const char *relpath) -{ - struct fat_mountpt_s *fs; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt && mountpt->i_private); - - /* Get the mountpoint private data from the inode structure */ - - fs = mountpt->i_private; - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret == OK) - { - /* If the file is open, the correct behavior is to remove the file - * name, but to keep the file cluster chain in place until the last - * open reference to the file is closed. - */ - -#ifdef CONFIG_CPP_HAVE_WARNING -# warning "Need to defer deleting cluster chain if the file is open" -#endif - - /* Remove the file */ - - ret = fat_remove(fs, relpath, false); - } - - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_mkdir - * - * Description: Create a directory - * - ****************************************************************************/ - -static int fat_mkdir(struct inode *mountpt, const char *relpath, mode_t mode) -{ - struct fat_mountpt_s *fs; - struct fat_dirinfo_s dirinfo; - uint8_t *direntry; - uint8_t *direntry2; - off_t parentsector; - off_t dirsector; - int32_t dircluster; - uint32_t parentcluster; - uint32_t crtime; - unsigned int i; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt && mountpt->i_private); - - /* Get the mountpoint private data from the inode structure */ - - fs = mountpt->i_private; - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Find the directory where the new directory should be created. */ - - ret = fat_finddirentry(fs, &dirinfo, relpath); - - /* If anything exists at this location, then we fail with EEXIST */ - - if (ret == OK) - { - ret = -EEXIST; - goto errout_with_semaphore; - } - - /* What we want to see is for fat_finddirentry to fail with -ENOENT. - * This error means that no failure occurred but that nothing exists - * with this name. NOTE: The name has already been set in dirinfo - * structure. - */ - - if (ret != -ENOENT) - { - goto errout_with_semaphore; - } - - /* NOTE: There is no check that dirinfo.fd_name contains the final - * directory name. We could be creating an intermediate directory - * in the full relpath. - */ - - /* Allocate a directory entry for the new directory in this directory */ - - ret = fat_allocatedirentry(fs, &dirinfo); - if (ret != OK) - { - goto errout_with_semaphore; - } - parentsector = fs->fs_currentsector; - - /* Allocate a cluster for new directory */ - - dircluster = fat_createchain(fs); - if (dircluster < 0) - { - ret = dircluster; - goto errout_with_semaphore; - } - else if (dircluster < 2) - { - ret = -ENOSPC; - goto errout_with_semaphore; - } - - dirsector = fat_cluster2sector(fs, dircluster); - if (dirsector < 0) - { - ret = dirsector; - goto errout_with_semaphore; - } - - /* Flush any existing, dirty data in fs_buffer (because we need - * it to create the directory entries. - */ - - ret = fat_fscacheflush(fs); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Get a pointer to the first directory entry in the sector */ - - direntry = fs->fs_buffer; - - /* Now erase the contents of fs_buffer */ - - fs->fs_currentsector = dirsector; - memset(direntry, 0, fs->fs_hwsectorsize); - - /* Now clear all sectors in the new directory cluster (except for the first) */ - - for (i = 1; i < fs->fs_fatsecperclus; i++) - { - ret = fat_hwwrite(fs, direntry, ++dirsector, 1); - if (ret < 0) - { - goto errout_with_semaphore; - } - } - - /* Now create the "." directory entry in the first directory slot. These - * are special directory entries and are not handled by the normal directory - * management routines. - */ - - memset(&direntry[DIR_NAME], ' ', DIR_MAXFNAME); - direntry[DIR_NAME] = '.'; - DIR_PUTATTRIBUTES(direntry, FATATTR_DIRECTORY); - - crtime = fat_systime2fattime(); - DIR_PUTCRTIME(direntry, crtime & 0xffff); - DIR_PUTWRTTIME(direntry, crtime & 0xffff); - DIR_PUTCRDATE(direntry, crtime >> 16); - DIR_PUTWRTDATE(direntry, crtime >> 16); - - /* Create ".." directory entry in the second directory slot */ - - direntry2 = direntry + DIR_SIZE; - - /* So far, the two entries are nearly the same */ - - memcpy(direntry2, direntry, DIR_SIZE); - direntry2[DIR_NAME+1] = '.'; - - /* Now add the cluster information to both directory entries */ - - DIR_PUTFSTCLUSTHI(direntry, dircluster >> 16); - DIR_PUTFSTCLUSTLO(direntry, dircluster); - - parentcluster = dirinfo.dir.fd_startcluster; - if (fs->fs_type != FSTYPE_FAT32 && parentcluster == fs->fs_rootbase) - { - parentcluster = 0; - } - - DIR_PUTFSTCLUSTHI(direntry2, parentcluster >> 16); - DIR_PUTFSTCLUSTLO(direntry2, parentcluster); - - /* Save the first sector of the directory cluster and re-read - * the parentsector - */ - - fs->fs_dirty = true; - ret = fat_fscacheread(fs, parentsector); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Write the new entry directory entry in the parent directory */ - - ret = fat_dirwrite(fs, &dirinfo, FATATTR_DIRECTORY, crtime); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Set subdirectory start cluster. We assume that fat_dirwrite() did not - * change the sector in the cache. - */ - - direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset]; - DIR_PUTFSTCLUSTLO(direntry, dircluster); - DIR_PUTFSTCLUSTHI(direntry, dircluster >> 16); - fs->fs_dirty = true; - - /* Now update the FAT32 FSINFO sector */ - - ret = fat_updatefsinfo(fs); - if (ret < 0) - { - goto errout_with_semaphore; - } - - fat_semgive(fs); - return OK; - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_rmdir - * - * Description: Remove a directory - * - ****************************************************************************/ - -int fat_rmdir(struct inode *mountpt, const char *relpath) -{ - struct fat_mountpt_s *fs; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt && mountpt->i_private); - - /* Get the mountpoint private data from the inode structure */ - - fs = mountpt->i_private; - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret == OK) - { - /* If the directory is open, the correct behavior is to remove the directory - * name, but to keep the directory cluster chain in place until the last - * open reference to the directory is closed. - */ - -#ifdef CONFIG_CPP_HAVE_WARNING -# warning "Need to defer deleting cluster chain if the directory is open" -#endif - - /* Remove the directory */ - - ret = fat_remove(fs, relpath, true); - } - - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_rename - * - * Description: Rename a file or directory - * - ****************************************************************************/ - -int fat_rename(struct inode *mountpt, const char *oldrelpath, - const char *newrelpath) -{ - struct fat_mountpt_s *fs; - struct fat_dirinfo_s dirinfo; - struct fat_dirseq_s dirseq; - uint8_t *direntry; - uint8_t dirstate[DIR_SIZE-DIR_ATTRIBUTES]; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt && mountpt->i_private); - - /* Get the mountpoint private data from the inode structure */ - - fs = mountpt->i_private; - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Find the directory entry for the oldrelpath (there may be multiple - * directory entries if long file name support is enabled). - */ - - ret = fat_finddirentry(fs, &dirinfo, oldrelpath); - if (ret != OK) - { - /* Some error occurred -- probably -ENOENT */ - - goto errout_with_semaphore; - } - - /* One more check: Make sure that the oldrelpath does not refer to the - * root directory. We can't rename the root directory. - */ - - if (dirinfo.fd_root) - { - ret = -EXDEV; - goto errout_with_semaphore; - } - - /* Save the information that will need to recover the directory sector and - * directory entry offset to the old directory. - * - * Save the positional information of the old directory entry. - */ - - memcpy(&dirseq, &dirinfo.fd_seq, sizeof(struct fat_dirseq_s)); - - /* Save the non-name-related portion of the directory entry intact */ - - direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset]; - memcpy(dirstate, &direntry[DIR_ATTRIBUTES], DIR_SIZE-DIR_ATTRIBUTES); - - /* Now find the directory where we should create the newpath object */ - - ret = fat_finddirentry(fs, &dirinfo, newrelpath); - if (ret == OK) - { - /* It is an error if the object at newrelpath already exists */ - - ret = -EEXIST; - goto errout_with_semaphore; - } - - /* What we expect is -ENOENT mean that the full directory path was - * followed but that the object does not exists in the terminal directory. - */ - - if (ret != -ENOENT) - { - goto errout_with_semaphore; - } - - /* Reserve a directory entry. If long file name support is enabled, then - * this might, in fact, allocate a sequence of directory entries. A side - * effect of fat_allocatedirentry() in either case is that it leaves the - * short file name entry in the sector cache. - */ - - ret = fat_allocatedirentry(fs, &dirinfo); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Then write the new file name into the directory entry. This, of course, - * may involve writing multiple directory entries if long file name - * support is enabled. A side effect of fat_allocatedirentry() in either - * case is that it leaves the short file name entry in the sector cache. - */ - - ret = fat_dirnamewrite(fs, &dirinfo); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Copy the unchanged information into the new short file name entry. */ - - direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset]; - memcpy(&direntry[DIR_ATTRIBUTES], dirstate, DIR_SIZE-DIR_ATTRIBUTES); - fs->fs_dirty = true; - - /* Remove the old entry, flushing the new directory entry to disk. If - * the old file name was a long file name, then multiple directory - * entries may be freed. - */ - - ret = fat_freedirentry(fs, &dirseq); - if (ret < 0) - { - goto errout_with_semaphore; - } - - /* Write the old entry to disk and update FSINFO if necessary */ - - ret = fat_updatefsinfo(fs); - if (ret < 0) - { - goto errout_with_semaphore; - } - - fat_semgive(fs); - return OK; - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Name: fat_stat - * - * Description: Return information about a file or directory - * - ****************************************************************************/ - -static int fat_stat(struct inode *mountpt, const char *relpath, struct stat *buf) -{ - struct fat_mountpt_s *fs; - struct fat_dirinfo_s dirinfo; - uint16_t fatdate; - uint16_t date2; - uint16_t fattime; - uint8_t *direntry; - uint8_t attribute; - int ret; - - /* Sanity checks */ - - DEBUGASSERT(mountpt && mountpt->i_private); - - /* Get the mountpoint private data from the inode structure */ - - fs = mountpt->i_private; - - /* Check if the mount is still healthy */ - - fat_semtake(fs); - ret = fat_checkmount(fs); - if (ret != OK) - { - goto errout_with_semaphore; - } - - /* Find the directory entry corresponding to relpath. */ - - ret = fat_finddirentry(fs, &dirinfo, relpath); - - /* If nothing was found, then we fail with the reported error */ - - if (ret < 0) - { - goto errout_with_semaphore; - } - - memset(buf, 0, sizeof(struct stat)); - if (dirinfo.fd_root) - { - /* It's directory name of the mount point */ - - buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR|S_IWOTH|S_IWGRP|S_IWUSR; - ret = OK; - goto errout_with_semaphore; - } - - /* Get the FAT attribute and map it so some meaningful mode_t values */ - - direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset]; - attribute = DIR_GETATTRIBUTES(direntry); - if ((attribute & FATATTR_VOLUMEID) != 0) - { - ret = -ENOENT; - goto errout_with_semaphore; - } - - /* Set the access permissions. The file/directory is always readable - * by everyone but may be writeable by no-one. - */ - - buf->st_mode = S_IROTH|S_IRGRP|S_IRUSR; - if ((attribute & FATATTR_READONLY) == 0) - { - buf->st_mode |= S_IWOTH|S_IWGRP|S_IWUSR; - } - - /* We will report only types file or directory */ - - if ((attribute & FATATTR_DIRECTORY) != 0) - { - buf->st_mode |= S_IFDIR; - } - else - { - buf->st_mode |= S_IFREG; - } - - /* File/directory size, access block size */ - - buf->st_size = DIR_GETFILESIZE(direntry); - buf->st_blksize = fs->fs_fatsecperclus * fs->fs_hwsectorsize; - buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize; - - /* Times */ - - fatdate = DIR_GETWRTDATE(direntry); - fattime = DIR_GETWRTTIME(direntry); - buf->st_mtime = fat_fattime2systime(fattime, fatdate); - - date2 = DIR_GETLASTACCDATE(direntry); - if (fatdate == date2) - { - buf->st_atime = buf->st_mtime; - } - else - { - buf->st_atime = fat_fattime2systime(0, date2); - } - - fatdate = DIR_GETCRDATE(direntry); - fattime = DIR_GETCRTIME(direntry); - buf->st_ctime = fat_fattime2systime(fattime, fatdate); - - ret = OK; - -errout_with_semaphore: - fat_semgive(fs); - return ret; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - |