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

AKASHI Takahiro takahiro.akashi at linaro.org
Thu Jun 28 20:45:21 UTC 2018


Hey Heinrich,

On Thu, Jun 28, 2018 at 08:14:17PM +0200, Heinrich Schuchardt wrote:
> This relates to a patch in
> https://git.linaro.org/people/takahiro.akashi/u-boot.git
> branch efi/for_sct.

Thanks for your comment, but
please don't cite and post my changes from this branch
*in any form*. It's just not intended to do so.
I wanna submit it as several separate series once I finish
major re-work.
For the meantime, I dropped the branch.

-Takahiro AKASHI

> 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