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.

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