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 <xypron.g...@gmx.de>
---
 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;
--
2.29.2

Reply via email to