[U-Boot] [PATCH 09/17] fs: fat: support write with non-zero offset
AKASHI Takahiro
takahiro.akashi at linaro.org
Mon Jul 23 08:41:03 UTC 2018
On Fri, Jul 20, 2018 at 07:46:49PM +0200, Heinrich Schuchardt wrote:
> On 07/20/2018 04:57 AM, AKASHI Takahiro wrote:
> > In this patch, all the necessary code for allowing for a file offset
> > at write is implemented. What plays a major roll here is get_set_cluster(),
> > which, in contrast to its counterpart, set_cluster(), only operates on
> > already-allocated clusters, overwriting with data.
> >
> > So, with a file offset specified, set_contents() seeks and writes data
> > with set_get_cluster() until the end of a file, and, once it reaches
> > there, continues writing with set_cluster() for the rest.
> >
> > Please note that a file will be trimmed as a result of write operation if
> > write ends before reaching file's end. This is an intended behavior
> > in order to maitain compatibility with the current interface.
>
> This does not match the EFI spec.
First, please understand my standpoint that I mentioned in a reply
to your comment on my cover letter.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
>
> When testing I observed that I could not write at an offset larger than
> the file size. This does not match the EFI spec:
So this is an intended behavior.
> EFI_FILE_PROTOCOL.SetPosition():
> seeking past the end of the file is allowed (a subsequent write would
> grow the file).
Surely, this is a discussion point.
Any comment from other folks?
-Takahiro AKASHI
> Best regards
>
> Heinrich
>
>
> > ---
> > fs/fat/fat_write.c | 287 ++++++++++++++++++++++++++++++++++++++++++---
> > 1 file changed, 272 insertions(+), 15 deletions(-)
> >
> > diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
> > index 3a9c53e253..cc45a33876 100644
> > --- a/fs/fat/fat_write.c
> > +++ b/fs/fat/fat_write.c
> > @@ -450,6 +450,120 @@ 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 beginning */
> > + 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 = lldiv(size, bytesperclust);
> > +
> > + 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
> > */
> > @@ -579,26 +693,158 @@ set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
> > 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;
> > - filesize = maxsize;
> > + filesize = pos + maxsize;
> >
> > debug("%llu bytes\n", filesize);
> >
> > - if (curclust) {
> > - /*
> > - * release already-allocated clusters anyway
> > - */
> > - if (clear_fatent(mydata, curclust)) {
> > - printf("Error: clearing FAT entries\n");
> > + 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) {
> > + 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;
> > }
> >
> > - curclust = find_empty_cluster(mydata);
> > - set_start_cluster(mydata, dentptr, curclust);
> > + 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;
> > + }
> > + }
> > +
> > + /* TODO: already partially written */
> > if (check_overflow(mydata, curclust, filesize)) {
> > printf("Error: no space left: %llu\n", filesize);
> > return -1;
> > @@ -852,6 +1098,16 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
> > goto exit;
> > }
> >
> > + /* A file exists */
> > + if (pos == -1)
> > + /* Append to the end */
> > + pos = FAT2CPU32(retdent->size);
> > + if (pos > retdent->size) {
> > + /* No hole allowed */
> > + ret = -EINVAL;
> > + goto exit;
> > + }
> > +
> > /* Update file size in a directory entry */
> > retdent->size = cpu_to_le32(pos + size);
> > } else {
> > @@ -872,6 +1128,12 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
> > goto exit;
> > }
> >
> > + 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 */
> > @@ -921,10 +1183,5 @@ 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 -EINVAL;
> > - }
> > -
> > return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
> > }
> >
>
More information about the U-Boot
mailing list