This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 479c393291 fs/fat/fs_fat32.[c|h]: fix fseek bug when file size is
multiple of cluster size
479c393291 is described below
commit 479c39329143561e68fbd57cbcbb1e0c96be068b
Author: Windrow14 <[email protected]>
AuthorDate: Thu Jul 4 10:22:00 2024 +0800
fs/fat/fs_fat32.[c|h]: fix fseek bug when file size is multiple of cluster
size
Unbinding `ff_currentcluster` and `f_pos`:
1. Added `ff_pos` in `struct fat_file_s`.
2. Added function `fat_zero_cluster` for doing zeroing for gap
between EOF and new position beyond EOF.
3. Added function `fat_get_sectors` for getting the sector where
`f_pos` is located, allocting new cluster when `f_pos` is beyond
EOF.
4. Modify function `fat_read`, and `fat_write` with above functions.
5. Remove redundant logics in `fat_seek` since now new cluster is
allocated when writing instead of seeking.
Signed-off-by: Yinzhe Wu <[email protected]>
Reviewed-by: Yuezhang Mo <[email protected]>
Reviewed-by: Jacky Cao <[email protected]>
Tested-by: Yinzhe Wu <[email protected]>
---
fs/fat/fs_fat32.c | 550 +++++++++++++++++++++++++++++-------------------------
fs/fat/fs_fat32.h | 1 +
2 files changed, 296 insertions(+), 255 deletions(-)
diff --git a/fs/fat/fs_fat32.c b/fs/fat/fs_fat32.c
index d4a913f8cd..e3b77e4ddc 100644
--- a/fs/fat/fs_fat32.c
+++ b/fs/fat/fs_fat32.c
@@ -54,6 +54,10 @@
# define OFF_MAX INT32_MAX
#endif
+#define ROUND_DOWN(a, b) ((a) & ~((b) - 1))
+#define ROUND_UP(a, b) (((a) + (b) - 1) & ~((b) - 1))
+#define DIV_ROUND_UP(a, b) (ROUND_UP(a, b) / (b))
+
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@@ -470,6 +474,271 @@ static int fat_close(FAR struct file *filep)
return ret;
}
+/****************************************************************************
+ * Name: fat_zero_cluster
+
+ * Description:
+ * Zero the data in a cluster. Use to zero the data in the gap between EOF
+ * and the write offset.
+ *
+ * Input Parameters:
+ * fs - A reference to the fat volume object instance
+ * cluster - Cluster index to be zeroed
+ * start - The starting position in the cluster
+ * end - The ending position in the cluster
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; A negated errno value is returned on
+ * any failure.
+ *
+ ****************************************************************************/
+
+static int fat_zero_cluster(FAR struct fat_mountpt_s *fs, int cluster,
+ int start, int end)
+{
+ FAR uint8_t *buf;
+ int zero_len = fs->fs_hwsectorsize - (start & (fs->fs_hwsectorsize - 1));
+ off_t i;
+ off_t sector = fat_cluster2sector(fs, cluster);
+ off_t start_sec = sector + start / fs->fs_hwsectorsize;
+ off_t end_sec = sector + DIV_ROUND_UP(end, fs->fs_hwsectorsize);
+ int ret;
+
+ buf = kmm_malloc(fs->fs_hwsectorsize);
+ if (!buf)
+ {
+ return -ENOMEM;
+ }
+
+ if (zero_len)
+ {
+ ret = fat_hwread(fs, buf, start_sec, 1);
+ if (ret < 0)
+ {
+ goto out;
+ }
+
+ memset(buf + fs->fs_hwsectorsize - zero_len, 0, zero_len);
+
+ ret = fat_hwwrite(fs, buf, start_sec, 1);
+ if (ret < 0)
+ {
+ goto out;
+ }
+
+ start_sec++;
+ }
+
+ memset(buf, 0, fs->fs_hwsectorsize - zero_len);
+
+ for (i = start_sec; i < end_sec; i++)
+ {
+ ret = fat_hwwrite(fs, buf, i, 1);
+ if (ret < 0)
+ {
+ goto out;
+ }
+ }
+
+ ret = OK;
+
+out:
+ kmm_free(buf);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_get_sectors
+ *
+ * Description:
+ * Get the sector index where ->f_pos is located. This function will
+ * allocate new clusters if get sectors for writing and ->f_pos is out of
+ * EOF and zero the data in the gap between EOF and the write offset.
+ *
+ * Input Parameters:
+ * fs - A reference to the file
+ * read - True if get sectors for reading
+ *
+ * Output:
+ * ->ff_currentsector - the sector index where ->f_pos is located
+ * ->ff_currentcluster - the cluster index where ->f_pos is located
+ * ->ff_sectorsincluster - sectors remaining in cluster
+ * ->ff_startcluster - the first cluster of the file when writing an
+ * empty file
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; A negated errno value is returned on
+ * any failure.
+ *
+ ****************************************************************************/
+
+static int fat_get_sectors(FAR struct file *filep, bool read)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct fat_mountpt_s *fs = inode->i_private;
+ FAR struct fat_file_s *ff = filep->f_priv;
+ int i;
+ int num_clu;
+ int new_num_clu;
+ int num_traversed;
+ int cluster;
+ int ret;
+ int zero_start;
+ int zero_end;
+ int clu_size = fs->fs_fatsecperclus * fs->fs_hwsectorsize;
+
+ num_clu = DIV_ROUND_UP(ff->ff_size, clu_size);
+ new_num_clu = DIV_ROUND_UP(filep->f_pos + 1, clu_size);
+
+ if (ff->ff_startcluster == 0)
+ {
+ /* empty file */
+
+ cluster = 0;
+ num_traversed = 0;
+ }
+ else if (ff->ff_currentcluster >= 2 &&
+ ff->ff_currentcluster < fs->fs_nclusters + 2 &&
+ ff->ff_pos <= filep->f_pos)
+ {
+ /* fatchain is traversed but not reach filep->f_pos */
+
+ cluster = ff->ff_currentcluster;
+ num_traversed = ff->ff_pos / clu_size + 1;
+ }
+ else
+ {
+ /* Traverse the FATchain from the first cluster of the file */
+
+ cluster = ff->ff_startcluster;
+ num_traversed = 1;
+ }
+
+ /* Traverse the existing chain */
+
+ for (i = num_traversed; i < num_clu && i < new_num_clu; i++)
+ {
+ cluster = fat_getcluster(fs, cluster);
+
+ /* The chain is broken */
+
+ if (cluster < 2 || cluster >= fs->fs_nclusters + 2)
+ {
+ return -EIO;
+ }
+ }
+
+ if (read)
+ {
+ goto out;
+ }
+
+ /* The 3 areas should be zeroed.
+ *
+ * cluster cluster+1..N cluster+2..N+1
+ * +-------------------+------------------+---------------------+
+ * | | (1) | (2) | (3) | | |
+ * +-------------------+------------------+---------------------+
+ * ^ ^
+ * ff_size f_pos
+ */
+
+ /* zero area (1) */
+
+ if (i == num_clu && filep->f_pos > ff->ff_size && ff->ff_size)
+ {
+ zero_start = ff->ff_size & (clu_size - 1);
+
+ if (num_clu == new_num_clu)
+ {
+ zero_end = filep->f_pos & (clu_size - 1);
+ }
+ else
+ {
+ zero_end = clu_size;
+ }
+
+ fat_ffcacheinvalidate(fs, ff);
+
+ ret = fat_zero_cluster(fs, cluster, zero_start, zero_end);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+
+ /* Append new clusters for writing */
+
+ for (; i < new_num_clu - 1; i++)
+ {
+ cluster = fat_extendchain(fs, cluster);
+
+ if (cluster < 2 || cluster >= fs->fs_nclusters + 2)
+ {
+ return -EIO;
+ }
+
+ /* zero area (2) */
+
+ ret = fat_zero_cluster(fs, cluster, 0, clu_size);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (ff->ff_startcluster == 0)
+ {
+ ff->ff_startcluster = cluster;
+ }
+ }
+
+ if (i == new_num_clu - 1)
+ {
+ cluster = fat_extendchain(fs, cluster);
+
+ if (cluster < 2 || cluster >= fs->fs_nclusters + 2)
+ {
+ return -EIO;
+ }
+
+ /* zero area (3) */
+
+ zero_end = filep->f_pos & (clu_size -1);
+ if (zero_end)
+ {
+ ret = fat_zero_cluster(fs, cluster, 0, zero_end);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ if (ff->ff_startcluster == 0)
+ {
+ ff->ff_startcluster = cluster;
+ }
+ }
+
+ if (filep->f_pos > ff->ff_size)
+ {
+ ff->ff_size = filep->f_pos;
+ }
+
+out:
+ ff->ff_currentcluster = cluster;
+
+ ret = fat_currentsector(fs, ff, filep->f_pos);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ff->ff_pos = ROUND_DOWN(filep->f_pos, clu_size);
+
+ return 0;
+}
+
/****************************************************************************
* Name: fat_read
****************************************************************************/
@@ -483,7 +752,6 @@ static ssize_t fat_read(FAR struct file *filep, FAR char
*buffer,
unsigned int bytesread;
unsigned int readsize;
size_t bytesleft;
- int32_t cluster;
FAR uint8_t *userbuffer = (FAR uint8_t *)buffer;
int sectorindex;
int ret;
@@ -544,32 +812,19 @@ static ssize_t fat_read(FAR struct file *filep, FAR char
*buffer,
ret = 0;
goto errout_with_lock;
}
-
- /* 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)
+ else
{
- buflen = bytesleft;
- }
+ /* Get the number of bytes left in the file */
- /* Get the first sector to read from. */
+ bytesleft = ff->ff_size - filep->f_pos;
- if (!ff->ff_currentsector)
- {
- /* The current sector can be determined from the current cluster and
- * the file offset.
+ /* Truncate read count so that it does not exceed the number of bytes
+ * left in the file.
*/
- ret = fat_currentsector(fs, ff, filep->f_pos);
- if (ret < 0)
+ if (buflen > bytesleft)
{
- goto errout_with_lock;
+ buflen = bytesleft;
}
}
@@ -585,26 +840,10 @@ static ssize_t fat_read(FAR struct file *filep, FAR char
*buffer,
{
bytesread = 0;
- /* Check if the current read stream has incremented to the next
- * cluster boundary
- */
-
- if (ff->ff_sectorsincluster < 1)
+ ret = fat_get_sectors(filep, true);
+ if (ret < 0)
{
- /* Find the next cluster in the FAT. */
-
- cluster = fat_getcluster(fs, ff->ff_currentcluster);
- if (cluster < 2 || cluster >= fs->fs_nclusters + 2)
- {
- ret = -EINVAL; /* Not the right error */
- goto errout_with_lock;
- }
-
- /* 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;
+ goto errout_with_lock;
}
#ifdef CONFIG_FAT_DIRECT_RETRY /* Warning avoidance */
@@ -731,7 +970,6 @@ static ssize_t fat_write(FAR struct file *filep, FAR const
char *buffer,
FAR struct inode *inode;
FAR struct fat_mountpt_s *fs;
FAR struct fat_file_s *ff;
- int32_t cluster;
unsigned int byteswritten;
unsigned int writesize;
FAR uint8_t *userbuffer = (FAR uint8_t *)buffer;
@@ -791,32 +1029,6 @@ static ssize_t fat_write(FAR struct file *filep, FAR
const char *buffer,
goto errout_with_lock;
}
- /* 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, filep->f_pos);
- if (ret < 0)
- {
- goto errout_with_lock;
- }
- }
-
/* 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)
@@ -827,36 +1039,10 @@ static ssize_t fat_write(FAR struct file *filep, FAR
const char *buffer,
while (buflen > 0)
{
- /* Check if the current write stream has incremented to the next
- * cluster boundary
- */
-
- if (ff->ff_sectorsincluster < 1)
+ ret = fat_get_sectors(filep, false);
+ if (ret < 0)
{
- /* 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_lock;
- }
- else if (cluster < 2 || cluster >= fs->fs_nclusters + 2)
- {
- ret = -ENOSPC;
- goto errout_with_lock;
- }
-
- /* 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);
+ goto errout_with_lock;
}
#ifdef CONFIG_FAT_DIRECT_RETRY /* Warning avoidance */
@@ -1005,13 +1191,13 @@ fat_write_restart:
byteswritten += writesize;
buflen -= writesize;
sectorindex = filep->f_pos & SEC_NDXMASK(fs);
- }
- /* The transfer has completed without error. Update the file size */
+ /* Update the file size */
- if (filep->f_pos > ff->ff_size)
- {
- ff->ff_size = filep->f_pos;
+ if (filep->f_pos > ff->ff_size)
+ {
+ ff->ff_size = filep->f_pos;
+ }
}
nxmutex_unlock(&fs->fs_lock);
@@ -1031,9 +1217,7 @@ static off_t fat_seek(FAR struct file *filep, off_t
offset, int whence)
FAR struct inode *inode;
FAR struct fat_mountpt_s *fs;
FAR struct fat_file_s *ff;
- int32_t cluster;
off_t position;
- unsigned int clustersize;
int ret;
/* Sanity checks */
@@ -1080,6 +1264,13 @@ static off_t fat_seek(FAR struct file *filep, off_t
offset, int whence)
return -EINVAL;
}
+ /* Invalid arguments are entered, returns an error. */
+
+ if (position < 0)
+ {
+ return -EINVAL;
+ }
+
/* Special case: We are seeking to the current position. This would
* happen normally with ftell() which does lseek(fd, 0, SEEK_CUR) but can
* also happen in other situation such as when SEEK_SET is used to assure
@@ -1117,158 +1308,7 @@ static off_t fat_seek(FAR struct file *filep, off_t
offset, int whence)
goto errout_with_lock;
}
- /* Attempts to set the position beyond 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_lock;
- }
-
- 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 follow the existing chain */
-
- cluster = fat_getcluster(fs, cluster);
- }
-
- if (cluster < 0)
- {
- /* An error occurred getting the cluster */
-
- ret = cluster;
- goto errout_with_lock;
- }
-
- /* Zero means that there is no further clusters available
- * in the chain.
- */
-
- if (cluster == 0)
- {
- /* At the position to the current location and
- * break out.
- */
-
- position = clustersize;
- break;
- }
-
- if (cluster >= fs->fs_nclusters + 2)
- {
- ret = -ENOSPC;
- goto errout_with_lock;
- }
-
- /* 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
- */
-
- 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_lock;
- }
- }
- }
-
- /* 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;
- }
+ filep->f_pos = position;
nxmutex_unlock(&fs->fs_lock);
return OK;
diff --git a/fs/fat/fs_fat32.h b/fs/fat/fs_fat32.h
index e1d7ce43a9..1f3ad5261f 100644
--- a/fs/fat/fs_fat32.h
+++ b/fs/fat/fs_fat32.h
@@ -907,6 +907,7 @@ struct fat_file_s
off_t ff_startcluster; /* Start cluster of file on media */
off_t ff_currentsector; /* Current sector being operated on */
off_t ff_cachesector; /* Current sector in the file buffer */
+ off_t ff_pos; /* Current position in the file */
uint8_t *ff_buffer; /* File buffer (for partial sector
accesses) */
};