test: Add a helper class to create disk images

Provide a way to create disk images which consist of multiple filesystem
images.

Include a cur_dir option to place the disk image in the current
directory rather than in the persistent-data directory. This matches the
behaviour of setup_image() which places disk images in source_dir where
sandbox expects to find them. This flag is a temporary feature and
should be removed once all tests are migrated to use the persistent-data
directory instead.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2026-04-04 08:03:05 -06:00 committed by Tom Rini
parent 1c1bfb1cf3
commit 7c69117337

View File

@ -97,6 +97,98 @@ class FsHelper:
self.cleanup()
class DiskHelper:
"""Helper class for creating disk images containing filesytems
Usage:
with DiskHelper(ubman.config, 0, 'mmc') as img, \
FsHelper(ubman.config, 'ext1', 1, 'mmc') as fsh:
# Write files to fsh.srcdir
...
# Create the filesystem
fsh.mk_fs()
# Add this filesystem to the disk
img.add_fs(fsh, DiskHelper.VFAT)
# Add more filesystems as needed (add another 'with' clause)
...
# Get the final disk image
data = img.create()
"""
# Partition-type codes
VFAT = 0xc
EXT4 = 0x83
def __init__(self, config, devnum, prefix, cur_dir=False):
"""Set up a new disk image
Args:
config (u_boot_config): U-Boot configuration
devnum (int): Device number (for filename)
prefix (str): Prefix string of volume's file name
cur_dir (bool): True to put the file in the current directory,
instead of the persistent-data directory
"""
self.fs_list = []
self.fname = os.path.join('' if cur_dir else config.persistent_data_dir,
f'{prefix}{devnum}.img')
def add_fs(self, fs_img, part_type, bootable=False):
"""Add a new filesystem
Args:
fs_img (FsHelper): Filesystem to add
part_type (DiskHelper.FAT or DiskHelper.EXT4): Partition type
bootable (bool): True to set the 'bootable' flat
"""
self.fs_list.append([fs_img, part_type, bootable])
def create(self):
"""Create the disk image
Create an image with a partition table and the filesystems
"""
spec = ''
pos = 1 # Reserve 1MB for the partition table itself
for fsi, part_type, bootable in self.fs_list:
if spec:
spec += '\n'
spec += f'type={part_type:x}, size={fsi.size_mb}M, start={pos}M'
if bootable:
spec += ', bootable'
pos += fsi.size_mb
img_size = pos
try:
check_call(f'qemu-img create {self.fname} {img_size}M', shell=True)
check_call(f'printf "{spec}" | sfdisk {self.fname}', shell=True)
except CalledProcessError:
os.remove(self.fname)
raise
pos = 1 # Reserve 1MB for the partition table itself
for fsi, part_type, bootable in self.fs_list:
check_call(
f'dd if={fsi.fs_img} of={self.fname} bs=1M seek={pos} conv=notrunc',
shell=True)
pos += fsi.size_mb
return self.fname
def cleanup(self, remove_full_img=False):
"""Remove created file"""
os.remove(self.fname)
def __enter__(self):
return self
def __exit__(self, extype, value, traceback):
self.cleanup()
def mk_fs(config, fs_type, size, prefix, src_dir=None, fs_img=None, quiet=False):
"""Create a file system volume