mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-05 04:36:13 +02:00
Merge patch series "fs: fat: Handle 'FAT sector size mismatch'"
Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com> says: The disk_read() and disk_write() functions of the FAT driver use the blk_dread() and blk_dwrite() respectively. The blk_* APIs read and write to the devices in terms of the device block size. However, the FAT driver reads in terms of the device block size (from fat_set_blk_dev and read_bootsectandvi) and sector size in the rest of the places. This causes buffer overflows or partial reads when the FAT sector size is not equal to device block size. Fix this by using blk_dread in fat_set_blk_dev and read_bootsectandvi instead of disk_read. And update disk_read/disk_write to handle FAT sector size and block size mismatch. Tested on blksz | FAT sector size ------+---------------- 4096 | 4096 512 | 512 4096 | 512 512 | 4096 CI test results --------------- https://github.com/u-boot/u-boot/pull/871 All checks have passed 93 successful checks No conflicts with base branch Code size change info --------------------- arm: (for 1/1 boards) all +32.0 text +32.0 qemu_arm : all +32 text +32 u-boot: add: 0/0, grow: 2/0 bytes: 24/0 (24) function old new delta read_bootsectandvi 420 432 +12 fat_set_blk_dev 204 216 +12 aarch64: (for 1/1 boards) all +12.0 rodata -8.0 text +20.0 qemu_arm64 : all +12 rodata -8 text +20 u-boot: add: 0/0, grow: 2/0 bytes: 20/0 (20) function old new delta read_bootsectandvi 408 420 +12 fat_set_blk_dev 204 212 +8 aarch64: (for 1/1 boards) all -2.0 data -8.0 rodata +6.0 qcom_qcs9100 : all -2 data -8 rodata +6 u-boot: add: 1/-1, grow: 8/-1 bytes: 708/-224 (484) function old new delta disk_rw - 628 +628 read_bootsectandvi 408 428 +20 fat_itr_root 500 520 +20 get_cluster 376 388 +12 set_contents 2076 2084 +8 fat_set_blk_dev 204 212 +8 static.set_fatent_value 536 540 +4 get_fatent 420 424 +4 fat_next_cluster 368 372 +4 disk_read 100 - -100 disk_write 132 8 -124 Link: https://lore.kernel.org/r/20260224035000.1617869-1-varadarajan.narayanan@oss.qualcomm.com
This commit is contained in:
commit
3d252b7beb
@ -147,3 +147,4 @@ CONFIG_NO_FB_CLEAR=y
|
||||
CONFIG_VIDEO_SIMPLE=y
|
||||
CONFIG_WDT=y
|
||||
CONFIG_WDT_QCOM=y
|
||||
CONFIG_FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH=y
|
||||
|
||||
@ -29,3 +29,11 @@ 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 FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH
|
||||
bool "Handle FAT sector size mismatch"
|
||||
default n
|
||||
depends on FS_FAT
|
||||
help
|
||||
Handle filesystems on media where the hardware block size and
|
||||
the sector size in the FAT metadata do not match.
|
||||
|
||||
152
fs/fat/fat.c
152
fs/fat/fat.c
@ -45,11 +45,146 @@ 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_FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH)
|
||||
static inline __u32 sect_to_block(__u32 sect, __u32 *off)
|
||||
{
|
||||
const ulong blksz = cur_part_info.blksz;
|
||||
|
||||
*off = 0;
|
||||
if (fat_sect_size && fat_sect_size < blksz) {
|
||||
int div = blksz / fat_sect_size;
|
||||
|
||||
*off = sect % div;
|
||||
return sect / div;
|
||||
} else if (fat_sect_size && (fat_sect_size > blksz)) {
|
||||
return sect * (fat_sect_size / blksz);
|
||||
}
|
||||
|
||||
return sect;
|
||||
}
|
||||
|
||||
static int disk_rw(__u32 sect, __u32 nr_sect, void *buf, bool read)
|
||||
{
|
||||
int ret;
|
||||
__u8 *block = NULL;
|
||||
__u32 rem, size, s, n;
|
||||
const ulong blksz = cur_part_info.blksz;
|
||||
const lbaint_t start = cur_part_info.start;
|
||||
|
||||
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, start + s, 1, block);
|
||||
if (ret != 1) {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rem > (blksz - offset))
|
||||
size = blksz - offset;
|
||||
else
|
||||
size = rem;
|
||||
|
||||
if (read) {
|
||||
memcpy(buf, block + offset, size);
|
||||
} else {
|
||||
memcpy(block + offset, buf, size);
|
||||
ret = blk_dwrite(cur_dev, start + s, 1, block);
|
||||
if (ret != 1) {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
rem -= size;
|
||||
buf += size;
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Do part 2, read/write directly to/from the given buffer */
|
||||
if (rem > blksz) {
|
||||
n = rem / blksz;
|
||||
|
||||
if (read)
|
||||
ret = blk_dread(cur_dev, start + s, n, buf);
|
||||
else
|
||||
ret = blk_dwrite(cur_dev, start + s, n, buf);
|
||||
|
||||
if (ret != n) {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
buf += n * blksz;
|
||||
rem = rem % blksz;
|
||||
s += n;
|
||||
}
|
||||
|
||||
/* Do part 3, read a block and copy the trailing sectors */
|
||||
if (rem) {
|
||||
ret = blk_dread(cur_dev, start + s, 1, block);
|
||||
if (ret != 1) {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
if (read) {
|
||||
memcpy(buf, block, rem);
|
||||
} else {
|
||||
memcpy(block, buf, rem);
|
||||
ret = blk_dwrite(cur_dev, start + s, 1, block);
|
||||
if (ret != 1) {
|
||||
ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if (block)
|
||||
free(block);
|
||||
|
||||
return (ret == -1) ? -1 : nr_sect;
|
||||
}
|
||||
|
||||
static int disk_read(__u32 sect, __u32 nr_sect, void *buf)
|
||||
{
|
||||
return disk_rw(sect, nr_sect, buf, true);
|
||||
}
|
||||
|
||||
int disk_write(__u32 sect, __u32 nr_sect, void *buf)
|
||||
{
|
||||
return disk_rw(sect, nr_sect, buf, false);
|
||||
}
|
||||
#else
|
||||
static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
|
||||
{
|
||||
ulong ret;
|
||||
@ -64,6 +199,7 @@ static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH */
|
||||
|
||||
int fat_set_blk_dev(struct blk_desc *dev_desc, struct disk_partition *info)
|
||||
{
|
||||
@ -73,7 +209,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 (blk_dread(cur_dev, cur_part_info.start, 1, buffer) != 1) {
|
||||
cur_dev = NULL;
|
||||
return -1;
|
||||
}
|
||||
@ -581,7 +717,8 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (disk_read(0, 1, block) < 0) {
|
||||
fat_sect_size = 0;
|
||||
if (blk_dread(cur_dev, cur_part_info.start, 1, block) != 1) {
|
||||
debug("Error: reading block\n");
|
||||
ret = -1;
|
||||
goto out_free;
|
||||
@ -651,11 +788,16 @@ 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 (!IS_ENABLED(CONFIG_FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH)) {
|
||||
log_err("FAT sector size mismatch (fs=%u, dev=%lu)\n",
|
||||
mydata->sect_size, cur_part_info.blksz);
|
||||
return -1;
|
||||
}
|
||||
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");
|
||||
|
||||
@ -192,6 +192,7 @@ out:
|
||||
}
|
||||
|
||||
static int total_sector;
|
||||
#if !IS_ENABLED(CONFIG_FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH)
|
||||
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_FS_FAT_HANDLE_SECTOR_SIZE_MISMATCH */
|
||||
|
||||
/*
|
||||
* Write fat buffer into block device
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user