[U-Boot] [PATCH 1/1] fs: fat: write with non-zero offset

Heinrich Schuchardt xypron.glpk at gmx.de
Thu Jun 28 18:14:17 UTC 2018


This relates to a patch in
https://git.linaro.org/people/takahiro.akashi/u-boot.git
branch efi/for_sct.

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