[U-Boot] [PATCH 1/1] fs: fat: write with non-zero offset
AKASHI Takahiro
takahiro.akashi at linaro.org
Thu Jun 28 20:45:21 UTC 2018
Hey Heinrich,
On Thu, Jun 28, 2018 at 08:14:17PM +0200, Heinrich Schuchardt wrote:
> This relates to a patch in
> https://git.linaro.org/people/takahiro.akashi/u-boot.git
> branch efi/for_sct.
Thanks for your comment, but
please don't cite and post my changes from this branch
*in any form*. It's just not intended to do so.
I wanna submit it as several separate series once I finish
major re-work.
For the meantime, I dropped the branch.
-Takahiro AKASHI
> 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