Message ID | 20180720025723.6736-10-takahiro.akashi@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series | fs: fat: extend FAT write operations | expand |
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. > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@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: EFI_FILE_PROTOCOL.SetPosition(): seeking past the end of the file is allowed (a subsequent write would grow the file). 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); > } >
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@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); > > } > > >
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); }
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. Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> --- fs/fat/fat_write.c | 287 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 272 insertions(+), 15 deletions(-)