[U-Boot] [BUG] cb8af8af5ba0 "fs: fat: support write with non-zero offset" leads to link error

Heinrich Schuchardt xypron.glpk at gmx.de
Mon Feb 25 18:20:46 UTC 2019


On 9/11/18 8:59 AM, Akashi, Takahiro wrote:
> From: AKASHI Takahiro <takahiro.akashi at 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 at 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



More information about the U-Boot mailing list