fs: fat: refactor write interface for a file offset

The current write implementation is quite simple: remove existing clusters
and then allocating new ones and filling them with data. This, inevitably,
enforces always writing from the beginning of a file.

As the first step to lift this restriction, fat_file_write() and
set_contents() are modified to accept an additional parameter, file offset
and further re-factored so that, in the next patch, all the necessary code
will be put into set_contents().

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
AKASHI Takahiro 2018-09-11 15:59:05 +09:00 committed by Alexander Graf
parent 4ced2039dc
commit 704df6aa0a

View file

@ -528,6 +528,42 @@ static int clear_fatent(fsdata *mydata, __u32 entry)
return 0; return 0;
} }
/*
* Set start cluster in directory entry
*/
static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
__u32 start_cluster)
{
if (mydata->fatsize == 32)
dentptr->starthi =
cpu_to_le16((start_cluster & 0xffff0000) >> 16);
dentptr->start = cpu_to_le16(start_cluster & 0xffff);
}
/*
* Check whether adding a file makes the file system to
* exceed the size of the block device
* Return -1 when overflow occurs, otherwise return 0
*/
static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
{
__u32 startsect, sect_num, offset;
if (clustnum > 0)
startsect = clust_to_sect(mydata, clustnum);
else
startsect = mydata->rootdir_sect;
sect_num = div_u64_rem(size, mydata->sect_size, &offset);
if (offset != 0)
sect_num++;
if (startsect + sect_num > total_sector)
return -1;
return 0;
}
/* /*
* Write at most 'maxsize' bytes from 'buffer' into * Write at most 'maxsize' bytes from 'buffer' into
* the file associated with 'dentptr' * the file associated with 'dentptr'
@ -535,29 +571,36 @@ static int clear_fatent(fsdata *mydata, __u32 entry)
* or return -1 on fatal errors. * or return -1 on fatal errors.
*/ */
static int static int
set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
loff_t maxsize, loff_t *gotsize) loff_t maxsize, loff_t *gotsize)
{ {
loff_t filesize = FAT2CPU32(dentptr->size); loff_t filesize;
unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
__u32 curclust = START(dentptr); __u32 curclust = START(dentptr);
__u32 endclust = 0, newclust = 0; __u32 endclust = 0, newclust = 0;
loff_t actsize; loff_t actsize;
*gotsize = 0; *gotsize = 0;
debug("Filesize: %llu bytes\n", filesize); filesize = maxsize;
if (maxsize > 0 && filesize > maxsize)
filesize = maxsize;
debug("%llu bytes\n", filesize); debug("%llu bytes\n", filesize);
if (!curclust) { if (curclust) {
if (filesize) { /*
debug("error: nonempty clusterless file!\n"); * release already-allocated clusters anyway
*/
if (clear_fatent(mydata, curclust)) {
printf("Error: clearing FAT entries\n");
return -1; return -1;
} }
return 0; }
curclust = find_empty_cluster(mydata);
set_start_cluster(mydata, dentptr, curclust);
if (check_overflow(mydata, curclust, filesize)) {
printf("Error: no space left: %llu\n", filesize);
return -1;
} }
actsize = bytesperclust; actsize = bytesperclust;
@ -568,6 +611,7 @@ set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
newclust = determine_fatent(mydata, endclust); newclust = determine_fatent(mydata, endclust);
if ((newclust - 1) != endclust) if ((newclust - 1) != endclust)
/* write to <curclust..endclust> */
goto getit; goto getit;
if (CHECK_CLUST(newclust, mydata->fatsize)) { if (CHECK_CLUST(newclust, mydata->fatsize)) {
@ -614,18 +658,8 @@ getit:
actsize = bytesperclust; actsize = bytesperclust;
curclust = endclust = newclust; curclust = endclust = newclust;
} while (1); } while (1);
}
/* return 0;
* Set start cluster in directory entry
*/
static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
__u32 start_cluster)
{
if (mydata->fatsize == 32)
dentptr->starthi =
cpu_to_le16((start_cluster & 0xffff0000) >> 16);
dentptr->start = cpu_to_le16(start_cluster & 0xffff);
} }
/* /*
@ -642,31 +676,6 @@ static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
set_name(dentptr, filename); set_name(dentptr, filename);
} }
/*
* Check whether adding a file makes the file system to
* exceed the size of the block device
* Return -1 when overflow occurs, otherwise return 0
*/
static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
{
__u32 startsect, sect_num, offset;
if (clustnum > 0) {
startsect = clust_to_sect(mydata, clustnum);
} else {
startsect = mydata->rootdir_sect;
}
sect_num = div_u64_rem(size, mydata->sect_size, &offset);
if (offset != 0)
sect_num++;
if (startsect + sect_num > total_sector)
return -1;
return 0;
}
/* /*
* Find a directory entry based on filename or start cluster number * Find a directory entry based on filename or start cluster number
* If the directory entry is not found, * If the directory entry is not found,
@ -784,11 +793,10 @@ static int normalize_longname(char *l_filename, const char *filename)
return 0; return 0;
} }
static int do_fat_write(const char *filename, void *buffer, loff_t size, int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
loff_t *actwrite) loff_t size, loff_t *actwrite)
{ {
dir_entry *retdent; dir_entry *retdent;
__u32 start_cluster;
fsdata datablock = { .fatbuf = NULL, }; fsdata datablock = { .fatbuf = NULL, };
fsdata *mydata = &datablock; fsdata *mydata = &datablock;
fat_itr *itr = NULL; fat_itr *itr = NULL;
@ -796,6 +804,8 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size,
char *filename_copy, *parent, *basename; char *filename_copy, *parent, *basename;
char l_filename[VFAT_MAXLEN_BYTES]; char l_filename[VFAT_MAXLEN_BYTES];
debug("writing %s\n", filename);
filename_copy = strdup(filename); filename_copy = strdup(filename);
if (!filename_copy) if (!filename_copy)
return -ENOMEM; return -ENOMEM;
@ -839,47 +849,8 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size,
goto exit; goto exit;
} }
/* Update file size and start_cluster in a directory entry */ /* Update file size in a directory entry */
retdent->size = cpu_to_le32(size); retdent->size = cpu_to_le32(pos + size);
start_cluster = START(retdent);
if (start_cluster) {
if (size) {
ret = check_overflow(mydata, start_cluster,
size);
if (ret) {
printf("Error: %llu overflow\n", size);
ret = -ENOSPC;
goto exit;
}
}
ret = clear_fatent(mydata, start_cluster);
if (ret) {
printf("Error: clearing FAT entries\n");
ret = -EIO;
goto exit;
}
if (!size)
set_start_cluster(mydata, retdent, 0);
} else if (size) {
ret = start_cluster = find_empty_cluster(mydata);
if (ret < 0) {
printf("Error: finding empty cluster\n");
ret = -ENOSPC;
goto exit;
}
ret = check_overflow(mydata, start_cluster, size);
if (ret) {
printf("Error: %llu overflow\n", size);
ret = -ENOSPC;
goto exit;
}
set_start_cluster(mydata, retdent, start_cluster);
}
} else { } else {
/* Create a new file */ /* Create a new file */
@ -907,32 +878,13 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size,
goto exit; goto exit;
} }
if (size) {
ret = start_cluster = find_empty_cluster(mydata);
if (ret < 0) {
printf("Error: finding empty cluster\n");
ret = -ENOSPC;
goto exit;
}
ret = check_overflow(mydata, start_cluster, size);
if (ret) {
printf("Error: %llu overflow\n", size);
ret = -ENOSPC;
goto exit;
}
} else {
start_cluster = 0;
}
/* Set attribute as archive for regular file */ /* Set attribute as archive for regular file */
fill_dentry(itr->fsdata, itr->dent, filename, fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
start_cluster, size, 0x20);
retdent = itr->dent; retdent = itr->dent;
} }
ret = set_contents(mydata, retdent, buffer, size, actwrite); ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
if (ret < 0) { if (ret < 0) {
printf("Error: writing contents\n"); printf("Error: writing contents\n");
ret = -EIO; ret = -EIO;
@ -971,6 +923,5 @@ int file_fat_write(const char *filename, void *buffer, loff_t offset,
return -EINVAL; return -EINVAL;
} }
printf("writing %s\n", filename); return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
return do_fat_write(filename, buffer, maxsize, actwrite);
} }