[U-Boot] [PATCH 1/1] fs: fat: write with non-zero offset
Heinrich Schuchardt
xypron.glpk at gmx.de
Thu Jun 28 18:14:17 UTC 2018
This relates to a patch in
https://git.linaro.org/people/takahiro.akashi/u-boot.git
branch efi/for_sct.
On 06/13/2018 08:32 AM, AKASHI Takahiro wrote:
> ---
> fs/fat/fat_write.c | 385 +++++++++++++++++++++++++++++++++++----------
> 1 file changed, 299 insertions(+), 86 deletions(-)
>
> diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
> index a930b66f28..7e63646655 100644
> --- a/fs/fat/fat_write.c
> +++ b/fs/fat/fat_write.c
> @@ -445,6 +445,119 @@ set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
> return 0;
> }
>
> +static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
> +
> +/*
> + * Read and modify data on existing and consecutive cluster blocks
> + */
> +static int
> +get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
> + loff_t size, loff_t *gotsize)
> +{
> + unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
> + __u32 startsect;
> + loff_t wsize;
> + int clustcount, i, ret;
> +
> + *gotsize = 0;
> + if (!size)
> + return 0;
> +
> + assert(pos < bytesperclust);
> + startsect = clust_to_sect(mydata, clustnum);
> +
> + debug("clustnum: %d, startsect: %d, pos: %lld\n", clustnum, startsect,
> + pos);
> +
> + /* partial write at begining */
> + if (pos) {
> + wsize = min(bytesperclust - pos, size);
> + ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
> + if (ret != mydata->clust_size) {
> + debug("Error reading data (got %d)\n", ret);
> + return -1;
> + }
> +
> + memcpy(tmpbuf_cluster + pos, buffer, wsize);
> + ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
> + if (ret != mydata->clust_size) {
> + debug("Error writing data (got %d)\n", ret);
> + return -1;
> + }
> +
> + size -= wsize;
> + buffer += wsize;
> + *gotsize += wsize;
> +
> + startsect += mydata->clust_size;
> +
> + if (!size)
> + return 0;
> + }
> +
> + /* full-cluster write */
> + if (size >= bytesperclust) {
> + clustcount = size / bytesperclust;
Hello Takahiro,
the line above should read
+ clustcount = lldiv(size, bytesperclust);
Otherwise I get an error
undefined reference to `__divdi3'
with qemu_x86_defconfig and gcc 7.3.0.
Best regards
Heinrich
> +
> + if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
> + wsize = clustcount * bytesperclust;
> + ret = disk_write(startsect,
> + clustcount * mydata->clust_size,
> + buffer);
> + if (ret != clustcount * mydata->clust_size) {
> + debug("Error writing data (got %d)\n", ret);
> + return -1;
> + }
> +
> + size -= wsize;
> + buffer += wsize;
> + *gotsize += wsize;
> +
> + startsect += clustcount * mydata->clust_size;
> + } else {
> + for (i = 0; i < clustcount; i++) {
> + memcpy(tmpbuf_cluster, buffer, bytesperclust);
> + ret = disk_write(startsect, mydata->clust_size,
> + tmpbuf_cluster);
> + if (ret != mydata->clust_size) {
> + debug("Error writing data (got %d)\n", ret);
> + return -1;
> + }
> +
> + size -= bytesperclust;
> + buffer += bytesperclust;
> + *gotsize += bytesperclust;
> +
> + startsect += mydata->clust_size;
> + }
> + }
> + }
> +
> + /* partial write at end */
> + if (size) {
> + wsize = size;
> + ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
> + if (ret != mydata->clust_size) {
> + debug("Error reading data (got %d)\n", ret);
> + return -1;
> + }
> + memcpy(tmpbuf_cluster, buffer, wsize);
> + ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
> + if (ret != mydata->clust_size) {
> + debug("Error writing data (got %d)\n", ret);
> + return -1;
> + }
> +
> + size -= wsize;
> + buffer += wsize;
> + *gotsize += wsize;
> + }
> +
> + assert(!size);
> +
> + return 0;
> +}
> +
> /*
> * Find the first empty cluster
> */
> @@ -523,6 +636,18 @@ static int clear_fatent(fsdata *mydata, __u32 entry)
> 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);
> +}
> +
> /*
> * Write at most 'maxsize' bytes from 'buffer' into
> * the file associated with 'dentptr'
> @@ -530,31 +655,164 @@ static int clear_fatent(fsdata *mydata, __u32 entry)
> * or return -1 on fatal errors.
> */
> 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 filesize = FAT2CPU32(dentptr->size);
> + loff_t filesize;
> unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
> __u32 curclust = START(dentptr);
> __u32 endclust = 0, newclust = 0;
> - loff_t actsize;
> + loff_t cur_pos, offset, actsize, wsize;
>
> *gotsize = 0;
> - debug("Filesize: %llu bytes\n", filesize);
> -
> - if (maxsize > 0 && filesize > maxsize)
> - filesize = maxsize;
> + filesize = pos + maxsize;
>
> debug("%llu bytes\n", filesize);
>
> + if (!filesize) {
> + if (!curclust)
> + return 0;
> + if (!CHECK_CLUST(curclust, mydata->fatsize) ||
> + IS_LAST_CLUST(curclust, mydata->fatsize)) {
> + clear_fatent(mydata, curclust);
> + set_start_cluster(mydata, dentptr, 0);
> + return 0;
> + }
> + debug("curclust: 0x%x\n", curclust);
> + debug("Invalid FAT entry\n");
> + return -1;
> + }
> +
> if (!curclust) {
> - if (filesize) {
> - debug("error: nonempty clusterless file!\n");
> + assert(pos == 0);
> + goto set_clusters;
> + }
> +
> + /* go to cluster at pos */
> + cur_pos = bytesperclust;
> + while (1) {
> + if (pos <= cur_pos)
> + break;
> + if (IS_LAST_CLUST(curclust, mydata->fatsize))
> + break;
> +
> + newclust = get_fatent(mydata, curclust);
> + if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
> + CHECK_CLUST(newclust, mydata->fatsize)) {
> + debug("curclust: 0x%x\n", curclust);
> + debug("Invalid FAT entry\n");
> return -1;
> }
> +
> + cur_pos += bytesperclust;
> + curclust = newclust;
> + }
> + if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
> + assert(pos == cur_pos);
> + goto set_clusters;
> + }
> +
> + assert(pos < cur_pos);
> + cur_pos -= bytesperclust;
> +
> + /* overwrite */
> + assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
> + !CHECK_CLUST(curclust, mydata->fatsize));
> +
> + while (1) {
> + /* search for allocated consecutive clusters */
> + actsize = bytesperclust;
> + endclust = curclust;
> + while (1) {
> + if (filesize <= (cur_pos + actsize))
> + break;
> +
> + newclust = get_fatent(mydata, endclust);
> +
> + if (IS_LAST_CLUST(newclust, mydata->fatsize))
> + break;
> + if (CHECK_CLUST(newclust, mydata->fatsize)) {
> + debug("curclust: 0x%x\n", curclust);
> + debug("Invalid FAT entry\n");
> + return -1;
> + }
> +
> + actsize += bytesperclust;
> + endclust = newclust;
> + }
> +
> + /* overwrite to <curclust..endclust> */
> + if (pos < cur_pos)
> + offset = 0;
> + else
> + offset = pos - cur_pos;
> + wsize = min(cur_pos + actsize, filesize) - pos;
> + if (get_set_cluster(mydata, curclust, offset, buffer, wsize,
> + &actsize)) {
> + printf("Error get-and-setting cluster\n");
> + return -1;
> + }
> + buffer += wsize;
> + *gotsize += wsize;
> + cur_pos += offset + wsize;
> +
> + if (filesize <= cur_pos)
> + break;
> +
> + /* CHECK: newclust = get_fatent(mydata, endclust); */
> +
> + if (IS_LAST_CLUST(newclust, mydata->fatsize))
> + /* no more clusters */
> + break;
> +
> + curclust = newclust;
> + }
> +
> + if (filesize <= cur_pos) {
> + /* no more write */
> + newclust = get_fatent(mydata, endclust);
> + if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
> + /* truncate the rest */
> + clear_fatent(mydata, newclust);
> +
> + /* Mark end of file in FAT */
> + if (mydata->fatsize == 12)
> + newclust = 0xfff;
> + else if (mydata->fatsize == 16)
> + newclust = 0xffff;
> + else if (mydata->fatsize == 32)
> + newclust = 0xfffffff;
> + set_fatent_value(mydata, endclust, newclust);
> + }
> +
> return 0;
> }
>
> + curclust = endclust;
> + filesize -= cur_pos;
> + assert (!(cur_pos % bytesperclust));
> +
> +set_clusters:
> + /* allocate and write */
> + assert(!pos);
> +
> + /* Assure that curclust is valid */
> + if (!curclust) {
> + curclust = find_empty_cluster(mydata);
> + set_start_cluster(mydata, dentptr, curclust);
> + } else {
> + newclust = get_fatent(mydata, curclust);
> +
> + if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
> + newclust = determine_fatent(mydata, curclust);
> + set_fatent_value(mydata, curclust, newclust);
> + curclust = newclust;
> + } else {
> + debug("error: something wrong\n");
> + return -1;
> + }
> + }
> +
> actsize = bytesperclust;
> endclust = curclust;
> do {
> @@ -563,6 +821,7 @@ set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
> newclust = determine_fatent(mydata, endclust);
>
> if ((newclust - 1) != endclust)
> + /* write to <curclust..endclust> */
> goto getit;
>
> if (CHECK_CLUST(newclust, mydata->fatsize)) {
> @@ -609,18 +868,8 @@ getit:
> actsize = bytesperclust;
> curclust = endclust = newclust;
> } while (1);
> -}
>
> -/*
> - * 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);
> + return 0;
> }
>
> /*
> @@ -637,6 +886,7 @@ static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
> set_name(dentptr, filename);
> }
>
> +#if 0 /* TODO: need re-work */
> /*
> * Check whether adding a file makes the file system to
> * exceed the size of the block device
> @@ -661,6 +911,7 @@ static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
> return -1;
> return 0;
> }
> +#endif
>
> /*
> * Find a directory entry based on filename or start cluster number
> @@ -792,11 +1043,10 @@ again:
> return 0;
> }
>
> -static int do_fat_write(const char *filename, void *buffer, loff_t size,
> - loff_t *actwrite)
> +int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
> + loff_t size, loff_t *actwrite)
> {
> dir_entry *retdent;
> - __u32 start_cluster;
> fsdata datablock = { .fatbuf = NULL, };
> fsdata *mydata = &datablock;
> fat_itr *itr = NULL;
> @@ -807,11 +1057,12 @@ static int do_fat_write(const char *filename, void *buffer, loff_t size,
> char bad[2] = " ";
> const char illegal[] = "<>:\"/\\|?*";
>
> + debug("writing %s\n", filename);
> +
> filename_copy = strdup(filename);
> if (!filename_copy)
> goto exit;
> split_filename(filename_copy, &dirname, &basename);
> -printf(">>> %s -> \"%s\" + \"%s\"\n", filename, dirname, basename);
> filename = basename;
>
> /* Check that the filename is valid */
> @@ -853,45 +1104,24 @@ printf(">>> %s -> \"%s\" + \"%s\"\n", filename, dirname, basename);
> retdent = find_directory_entry(itr, l_filename);
>
> if (retdent) {
> - /* Update file size and start_cluster in a directory entry */
> - retdent->size = cpu_to_le32(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);
> - goto exit;
> - }
> - }
> -
> - ret = clear_fatent(mydata, start_cluster);
> - if (ret) {
> - printf("Error: clearing FAT entries\n");
> - 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");
> - goto exit;
> - }
> -
> - ret = check_overflow(mydata, start_cluster, size);
> - if (ret) {
> - printf("Error: %llu overflow\n", size);
> - goto exit;
> - }
> -
> - set_start_cluster(mydata, retdent, start_cluster);
> + /* A file exists */
> + if (pos == -1)
> + /* Append to the end */
> + pos = FAT2CPU32(retdent->size);
> + if (pos > retdent->size) {
> + /* No hole allowed */
> + goto exit;
> }
> +
> + /* Update file size in a directory entry */
> + retdent->size = cpu_to_le32(pos + size);
> } else {
> -/* FIXME: empty_dentptr */
> + /* Create a new file */
> + if (pos) {
> + /* No hole allowed */
> + ret = -EINVAL;
> + goto exit;
> + }
> memset(itr->dent, 0, sizeof(*itr->dent));
>
> /* Set short name to set alias checksum field in dir_slot */
> @@ -901,30 +1131,13 @@ printf(">>> %s -> \"%s\" + \"%s\"\n", filename, dirname, basename);
> goto exit;
> }
>
> - if (size) {
> - ret = start_cluster = find_empty_cluster(mydata);
> - if (ret < 0) {
> - printf("Error: finding empty cluster\n");
> - goto exit;
> - }
> -
> - ret = check_overflow(mydata, start_cluster, size);
> - if (ret) {
> - printf("Error: %llu overflow\n", size);
> - goto exit;
> - }
> - } else {
> - start_cluster = 0;
> - }
> -
> /* Set attribute as archieve for regular file */
> - fill_dentry(itr->fsdata, itr->dent, filename,
> - start_cluster, size, 0x20);
> + fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
>
> retdent = itr->dent;
> }
>
> - ret = set_contents(mydata, retdent, buffer, size, actwrite);
> + ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
> if (ret < 0) {
> printf("Error: writing contents\n");
> goto exit;
> @@ -954,11 +1167,11 @@ exit:
> int file_fat_write(const char *filename, void *buffer, loff_t offset,
> loff_t maxsize, loff_t *actwrite)
> {
> - if (offset != 0) {
> - printf("Error: non zero offset is currently not supported.\n");
> - return -1;
> - }
> + int ret;
>
> - printf("writing %s\n", filename);
> - return do_fat_write(filename, buffer, maxsize, actwrite);
> + ret = file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
> + if (ret)
> + printf("** Unable to write file %s **\n", filename);
> +
> + return ret;
> }
>
More information about the U-Boot
mailing list