mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 23:24:38 +00:00
Merge patch series "fs: fat: calculate FAT type based on cluster count"
To quote the author:
This series fixes an issue where the FAT type (FAT12, FAT16) is not
correctly detected, e.g. when the BPB field BS_FilSysType contains the
valid value "FAT ".
This issue occures, for example, if a partition is formatted by
swupdate using its diskformat handler. swupdate uses the FAT library
from http://elm-chan.org/fsw/ff/ internally.
See https://groups.google.com/g/swupdate/c/7Yc3NupjXx8 for a
discussion in the swupdate mailing list.
Please refer to the commit messages for more details.
1. Added bootsector checks
Most tests from https://www.win.tue.nl/~aeb/linux/fs/fat/fat-2.html
are added in the commit 'fs: fat: add bootsector validity check'.
Only the tests VIII, IX and X are not implemented.
I also checked the Linux kernel code (v6.6) and did not find any
checks on 'vistart->fs_type'. This is the reason why is skipped them
here.
See section '2. Size comparisons' for the impact on the binary size.
2. Size comparisons
I executed bloat-o-meter from the Linux kernel for an arm64
target (config xilinx_zynqmp_mini_emmc0_defconfig):
Comparison of the binary spl/u-boot-spl between master (rev
e17d174773
) and this patch
series (including the added validity checks of the boot sector):
add/remove: 0/0 grow/shrink: 1/1 up/down: 100/-12 (88)
Function old new delta
read_bootsectandvi 308 408 +100
fat_itr_root 444 432 -12
Total: Before=67977, After=68065, chg +0.13%
When compare the size of the binary spl/u-boot-spl between master this
series without the the validity checks of the boot sector:
add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-24 (-24)
Function old new delta
read_bootsectandvi 308 296 -12
fat_itr_root 444 432 -12
Total: Before=67977, After=67953, chg -0.04%
So the size of the spl on this arm64 target increases by 88 bytes for
this series. When i remove the validity check the size decreases by 24 bytes.
This commit is contained in:
commit
e2a9edc0ce
6 changed files with 170 additions and 41 deletions
114
fs/fat/fat.c
114
fs/fat/fat.c
|
@ -18,12 +18,17 @@
|
|||
#include <fs.h>
|
||||
#include <log.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <part.h>
|
||||
#include <malloc.h>
|
||||
#include <memalign.h>
|
||||
#include <asm/cache.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
/* maximum number of clusters for FAT12 */
|
||||
#define MAX_FAT12 0xFF4
|
||||
|
||||
/*
|
||||
* Convert a string to lowercase. Converts at most 'len' characters,
|
||||
|
@ -483,6 +488,73 @@ static __u8 mkcksum(struct nameext *nameext)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the FAT type is FAT12 or FAT16
|
||||
*
|
||||
* Based on fat_fill_super() from the Linux kernel's fs/fat/inode.c
|
||||
*/
|
||||
static int determine_legacy_fat_bits(const boot_sector *bs)
|
||||
{
|
||||
u16 fat_start = bs->reserved;
|
||||
u32 dir_start = fat_start + bs->fats * bs->fat_length;
|
||||
u32 rootdir_sectors = get_unaligned_le16(bs->dir_entries) *
|
||||
sizeof(dir_entry) /
|
||||
get_unaligned_le16(bs->sector_size);
|
||||
u32 data_start = dir_start + rootdir_sectors;
|
||||
u16 sectors = get_unaligned_le16(bs->sectors);
|
||||
u32 total_sectors = sectors ? sectors : bs->total_sect;
|
||||
u32 total_clusters = (total_sectors - data_start) /
|
||||
bs->cluster_size;
|
||||
|
||||
return (total_clusters > MAX_FAT12) ? 16 : 12;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the boot sector's media field is valid
|
||||
*
|
||||
* Based on fat_valid_media() from Linux kernel's include/linux/msdos_fs.h
|
||||
*/
|
||||
static int fat_valid_media(u8 media)
|
||||
{
|
||||
return media >= 0xf8 || media == 0xf0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the given boot sector is valid
|
||||
*
|
||||
* Based on fat_read_bpb() from the Linux kernel's fs/fat/inode.c
|
||||
*/
|
||||
static int is_bootsector_valid(const boot_sector *bs)
|
||||
{
|
||||
u16 sector_size = get_unaligned_le16(bs->sector_size);
|
||||
u16 dir_per_block = sector_size / sizeof(dir_entry);
|
||||
|
||||
if (!bs->reserved)
|
||||
return 0;
|
||||
|
||||
if (!bs->fats)
|
||||
return 0;
|
||||
|
||||
if (!fat_valid_media(bs->media))
|
||||
return 0;
|
||||
|
||||
if (!is_power_of_2(sector_size) ||
|
||||
sector_size < 512 ||
|
||||
sector_size > 4096)
|
||||
return 0;
|
||||
|
||||
if (!is_power_of_2(bs->cluster_size))
|
||||
return 0;
|
||||
|
||||
if (!bs->fat_length && !bs->fat32_length)
|
||||
return 0;
|
||||
|
||||
if (get_unaligned_le16(bs->dir_entries) & (dir_per_block - 1))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read boot sector and volume info from a FAT filesystem
|
||||
*/
|
||||
|
@ -506,7 +578,8 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
|
|||
|
||||
if (disk_read(0, 1, block) < 0) {
|
||||
debug("Error: reading block\n");
|
||||
goto fail;
|
||||
ret = -1;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
memcpy(bs, block, sizeof(boot_sector));
|
||||
|
@ -516,8 +589,14 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
|
|||
bs->heads = FAT2CPU16(bs->heads);
|
||||
bs->total_sect = FAT2CPU32(bs->total_sect);
|
||||
|
||||
if (!is_bootsector_valid(bs)) {
|
||||
debug("Error: bootsector is invalid\n");
|
||||
ret = -1;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* FAT32 entries */
|
||||
if (bs->fat_length == 0) {
|
||||
if (!bs->fat_length && bs->fat32_length) {
|
||||
/* Assume FAT32 */
|
||||
bs->fat32_length = FAT2CPU32(bs->fat32_length);
|
||||
bs->flags = FAT2CPU16(bs->flags);
|
||||
|
@ -528,28 +607,11 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
|
|||
*fatsize = 32;
|
||||
} else {
|
||||
vistart = (volume_info *)&(bs->fat32_length);
|
||||
*fatsize = 0;
|
||||
*fatsize = determine_legacy_fat_bits(bs);
|
||||
}
|
||||
memcpy(volinfo, vistart, sizeof(volume_info));
|
||||
|
||||
if (*fatsize == 32) {
|
||||
if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
|
||||
goto exit;
|
||||
} else {
|
||||
if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
|
||||
*fatsize = 12;
|
||||
goto exit;
|
||||
}
|
||||
if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
|
||||
*fatsize = 16;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Error: broken fs_type sign\n");
|
||||
fail:
|
||||
ret = -1;
|
||||
exit:
|
||||
out_free:
|
||||
free(block);
|
||||
return ret;
|
||||
}
|
||||
|
@ -571,7 +633,7 @@ static int get_fs_info(fsdata *mydata)
|
|||
mydata->total_sect = bs.total_sect;
|
||||
} else {
|
||||
mydata->fatlength = bs.fat_length;
|
||||
mydata->total_sect = (bs.sectors[1] << 8) + bs.sectors[0];
|
||||
mydata->total_sect = get_unaligned_le16(bs.sectors);
|
||||
if (!mydata->total_sect)
|
||||
mydata->total_sect = bs.total_sect;
|
||||
}
|
||||
|
@ -583,7 +645,7 @@ static int get_fs_info(fsdata *mydata)
|
|||
|
||||
mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats;
|
||||
|
||||
mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
|
||||
mydata->sect_size = get_unaligned_le16(bs.sector_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",
|
||||
|
@ -607,8 +669,7 @@ static int get_fs_info(fsdata *mydata)
|
|||
(mydata->clust_size * 2);
|
||||
mydata->root_cluster = bs.root_cluster;
|
||||
} else {
|
||||
mydata->rootdir_size = ((bs.dir_entries[1] * (int)256 +
|
||||
bs.dir_entries[0]) *
|
||||
mydata->rootdir_size = (get_unaligned_le16(bs.dir_entries) *
|
||||
sizeof(dir_entry)) /
|
||||
mydata->sect_size;
|
||||
mydata->data_begin = mydata->rootdir_sect +
|
||||
|
@ -1157,9 +1218,8 @@ int file_fat_detectfs(void)
|
|||
|
||||
memcpy(vol_label, volinfo.volume_label, 11);
|
||||
vol_label[11] = '\0';
|
||||
volinfo.fs_type[5] = '\0';
|
||||
|
||||
printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
|
||||
printf("Filesystem: FAT%d \"%s\"\n", fatsize, vol_label);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -34,12 +34,6 @@ struct disk_partition;
|
|||
/* Maximum number of entry for long file name according to spec */
|
||||
#define MAX_LFN_SLOT 20
|
||||
|
||||
/* Filesystem identifiers */
|
||||
#define FAT12_SIGN "FAT12 "
|
||||
#define FAT16_SIGN "FAT16 "
|
||||
#define FAT32_SIGN "FAT32 "
|
||||
#define SIGNLEN 8
|
||||
|
||||
/* File attributes */
|
||||
#define ATTR_RO 1
|
||||
#define ATTR_HIDDEN 2
|
||||
|
|
|
@ -220,7 +220,7 @@ static size_t create_fat(void *dst, size_t size, const char *filename,
|
|||
bs->root_cluster = cpu_to_le32(root_sector);
|
||||
|
||||
vi->ext_boot_sign = 0x29;
|
||||
memcpy(vi->fs_type, FAT32_SIGN, sizeof(vi->fs_type));
|
||||
memcpy(vi->fs_type, "FAT32 ", sizeof(vi->fs_type));
|
||||
|
||||
memcpy(dst + 0x1fe, "\x55\xAA", 2);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import re
|
|||
import os
|
||||
from subprocess import call, check_call, check_output, CalledProcessError
|
||||
|
||||
def mk_fs(config, fs_type, size, prefix):
|
||||
def mk_fs(config, fs_type, size, prefix, size_gran = 0x100000):
|
||||
"""Create a file system volume
|
||||
|
||||
Args:
|
||||
|
@ -17,6 +17,7 @@ def mk_fs(config, fs_type, size, prefix):
|
|||
fs_type (str): File system type, e.g. 'ext4'
|
||||
size (int): Size of file system in bytes
|
||||
prefix (str): Prefix string of volume's file name
|
||||
size_gran (int): Size granularity of file system image in bytes
|
||||
|
||||
Raises:
|
||||
CalledProcessError: if any error occurs when creating the filesystem
|
||||
|
@ -24,7 +25,9 @@ def mk_fs(config, fs_type, size, prefix):
|
|||
fs_img = f'{prefix}.{fs_type}.img'
|
||||
fs_img = os.path.join(config.persistent_data_dir, fs_img)
|
||||
|
||||
if fs_type == 'fat16':
|
||||
if fs_type == 'fat12':
|
||||
mkfs_opt = '-F 12'
|
||||
elif fs_type == 'fat16':
|
||||
mkfs_opt = '-F 16'
|
||||
elif fs_type == 'fat32':
|
||||
mkfs_opt = '-F 32'
|
||||
|
@ -36,7 +39,7 @@ def mk_fs(config, fs_type, size, prefix):
|
|||
else:
|
||||
fs_lnxtype = fs_type
|
||||
|
||||
count = (size + 0x100000 - 1) // 0x100000
|
||||
count = (size + size_gran - 1) // size_gran
|
||||
|
||||
# Some distributions do not add /sbin to the default PATH, where mkfs lives
|
||||
if '/sbin' not in os.environ["PATH"].split(os.pathsep):
|
||||
|
@ -44,7 +47,7 @@ def mk_fs(config, fs_type, size, prefix):
|
|||
|
||||
try:
|
||||
check_call(f'rm -f {fs_img}', shell=True)
|
||||
check_call(f'dd if=/dev/zero of={fs_img} bs=1M count={count}',
|
||||
check_call(f'dd if=/dev/zero of={fs_img} bs={size_gran} count={count}',
|
||||
shell=True)
|
||||
check_call(f'mkfs.{fs_lnxtype} {mkfs_opt} {fs_img}', shell=True)
|
||||
if fs_type == 'ext4':
|
||||
|
|
|
@ -12,9 +12,10 @@ import u_boot_utils as util
|
|||
from tests import fs_helper
|
||||
|
||||
supported_fs_basic = ['fat16', 'fat32', 'ext4']
|
||||
supported_fs_ext = ['fat16', 'fat32']
|
||||
supported_fs_mkdir = ['fat16', 'fat32']
|
||||
supported_fs_unlink = ['fat16', 'fat32']
|
||||
supported_fs_ext = ['fat12', 'fat16', 'fat32']
|
||||
supported_fs_fat = ['fat12', 'fat16']
|
||||
supported_fs_mkdir = ['fat12', 'fat16', 'fat32']
|
||||
supported_fs_unlink = ['fat12', 'fat16', 'fat32']
|
||||
supported_fs_symlink = ['ext4']
|
||||
|
||||
#
|
||||
|
@ -49,6 +50,7 @@ def pytest_configure(config):
|
|||
"""
|
||||
global supported_fs_basic
|
||||
global supported_fs_ext
|
||||
global supported_fs_fat
|
||||
global supported_fs_mkdir
|
||||
global supported_fs_unlink
|
||||
global supported_fs_symlink
|
||||
|
@ -61,6 +63,7 @@ def pytest_configure(config):
|
|||
print('*** FS TYPE modified: %s' % supported_fs)
|
||||
supported_fs_basic = intersect(supported_fs, supported_fs_basic)
|
||||
supported_fs_ext = intersect(supported_fs, supported_fs_ext)
|
||||
supported_fs_fat = intersect(supported_fs, supported_fs_fat)
|
||||
supported_fs_mkdir = intersect(supported_fs, supported_fs_mkdir)
|
||||
supported_fs_unlink = intersect(supported_fs, supported_fs_unlink)
|
||||
supported_fs_symlink = intersect(supported_fs, supported_fs_symlink)
|
||||
|
@ -83,6 +86,9 @@ def pytest_generate_tests(metafunc):
|
|||
if 'fs_obj_ext' in metafunc.fixturenames:
|
||||
metafunc.parametrize('fs_obj_ext', supported_fs_ext,
|
||||
indirect=True, scope='module')
|
||||
if 'fs_obj_fat' in metafunc.fixturenames:
|
||||
metafunc.parametrize('fs_obj_fat', supported_fs_fat,
|
||||
indirect=True, scope='module')
|
||||
if 'fs_obj_mkdir' in metafunc.fixturenames:
|
||||
metafunc.parametrize('fs_obj_mkdir', supported_fs_mkdir,
|
||||
indirect=True, scope='module')
|
||||
|
@ -624,3 +630,44 @@ def fs_obj_symlink(request, u_boot_config):
|
|||
finally:
|
||||
call('rmdir %s' % mount_dir, shell=True)
|
||||
call('rm -f %s' % fs_img, shell=True)
|
||||
|
||||
#
|
||||
# Fixture for fat test
|
||||
#
|
||||
@pytest.fixture()
|
||||
def fs_obj_fat(request, u_boot_config):
|
||||
"""Set up a file system to be used in fat test.
|
||||
|
||||
Args:
|
||||
request: Pytest request object.
|
||||
u_boot_config: U-Boot configuration.
|
||||
|
||||
Return:
|
||||
A fixture for fat test, i.e. a duplet of file system type and
|
||||
volume file name.
|
||||
"""
|
||||
|
||||
# the maximum size of a FAT12 filesystem resulting in 4084 clusters
|
||||
MAX_FAT12_SIZE = 261695 * 1024
|
||||
|
||||
# the minimum size of a FAT16 filesystem that can be created with
|
||||
# mkfs.vfat resulting in 4087 clusters
|
||||
MIN_FAT16_SIZE = 8208 * 1024
|
||||
|
||||
fs_type = request.param
|
||||
fs_img = ''
|
||||
|
||||
fs_ubtype = fstype_to_ubname(fs_type)
|
||||
check_ubconfig(u_boot_config, fs_ubtype)
|
||||
|
||||
fs_size = MAX_FAT12_SIZE if fs_type == 'fat12' else MIN_FAT16_SIZE
|
||||
|
||||
try:
|
||||
# the volume size depends on the filesystem
|
||||
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, fs_size, f'{fs_size}', 1024)
|
||||
except:
|
||||
pytest.skip('Setup failed for filesystem: ' + fs_type)
|
||||
return
|
||||
else:
|
||||
yield [fs_ubtype, fs_img]
|
||||
call('rm -f %s' % fs_img, shell=True)
|
||||
|
|
25
test/py/tests/test_fs/test_fs_fat.py
Normal file
25
test/py/tests/test_fs/test_fs_fat.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
# Copyright (c) 2023 Weidmüller Interface GmbH & Co. KG
|
||||
# Author: Christian Taedcke <christian.taedcke@weidmueller.com>
|
||||
#
|
||||
# U-Boot File System: FAT Test
|
||||
|
||||
"""
|
||||
This test verifies fat specific file system behaviour.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import re
|
||||
|
||||
@pytest.mark.boardspec('sandbox')
|
||||
@pytest.mark.slow
|
||||
class TestFsFat(object):
|
||||
def test_fs_fat1(self, u_boot_console, fs_obj_fat):
|
||||
"""Test that `fstypes` prints a result which includes `sandbox`."""
|
||||
fs_type,fs_img = fs_obj_fat
|
||||
with u_boot_console.log.section('Test Case 1 - fatinfo'):
|
||||
# Test Case 1 - ls
|
||||
output = u_boot_console.run_command_list([
|
||||
'host bind 0 %s' % fs_img,
|
||||
'fatinfo host 0:0'])
|
||||
assert(re.search('Filesystem: %s' % fs_type.upper(), ''.join(output)))
|
Loading…
Reference in a new issue