[PATCH] fs: fat: Handle 'FAT sector size mismatch'
Varadarajan Narayanan
varadarajan.narayanan at oss.qualcomm.com
Tue Feb 17 04:56:43 CET 2026
Do FAT read and write based on the device sector size
instead of the size recorded in the FAT meta data.
FAT code issues i/o in terms of the sector size. Convert that to
device sector size before doing the actual i/o. Additionally,
handle leading/trailing blocks when the meta data based block
no and i/o size is not an exact multiple of the device sector
size or vice versa.
Tested on UFS device with sector size 4096 and meta data recorded
sector size 512.
Signed-off-by: Varadarajan Narayanan <varadarajan.narayanan at oss.qualcomm.com>
---
Changes in v5:
- Fix CI error reported in https://dev.azure.com/u-boot/u-boot/_build/results?buildId=12821&view=logs&j=a007cd84-3be0-5a02-8a7a-a34fb3052485&t=216c79d4-eaff-5600-0b8f-3a63c01a45d6
- All CI tests passed. (Please see https://github.com/u-boot/u-boot/pull/868)
- U-boot SPL for (am335x_evm, am335x_hs_evm, am335x_hs_evm_spi) includes the
FAT driver. The additional checks done in this patch resulted in increased
code size causing it to overshoot the '.sram' area.
- Address this by using the existing disk_read & disk_write routines for
SPL.
- Also bail out in case of sector size mismatch for SPL
Changes in v4:
- Fix clang warning reported in https://source.denx.de/u-boot/u-boot/-/jobs/1365316
- Remove unused function 'size_to_blocks()'
- Triggered CI, it fails with this error https://dev.azure.com/u-boot/u-boot/_build/results?buildId=12817&view=logs&j=a007cd84-3be0-5a02-8a7a-a34fb3052485&t=216c79d4-eaff-5600-0b8f-3a63c01a45d6&l=405. But that seems to be seen on top-of-tree without this change also.
- Link to v3: https://lore.kernel.org/u-boot/20260122063442.2622684-1-balaji.selvanathan@oss.qualcomm.com/
Changes in v3:
- Respin, no changes
- Link to v2: https://lore.kernel.org/u-boot/20241125085213.460723-1-quic_varada@quicinc.com/
Changes in v2:
- fat_sect_size is declared as static
- The else block in disk_write() function has been removed
- The commit message has additional details about testing
- Link to v1: https://lore.kernel.org/u-boot/20241119073048.3469260-1-quic_varada@quicinc.com/
---
fs/fat/fat.c | 210 ++++++++++++++++++++++++++++++++++++++++++++-
fs/fat/fat_write.c | 2 +
2 files changed, 210 insertions(+), 2 deletions(-)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 9ce5df59f9b..a725d924fd9 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -45,11 +45,13 @@ static void downcase(char *str, size_t len)
static struct blk_desc *cur_dev;
static struct disk_partition cur_part_info;
+static int fat_sect_size;
#define DOS_BOOT_MAGIC_OFFSET 0x1fe
#define DOS_FS_TYPE_OFFSET 0x36
#define DOS_FS32_TYPE_OFFSET 0x52
+#if IS_ENABLED(CONFIG_SPL_FS_FAT)
static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
{
ulong ret;
@@ -64,6 +66,206 @@ static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
return ret;
}
+#else
+static inline __u32 sect_to_block(__u32 sect, __u32 *off)
+{
+ *off = 0;
+ if (fat_sect_size && fat_sect_size < cur_part_info.blksz) {
+ int div = cur_part_info.blksz / fat_sect_size;
+
+ *off = sect % div;
+ return sect / div;
+ } else if (fat_sect_size && (fat_sect_size > cur_part_info.blksz)) {
+ return sect * (fat_sect_size / cur_part_info.blksz);
+ }
+
+ return sect;
+}
+
+static int disk_read(__u32 sect, __u32 nr_sect, void *buf)
+{
+ int ret;
+ __u8 *block = NULL;
+ __u32 rem, size;
+ __u32 s, n;
+
+ rem = nr_sect * fat_sect_size;
+ /*
+ * block N block N + 1 block N + 2
+ * +-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | |s|e|c|t|o|r| | |s|e|c|t|o|r| | |s|e|c|t|o|r| | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * . . . | | | | . . .
+ * ------+---------------+---------------+---------------+------
+ * |<--- FAT reads in sectors --->|
+ *
+ * | part 1 | part 2 | part 3 |
+ *
+ */
+
+ /* Do part 1 */
+ if (fat_sect_size) {
+ __u32 offset;
+
+ /* Read one block and copy out the leading sectors */
+ block = malloc_cache_aligned(cur_dev->blksz);
+ if (!block) {
+ printf("Error: allocating block: %lu\n", cur_dev->blksz);
+ return -1;
+ }
+
+ s = sect_to_block(sect, &offset);
+ offset = offset * fat_sect_size;
+
+ ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block);
+ if (ret != 1) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (rem > (cur_part_info.blksz - offset))
+ size = cur_part_info.blksz - offset;
+ else
+ size = rem;
+
+ memcpy(buf, block + offset, size);
+ rem -= size;
+ buf += size;
+ s++;
+ } else {
+ /*
+ * fat_sect_size not being set implies, this is the first read
+ * to partition. The first sector is being read to get the
+ * FS meta data. The FAT sector size is got from this meta data.
+ */
+ ret = blk_dread(cur_dev, cur_part_info.start + s, 1, buf);
+ if (ret != 1)
+ return -1;
+ }
+
+ /* Do part 2, read directly into the given buffer */
+ if (rem > cur_part_info.blksz) {
+ n = rem / cur_part_info.blksz;
+ ret = blk_dread(cur_dev, cur_part_info.start + s, n, buf);
+ if (ret != n) {
+ ret = -1;
+ goto exit;
+ }
+ buf += n * cur_part_info.blksz;
+ rem = rem % cur_part_info.blksz;
+ s += n;
+ }
+
+ /* Do part 3, read a block and copy the trailing sectors */
+ if (rem) {
+ ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block);
+ if (ret != 1) {
+ ret = -1;
+ goto exit;
+ } else {
+ memcpy(buf, block, rem);
+ }
+ }
+exit:
+ if (block)
+ free(block);
+
+ return (ret == -1) ? -1 : nr_sect;
+}
+
+int disk_write(__u32 sect, __u32 nr_sect, void *buf)
+{
+ int ret;
+ __u8 *block = NULL;
+ __u32 rem, size;
+ __u32 s, n;
+
+ rem = nr_sect * fat_sect_size;
+ /*
+ * block N block N + 1 block N + 2
+ * +-+-+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | |s|e|c|t|o|r| | |s|e|c|t|o|r| | |s|e|c|t|o|r| | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * . . . | | | | . . .
+ * ------+---------------+---------------+---------------+------
+ * |<--- FAT reads in sectors --->|
+ *
+ * | part 1 | part 2 | part 3 |
+ *
+ */
+
+ /* Do part 1 */
+ if (fat_sect_size) {
+ __u32 offset;
+
+ /* Read one block and overwrite the leading sectors */
+ block = malloc_cache_aligned(cur_dev->blksz);
+ if (!block) {
+ printf("Error: allocating block: %lu\n", cur_dev->blksz);
+ return -1;
+ }
+
+ s = sect_to_block(sect, &offset);
+ offset = offset * fat_sect_size;
+
+ ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block);
+ if (ret != 1) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (rem > (cur_part_info.blksz - offset))
+ size = cur_part_info.blksz - offset;
+ else
+ size = rem;
+
+ memcpy(block + offset, buf, size);
+ ret = blk_dwrite(cur_dev, cur_part_info.start + s, 1, block);
+ if (ret != 1) {
+ ret = -1;
+ goto exit;
+ }
+
+ rem -= size;
+ buf += size;
+ s++;
+ }
+
+ /* Do part 2, write directly from the given buffer */
+ if (rem > cur_part_info.blksz) {
+ n = rem / cur_part_info.blksz;
+ ret = blk_dwrite(cur_dev, cur_part_info.start + s, n, buf);
+ if (ret != n) {
+ ret = -1;
+ goto exit;
+ }
+ buf += n * cur_part_info.blksz;
+ rem = rem % cur_part_info.blksz;
+ s += n;
+ }
+
+ /* Do part 3, read a block and copy the trailing sectors */
+ if (rem) {
+ ret = blk_dread(cur_dev, cur_part_info.start + s, 1, block);
+ if (ret != 1) {
+ ret = -1;
+ goto exit;
+ } else {
+ memcpy(block, buf, rem);
+ }
+ ret = blk_dwrite(cur_dev, cur_part_info.start + s, 1, block);
+ if (ret != 1) {
+ ret = -1;
+ goto exit;
+ }
+ }
+exit:
+ if (block)
+ free(block);
+
+ return (ret == -1) ? -1 : nr_sect;
+}
+#endif /* CONFIG_SPL_FS_FAT */
int fat_set_blk_dev(struct blk_desc *dev_desc, struct disk_partition *info)
{
@@ -581,6 +783,7 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
return -1;
}
+ fat_sect_size = 0;
if (disk_read(0, 1, block) < 0) {
debug("Error: reading block\n");
ret = -1;
@@ -651,11 +854,14 @@ static int get_fs_info(fsdata *mydata)
mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats;
mydata->sect_size = get_unaligned_le16(bs.sector_size);
+ fat_sect_size = mydata->sect_size;
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);
+ log_info("FAT sector size mismatch (fs=%u, dev=%lu)\n",
+ mydata->sect_size, cur_part_info.blksz);
+#if IS_ENABLED(CONFIG_SPL_FS_FAT)
return -1;
+#endif
}
if (mydata->clust_size == 0) {
log_err("FAT cluster size not set\n");
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index 0b924541187..86132a9177c 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -192,6 +192,7 @@ out:
}
static int total_sector;
+#if IS_ENABLED(CONFIG_SPL_FS_FAT)
static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
{
ulong ret;
@@ -211,6 +212,7 @@ static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
return ret;
}
+#endif /* CONFIG_SPL_FS_FAT */
/*
* Write fat buffer into block device
--
2.34.1
More information about the U-Boot
mailing list