On 3/31/23 00:55, Heinrich Schuchardt wrote:


Am 30. März 2023 23:32:22 MESZ schrieb Simon Glass <s...@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 <s...@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;

Reply via email to