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>
This commit is contained in:
parent
a9f6706cf0
commit
9c709c7b41
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user