From 569b0e1938a5c78b6f4152704ef8c8a4e04fcb09 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 26 Nov 2020 19:06:55 +0100 Subject: fs: fat: flush new directory cluster When handling long file names directory entries may be split over multiple clusters. We must make sure that new clusters are zero filled on disk. When allocating a new cluster for a directory flush it. The flushing should be executed before updating the FAT. This way if flushing fails, we still have a valid directory structure. Signed-off-by: Heinrich Schuchardt --- fs/fat/fat_write.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 0746d73f8d..941f8789ab 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -738,17 +738,32 @@ static int find_empty_cluster(fsdata *mydata) return entry; } -/* - * Allocate a cluster for additional directory entries +/** + * new_dir_table() - allocate a cluster for additional directory entries + * + * @itr: directory iterator + * Return: 0 on success, -EIO otherwise */ static int new_dir_table(fat_itr *itr) { fsdata *mydata = itr->fsdata; int dir_newclust = 0; + int dir_oldclust = itr->clust; unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; dir_newclust = find_empty_cluster(mydata); - set_fatent_value(mydata, itr->clust, dir_newclust); + + /* + * Flush before updating FAT to ensure valid directory structure + * in case of failure. + */ + itr->clust = dir_newclust; + itr->next_clust = dir_newclust; + memset(itr->block, 0x00, bytesperclust); + if (flush_dir(itr)) + return -EIO; + + set_fatent_value(mydata, dir_oldclust, dir_newclust); if (mydata->fatsize == 32) set_fatent_value(mydata, dir_newclust, 0xffffff8); else if (mydata->fatsize == 16) @@ -756,13 +771,8 @@ static int new_dir_table(fat_itr *itr) else if (mydata->fatsize == 12) set_fatent_value(mydata, dir_newclust, 0xff8); - itr->clust = dir_newclust; - itr->next_clust = dir_newclust; - if (flush_dirty_fat_buffer(mydata) < 0) - return -1; - - memset(itr->block, 0x00, bytesperclust); + return -EIO; itr->dent = (dir_entry *)itr->block; itr->last_cluster = 1; -- cgit v1.2.3