Hi Simon, On Mon, Jun 19, 2023 at 8:02 PM Simon Glass <[email protected]> wrote: > > 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.
I am completely confused. The CDROM uses iso9660 file system. This has nothing to do with FAT. > > 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). > > Signed-off-by: Simon Glass <[email protected]> > --- > > (no changes since v2) > > Changes in v2: > - Use log_warning() for the warning > > 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 d1476aa433d6..686b321163fb 100644 > --- a/fs/fat/fat.c > +++ b/fs/fat/fat.c > @@ -45,13 +45,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) > @@ -68,7 +148,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; > } > @@ -213,7 +293,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; > } > @@ -267,7 +347,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; > @@ -281,7 +361,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; > @@ -294,7 +374,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; > @@ -506,7 +586,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; > } > @@ -588,9 +668,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) { > - log_err("FAT sector size mismatch (fs=%u, dev=%lu)\n", > - mydata->sect_size, cur_part_info.blksz); > - return -1; > + if (IS_ENABLED(CONFIG_FAT_BLK_XLATE)) { > + log_warning("FAT sector size mismatch (fs=%u, > dev=%lu): translating for read-only\n", > + mydata->sect_size, cur_part_info.blksz); > + } else { > + log_err("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) { > log_err("FAT cluster size not set\n"); > @@ -848,7 +933,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 e2a9913f807a..95f7a60caa2b 100644 > --- a/fs/fat/fat_write.c > +++ b/fs/fat/fat_write.c > @@ -477,7 +477,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; > } > @@ -712,7 +712,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; > @@ -778,7 +779,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; Regards, Bin

