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 <quic_var...@quicinc.com>
---
 fs/fat/fat.c       | 218 ++++++++++++++++++++++++++++++++++++++++++---
 fs/fat/fat_write.c |  19 ----
 2 files changed, 205 insertions(+), 32 deletions(-)

diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index e2570e8167..efbf892c7a 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -44,24 +44,214 @@ 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
 
-static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
+inline __u32 sect_to_block(__u32 sect, __u32 *off)
 {
-       ulong ret;
+       *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);
+       }
 
-       if (!cur_dev)
-               return -1;
+       return sect;
+}
 
-       ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
+inline __u32 size_to_blocks(__u32 size)
+{
+       return (size + (cur_part_info.blksz - 1)) / cur_part_info.blksz;
+}
 
-       if (ret != nr_blocks)
-               return -1;
+static int disk_read(__u32 sect, __u32 nr_sect, void *buf)
+{
+       int ret;
+       __u8 *block = NULL;
+       __u32 rem, size;
+       __u32 s, n;
 
-       return ret;
+       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;
 }
 
 int fat_set_blk_dev(struct blk_desc *dev_desc, struct disk_partition *info)
@@ -575,6 +765,8 @@ 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;
@@ -645,12 +837,12 @@ 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);
-               return -1;
-       }
+       if (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 (mydata->clust_size == 0) {
                log_err("FAT cluster size not set\n");
                return -1;
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index ea877ee917..f7a210d790 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -192,25 +192,6 @@ out:
 }
 
 static int total_sector;
-static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
-{
-       ulong ret;
-
-       if (!cur_dev)
-               return -1;
-
-       if (cur_part_info.start + block + nr_blocks >
-               cur_part_info.start + total_sector) {
-               printf("error: overflow occurs\n");
-               return -1;
-       }
-
-       ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
-       if (nr_blocks && ret == 0)
-               return -1;
-
-       return ret;
-}
 
 /*
  * Write fat buffer into block device
-- 
2.34.1

Reply via email to