
On 9/11/18 8:59 AM, Akashi, Takahiro wrote:
From: AKASHI Takahiro takahiro.akashi@linaro.org
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 maintain compatibility with the current interface.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
fs/fat/fat_write.c | 288 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 273 insertions(+), 15 deletions(-)
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index c22d8c7a46a1..651c7866debc 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -450,6 +450,121 @@ 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
*/ @@ -578,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));
This patch was merged as cb8af8af5ba03ae8e0a7315b66bfcc46d5c55627
When compiled with DEBUG=1 the line above leads to a link error:
fs/fat/fat_write.c:831: undefined reference to `__aeabi_ldivmod'
We should use function do_div() for the division.
Best regards
Heinrich