test_fs: Allow running unprivileged

There is no need to mount the filesystem on the host side.
All filesystem tools offer some way to fill the fs without mounting.

So, create the content on the host side, create and fill the fs
without mounting.
No more sudo or guestmount needed.

This new approach works because the tests don't care about user IDs
and no device files are needed.
If user IDs start to matter it's still possible to use wrapper
tools like fakeroot in future while filling the fs.

Signed-off-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Simon Glass <sjg@chromium.org>
Tested-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>
This commit is contained in:
Richard Weinberger 2024-11-21 15:32:07 -07:00 committed by Tom Rini
parent dc1859f8d2
commit 463bc7b82e
3 changed files with 47 additions and 143 deletions

View file

@ -9,7 +9,7 @@ import re
import os import os
from subprocess import call, check_call, check_output, CalledProcessError from subprocess import call, check_call, check_output, CalledProcessError
def mk_fs(config, fs_type, size, prefix, size_gran = 0x100000): def mk_fs(config, fs_type, size, prefix, src_dir=None, size_gran = 0x100000):
"""Create a file system volume """Create a file system volume
Args: Args:
@ -17,6 +17,7 @@ def mk_fs(config, fs_type, size, prefix, size_gran = 0x100000):
fs_type (str): File system type, e.g. 'ext4' fs_type (str): File system type, e.g. 'ext4'
size (int): Size of file system in bytes size (int): Size of file system in bytes
prefix (str): Prefix string of volume's file name prefix (str): Prefix string of volume's file name
src_dir (str): Root directory to use, or None for none
size_gran (int): Size granularity of file system image in bytes size_gran (int): Size granularity of file system image in bytes
Raises: Raises:
@ -39,6 +40,12 @@ def mk_fs(config, fs_type, size, prefix, size_gran = 0x100000):
else: else:
fs_lnxtype = fs_type fs_lnxtype = fs_type
if src_dir:
if fs_lnxtype == 'ext4':
mkfs_opt = mkfs_opt + ' -d ' + src_dir
elif fs_lnxtype != 'vfat':
raise ValueError(f'src_dir not implemented for fs {fs_lnxtype}')
count = (size + size_gran - 1) // size_gran count = (size + size_gran - 1) // size_gran
# Some distributions do not add /sbin to the default PATH, where mkfs lives # Some distributions do not add /sbin to the default PATH, where mkfs lives
@ -55,6 +62,8 @@ def mk_fs(config, fs_type, size, prefix, size_gran = 0x100000):
shell=True).decode() shell=True).decode()
if 'metadata_csum' in sb_content: if 'metadata_csum' in sb_content:
check_call(f'tune2fs -O ^metadata_csum {fs_img}', shell=True) check_call(f'tune2fs -O ^metadata_csum {fs_img}', shell=True)
elif fs_lnxtype == 'vfat' and src_dir:
check_call(f'mcopy -i {fs_img} -vsmpQ {src_dir}/* ::/', shell=True)
return fs_img return fs_img
except CalledProcessError: except CalledProcessError:
call(f'rm -f {fs_img}', shell=True) call(f'rm -f {fs_img}', shell=True)

View file

@ -156,64 +156,6 @@ def tool_is_in_path(tool):
return True return True
return False return False
fuse_mounted = False
def mount_fs(fs_type, device, mount_point):
"""Mount a volume.
Args:
fs_type: File system type.
device: Volume's file name.
mount_point: Mount point.
Return:
Nothing.
"""
global fuse_mounted
try:
check_call('guestmount --pid-file guestmount.pid -a %s -m /dev/sda %s'
% (device, mount_point), shell=True)
fuse_mounted = True
return
except CalledProcessError:
fuse_mounted = False
mount_opt = 'loop,rw'
if re.match('fat', fs_type):
mount_opt += ',umask=0000'
check_call('sudo mount -o %s %s %s'
% (mount_opt, device, mount_point), shell=True)
# may not be effective for some file systems
check_call('sudo chmod a+rw %s' % mount_point, shell=True)
def umount_fs(mount_point):
"""Unmount a volume.
Args:
mount_point: Mount point.
Return:
Nothing.
"""
if fuse_mounted:
call('sync')
call('guestunmount %s' % mount_point, shell=True)
try:
with open("guestmount.pid", "r") as pidfile:
pid = int(pidfile.read())
util.waitpid(pid, kill=True)
os.remove("guestmount.pid")
except FileNotFoundError:
pass
else:
call('sudo umount %s' % mount_point, shell=True)
# #
# Fixture for basic fs test # Fixture for basic fs test
# derived from test/fs/fs-test.sh # derived from test/fs/fs-test.sh
@ -241,14 +183,6 @@ def fs_obj_basic(request, u_boot_config):
small_file = mount_dir + '/' + SMALL_FILE small_file = mount_dir + '/' + SMALL_FILE
big_file = mount_dir + '/' + BIG_FILE big_file = mount_dir + '/' + BIG_FILE
try:
# 3GiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
try: try:
check_call('mkdir -p %s' % mount_dir, shell=True) check_call('mkdir -p %s' % mount_dir, shell=True)
except CalledProcessError as err: except CalledProcessError as err:
@ -256,15 +190,6 @@ def fs_obj_basic(request, u_boot_config):
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
return return
try:
# Mount the image so we can populate it.
mount_fs(fs_type, fs_img, mount_dir)
except CalledProcessError as err:
pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
call('rmdir %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True)
return
try: try:
# Create a subdirectory. # Create a subdirectory.
check_call('mkdir %s/SUBDIR' % mount_dir, shell=True) check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
@ -326,15 +251,20 @@ def fs_obj_basic(request, u_boot_config):
% big_file, shell=True).decode() % big_file, shell=True).decode()
md5val.append(out.split()[0]) md5val.append(out.split()[0])
try:
# 3GiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB', mount_dir)
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
except CalledProcessError as err: except CalledProcessError as err:
pytest.skip('Setup failed for filesystem: ' + fs_type + '. {}'.format(err)) pytest.skip('Setup failed for filesystem: ' + fs_type + '. {}'.format(err))
umount_fs(mount_dir)
return return
else: else:
umount_fs(mount_dir)
yield [fs_ubtype, fs_img, md5val] yield [fs_ubtype, fs_img, md5val]
finally: finally:
call('rmdir %s' % mount_dir, shell=True) call('rm -rf %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
# #
@ -363,14 +293,6 @@ def fs_obj_ext(request, u_boot_config):
min_file = mount_dir + '/' + MIN_FILE min_file = mount_dir + '/' + MIN_FILE
tmp_file = mount_dir + '/tmpfile' tmp_file = mount_dir + '/tmpfile'
try:
# 128MiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
try: try:
check_call('mkdir -p %s' % mount_dir, shell=True) check_call('mkdir -p %s' % mount_dir, shell=True)
except CalledProcessError as err: except CalledProcessError as err:
@ -378,15 +300,6 @@ def fs_obj_ext(request, u_boot_config):
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
return return
try:
# Mount the image so we can populate it.
mount_fs(fs_type, fs_img, mount_dir)
except CalledProcessError as err:
pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
call('rmdir %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True)
return
try: try:
# Create a test directory # Create a test directory
check_call('mkdir %s/dir1' % mount_dir, shell=True) check_call('mkdir %s/dir1' % mount_dir, shell=True)
@ -427,15 +340,21 @@ def fs_obj_ext(request, u_boot_config):
md5val.append(out.split()[0]) md5val.append(out.split()[0])
check_call('rm %s' % tmp_file, shell=True) check_call('rm %s' % tmp_file, shell=True)
try:
# 128MiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', mount_dir)
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
except CalledProcessError: except CalledProcessError:
pytest.skip('Setup failed for filesystem: ' + fs_type) pytest.skip('Setup failed for filesystem: ' + fs_type)
umount_fs(mount_dir)
return return
else: else:
umount_fs(mount_dir)
yield [fs_ubtype, fs_img, md5val] yield [fs_ubtype, fs_img, md5val]
finally: finally:
call('rmdir %s' % mount_dir, shell=True) call('rm -rf %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
# #
@ -461,7 +380,7 @@ def fs_obj_mkdir(request, u_boot_config):
try: try:
# 128MiB volume # 128MiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB') fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', None)
except: except:
pytest.skip('Setup failed for filesystem: ' + fs_type) pytest.skip('Setup failed for filesystem: ' + fs_type)
return return
@ -492,14 +411,6 @@ def fs_obj_unlink(request, u_boot_config):
mount_dir = u_boot_config.persistent_data_dir + '/mnt' mount_dir = u_boot_config.persistent_data_dir + '/mnt'
try:
# 128MiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
try: try:
check_call('mkdir -p %s' % mount_dir, shell=True) check_call('mkdir -p %s' % mount_dir, shell=True)
except CalledProcessError as err: except CalledProcessError as err:
@ -507,15 +418,6 @@ def fs_obj_unlink(request, u_boot_config):
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
return return
try:
# Mount the image so we can populate it.
mount_fs(fs_type, fs_img, mount_dir)
except CalledProcessError as err:
pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
call('rmdir %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True)
return
try: try:
# Test Case 1 & 3 # Test Case 1 & 3
check_call('mkdir %s/dir1' % mount_dir, shell=True) check_call('mkdir %s/dir1' % mount_dir, shell=True)
@ -538,15 +440,20 @@ def fs_obj_unlink(request, u_boot_config):
check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1' check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1'
% mount_dir, shell=True) % mount_dir, shell=True)
try:
# 128MiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', mount_dir)
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
except CalledProcessError: except CalledProcessError:
pytest.skip('Setup failed for filesystem: ' + fs_type) pytest.skip('Setup failed for filesystem: ' + fs_type)
umount_fs(mount_dir)
return return
else: else:
umount_fs(mount_dir)
yield [fs_ubtype, fs_img] yield [fs_ubtype, fs_img]
finally: finally:
call('rmdir %s' % mount_dir, shell=True) call('rm -rf %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
# #
@ -575,14 +482,6 @@ def fs_obj_symlink(request, u_boot_config):
small_file = mount_dir + '/' + SMALL_FILE small_file = mount_dir + '/' + SMALL_FILE
medium_file = mount_dir + '/' + MEDIUM_FILE medium_file = mount_dir + '/' + MEDIUM_FILE
try:
# 1GiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x40000000, '1GB')
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
try: try:
check_call('mkdir -p %s' % mount_dir, shell=True) check_call('mkdir -p %s' % mount_dir, shell=True)
except CalledProcessError as err: except CalledProcessError as err:
@ -590,15 +489,6 @@ def fs_obj_symlink(request, u_boot_config):
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
return return
try:
# Mount the image so we can populate it.
mount_fs(fs_type, fs_img, mount_dir)
except CalledProcessError as err:
pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
call('rmdir %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True)
return
try: try:
# Create a subdirectory. # Create a subdirectory.
check_call('mkdir %s/SUBDIR' % mount_dir, shell=True) check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
@ -621,15 +511,20 @@ def fs_obj_symlink(request, u_boot_config):
% medium_file, shell=True).decode() % medium_file, shell=True).decode()
md5val.extend([out.split()[0]]) md5val.extend([out.split()[0]])
try:
# 1GiB volume
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x40000000, '1GB', mount_dir)
except CalledProcessError as err:
pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
return
except CalledProcessError: except CalledProcessError:
pytest.skip('Setup failed for filesystem: ' + fs_type) pytest.skip('Setup failed for filesystem: ' + fs_type)
umount_fs(mount_dir)
return return
else: else:
umount_fs(mount_dir)
yield [fs_ubtype, fs_img, md5val] yield [fs_ubtype, fs_img, md5val]
finally: finally:
call('rmdir %s' % mount_dir, shell=True) call('rm -rf %s' % mount_dir, shell=True)
call('rm -f %s' % fs_img, shell=True) call('rm -f %s' % fs_img, shell=True)
# #
@ -665,7 +560,7 @@ def fs_obj_fat(request, u_boot_config):
try: try:
# the volume size depends on the filesystem # the volume size depends on the filesystem
fs_img = fs_helper.mk_fs(u_boot_config, fs_type, fs_size, f'{fs_size}', 1024) fs_img = fs_helper.mk_fs(u_boot_config, fs_type, fs_size, f'{fs_size}', None, 1024)
except: except:
pytest.skip('Setup failed for filesystem: ' + fs_type) pytest.skip('Setup failed for filesystem: ' + fs_type)
return return

View file

@ -540,8 +540,8 @@ def test_ut_dm_init(u_boot_console):
u_boot_utils.run_and_log( u_boot_utils.run_and_log(
u_boot_console, f'sfdisk {fn}', stdin=b'type=83') u_boot_console, f'sfdisk {fn}', stdin=b'type=83')
fs_helper.mk_fs(u_boot_console.config, 'ext2', 0x200000, '2MB') fs_helper.mk_fs(u_boot_console.config, 'ext2', 0x200000, '2MB', None)
fs_helper.mk_fs(u_boot_console.config, 'fat32', 0x100000, '1MB') fs_helper.mk_fs(u_boot_console.config, 'fat32', 0x100000, '1MB', None)
mmc_dev = 6 mmc_dev = 6
fn = os.path.join(u_boot_console.config.source_dir, f'mmc{mmc_dev}.img') fn = os.path.join(u_boot_console.config.source_dir, f'mmc{mmc_dev}.img')