diff options
author | AKASHI Takahiro | 2019-05-24 14:10:36 +0900 |
---|---|---|
committer | Tom Rini | 2019-05-28 18:55:08 -0400 |
commit | 9c709c7b4177d063733070c7256f0b8635996d65 (patch) | |
tree | fe97738ca54b3d0d4057ec6978321c3b57a842b6 /fs/fat | |
parent | a9f6706cf0ba330281ae7d6a0af65cc26ffb7d25 (diff) |
fs: fat: flush a directory cluster properly
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>
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/fat_write.c | 33 |
1 files changed, 14 insertions, 19 deletions
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 11b85d35ede..5ea15fab3af 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -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; |