A write at an offset that ends before the end of the file trims the
file at that point. This was chosen on purpose in commit cb8af8af5ba0
("fs: fat: support write with non-zero offset") to stay compatible with
the interface of the fatwrite command, which replaces the whole file.Callers that update a part of a file, like the EFI file protocol, need the data after the written range to stay. Move the body of file_fat_write_at() to a static function that takes a keep_tail flag. With the flag set, set_contents() does not free the clusters after the written range, and the size in the directory entry only grows. Keep file_fat_write_at() as a wrapper with the current behaviour and add file_fat_overwrite() for the new one. Signed-off-by: Cedric Hombourger <[email protected]> --- fs/fat/fat_write.c | 32 ++++++++++++++++++++++++-------- include/fat.h | 2 ++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index a1d28b4e305766b8fb8ee097fafab235faf8ad52..03cd6d368b01d7b4d08502e1783e85f61c39e323 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -925,10 +925,12 @@ static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size) * the file associated with 'dentptr' * Update the number of bytes written in *gotsize and return 0 * or return -1 on fatal errors. + * Unless 'keep_tail' is set, a write that ends before the end of the file + * truncates the file there. */ static int set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer, - loff_t maxsize, loff_t *gotsize) + loff_t maxsize, loff_t *gotsize, bool keep_tail) { unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; __u32 curclust = START(dentptr); @@ -1045,7 +1047,7 @@ set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer, if (filesize <= cur_pos) { /* no more write */ newclust = get_fatent(mydata, endclust); - if (!IS_LAST_CLUST(newclust, mydata->fatsize)) { + if (!keep_tail && !IS_LAST_CLUST(newclust, mydata->fatsize)) { /* truncate the rest */ clear_fatent(mydata, newclust); @@ -1469,8 +1471,8 @@ static int normalize_longname(char *l_filename, const char *filename) return 0; } -int file_fat_write_at(const char *filename, loff_t pos, void *buffer, - loff_t size, loff_t *actwrite) +static int fat_write_at(const char *filename, loff_t pos, void *buffer, + loff_t size, loff_t *actwrite, bool keep_tail) { dir_entry *retdent; fsdata datablock = { .fatbuf = NULL, }; @@ -1535,7 +1537,8 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer, } /* Update file size in a directory entry */ - retdent->size = cpu_to_le32(pos + size); + if (!keep_tail || pos + size > FAT2CPU32(retdent->size)) + retdent->size = cpu_to_le32(pos + size); /* Update change date */ dentry_set_time(retdent); } else { @@ -1552,7 +1555,8 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer, retdent = itr->dent; } - ret = set_contents(mydata, retdent, pos, buffer, size, actwrite); + ret = set_contents(mydata, retdent, pos, buffer, size, actwrite, + keep_tail); if (ret < 0) { printf("Error: writing contents\n"); ret = -EIO; @@ -1578,6 +1582,18 @@ exit: return ret; } +int file_fat_write_at(const char *filename, loff_t pos, void *buffer, + loff_t size, loff_t *actwrite) +{ + return fat_write_at(filename, pos, buffer, size, actwrite, false); +} + +int file_fat_overwrite(const char *filename, void *buffer, loff_t offset, + loff_t maxsize, loff_t *actwrite) +{ + return fat_write_at(filename, offset, buffer, maxsize, actwrite, true); +} + int file_fat_write(const char *filename, void *buffer, loff_t offset, loff_t maxsize, loff_t *actwrite) { @@ -1877,7 +1893,7 @@ int fat_mkdir(const char *dirname) set_start_cluster(mydata, &dotdent[1], itr->start_clust); ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent, - bytesperclust, &actwrite); + bytesperclust, &actwrite, false); if (ret < 0) { printf("Error: writing contents\n"); goto exit; @@ -1885,7 +1901,7 @@ int fat_mkdir(const char *dirname) /* Write twice for "." */ set_start_cluster(mydata, &dotdent[0], START(retdent)); ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent, - bytesperclust, &actwrite); + bytesperclust, &actwrite, false); if (ret < 0) { printf("Error: writing contents\n"); goto exit; diff --git a/include/fat.h b/include/fat.h index 40da0370a4442edad872661408c856b7dac0787c..499e1924838049568466d8d4329c24ac4c26b087 100644 --- a/include/fat.h +++ b/include/fat.h @@ -225,6 +225,8 @@ int fat_register_device(struct blk_desc *dev_desc, int part_no); int file_fat_write(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actwrite); +int file_fat_overwrite(const char *filename, void *buf, loff_t offset, + loff_t len, loff_t *actwrite); int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actread); int fat_opendir(const char *filename, struct fs_dir_stream **dirsp); -- 2.47.3
