fs: fat: flush new directory cluster
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Thu, 26 Nov 2020 18:06:55 +0000 (19:06 +0100)
committerHeinrich Schuchardt <xypron.glpk@gmx.de>
Thu, 10 Dec 2020 08:14:59 +0000 (09:14 +0100)
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.glpk@gmx.de>
fs/fat/fat_write.c

index 0746d73..941f878 100644 (file)
@@ -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;