From 704df6aa0a284627940e1b15dc6f1aa528753c4b Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 11 Sep 2018 15:59:05 +0900 Subject: [PATCH] fs: fat: refactor write interface for a file offset The current write implementation is quite simple: remove existing clusters and then allocating new ones and filling them with data. This, inevitably, enforces always writing from the beginning of a file. As the first step to lift this restriction, fat_file_write() and set_contents() are modified to accept an additional parameter, file offset and further re-factored so that, in the next patch, all the necessary code will be put into set_contents(). Signed-off-by: AKASHI Takahiro Signed-off-by: Alexander Graf --- fs/fat/fat_write.c | 179 +++++++++++++++++++---------------------------------- 1 file changed, 65 insertions(+), 114 deletions(-) diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 37ef056..c22d8c7 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -529,35 +529,78 @@ static int clear_fatent(fsdata *mydata, __u32 entry) } /* + * Set start cluster in directory entry + */ +static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr, + __u32 start_cluster) +{ + if (mydata->fatsize == 32) + dentptr->starthi = + cpu_to_le16((start_cluster & 0xffff0000) >> 16); + dentptr->start = cpu_to_le16(start_cluster & 0xffff); +} + +/* + * Check whether adding a file makes the file system to + * exceed the size of the block device + * Return -1 when overflow occurs, otherwise return 0 + */ +static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) +{ + __u32 startsect, sect_num, offset; + + if (clustnum > 0) + startsect = clust_to_sect(mydata, clustnum); + else + startsect = mydata->rootdir_sect; + + sect_num = div_u64_rem(size, mydata->sect_size, &offset); + + if (offset != 0) + sect_num++; + + if (startsect + sect_num > total_sector) + return -1; + return 0; +} + +/* * Write at most 'maxsize' bytes from 'buffer' into * the file associated with 'dentptr' * Update the number of bytes written in *gotsize and return 0 * or return -1 on fatal errors. */ static int -set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, - loff_t maxsize, loff_t *gotsize) +set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer, + loff_t maxsize, loff_t *gotsize) { - loff_t filesize = FAT2CPU32(dentptr->size); + loff_t filesize; unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; __u32 curclust = START(dentptr); __u32 endclust = 0, newclust = 0; loff_t actsize; *gotsize = 0; - debug("Filesize: %llu bytes\n", filesize); - - if (maxsize > 0 && filesize > maxsize) - filesize = maxsize; + filesize = maxsize; debug("%llu bytes\n", filesize); - if (!curclust) { - if (filesize) { - debug("error: nonempty clusterless file!\n"); + if (curclust) { + /* + * release already-allocated clusters anyway + */ + if (clear_fatent(mydata, curclust)) { + printf("Error: clearing FAT entries\n"); return -1; } - return 0; + } + + curclust = find_empty_cluster(mydata); + set_start_cluster(mydata, dentptr, curclust); + + if (check_overflow(mydata, curclust, filesize)) { + printf("Error: no space left: %llu\n", filesize); + return -1; } actsize = bytesperclust; @@ -568,6 +611,7 @@ set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, newclust = determine_fatent(mydata, endclust); if ((newclust - 1) != endclust) + /* write to */ goto getit; if (CHECK_CLUST(newclust, mydata->fatsize)) { @@ -614,18 +658,8 @@ getit: actsize = bytesperclust; curclust = endclust = newclust; } while (1); -} -/* - * Set start cluster in directory entry - */ -static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr, - __u32 start_cluster) -{ - if (mydata->fatsize == 32) - dentptr->starthi = - cpu_to_le16((start_cluster & 0xffff0000) >> 16); - dentptr->start = cpu_to_le16(start_cluster & 0xffff); + return 0; } /* @@ -643,31 +677,6 @@ static void fill_dentry(fsdata *mydata, dir_entry *dentptr, } /* - * Check whether adding a file makes the file system to - * exceed the size of the block device - * Return -1 when overflow occurs, otherwise return 0 - */ -static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) -{ - __u32 startsect, sect_num, offset; - - if (clustnum > 0) { - startsect = clust_to_sect(mydata, clustnum); - } else { - startsect = mydata->rootdir_sect; - } - - sect_num = div_u64_rem(size, mydata->sect_size, &offset); - - if (offset != 0) - sect_num++; - - if (startsect + sect_num > total_sector) - return -1; - return 0; -} - -/* * Find a directory entry based on filename or start cluster number * If the directory entry is not found, * the new position for writing a directory entry will be returned @@ -784,11 +793,10 @@ static int normalize_longname(char *l_filename, const char *filename) return 0; } -static int do_fat_write(const char *filename, void *buffer, loff_t size, - loff_t *actwrite) +int file_fat_write_at(const char *filename, loff_t pos, void *buffer, + loff_t size, loff_t *actwrite) { dir_entry *retdent; - __u32 start_cluster; fsdata datablock = { .fatbuf = NULL, }; fsdata *mydata = &datablock; fat_itr *itr = NULL; @@ -796,6 +804,8 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size, char *filename_copy, *parent, *basename; char l_filename[VFAT_MAXLEN_BYTES]; + debug("writing %s\n", filename); + filename_copy = strdup(filename); if (!filename_copy) return -ENOMEM; @@ -839,47 +849,8 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size, goto exit; } - /* Update file size and start_cluster in a directory entry */ - retdent->size = cpu_to_le32(size); - start_cluster = START(retdent); - - if (start_cluster) { - if (size) { - ret = check_overflow(mydata, start_cluster, - size); - if (ret) { - printf("Error: %llu overflow\n", size); - ret = -ENOSPC; - goto exit; - } - } - - ret = clear_fatent(mydata, start_cluster); - if (ret) { - printf("Error: clearing FAT entries\n"); - ret = -EIO; - goto exit; - } - - if (!size) - set_start_cluster(mydata, retdent, 0); - } else if (size) { - ret = start_cluster = find_empty_cluster(mydata); - if (ret < 0) { - printf("Error: finding empty cluster\n"); - ret = -ENOSPC; - goto exit; - } - - ret = check_overflow(mydata, start_cluster, size); - if (ret) { - printf("Error: %llu overflow\n", size); - ret = -ENOSPC; - goto exit; - } - - set_start_cluster(mydata, retdent, start_cluster); - } + /* Update file size in a directory entry */ + retdent->size = cpu_to_le32(pos + size); } else { /* Create a new file */ @@ -907,32 +878,13 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size, goto exit; } - if (size) { - ret = start_cluster = find_empty_cluster(mydata); - if (ret < 0) { - printf("Error: finding empty cluster\n"); - ret = -ENOSPC; - goto exit; - } - - ret = check_overflow(mydata, start_cluster, size); - if (ret) { - printf("Error: %llu overflow\n", size); - ret = -ENOSPC; - goto exit; - } - } else { - start_cluster = 0; - } - /* Set attribute as archive for regular file */ - fill_dentry(itr->fsdata, itr->dent, filename, - start_cluster, size, 0x20); + fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20); retdent = itr->dent; } - ret = set_contents(mydata, retdent, buffer, size, actwrite); + ret = set_contents(mydata, retdent, pos, buffer, size, actwrite); if (ret < 0) { printf("Error: writing contents\n"); ret = -EIO; @@ -971,6 +923,5 @@ int file_fat_write(const char *filename, void *buffer, loff_t offset, return -EINVAL; } - printf("writing %s\n", filename); - return do_fat_write(filename, buffer, maxsize, actwrite); + return file_fat_write_at(filename, offset, buffer, maxsize, actwrite); }