[PATCH 33/38] fs: fat: Support reading from a larger block size

Heinrich Schuchardt xypron.glpk at gmx.de
Fri Mar 31 12:23:30 CEST 2023


On 3/31/23 00:55, Heinrich Schuchardt wrote:
>
>
> Am 30. März 2023 23:32:22 MESZ schrieb Simon Glass <sjg at chromium.org>:
>> At present it is not possible to read from some CDROM drives since the
>> FAT sector size does not match the media's block size. Add a conversion
>> option for this, so that reading is possible.
>>
>> This does increase SPL size for read-only FAT support by 25 bytes but
>> all but 6 are covered by the previous patch. We could reduce the
>> overhead of this feature to 0 bytes by making the code uglier (using
>> a static variable).
>
> 512 and 2048 are not the only physical sector sizes. Some hard drives use 4096.

To complete the logic you have first to take into account the size of a
FAT sector (BPB_BytsPerSec) defined in the FAT boot sector which can
take values 512, 1024, 2048, or 4096.

Cf. https://academy.cba.mit.edu/classes/networking_communications/SD/FAT.pdf

Best regards

Heinrich

>
> This change deserves a test case.
>
> Best regards
>
> Heinrich
>
>>
>> Signed-off-by: Simon Glass <sjg at chromium.org>
>> ---
>>
>> fs/fat/Kconfig     |  13 ++++++
>> fs/fat/fat.c       | 107 ++++++++++++++++++++++++++++++++++++++++-----
>> fs/fat/fat_write.c |   8 ++--
>> 3 files changed, 114 insertions(+), 14 deletions(-)
>>
>> diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig
>> index 9bb11eac9f7a..b0aa888c6cc4 100644
>> --- a/fs/fat/Kconfig
>> +++ b/fs/fat/Kconfig
>> @@ -22,3 +22,16 @@ config FS_FAT_MAX_CLUSTSIZE
>> 	  is the smallest amount of disk space that can be used to hold a
>> 	  file. Unless you have an extremely tight memory memory constraints,
>> 	  leave the default.
>> +
>> +config FAT_BLK_XLATE
>> +	bool "Enable FAT filesystem on a device with a larger block size"
>> +	depends on FS_FAT
>> +	help
>> +	  This provides a simple translation mechanism for reading FAT
>> +	  filesystems which don't use the same sector size as the underlying
>> +	  media. For example, the FAT filesystem may use 512 bytes but the
>> +	  media uses 2048, e.g. on a CDROM drive.
>> +
>> +	  This only supports the case where the FAT filesystem's sector size is
>> +	  smaller than the media's block size. It does not support creating or
>> +	  writing files.
>> diff --git a/fs/fat/fat.c b/fs/fat/fat.c
>> index f0df7988e172..02c6d55a99b3 100644
>> --- a/fs/fat/fat.c
>> +++ b/fs/fat/fat.c
>> @@ -43,13 +43,93 @@ static struct disk_partition cur_part_info;
>> #define DOS_FS_TYPE_OFFSET	0x36
>> #define DOS_FS32_TYPE_OFFSET	0x52
>>
>> -static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
>> +/**
>> + * disk_read_conv() - Read blocks and break them into smaller ones
>> + *
>> + * This is used when the FAT filesystem is hosted on a block device with a
>> + * block size greated than 512 bytes, e.g. the 2048 bytes of a CDROM drive. It
>> + * reads the blocks into a buffer and pulls out what is requested by the calling
>> + * function.
>> + *
>> + * It uses an internal 2KB buffer on the stack.
>> + *
>> + * @mydata: Filesystem information
>> + * @block: Block number to read, in terms of mydata->sect_size
>> + * @nr_blocks: Number of blocks to read, in terms of mydata->sect_size
>> + * @buf: Buffer for data
>> + */
>> +static int disk_read_conv(fsdata *mydata, __u32 block, __u32 nr_blocks,
>> +			  void *buf)
>> +{
>> +	uint factor, whole, remain, upto;
>> +	ulong base, index;
>> +	uint to_copy;
>> +	u8 tbuf[2048];
>> +	int ret;
>> +
>> +	log_debug("mydata %x, cur_dev %lx, block %x, nr_block %x\n",
>> +		  mydata->sect_size, cur_dev->blksz, block, nr_blocks);
>> +	if (mydata->sect_size > cur_dev->blksz ||
>> +	    cur_dev->blksz > sizeof(tbuf)) {
>> +		log_err("Block size %lx not supported\n", cur_dev->blksz);
>> +		return -EIO;
>> +	}
>> +	factor = cur_dev->blksz / mydata->sect_size;
>> +
>> +	/* get the first partial block */
>> +	base = cur_part_info.start + block / factor;
>> +	index = block % factor;
>> +	log_debug("cur_part_info.start %llx, block %x, base %lx, index %lx\n",
>> +		  (unsigned long long)cur_part_info.start, block, base, index);
>> +	ret = blk_dread(cur_dev, base, 1, tbuf);
>> +	if (ret != 1)
>> +		return -EIO;
>> +
>> +	to_copy = min((ulong)nr_blocks, factor - index);
>> +	log_debug("to_copy %x\n", to_copy);
>> +	memcpy(buf, tbuf + index * mydata->sect_size,
>> +	       to_copy * mydata->sect_size);
>> +	upto = to_copy;
>> +
>> +	/* load any whole blocks */
>> +	remain = nr_blocks - upto;
>> +	whole = remain / factor;
>> +	log_debug("factor %x, whole %x, remain %x\n", factor, whole, remain);
>> +	if (whole) {
>> +		ret = blk_dread(cur_dev, base + 1, whole,
>> +				buf + upto * mydata->sect_size);
>> +		if (ret != whole)
>> +			return -EIO;
>> +		upto += whole * factor;
>> +		remain = nr_blocks - upto;
>> +	}
>> +
>> +	/* load any blocks at the end */
>> +	log_debug("end: remain %x\n", remain);
>> +	if (remain) {
>> +		ret = blk_dread(cur_dev, base + 1 + whole, 1, tbuf);
>> +		if (ret != 1)
>> +			return -EIO;
>> +		memcpy(buf + upto * mydata->sect_size, tbuf,
>> +		       remain * mydata->sect_size);
>> +		upto += remain;
>> +	}
>> +
>> +	return upto;
>> +}
>> +
>> +static int disk_read(fsdata *mydata, __u32 block, __u32 nr_blocks, void *buf)
>> {
>> 	ulong ret;
>>
>> 	if (!cur_dev)
>> 		return -1;
>>
>> +	/* support converting from a larger block size */
>> +	if (IS_ENABLED(CONFIG_FAT_BLK_XLATE) && mydata &&
>> +	    mydata->sect_size != cur_dev->blksz)
>> +		return disk_read_conv(mydata, block, nr_blocks, buf);
>> +
>> 	ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
>>
>> 	if (ret != nr_blocks)
>> @@ -66,7 +146,7 @@ int fat_set_blk_dev(struct blk_desc *dev_desc, struct disk_partition *info)
>> 	cur_part_info = *info;
>>
>> 	/* Make sure it has a valid FAT header */
>> -	if (disk_read(0, 1, buffer) != 1) {
>> +	if (disk_read(NULL, 0, 1, buffer) != 1) {
>> 		cur_dev = NULL;
>> 		return -1;
>> 	}
>> @@ -211,7 +291,7 @@ static __u32 get_fatent(fsdata *mydata, __u32 entry)
>> 		if (flush_dirty_fat_buffer(mydata) < 0)
>> 			return -1;
>>
>> -		if (disk_read(startblock, getsize, bufptr) < 0) {
>> +		if (disk_read(mydata, startblock, getsize, bufptr) < 0) {
>> 			debug("Error reading FAT blocks\n");
>> 			return ret;
>> 		}
>> @@ -265,7 +345,7 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
>> 		debug("FAT: Misaligned buffer address (%p)\n", buffer);
>>
>> 		while (size >= mydata->sect_size) {
>> -			ret = disk_read(startsect++, 1, tmpbuf);
>> +			ret = disk_read(mydata, startsect++, 1, tmpbuf);
>> 			if (ret != 1) {
>> 				debug("Error reading data (got %d)\n", ret);
>> 				return -1;
>> @@ -279,7 +359,7 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
>> 		__u32 bytes_read;
>> 		__u32 sect_count = size / mydata->sect_size;
>>
>> -		ret = disk_read(startsect, sect_count, buffer);
>> +		ret = disk_read(mydata, startsect, sect_count, buffer);
>> 		if (ret != sect_count) {
>> 			debug("Error reading data (got %d)\n", ret);
>> 			return -1;
>> @@ -292,7 +372,7 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
>> 	if (size) {
>> 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
>>
>> -		ret = disk_read(startsect, 1, tmpbuf);
>> +		ret = disk_read(mydata, startsect, 1, tmpbuf);
>> 		if (ret != 1) {
>> 			debug("Error reading data (got %d)\n", ret);
>> 			return -1;
>> @@ -504,7 +584,7 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
>> 		return -1;
>> 	}
>>
>> -	if (disk_read(0, 1, block) < 0) {
>> +	if (disk_read(NULL, 0, 1, block) < 0) {
>> 		debug("Error: reading block\n");
>> 		goto fail;
>> 	}
>> @@ -586,9 +666,14 @@ static int get_fs_info(fsdata *mydata)
>> 	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
>> 	mydata->clust_size = bs.cluster_size;
>> 	if (mydata->sect_size != cur_part_info.blksz) {
>> -		printf("** FAT sector size mismatch (fs=%hu, dev=%lu)\n",
>> -				mydata->sect_size, cur_part_info.blksz);
>> -		return -1;
>> +		if (IS_ENABLED(CONFIG_FAT_BLK_XLATE)) {
>> +			printf("Warning: FAT sector size mismatch (fs=%u, dev=%lu): translating for read-only\n",
>> +			       mydata->sect_size, cur_part_info.blksz);
>> +		} else {
>> +			printf("** FAT sector size mismatch (fs=%u, dev=%lu), see CONFIG_FAT_BLK_XLATE\n",
>> +			       mydata->sect_size, cur_part_info.blksz);
>> +			return -1;
>> +		}
>> 	}
>> 	if (mydata->clust_size == 0) {
>> 		printf("** FAT cluster size not set\n");
>> @@ -846,7 +931,7 @@ void *fat_next_cluster(fat_itr *itr, unsigned int *nbytes)
>> 	 * dent at a time and iteratively constructing the vfat long
>> 	 * name.
>> 	 */
>> -	ret = disk_read(sect, read_size, itr->block);
>> +	ret = disk_read(itr->fsdata, sect, read_size, itr->block);
>> 	if (ret < 0) {
>> 		debug("Error: reading block\n");
>> 		return NULL;
>> diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
>> index 4d2d4db07fa6..baef52af6363 100644
>> --- a/fs/fat/fat_write.c
>> +++ b/fs/fat/fat_write.c
>> @@ -474,7 +474,7 @@ static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
>>
>> 		startblock += mydata->fat_sect;
>>
>> -		if (disk_read(startblock, getsize, bufptr) < 0) {
>> +		if (disk_read(NULL, startblock, getsize, bufptr) < 0) {
>> 			debug("Error reading FAT blocks\n");
>> 			return -1;
>> 		}
>> @@ -709,7 +709,8 @@ get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
>> 	/* partial write at beginning */
>> 	if (pos) {
>> 		wsize = min(bytesperclust - pos, size);
>> -		ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
>> +		ret = disk_read(NULL, startsect, mydata->clust_size,
>> +				tmpbuf_cluster);
>> 		if (ret != mydata->clust_size) {
>> 			debug("Error reading data (got %d)\n", ret);
>> 			return -1;
>> @@ -775,7 +776,8 @@ get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
>> 	/* partial write at end */
>> 	if (size) {
>> 		wsize = size;
>> -		ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
>> +		ret = disk_read(NULL, startsect, mydata->clust_size,
>> +				tmpbuf_cluster);
>> 		if (ret != mydata->clust_size) {
>> 			debug("Error reading data (got %d)\n", ret);
>> 			return -1;



More information about the U-Boot mailing list