From 39fe6a0dff9c829c7388a8e8bf6214935a98ccdd Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 4 Jan 2018 10:54:54 -0600 Subject: [PATCH] Squashed commit of the following: fs/nxffs: Add partial implementation of the truncate method: It extend files, but cannot yet shrink them. fs/smartfs: Add partial implementation of the truncate method: It extend files, but cannot yet shrink them. fs/fat: Add partial implementation of the truncate method: It extend files, but no yet shrink them. fs/nfs: Add support for the truncate method to the NFS file system. --- fs/fat/fs_fat32.c | 241 +++++++++++++++++++++++++++- fs/nfs/nfs_vfsops.c | 74 +++++++-- fs/nxffs/Make.defs | 11 +- fs/nxffs/nxffs.h | 27 +++- fs/nxffs/nxffs_dirent.c | 3 +- fs/nxffs/nxffs_initialize.c | 2 +- fs/nxffs/nxffs_ioctl.c | 3 +- fs/nxffs/nxffs_read.c | 3 +- fs/nxffs/nxffs_stat.c | 3 +- fs/nxffs/nxffs_unlink.c | 3 +- fs/nxffs/nxffs_write.c | 180 ++++++++++++++++++++- fs/smartfs/smartfs.h | 12 +- fs/smartfs/smartfs_smart.c | 312 ++++++++++++++++++++++++++++++++++-- fs/vfs/fs_truncate.c | 2 +- 14 files changed, 831 insertions(+), 45 deletions(-) diff --git a/fs/fat/fs_fat32.c b/fs/fat/fs_fat32.c index 165af5e4709..3515b0d1fda 100644 --- a/fs/fat/fs_fat32.c +++ b/fs/fat/fs_fat32.c @@ -87,6 +87,7 @@ static int fat_sync(FAR struct file *filep); static int fat_dup(FAR const struct file *oldp, FAR struct file *newp); static int fat_fstat(FAR const struct file *filep, FAR struct stat *buf); +static int fat_truncate(FAR struct file *filep, off_t length); static int fat_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir); @@ -139,7 +140,7 @@ const struct mountpt_operations fat_operations = fat_sync, /* sync */ fat_dup, /* dup */ fat_fstat, /* fstat */ - NULL, /* truncate */ + fat_truncate, /* truncate */ fat_opendir, /* opendir */ NULL, /* closedir */ @@ -794,7 +795,7 @@ static ssize_t fat_write(FAR struct file *filep, FAR const char *buffer, ff->ff_sectorsincluster = fs->fs_fatsecperclus; } - /* The current sector can then be determined from the currentcluster + /* The current sector can then be determined from the current cluster * and the file offset. */ @@ -1622,7 +1623,7 @@ errout_with_semaphore: * * Description: * Obtain information about an open file associated with the file - * descriptor 'fd', and will write it to the area pointed to by 'buf'. + * structure 'filep', and will write it to the area pointed to by 'buf'. * ****************************************************************************/ @@ -1687,6 +1688,240 @@ errout_with_semaphore: return ret; } +/**************************************************************************** + * Name: fat_truncate + * + * Description: + * Set the length of the open, regular file associated with the file + * structure 'filep' to 'length'. + * + ****************************************************************************/ + +static int fat_truncate(FAR struct file *filep, off_t length) +{ + FAR struct inode *inode; + FAR struct fat_mountpt_s *fs; + FAR struct fat_file_s *ff; + int32_t cluster; + off_t remaining; + off_t oldsize; + off_t pos; + unsigned int zerosize; + int sectndx; + int ret; + + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + ff = filep->f_priv; + + /* Check for the forced mount condition */ + + if ((ff->ff_bflags & UMOUNT_FORCED) != 0) + { + return -EPIPE; + } + + 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; + } + + /* Are we shrinking the file? Or extending it? */ + + oldsize = ff->ff_size; + if (oldsize == length) + { + ret = OK; + goto errout_with_semaphore; + } + else if (oldsize > length) + { + /* We are shrinking the file */ + /* REVISIT: Logic to shrink the file has not yet been implemented */ + + ret = -ENOSYS; + goto errout_with_semaphore; + } + + /* Otherwise we are extending the file. This is essentially the same as a + * write except that (1) we write zeros and (2) we don't update the file + * position. + */ + + pos = ff->ff_size; + + /* 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 current cluster + * and the file offset. + */ + + ret = fat_currentsector(fs, ff, pos); + if (ret < 0) + { + goto errout_with_semaphore; + } + } + + /* Loop until either (1) the file has been fully extended with zeroed data + * or (2) an error occurs. We assume we start with the current sector in + * cache (ff_currentsector) + */ + + sectndx = pos & SEC_NDXMASK(fs); + remaining = length - pos; + + while (remaining > 0) + { + /* Check if the current write 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 zero the first sector from the new cluster */ + + ff->ff_currentcluster = cluster; + ff->ff_sectorsincluster = fs->fs_fatsecperclus; + ff->ff_currentsector = fat_cluster2sector(fs, cluster); + } + + /* Decide whether we are performing a read-modify-write + * operation, in which case we have to read the existing sector + * into the buffer first. + * + * There are two cases where we can avoid this read: + * + * - If we are performing a whole-sector clear that was rejected + * by fat_hwwrite(), i.e. sectndx == 0 and remaining >= sector size. + * + * - If the clear is aligned to the beginning of the sector and + * extends beyond the end of the file, i.e. sectndx == 0 and + * file pos + remaining >= file size. + */ + + if (sectndx == 0 && (remaining >= fs->fs_hwsectorsize || + (pos + remaining) >= ff->ff_size)) + { + /* 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; + } + else + { + /* 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; + } + } + + /* Copy the requested part of the sector from the user buffer */ + + zerosize = fs->fs_hwsectorsize - sectndx; + if (zerosize > remaining) + { + /* We will not zero to the end of the sector. */ + + zerosize = remaining; + } + else + { + /* We will zero 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++; + } + + /* Zero the data into the cached sector and make sure that the cached + * sector is marked "dirty" so that it will be written back. + */ + + memset(&ff->ff_buffer[sectndx], 0, zerosize); + ff->ff_bflags |= (FFBUFF_DIRTY | FFBUFF_VALID | FFBUFF_MODIFIED); + + /* Set up for the next sector */ + + pos += zerosize; + remaining -= zerosize; + sectndx = pos & SEC_NDXMASK(fs); + } + + /* The truncation has completed without error. Update the file size */ + + ff->ff_size = length; + ret = OK; + +errout_with_semaphore: + fat_semgive(fs); + return ret; +} + /**************************************************************************** * Name: fat_readdir * diff --git a/fs/nfs/nfs_vfsops.c b/fs/nfs/nfs_vfsops.c index 94f73b04b2f..41fbfc46ff7 100644 --- a/fs/nfs/nfs_vfsops.c +++ b/fs/nfs/nfs_vfsops.c @@ -135,11 +135,15 @@ struct nfsstats nfsstats; * Private Function Prototypes ****************************************************************************/ -static int nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np, - FAR const char *relpath, mode_t mode); -static int nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np); -static int nfs_fileopen(FAR struct nfsmount *nmp, struct nfsnode *np, - FAR const char *relpath, int oflags, mode_t mode); +static int nfs_filecreate(FAR struct nfsmount *nmp, + FAR struct nfsnode *np, FAR const char *relpath, + mode_t mode); +static int nfs_filetruncate(FAR struct nfsmount *nmp, + FAR struct nfsnode *np, uint32_t length); +static int nfs_fileopen(FAR struct nfsmount *nmp, + FAR struct nfsnode *np, FAR const char *relpath, + int oflags, mode_t mode); + static int nfs_open(FAR struct file *filep, const char *relpath, int oflags, mode_t mode); static int nfs_close(FAR struct file *filep); @@ -148,6 +152,7 @@ static ssize_t nfs_write(FAR struct file *filep, const char *buffer, size_t buflen); static int nfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int nfs_fstat(FAR const struct file *filep, FAR struct stat *buf); +static int nfs_truncate(FAR struct file *filep, off_t length); static int nfs_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir); static int nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir); @@ -191,7 +196,7 @@ const struct mountpt_operations nfs_operations = NULL, /* sync */ nfs_dup, /* dup */ nfs_fstat, /* fstat */ - NULL, /* truncate */ + nfs_truncate, /* truncate */ nfs_opendir, /* opendir */ NULL, /* closedir */ @@ -225,7 +230,7 @@ const struct mountpt_operations nfs_operations = * ****************************************************************************/ -static int nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np, +static int nfs_filecreate(FAR struct nfsmount *nmp, FAR struct nfsnode *np, FAR const char *relpath, mode_t mode) { struct file_handle fhandle; @@ -408,7 +413,8 @@ static int nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np, * ****************************************************************************/ -static int nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np) +static int nfs_filetruncate(FAR struct nfsmount *nmp, + FAR struct nfsnode *np, uint32_t length) { FAR uint32_t *ptr; int reqlen; @@ -436,7 +442,7 @@ static int nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np) *ptr++ = nfs_false; /* Don't change uid */ *ptr++ = nfs_false; /* Don't change gid */ *ptr++ = nfs_true; /* Use the following size */ - *ptr++ = 0; /* Truncate to zero length */ + *ptr++ = length; /* Truncate to the specified length */ *ptr++ = 0; *ptr++ = HTONL(NFSV3SATTRTIME_TOSERVER); /* Use the server's time */ *ptr++ = HTONL(NFSV3SATTRTIME_TOSERVER); /* Use the server's time */ @@ -473,7 +479,7 @@ static int nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np) * ****************************************************************************/ -static int nfs_fileopen(FAR struct nfsmount *nmp, struct nfsnode *np, +static int nfs_fileopen(FAR struct nfsmount *nmp, FAR struct nfsnode *np, FAR const char *relpath, int oflags, mode_t mode) { struct file_handle fhandle; @@ -550,7 +556,7 @@ static int nfs_fileopen(FAR struct nfsmount *nmp, struct nfsnode *np, * the SETATTR call by setting the length to zero. */ - return nfs_filetruncate(nmp, np); + return nfs_filetruncate(nmp, np, 0); } return OK; @@ -1202,7 +1208,7 @@ static int nfs_dup(FAR const struct file *oldp, FAR struct file *newp) * * Description: * Obtain information about an open file associated with the file - * descriptor 'fd', and will write it to the area pointed to by 'buf'. + * structure 'filep', and will write it to the area pointed to by 'buf'. * ****************************************************************************/ @@ -1271,6 +1277,50 @@ errout_with_semaphore: return -error; } +/**************************************************************************** + * Name: nfs_truncate + * + * Description: + * Set the length of the open, regular file associated with the file + * structure 'filep' to 'length'. + * + ****************************************************************************/ + +static int nfs_truncate(FAR struct file *filep, off_t length) +{ + struct nfsmount *nmp; + struct nfsnode *np; + int error; + + finfo("Truncate to %ld bytes\n", (long)length); + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + nmp = (FAR struct nfsmount *)filep->f_inode->i_private; + np = (FAR struct nfsnode *)filep->f_priv; + + DEBUGASSERT(nmp != NULL); + + /* Make sure that the mount is still healthy */ + + nfs_semtake(nmp); + error = nfs_checkmount(nmp); + if (error != OK) + { + ferr("ERROR: nfs_checkmount failed: %d\n", error); + goto errout_with_semaphore; + } + + /* Then perform the SETATTR RPC to set the new file size */ + + error = nfs_filetruncate(nmp, np, length); + +errout_with_semaphore: + nfs_semgive(nmp); + return -error; +} + /**************************************************************************** * Name: nfs_opendir * diff --git a/fs/nxffs/Make.defs b/fs/nxffs/Make.defs index a63d7393734..c82038dba33 100644 --- a/fs/nxffs/Make.defs +++ b/fs/nxffs/Make.defs @@ -34,11 +34,14 @@ ############################################################################ ifeq ($(CONFIG_FS_NXFFS),y) + ASRCS += -CSRCS += nxffs_block.c nxffs_blockstats.c nxffs_cache.c nxffs_dirent.c \ - nxffs_dump.c nxffs_initialize.c nxffs_inode.c nxffs_ioctl.c \ - nxffs_open.c nxffs_pack.c nxffs_read.c nxffs_reformat.c \ - nxffs_stat.c nxffs_unlink.c nxffs_util.c nxffs_write.c + +CSRCS += nxffs_block.c nxffs_blockstats.c nxffs_cache.c nxffs_dirent.c +CSRCS += nxffs_dump.c nxffs_initialize.c nxffs_inode.c nxffs_ioctl.c +CSRCS += nxffs_open.c nxffs_pack.c nxffs_read.c nxffs_reformat.c +CSRCS += nxffs_stat.c nxffs_truncate.c nxffs_unlink.c nxffs_util.c +CSRCS += nxffs_write.c # Include NXFFS build support diff --git a/fs/nxffs/nxffs.h b/fs/nxffs/nxffs.h index 30f84b1f328..d59e30f2021 100644 --- a/fs/nxffs/nxffs.h +++ b/fs/nxffs/nxffs.h @@ -1,7 +1,8 @@ /**************************************************************************** * fs/nxffs/nxffs.h * - * Copyright (C) 2011, 2013, 2015, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2013, 2015, 2017-2018 Gregory Nutt. All rights + * reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt @@ -870,6 +871,26 @@ int nxffs_wrinode(FAR struct nxffs_volume_s *volume, int nxffs_updateinode(FAR struct nxffs_volume_s *volume, FAR struct nxffs_entry_s *entry); +/**************************************************************************** + * Name: nxffs_wrextend + * + * Description: + * Zero-extend a file. + * + * Input parameters + * volume - Describes the NXFFS volume + * entry - Describes the new inode entry + * length - The new, extended length of the file + * + * Assumptions: + * The caller holds the NXFFS semaphore. + * The caller has verified that the file is writable. + * + ****************************************************************************/ + +int nxffs_wrextend(FAR struct nxffs_volume_s *volume, + FAR struct nxffs_wrfile_s *wrfile, off_t length); + /**************************************************************************** * Name: nxffs_wrreserve * @@ -1091,12 +1112,16 @@ ssize_t nxffs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); ssize_t nxffs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + int nxffs_dup(FAR const struct file *oldp, FAR struct file *newp); int nxffs_fstat(FAR const struct file *filep, FAR struct stat *buf); +int nxffs_truncate(FAR struct file *filep, off_t length); + int nxffs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir); int nxffs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); int nxffs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); + int nxffs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle); int nxffs_unbind(FAR void *handle, FAR struct inode **blkdriver, diff --git a/fs/nxffs/nxffs_dirent.c b/fs/nxffs/nxffs_dirent.c index 9afe9848671..298238232b3 100644 --- a/fs/nxffs/nxffs_dirent.c +++ b/fs/nxffs/nxffs_dirent.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/nxffs/nxffs_dirent.c * - * Copyright (C) 2011, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2017-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt @@ -47,6 +47,7 @@ #include #include +#include #include #include #include diff --git a/fs/nxffs/nxffs_initialize.c b/fs/nxffs/nxffs_initialize.c index eba886971d2..b3a897210f2 100644 --- a/fs/nxffs/nxffs_initialize.c +++ b/fs/nxffs/nxffs_initialize.c @@ -75,7 +75,7 @@ const struct mountpt_operations nxffs_operations = NULL, /* sync -- No buffered data */ nxffs_dup, /* dup */ nxffs_fstat, /* fstat */ - NULL, /* truncate */ + nxffs_truncate, /* truncate */ nxffs_opendir, /* opendir */ NULL, /* closedir */ diff --git a/fs/nxffs/nxffs_ioctl.c b/fs/nxffs/nxffs_ioctl.c index e80d4d265a9..03f6416d708 100644 --- a/fs/nxffs/nxffs_ioctl.c +++ b/fs/nxffs/nxffs_ioctl.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/nxffs/nxffs_ioctl.c * - * Copyright (C) 2011, 2013, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2013, 2017-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt @@ -46,6 +46,7 @@ #include #include +#include #include #include #include diff --git a/fs/nxffs/nxffs_read.c b/fs/nxffs/nxffs_read.c index 49014d6144b..c2ae6134801 100644 --- a/fs/nxffs/nxffs_read.c +++ b/fs/nxffs/nxffs_read.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/nxffs/nxffs_read.c * - * Copyright (C) 2011, 2013, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2013, 2017-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt @@ -48,6 +48,7 @@ #include #include +#include #include #include diff --git a/fs/nxffs/nxffs_stat.c b/fs/nxffs/nxffs_stat.c index 5d2ea2927c5..0de686c0fe9 100644 --- a/fs/nxffs/nxffs_stat.c +++ b/fs/nxffs/nxffs_stat.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/nxffs/nxffs_stat.c * - * Copyright (C) 2011, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2017-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt @@ -50,6 +50,7 @@ #include #include +#include #include #include diff --git a/fs/nxffs/nxffs_unlink.c b/fs/nxffs/nxffs_unlink.c index 786acc1682f..e5885038466 100644 --- a/fs/nxffs/nxffs_unlink.c +++ b/fs/nxffs/nxffs_unlink.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/nxffs/nxffs_unlink.c * - * Copyright (C) 2011, 2013, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2013, 2017-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt @@ -46,6 +46,7 @@ #include #include +#include #include #include diff --git a/fs/nxffs/nxffs_write.c b/fs/nxffs/nxffs_write.c index ba51895cceb..308ef6914a9 100644 --- a/fs/nxffs/nxffs_write.c +++ b/fs/nxffs/nxffs_write.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/nxffs/nxffs_write.c * - * Copyright (C) 2011, 2013, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2011, 2013, 2017-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -327,7 +328,7 @@ static inline int nxffs_reverify(FAR struct nxffs_volume_s *volume, * volume - Describes the NXFFS volume * wrfile - Describes the open file to be written. * buffer - Address of buffer of data to be written. - * buflen - The number of bytes remaimining to be written + * buflen - The number of bytes remaining to be written * * Returned Value: * The number of bytes written is returned on success. Otherwise, a @@ -414,6 +415,96 @@ static inline ssize_t nxffs_wrappend(FAR struct nxffs_volume_s *volume, return nbytestowrite; } +/**************************************************************************** + * Name: nxffs_zappend + * + * Description: + * Zero-extend FLASH data to the data block. + * + * Input Parameters: + * volume - Describes the NXFFS volume + * wrfile - Describes the open file to be written. + * nzeros - The number of bytes of zeroed data to be written + * + * Returned Value: + * The number of bytes written is returned on success. Otherwise, a + * negated errno value is returned indicating the nature of the failure. + * + ****************************************************************************/ + +static inline ssize_t nxffs_zappend(FAR struct nxffs_volume_s *volume, + FAR struct nxffs_wrfile_s *wrfile, + off_t nzeros) +{ + ssize_t maxsize; + size_t nbytestoclear; + ssize_t remaining; + off_t offset; + int ret; + + /* Get the offset to the start of unwritten data */ + + offset = volume->iooffset + wrfile->datlen + SIZEOF_NXFFS_DATA_HDR; + + /* Determine that maximum amount of data that can be written to this + * block. + */ + + maxsize = volume->geo.blocksize - offset; + DEBUGASSERT(maxsize > 0); + + /* Write as many bytes as we can into the data buffer */ + + nbytestoclear = MIN(maxsize, nzeros); + remaining = maxsize - nbytestoclear; + + if (nbytestoclear > 0) + { + /* Copy the data into the volume write cache */ + + memset(&volume->cache[offset], 0, nbytestoclear); + + /* Increment the number of bytes written to the data block */ + + wrfile->datlen += nbytestoclear; + + /* Re-calculate the CRC */ + + offset = volume->iooffset + SIZEOF_NXFFS_DATA_HDR; + wrfile->crc = crc32(&volume->cache[offset], wrfile->datlen); + + /* And write the partial write block to FLASH -- unless the data + * block is full. In that case, the block will be written below. + */ + + if (remaining > 0) + { + ret = nxffs_wrcache(volume); + if (ret < 0) + { + ferr("ERROR: nxffs_wrcache failed: %d\n", -ret); + return ret; + } + } + } + + /* Check if the data block is now full */ + + if (remaining <= 0) + { + /* The data block is full, write the block to FLASH */ + + ret = nxffs_wrblkhdr(volume, wrfile); + if (ret < 0) + { + ferr("ERROR: nxffs_wrblkdhr failed: %d\n", -ret); + return ret; + } + } + + return 0; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -534,6 +625,91 @@ errout: return ret; } +/**************************************************************************** + * Name: nxffs_wrextend + * + * Description: + * Zero-extend a file. + * + * Input parameters + * volume - Describes the NXFFS volume + * entry - Describes the new inode entry + * length - The new, extended length of the file + * + * Assumptions: + * The caller holds the NXFFS semaphore. + * The caller has verified that the file is writable. + * + ****************************************************************************/ + +int nxffs_wrextend(FAR struct nxffs_volume_s *volume, + FAR struct nxffs_wrfile_s *wrfile, off_t length) +{ + ssize_t remaining; + ssize_t nwritten; + off_t oldsize; + int ret; + + finfo("Extend file to %ld bytes to offset %d\n", (long)length); + DEBUGASSERT(volume != NULL && wrfile != NULL); + + oldsize = wrfile->ofile.entry.datlen; + DEBUGASSERT(length > oldsize); + + /* Loop until we successfully appended all of the data to the file (or an + * error occurs) + */ + + remaining = length - oldsize; + while (remaining > 0) + { + /* Have we already allocated the data block? */ + + if (wrfile->doffset == 0) + { + /* No, allocate the data block now, re-packing if necessary. */ + + wrfile->datlen = 0; + ret = nxffs_wralloc(volume, wrfile, remaining); + if (ret < 0) + { + ferr("ERROR: Failed to allocate a data block: %d\n", -ret); + return ret; + } + } + + /* Seek to the FLASH block containing the data block */ + + nxffs_ioseek(volume, wrfile->doffset); + + /* Verify that the FLASH data that was previously written is still intact */ + + ret = nxffs_reverify(volume, wrfile); + if (ret < 0) + { + ferr("ERROR: Failed to verify FLASH data block: %d\n", -ret); + return ret; + } + + /* Append the data to the end of the data block and write the updated + * block to flash. + */ + + nwritten = nxffs_zappend(volume, wrfile, remaining); + if (nwritten < 0) + { + ferr("ERROR: Failed to zero extend FLASH data block: %d\n", -ret); + return (int)nwritten; + } + + /* Decrement the number of bytes remaining to be written */ + + remaining -= nwritten; + } + + return OK; +} + /**************************************************************************** * Name: nxffs_wrreserve * diff --git a/fs/smartfs/smartfs.h b/fs/smartfs/smartfs.h index 36ed65b41b8..44a34aa6eb1 100644 --- a/fs/smartfs/smartfs.h +++ b/fs/smartfs/smartfs.h @@ -209,12 +209,18 @@ #define SMARTFS_ERASEDSTATE_16BIT (uint16_t) ((CONFIG_SMARTFS_ERASEDSTATE << 8) | \ CONFIG_SMARTFS_ERASEDSTATE) +/* Size of temporary buffer used when a file is zero extended by ftruncate() + * logic. + */ + +#define SMARTFS_TRUNCBUFFER_SIZE 512 + #ifndef offsetof -#define offsetof(type, member) ( (size_t) &( ( (type *) 0)->member)) +# define offsetof(type, member) ((size_t) & (((type *)0)->member)) #endif -#define SMARTFS_NEXTSECTOR(h) ( *((uint16_t *) h->nextsector)) -#define SMARTFS_USED(h) ( *((uint16_t *) h->used)) +#define SMARTFS_NEXTSECTOR(h) (*((uint16_t *)h->nextsector)) +#define SMARTFS_USED(h) (*((uint16_t *)h->used)) #ifdef CONFIG_MTD_SMART_ENABLE_CRC #define CONFIG_SMARTFS_USE_SECTOR_BUFFER diff --git a/fs/smartfs/smartfs_smart.c b/fs/smartfs/smartfs_smart.c index 9c376f10d5b..fe3b8d3cdc5 100644 --- a/fs/smartfs/smartfs_smart.c +++ b/fs/smartfs/smartfs_smart.c @@ -83,6 +83,7 @@ static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int smartfs_fstat(FAR const struct file *filep, FAR struct stat *buf); +static int smartfs_truncate(FAR struct file *filep, off_t length); static int smartfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, @@ -149,7 +150,7 @@ const struct mountpt_operations smartfs_operations = smartfs_sync, /* sync */ smartfs_dup, /* dup */ smartfs_fstat, /* fstat */ - NULL, /* truncate */ + smartfs_truncate, /* truncate */ smartfs_opendir, /* opendir */ NULL, /* closedir */ @@ -618,22 +619,23 @@ static int smartfs_sync_internal(struct smartfs_mountpt_s *fs, { /* Update the header with the number of bytes written */ - header = (struct smartfs_chain_header_s *) sf->buffer; - if (*((uint16_t *) header->used) == SMARTFS_ERASEDSTATE_16BIT) + header = (struct smartfs_chain_header_s *)sf->buffer; + if (*((uint16_t *)header->used) == SMARTFS_ERASEDSTATE_16BIT) { - *((uint16_t *) header->used) = sf->byteswritten; + *((uint16_t *)header->used) = sf->byteswritten; } else { - *((uint16_t *) header->used) += sf->byteswritten; + *((uint16_t *)header->used) += sf->byteswritten; } /* Write the entire sector to FLASH */ readwrite.logsector = sf->currsector; - readwrite.offset = 0; - readwrite.count = fs->fs_llformat.availbytes; - readwrite.buffer = sf->buffer; + readwrite.offset = 0; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.buffer = sf->buffer; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); if (ret < 0) { @@ -657,9 +659,10 @@ static int smartfs_sync_internal(struct smartfs_mountpt_s *fs, /* Read the existing sector used bytes value */ readwrite.logsector = sf->currsector; - readwrite.offset = 0; - readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; - readwrite.count = sizeof(struct smartfs_chain_header_s); + readwrite.offset = 0; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + readwrite.count = sizeof(struct smartfs_chain_header_s); + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); if (ret < 0) { @@ -681,8 +684,9 @@ static int smartfs_sync_internal(struct smartfs_mountpt_s *fs, } readwrite.offset = offsetof(struct smartfs_chain_header_s, used); - readwrite.count = sizeof(uint16_t); + readwrite.count = sizeof(uint16_t); readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[readwrite.offset]; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); if (ret < 0) { @@ -774,7 +778,9 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer, /* Limit the write based on available data to write */ if (readwrite.count > buflen) - readwrite.count = buflen; + { + readwrite.count = buflen; + } /* Limit the write based on current file length */ @@ -1308,6 +1314,286 @@ static int smartfs_fstat(FAR const struct file *filep, FAR struct stat *buf) return OK; } +/**************************************************************************** + * Name: smartfs_truncate + * + * Description: + * Set the length of the open, regular file associated with the file + * structure 'filep' to 'length'. + * + ****************************************************************************/ + +static int smartfs_truncate(FAR struct file *filep, off_t length) +{ + struct smart_read_write_s readwrite; + FAR struct inode *inode; + FAR struct smartfs_mountpt_s *fs; + FAR struct smartfs_ofile_s *sf; + FAR struct smartfs_chain_header_s *header; +#ifndef CONFIG_SMARTFS_USE_SECTOR_BUFFER + FAR uint8_t *buffer; +#endif + off_t remaining; + off_t savepos; + off_t oldsize; + int ret; + + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + sf = filep->f_priv; + inode = filep->f_inode; + fs = inode->i_private; + + DEBUGASSERT(fs != NULL); + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Test the permissions. Only allow truncation if the file was opened with + * write flags. + */ + + if ((sf->oflags & O_WROK) == 0) + { + ret = -EACCES; + goto errout_with_semaphore; + } + + /* Are we shrinking the file? Or extending it? */ + + oldsize = sf->entry.datlen; + if (oldsize == length) + { + ret = OK; + goto errout_with_semaphore; + } + else if (oldsize > length) + { + /* We are shrinking the file */ + /* REVISIT: Logic to shrink the file has not yet been implemented */ + + ret = -ENOSYS; + goto errout_with_semaphore; + } + + /* Otherwise we are extending the file. This is essentially the same as a + * write except that (1) we write zeros and (2) we don't update the file + * position. + */ + +#ifndef CONFIG_SMARTFS_USE_SECTOR_BUFFER + /* In order to perform the writes we will have to have a sector buffer. If + * SmartFS is not configured with a sector buffer then we will, then we + * will, unfortunately, need to allocate one. + */ + + buffer = (FAR uint8_t *)kmm_malloc(SMARTFS_TRUNCBUFFER_SIZE); + if (buffer == NULL) + { + ret = -ENOMEM; + goto errout_with_semaphore; + } +#endif + + /* Seek to the end of the file (remembering the current file position) */ + + savepos = sf->filepos; + (void)smartfs_seek_internal(fs, sf, 0, SEEK_END); + + /* Loop until either (1) the file has been fully extended with zeroed data + * or (2) an error occurs. We assume we start with the current sector in + * cache (ff_currentsector) + */ + + remaining = length - oldsize; + while (remaining > 0) + { + /* We will fill up the current sector. Write data to the current + * sector first. + */ + +#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER + readwrite.count = fs->fs_llformat.availbytes - sf->curroffset; + if (readwrite.count > remaining) + { + readwrite.count = remaining; + } + + memset(&sf->buffer[sf->curroffset], 0, readwrite.count); + sf->bflags |= SMARTFS_BFLAG_DIRTY; + +#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ + readwrite.offset = sf->curroffset; + readwrite.logsector = sf->currsector; + readwrite.buffer = buffer; + + /* Select max size that available in the current sector */ + + readwrite.count = fs->fs_llformat.availbytes - sf->curroffset; + if (readwrite.count > remaining) + { + /* Limit the write to the size for our smaller working buffer*/ + + readwrite.count = SMARTFS_TRUNCBUFFER_SIZE; + } + + if (readwrite.count > remaining) + { + /* Futher limit the write to the remaining bytes to write */ + + readwrite.count = remaining; + } + + /* Perform the write */ + + if (readwrite.count > 0) + { + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + ferr("ERROR: Error %d writing sector %d data\n", + ret, sf->currsector); + goto errout_with_buffer; + } + } +#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ + + /* Update our control variables */ + + sf->entry.datlen += readwrite.count; + sf->byteswritten += readwrite.count; + sf->curroffset += readwrite.count; + remaining -= readwrite.count; + + /* Test if we wrote a full sector of data */ + +#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER + if (sf->curroffset == fs->fs_llformat.availbytes && remaining) + { + /* First get a new chained sector */ + + ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF); + if (ret < 0) + { + ferr("ERROR: Error %d allocating new sector\n", ret); + goto errout_with_buffer; + } + + /* Copy the new sector to the old one and chain it */ + + header = (struct smartfs_chain_header_s *) sf->buffer; + *((uint16_t *) header->nextsector) = (uint16_t) ret; + + /* Now sync the file to write this sector out */ + + ret = smartfs_sync_internal(fs, sf); + if (ret != OK) + { + goto errout_with_buffer; + } + + /* Record the new sector in our tracking variables and reset the + * offset to "zero". + */ + + if (sf->currsector == SMARTFS_NEXTSECTOR(header)) + { + /* Error allocating logical sector! */ + + ferr("ERROR: Duplicate logical sector %d\n", sf->currsector); + } + + sf->bflags = SMARTFS_BFLAG_DIRTY; + sf->currsector = SMARTFS_NEXTSECTOR(header); + sf->curroffset = sizeof(struct smartfs_chain_header_s); + memset(sf->buffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes); + header->type = SMARTFS_DIRENT_TYPE_FILE; + } +#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ + + if (sf->curroffset == fs->fs_llformat.availbytes) + { + /* Sync the file to write this sector out */ + + ret = smartfs_sync_internal(fs, sf); + if (ret != OK) + { + goto errout_with_buffer; + } + + /* Allocate a new sector if needed */ + + if (remaining > 0) + { + /* Allocate a new sector */ + + ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF); + if (ret < 0) + { + ferr("ERROR: Error %d allocating new sector\n", ret); + goto errout_with_buffer; + } + + /* Copy the new sector to the old one and chain it */ + + header = (struct smartfs_chain_header_s *)fs->fs_rwbuffer; + *((uint16_t *) header->nextsector) = (uint16_t) ret; + + readwrite.offset = offsetof(struct smartfs_chain_header_s, + nextsector); + readwrite.buffer = (uint8_t *) header->nextsector; + readwrite.count = sizeof(uint16_t); + + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + ferr("ERROR: Error %d writing next sector\n", ret); + goto errout_with_buffer; + } + + /* Record the new sector in our tracking variables and + * reset the offset to "zero". + */ + + if (sf->currsector == SMARTFS_NEXTSECTOR(header)) + { + /* Error allocating logical sector! */ + + ferr("ERROR: Duplicate logical sector %d\n", sf->currsector); + } + + sf->currsector = SMARTFS_NEXTSECTOR(header); + sf->curroffset = sizeof(struct smartfs_chain_header_s); + } + } +#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ + } + + /* The file was successfully extended with zeros */ + + ret = OK; + +errout_with_buffer: + /* Restore the original file position */ + + (void)smartfs_seek_internal(fs, sf, savepos, SEEK_SET); + + /* Release the allocated buffer */ + +#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER + kmm_free(buffer); +#endif + +errout_with_semaphore: + /* Relinquish exclusive access */ + + smartfs_semgive(fs); + return ret; +} + /**************************************************************************** * Name: smartfs_opendir * diff --git a/fs/vfs/fs_truncate.c b/fs/vfs/fs_truncate.c index b5f521732d3..3d383916b7d 100644 --- a/fs/vfs/fs_truncate.c +++ b/fs/vfs/fs_truncate.c @@ -89,7 +89,7 @@ int file_truncate(FAR struct file *filep, off_t length) } /* A NULL write() method is an indicator of a read-only file system (but - * possible not the only indicator) + * possible not the only indicator -- sufficient, but not necessary") */ if (inode->u.i_mops->write == NULL)