fs: fat: flush a directory cluster properly
authorAKASHI Takahiro <takahiro.akashi@linaro.org>
Fri, 24 May 2019 05:10:36 +0000 (14:10 +0900)
committerTom Rini <trini@konsulko.com>
Tue, 28 May 2019 22:55:08 +0000 (18:55 -0400)
When a long name directory entry is created, multiple directory entries
may be occupied across a directory cluster boundary. Since only one
directory cluster is cached in a directory iterator, a first cluster must
be written back to device before switching over a second cluster.

Without this patch, some added files may be lost even if you don't see
any failures on write operation.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Tested-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
fs/fat/fat_write.c

index 11b85d35eded4a55b55b475d3a77f09d5a639a97..5ea15fab3af6f00a7ef2ee79095c4ba2f0ef93fd 100644 (file)
@@ -209,7 +209,8 @@ name11_12:
        return 1;
 }
 
-static int flush_dir_table(fat_itr *itr);
+static int new_dir_table(fat_itr *itr);
+static int flush_dir(fat_itr *itr);
 
 /*
  * Fill dir_slot entries with appropriate name, id, and attr
@@ -242,19 +243,15 @@ fill_dir_slot(fat_itr *itr, const char *l_name)
                memcpy(itr->dent, slotptr, sizeof(dir_slot));
                slotptr--;
                counter--;
+
+               if (itr->remaining == 0)
+                       flush_dir(itr);
+
                if (!fat_itr_next(itr))
-                       if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+                       if (!itr->dent && !itr->is_root && new_dir_table(itr))
                                return -1;
        }
 
-       if (!itr->dent && !itr->is_root)
-               /*
-                * don't care return value here because we have already
-                * finished completing an entry with name, only ending up
-                * no more entry left
-                */
-               flush_dir_table(itr);
-
        return 0;
 }
 
@@ -621,18 +618,14 @@ static int find_empty_cluster(fsdata *mydata)
 }
 
 /*
- * Write directory entries in itr's buffer to block device
+ * Allocate a cluster for additional directory entries
  */
-static int flush_dir_table(fat_itr *itr)
+static int new_dir_table(fat_itr *itr)
 {
        fsdata *mydata = itr->fsdata;
        int dir_newclust = 0;
        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 
-       if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) {
-               printf("error: writing directory entry\n");
-               return -1;
-       }
        dir_newclust = find_empty_cluster(mydata);
        set_fatent_value(mydata, itr->clust, dir_newclust);
        if (mydata->fatsize == 32)
@@ -987,7 +980,7 @@ static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
                        return itr->dent;
        }
 
-       if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+       if (!itr->dent && !itr->is_root && new_dir_table(itr))
                /* indicate that allocating dent failed */
                itr->dent = NULL;
 
@@ -1164,14 +1157,16 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
 
                memset(itr->dent, 0, sizeof(*itr->dent));
 
-               /* Set short name to set alias checksum field in dir_slot */
+               /* Calculate checksum for short name */
                set_name(itr->dent, filename);
+
+               /* Set long name entries */
                if (fill_dir_slot(itr, filename)) {
                        ret = -EIO;
                        goto exit;
                }
 
-               /* Set attribute as archive for regular file */
+               /* Set short name entry */
                fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
 
                retdent = itr->dent;