* [PATCH 1/2] wic: re-implement sector-size support
@ 2026-02-13 16:36 Trevor Woerner
2026-02-13 16:36 ` [PATCH 2/2] ufs image class: add Trevor Woerner
0 siblings, 1 reply; 4+ messages in thread
From: Trevor Woerner @ 2026-02-13 16:36 UTC (permalink / raw)
To: openembedded-core
The previous implementation to add variable sector-size support:
- required the variable WIC_SECTOR_SIZE either be defined in a
configuration file or be defined in a --vars file
- this means that every invocation of "wic ls", "wic cp", or "wic rm"
needed this variable defined (config or --vars)
- required the user to create separate *wks files for every sector size
they wanted to use
- required the user to specify the mkfs-extraopts by hand to specify the
correct sector size: e.g.
bootloader --ptable gpt
part --fstype ext4 --source rootfs --label rofs-a --mkfs-extraopts "-b 4096"
part --fstype ext4 --source rootfs --use-uuid --mkfs-extraopts "-b 4096"
- specifying --mkfs-extraopts replaces the defaults with the
user-supplied values
- only provided details to support using variable sector-sizes with
ext[234] filesystems
- it would not be possible to generate images with different sector
sizes in the same build since the configuration and *wks files would
need to change and the build re-run for each size
Update the sector-size handling so that:
- the sector-size will now be provided on the cmdline to the "wic ls",
"wic cp", "wic rm", and "wic create" commands: default = 512
- this means the configuration and/or --vars file does not need to be
changed in order to perform those operations on images with different
sector sizes
- support is provided implicitly for mkdosfs and ext[234] partitions
- the user no longer needs to know and supply the sector-size magic in
--mkfs-extraopts (thereby clobbering the other defaults)
AI-Generated: codex/gpt-5.1-codex-max
Signed-off-by: Trevor Woerner <twoerner@gmail.com>
---
meta/lib/oeqa/selftest/cases/wic.py | 39 ++++---------
scripts/lib/wic/engine.py | 45 +++++++--------
scripts/lib/wic/help.py | 23 +++++---
scripts/lib/wic/partition.py | 55 +++++++++++++++++--
scripts/lib/wic/plugins/imager/direct.py | 18 ++----
scripts/lib/wic/plugins/source/bootimg_efi.py | 5 +-
.../lib/wic/plugins/source/bootimg_pcbios.py | 11 ++--
.../wic/plugins/source/isoimage_isohybrid.py | 5 +-
scripts/wic | 8 +++
9 files changed, 125 insertions(+), 84 deletions(-)
diff --git a/meta/lib/oeqa/selftest/cases/wic.py b/meta/lib/oeqa/selftest/cases/wic.py
index ecaee5a29144..7ad61fa700f7 100644
--- a/meta/lib/oeqa/selftest/cases/wic.py
+++ b/meta/lib/oeqa/selftest/cases/wic.py
@@ -935,33 +935,22 @@ bootloader --ptable gpt""")
finally:
os.remove(wks_file)
- def test_wic_sector_size(self):
- """Test generation image sector size"""
-
+ def test_wic_sector_size_cli(self):
+ """Test sector size handling via CLI option."""
+
oldpath = os.environ['PATH']
os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
try:
- # Add WIC_SECTOR_SIZE into config
- config = 'WIC_SECTOR_SIZE = "4096"\n'\
- 'WICVARS:append = " WIC_SECTOR_SIZE"\n'
- self.append_config(config)
bitbake('core-image-minimal')
- # Check WIC_SECTOR_SIZE apply to bitbake variable
- wic_sector_size_str = get_bb_var('WIC_SECTOR_SIZE', 'core-image-minimal')
- wic_sector_size = int(wic_sector_size_str)
- self.assertEqual(4096, wic_sector_size)
-
- self.logger.info("Test wic_sector_size: %d \n" % wic_sector_size)
-
with NamedTemporaryFile("w", suffix=".wks") as wks:
wks.writelines(
['bootloader --ptable gpt\n',
- 'part --fstype ext4 --source rootfs --label rofs-a --mkfs-extraopts "-b 4096"\n',
- 'part --fstype ext4 --source rootfs --use-uuid --mkfs-extraopts "-b 4096"\n'])
+ 'part --fstype ext4 --source rootfs --label rofs-a\n',
+ 'part --fstype ext4 --source rootfs --use-uuid\n'])
wks.flush()
- cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
+ cmd = "wic create %s -e core-image-minimal -o %s --sector-size 4096" % (wks.name, self.resultdir)
runCmd(cmd)
wksname = os.path.splitext(os.path.basename(wks.name))[0]
images = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
@@ -969,24 +958,18 @@ bootloader --ptable gpt""")
sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
# list partitions
- result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
+ result = runCmd("wic ls %s -n %s --sector-size 4096" % (images[0], sysroot))
self.assertEqual(3, len(result.output.split('\n')))
- # verify partition size with wic
- res = runCmd("export PARTED_SECTOR_SIZE=%d; parted -m %s unit b p" % (wic_sector_size, images[0]),
+ # verify partition size with parted output
+ res = runCmd("export PARTED_SECTOR_SIZE=%d; parted -m %s unit b p" % (4096, images[0]),
stderr=subprocess.PIPE)
- # parse parted output which looks like this:
- # BYT;\n
- # /var/tmp/wic/build/tmpgjzzefdd-202410281021-sda.direct:78569472B:file:4096:4096:gpt::;\n
- # 1:139264B:39284735B:39145472B:ext4:rofs-a:;\n
- # 2:39284736B:78430207B:39145472B:ext4:primary:;\n
disk_info = res.output.splitlines()[1]
- # Check sector sizes
sector_size_logical = int(disk_info.split(":")[3])
sector_size_physical = int(disk_info.split(":")[4])
- self.assertEqual(wic_sector_size, sector_size_logical, "Logical sector size is not %d." % wic_sector_size)
- self.assertEqual(wic_sector_size, sector_size_physical, "Physical sector size is not %d." % wic_sector_size)
+ self.assertEqual(4096, sector_size_logical, "Logical sector size is not 4096.")
+ self.assertEqual(4096, sector_size_physical, "Physical sector size is not 4096.")
finally:
os.environ['PATH'] = oldpath
diff --git a/scripts/lib/wic/engine.py b/scripts/lib/wic/engine.py
index 9d596be3a723..7753f431f91d 100644
--- a/scripts/lib/wic/engine.py
+++ b/scripts/lib/wic/engine.py
@@ -224,7 +224,7 @@ def wic_list(args, scripts_path):
class Disk:
- def __init__(self, imagepath, native_sysroot, fstypes=('fat', 'ext')):
+ def __init__(self, imagepath, native_sysroot, fstypes=('fat', 'ext'), sector_size=512):
self.imagepath = imagepath
self.native_sysroot = native_sysroot
self.fstypes = fstypes
@@ -233,16 +233,7 @@ class Disk:
self._lsector_size = None
self._psector_size = None
self._ptable_format = None
-
- # define sector size
- sector_size_str = get_bitbake_var('WIC_SECTOR_SIZE')
- if sector_size_str is not None:
- try:
- self.sector_size = int(sector_size_str)
- except ValueError:
- self.sector_size = None
- else:
- self.sector_size = None
+ self.sector_size = sector_size
# find parted
# read paths from $PATH environment variable
@@ -271,7 +262,7 @@ class Disk:
if self._partitions is None:
self._partitions = OrderedDict()
- if self.sector_size is not None:
+ if self.sector_size:
out = exec_cmd("export PARTED_SECTOR_SIZE=%d; %s -sm %s unit B print" % \
(self.sector_size, self.parted, self.imagepath), True)
else:
@@ -312,11 +303,15 @@ class Disk:
raise WicError("Partition %s is not in the image" % pnum)
part = self.partitions[pnum]
# check if fstype is supported
+ # NOTE: parted is unable to identify dos-type partitions with a 4k sector-size
+ # if the type is empty and a non-default sector size is used, assume 'fat'
+ # fdisk identifies them without issue
+ part_fstype = part.fstype if part.fstype or self.sector_size == 512 else 'fat'
for fstype in self.fstypes:
- if part.fstype.startswith(fstype):
+ if part_fstype.startswith(fstype):
break
else:
- raise WicError("Not supported fstype: {}".format(part.fstype))
+ raise WicError("Not supported fstype: {}".format(part_fstype))
if pnum not in self._partimages:
tmpf = tempfile.NamedTemporaryFile(prefix="wic-part")
dst_fname = tmpf.name
@@ -586,8 +581,9 @@ class Disk:
label = part.get("name")
label_str = "-n {}".format(label) if label else ''
- cmd = "{} {} -C {} {}".format(self.mkdosfs, label_str, partfname,
- part['size'])
+ sector_str = "-S {}".format(self.sector_size) if self.sector_size else ''
+ cmd = "{} {} {} -C {} {}".format(self.mkdosfs, label_str, sector_str, partfname,
+ part['size'])
exec_cmd(cmd)
# copy content from the temporary directory to the new partition
cmd = "{} -snompi {} {}/* ::".format(self.mcopy, partfname, tmpdir)
@@ -609,14 +605,19 @@ class Disk:
def wic_ls(args, native_sysroot):
"""List contents of partitioned image or vfat partition."""
- disk = Disk(args.path.image, native_sysroot)
+ disk = Disk(args.path.image, native_sysroot, sector_size=args.sector_size)
if not args.path.part:
if disk.partitions:
print('Num Start End Size Fstype')
for part in disk.partitions.values():
+ # size values are in bytes from parted; convert to sectors if a custom sector size was requested
+ display_size = part.size
+ if args.sector_size and args.sector_size != disk._lsector_size:
+ display_size = part.size // args.sector_size
print("{:2d} {:12d} {:12d} {:12d} {}".format(\
- part.pnum, part.start, part.end,
- part.size, part.fstype))
+ part.pnum, part.start // args.sector_size,
+ part.end // args.sector_size,
+ display_size, part.fstype))
else:
path = args.path.path or '/'
print(disk.dir(args.path.part, path))
@@ -627,9 +628,9 @@ def wic_cp(args, native_sysroot):
partitioned image.
"""
if isinstance(args.dest, str):
- disk = Disk(args.src.image, native_sysroot)
+ disk = Disk(args.src.image, native_sysroot, sector_size=args.sector_size)
else:
- disk = Disk(args.dest.image, native_sysroot)
+ disk = Disk(args.dest.image, native_sysroot, sector_size=args.sector_size)
disk.copy(args.src, args.dest)
@@ -638,7 +639,7 @@ def wic_rm(args, native_sysroot):
Remove files or directories from the vfat partition of
partitioned image.
"""
- disk = Disk(args.path.image, native_sysroot)
+ disk = Disk(args.path.image, native_sysroot, sector_size=args.sector_size)
disk.remove(args.path.part, args.path.path, args.recursive_delete)
def wic_write(args, native_sysroot):
diff --git a/scripts/lib/wic/help.py b/scripts/lib/wic/help.py
index 6b49a67de938..5d7c40456a80 100644
--- a/scripts/lib/wic/help.py
+++ b/scripts/lib/wic/help.py
@@ -118,7 +118,7 @@ wic_create_usage = """
usage: wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>]
[-e | --image-name] [-s, --skip-build-check] [-D, --debug]
[-r, --rootfs-dir] [-b, --bootimg-dir]
- [-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs]
+ [-k, --kernel-dir] [-n, --native-sysroot] [--sector-size <bytes>] [-f, --build-rootfs]
[-c, --compress-with] [-m, --bmap]
This command creates an OpenEmbedded image based on the 'OE kickstart
@@ -139,13 +139,16 @@ SYNOPSIS
wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>]
[-e | --image-name] [-s, --skip-build-check] [-D, --debug]
[-r, --rootfs-dir] [-b, --bootimg-dir]
- [-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs]
+ [-k, --kernel-dir] [-n, --native-sysroot] [--sector-size <bytes>] [-f, --build-rootfs]
[-c, --compress-with] [-m, --bmap] [--no-fstab-update]
DESCRIPTION
This command creates an OpenEmbedded image based on the 'OE
kickstart commands' found in the <wks file>.
+ Use the --sector-size option to select the sector size (in bytes)
+ used for partition layout calculations (default is 512).
+
In order to do this, wic needs to know the locations of the
various build artifacts required to build the image.
@@ -278,7 +281,7 @@ wic_ls_usage = """
List content of a partitioned image
- usage: wic ls <image>[:<partition>[<path>]] [--native-sysroot <path>]
+ usage: wic ls <image>[:<partition>[<path>]] [--native-sysroot <path>] [--sector-size <bytes>]
This command outputs either list of image partitions or directory contents
of vfat and ext* partitions.
@@ -296,7 +299,7 @@ SYNOPSIS
wic ls <image>
wic ls <image>:<vfat or ext* partition>
wic ls <image>:<vfat or ext* partition><path>
- wic ls <image>:<vfat or ext* partition><path> --native-sysroot <path>
+ wic ls <image>:<vfat or ext* partition><path> --native-sysroot <path> [--sector-size <bytes>]
DESCRIPTION
This command lists either partitions of the image or directory contents
@@ -336,6 +339,8 @@ DESCRIPTION
The -n option is used to specify the path to the native sysroot
containing the tools(parted and mtools) to use.
+ The --sector-size option sets the sector size used for partition math
+ (default is 512 bytes).
"""
@@ -343,7 +348,7 @@ wic_cp_usage = """
Copy files and directories to/from the vfat or ext* partition
- usage: wic cp <src> <dest> [--native-sysroot <path>]
+ usage: wic cp <src> <dest> [--native-sysroot <path>] [--sector-size <bytes>]
source/destination image in format <image>:<partition>[<path>]
@@ -364,7 +369,7 @@ SYNOPSIS
wic cp <src> <dest>:<partition>
wic cp <src>:<partition> <dest>
wic cp <src> <dest-image>:<partition><path>
- wic cp <src> <dest-image>:<partition><path> --native-sysroot <path>
+ wic cp <src> <dest-image>:<partition><path> --native-sysroot <path> [--sector-size <bytes>]
DESCRIPTION
This command copies files or directories either
@@ -408,13 +413,15 @@ DESCRIPTION
The -n option is used to specify the path to the native sysroot
containing the tools(parted and mtools) to use.
+ The --sector-size option sets the sector size used for partition math
+ (default is 512 bytes).
"""
wic_rm_usage = """
Remove files or directories from the vfat or ext* partitions
- usage: wic rm <image>:<partition><path> [--native-sysroot <path>]
+ usage: wic rm <image>:<partition><path> [--native-sysroot <path>] [--sector-size <bytes>]
This command removes files or directories from the vfat or ext* partitions of
the partitioned image.
@@ -466,6 +473,8 @@ DESCRIPTION
The -n option is used to specify the path to the native sysroot
containing the tools(parted and mtools) to use.
+ The --sector-size option sets the sector size used for partition math
+ (default is 512 bytes).
The -r option is used to remove directories and their contents
recursively,this only applies to ext* partition.
diff --git a/scripts/lib/wic/partition.py b/scripts/lib/wic/partition.py
index 8fed686e903e..df6e3271649b 100644
--- a/scripts/lib/wic/partition.py
+++ b/scripts/lib/wic/partition.py
@@ -65,6 +65,50 @@ class Partition():
self.lineno = lineno
self.source_file = ""
+ self.sector_size = 512
+
+ def _mkdosfs_extraopts(self):
+ """
+ Build mkdosfs extra options ensuring the CLI sector size is applied.
+ """
+ extraopts = self.mkfs_extraopts or ''
+ tokens = []
+ skip_next = False
+ for tok in extraopts.split():
+ if skip_next:
+ skip_next = False
+ continue
+ if tok == '-S':
+ skip_next = True
+ continue
+ if tok.startswith('-S'):
+ continue
+ tokens.append(tok)
+ tokens.extend(['-S', str(self.sector_size)])
+ return ' '.join(tokens).strip()
+
+ def _mkfs_ext_extraopts(self, base_opts):
+ """
+ Build mkfs.ext* extra options ensuring the CLI sector size is applied.
+ """
+ extraopts = self.mkfs_extraopts or base_opts
+ # Only add an explicit block size when a non-default sector size is requested.
+ if self.sector_size and self.sector_size != 512:
+ tokens = []
+ skip_next = False
+ for tok in extraopts.split():
+ if skip_next:
+ skip_next = False
+ continue
+ if tok == '-b':
+ skip_next = True
+ continue
+ if tok.startswith('-b'):
+ continue
+ tokens.append(tok)
+ tokens.extend(['-b', str(self.sector_size)])
+ return ' '.join(tokens).strip()
+ return extraopts
def get_extra_block_count(self, current_blocks):
"""
@@ -138,6 +182,8 @@ class Partition():
Prepare content for individual partitions, depending on
partition command parameters.
"""
+ # capture the sector size requested on the CLI for mkdosfs invocations
+ self.sector_size = getattr(creator, 'sector_size', 512) or 512
self.updated_fstab_path = updated_fstab_path
if self.updated_fstab_path and not (self.fstype.startswith("ext") or self.fstype == "msdos"):
self.update_fstab_in_rootfs = True
@@ -293,7 +339,7 @@ class Partition():
with open(rootfs, 'w') as sparse:
os.ftruncate(sparse.fileno(), rootfs_size * 1024)
- extraopts = self.mkfs_extraopts or "-F -i 8192"
+ extraopts = self._mkfs_ext_extraopts("-F -i 8192")
# use hash_seed to generate reproducible ext4 images
(extraopts, pseudo) = self.get_hash_seed_ext4(extraopts, pseudo)
@@ -401,7 +447,7 @@ class Partition():
size_str = ""
- extraopts = self.mkfs_extraopts or '-S 512'
+ extraopts = self._mkdosfs_extraopts()
dosfs_cmd = "mkdosfs %s -i %s %s %s -C %s %d" % \
(label_str, self.fsuuid, size_str, extraopts, rootfs,
@@ -452,7 +498,7 @@ class Partition():
with open(rootfs, 'w') as sparse:
os.ftruncate(sparse.fileno(), size * 1024)
- extraopts = self.mkfs_extraopts or "-i 8192"
+ extraopts = self._mkfs_ext_extraopts("-i 8192")
# use hash_seed to generate reproducible ext4 images
(extraopts, pseudo) = self.get_hash_seed_ext4(extraopts, None)
@@ -498,7 +544,7 @@ class Partition():
size_str = ""
- extraopts = self.mkfs_extraopts or '-S 512'
+ extraopts = self._mkdosfs_extraopts()
dosfs_cmd = "mkdosfs %s -i %s %s %s -C %s %d" % \
(label_str, self.fsuuid, extraopts, size_str, rootfs,
@@ -559,4 +605,3 @@ class Partition():
logger.warn("%s Inodes (of size %d) are too small." %
(get_err_str(self), size))
break
-
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
index ad922cfbf122..3adc6eee6280 100644
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -67,6 +67,7 @@ class DirectPlugin(ImagerPlugin):
self._image = None
self.ptable_format = self.ks.bootloader.ptable
self.parts = self.ks.partitions
+ self.sector_size = options.sector_size or 512
# as a convenience, set source to the boot partition source
# instead of forcing it to be set via bootloader --source
@@ -78,7 +79,7 @@ class DirectPlugin(ImagerPlugin):
image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
self._image = PartitionedImage(image_path, self.ptable_format, self.ks.bootloader.diskid,
self.parts, self.native_sysroot,
- options.extra_space)
+ options.extra_space, self.sector_size)
def setup_workdir(self, workdir):
if workdir:
@@ -294,15 +295,13 @@ MBR_OVERHEAD = 1
# Overhead of the GPT partitioning scheme
GPT_OVERHEAD = 34
-# Size of a sector in bytes
-SECTOR_SIZE = 512
-
class PartitionedImage():
"""
Partitioned image in a file.
"""
- def __init__(self, path, ptable_format, disk_id, partitions, native_sysroot=None, extra_space=0):
+ def __init__(self, path, ptable_format, disk_id, partitions, native_sysroot=None, extra_space=0,
+ sector_size=512):
self.path = path # Path to the image file
self.numpart = 0 # Number of allocated partitions
self.realpart = 0 # Number of partitions in the partition table
@@ -332,14 +331,7 @@ class PartitionedImage():
self.partitions = partitions
self.partimages = []
# Size of a sector used in calculations
- sector_size_str = get_bitbake_var('WIC_SECTOR_SIZE')
- if sector_size_str is not None:
- try:
- self.sector_size = int(sector_size_str)
- except ValueError:
- self.sector_size = SECTOR_SIZE
- else:
- self.sector_size = SECTOR_SIZE
+ self.sector_size = sector_size or 512
self.native_sysroot = native_sysroot
num_real_partitions = len([p for p in self.partitions if not p.no_table])
diff --git a/scripts/lib/wic/plugins/source/bootimg_efi.py b/scripts/lib/wic/plugins/source/bootimg_efi.py
index 430b0a4b023a..864d6898fc9d 100644
--- a/scripts/lib/wic/plugins/source/bootimg_efi.py
+++ b/scripts/lib/wic/plugins/source/bootimg_efi.py
@@ -415,8 +415,9 @@ class BootimgEFIPlugin(SourcePlugin):
label = part.label if part.label else "ESP"
- dosfs_cmd = "mkdosfs -v -n %s -i %s -C %s %d" % \
- (label, part.fsuuid, bootimg, blocks)
+ sector_size = getattr(creator, 'sector_size', 512) or 512
+ dosfs_cmd = "mkdosfs -v -n %s -i %s -S %d -C %s %d" % \
+ (label, part.fsuuid, sector_size, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
logger.debug("mkdosfs:\n%s" % (str(out)))
diff --git a/scripts/lib/wic/plugins/source/bootimg_pcbios.py b/scripts/lib/wic/plugins/source/bootimg_pcbios.py
index a7cc5d12c620..32edac47fa09 100644
--- a/scripts/lib/wic/plugins/source/bootimg_pcbios.py
+++ b/scripts/lib/wic/plugins/source/bootimg_pcbios.py
@@ -132,7 +132,7 @@ class BootimgPcbiosPlugin(SourcePlugin):
cls._do_prepare_grub(part, cr_workdir, oe_builddir,
kernel_dir, rootfs_dir, native_sysroot)
elif source_params['loader-bios'] == 'syslinux':
- cls._do_prepare_syslinux(part, cr_workdir, bootimg_dir,
+ cls._do_prepare_syslinux(part, creator, cr_workdir, bootimg_dir,
kernel_dir, native_sysroot)
else:
raise WicError("unrecognized bootimg_pcbios loader: %s" % source_params['loader-bios'])
@@ -142,7 +142,7 @@ class BootimgPcbiosPlugin(SourcePlugin):
except KeyError:
# Required by do_install_disk
cls.loader = 'syslinux'
- cls._do_prepare_syslinux(part, cr_workdir, bootimg_dir,
+ cls._do_prepare_syslinux(part, creator, cr_workdir, bootimg_dir,
kernel_dir, native_sysroot)
@classmethod
@@ -240,7 +240,7 @@ class BootimgPcbiosPlugin(SourcePlugin):
cfg.close()
@classmethod
- def _do_prepare_syslinux(cls, part, cr_workdir, bootimg_dir,
+ def _do_prepare_syslinux(cls, part, creator, cr_workdir, bootimg_dir,
kernel_dir, native_sysroot):
"""
Called to do the actual content population for a partition i.e. it
@@ -292,8 +292,9 @@ class BootimgPcbiosPlugin(SourcePlugin):
label = part.label if part.label else "boot"
- dosfs_cmd = "mkdosfs -n %s -i %s -S 512 -C %s %d" % \
- (label, part.fsuuid, bootimg, blocks)
+ sector_size = getattr(creator, 'sector_size', 512) or 512
+ dosfs_cmd = "mkdosfs -n %s -i %s -S %d -C %s %d" % \
+ (label, part.fsuuid, sector_size, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
diff --git a/scripts/lib/wic/plugins/source/isoimage_isohybrid.py b/scripts/lib/wic/plugins/source/isoimage_isohybrid.py
index fdab188db1f8..9195ef5f3184 100644
--- a/scripts/lib/wic/plugins/source/isoimage_isohybrid.py
+++ b/scripts/lib/wic/plugins/source/isoimage_isohybrid.py
@@ -367,8 +367,9 @@ class IsoImagePlugin(SourcePlugin):
esp_label = source_params.get('esp_label', 'EFIimg')
- dosfs_cmd = 'mkfs.vfat -n \'%s\' -S 512 -C %s %d' \
- % (esp_label, bootimg, blocks)
+ sector_size = getattr(creator, 'sector_size', 512) or 512
+ dosfs_cmd = "mkfs.vfat -n '%s' -S %d -C %s %d" % \
+ (esp_label, sector_size, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mmd_cmd = "mmd -i %s ::/EFI" % bootimg
diff --git a/scripts/wic b/scripts/wic
index 9137208f5e8f..0cef84ef630c 100755
--- a/scripts/wic
+++ b/scripts/wic
@@ -376,6 +376,8 @@ def wic_init_parser_create(subparser):
default="direct", help="the wic imager plugin")
subparser.add_argument("--extra-space", type=int, dest="extra_space",
default=0, help="additional free disk space to add to the image")
+ subparser.add_argument("--sector-size", dest="sector_size", type=int, default=512,
+ help="sector size in bytes (default: 512)")
return
@@ -413,6 +415,8 @@ def imgtype(arg):
def wic_init_parser_ls(subparser):
subparser.add_argument("path", type=imgtype,
help="image spec: <image>[:<vfat partition>[<path>]]")
+ subparser.add_argument("--sector-size", dest="sector_size", type=int, default=512,
+ help="sector size in bytes (default: 512)")
subparser.add_argument("-n", "--native-sysroot",
help="path to the native sysroot containing the tools")
subparser.add_argument("-e", "--image-name", dest="image_name",
@@ -433,6 +437,8 @@ def wic_init_parser_cp(subparser):
help="image spec: <image>:<vfat partition>[<path>] or <file>")
subparser.add_argument("dest",
help="image spec: <image>:<vfat partition>[<path>] or <file>")
+ subparser.add_argument("--sector-size", dest="sector_size", type=int, default=512,
+ help="sector size in bytes (default: 512)")
subparser.add_argument("-n", "--native-sysroot",
help="path to the native sysroot containing the tools")
subparser.add_argument("-e", "--image-name", dest="image_name",
@@ -445,6 +451,8 @@ def wic_init_parser_cp(subparser):
def wic_init_parser_rm(subparser):
subparser.add_argument("path", type=imgpathtype,
help="path: <image>:<vfat partition><path>")
+ subparser.add_argument("--sector-size", dest="sector_size", type=int, default=512,
+ help="sector size in bytes (default: 512)")
subparser.add_argument("-n", "--native-sysroot",
help="path to the native sysroot containing the tools")
subparser.add_argument("-r", dest="recursive_delete", action="store_true", default=False,
--
2.51.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] ufs image class: add
2026-02-13 16:36 [PATCH 1/2] wic: re-implement sector-size support Trevor Woerner
@ 2026-02-13 16:36 ` Trevor Woerner
2026-02-15 16:41 ` [OE-core] " Mathieu Dubois-Briand
0 siblings, 1 reply; 4+ messages in thread
From: Trevor Woerner @ 2026-02-13 16:36 UTC (permalink / raw)
To: openembedded-core
Add an image class and wks file that demonstrates generating a wic image
with a 4096-byte sector size.
Signed-off-by: Trevor Woerner <twoerner@gmail.com>
---
meta/classes-recipe/image.bbclass | 2 +-
meta/classes-recipe/image_types_ufs.bbclass | 221 ++++++++++++++++++++
scripts/lib/wic/canned-wks/mkdisk-ufs.wks | 5 +
3 files changed, 227 insertions(+), 1 deletion(-)
create mode 100644 meta/classes-recipe/image_types_ufs.bbclass
create mode 100644 scripts/lib/wic/canned-wks/mkdisk-ufs.wks
diff --git a/meta/classes-recipe/image.bbclass b/meta/classes-recipe/image.bbclass
index 53f1a9dc45b0..97465836c14a 100644
--- a/meta/classes-recipe/image.bbclass
+++ b/meta/classes-recipe/image.bbclass
@@ -18,7 +18,7 @@ inherit populate_sdk_base
IMGCLASSES += "${@['', 'populate_sdk_ext']['linux' in d.getVar("SDK_OS")]}"
IMGCLASSES += "${@bb.utils.contains_any('IMAGE_FSTYPES', 'live iso hddimg', 'image-live', '', d)}"
IMGCLASSES += "${@bb.utils.contains('IMAGE_FSTYPES', 'container', 'image-container', '', d)}"
-IMGCLASSES += "image_types_wic"
+IMGCLASSES += "image_types_wic image_types_ufs"
IMGCLASSES += "rootfs-postcommands"
IMGCLASSES += "image-postinst-intercepts"
IMGCLASSES += "overlayfs-etc"
diff --git a/meta/classes-recipe/image_types_ufs.bbclass b/meta/classes-recipe/image_types_ufs.bbclass
new file mode 100644
index 000000000000..975e06080508
--- /dev/null
+++ b/meta/classes-recipe/image_types_ufs.bbclass
@@ -0,0 +1,221 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+# The WICUFSVARS variable is used to define the base list of bitbake variables used in wic code
+# variables from this list are written to <image>.env file
+WICUFSVARS ?= "\
+ APPEND \
+ ASSUME_PROVIDED \
+ BBLAYERS \
+ DEPLOY_DIR_IMAGE \
+ FAKEROOTCMD \
+ HOSTTOOLS_DIR \
+ IMAGE_BASENAME \
+ IMAGE_BOOT_FILES \
+ IMAGE_CLASSES \
+ IMAGE_EFI_BOOT_FILES \
+ IMAGE_EXTRA_PARTITION_FILES \
+ IMAGE_LINK_NAME \
+ IMAGE_ROOTFS \
+ IMGDEPLOYDIR \
+ INITRAMFS_FSTYPES \
+ INITRAMFS_IMAGE \
+ INITRAMFS_IMAGE_BUNDLE \
+ INITRAMFS_LINK_NAME \
+ INITRD \
+ INITRD_LIVE \
+ ISODIR \
+ KERNEL_CONSOLE \
+ KERNEL_IMAGETYPE \
+ MACHINE \
+ PSEUDO_INCLUDE_PATHS \
+ RECIPE_SYSROOT_NATIVE \
+ ROOTFS_SIZE \
+ STAGING_DATADIR \
+ STAGING_DIR \
+ STAGING_DIR_HOST \
+ STAGING_LIBDIR \
+ TARGET_SYS \
+"
+
+inherit_defer ${@bb.utils.contains('INITRAMFS_IMAGE_BUNDLE', '1', 'kernel-artifact-names', '', d)}
+
+WKSUFS_FILE ??= "${WKS_FILE}"
+WKSUFS_FILES ?= "${WKSUFS_FILE} ${IMAGE_BASENAME}.wks"
+WKSUFS_SEARCH_PATH ?= "${THISDIR}:${@':'.join('%s/wic' % p for p in '${BBPATH}'.split(':'))}:${@':'.join('%s/scripts/lib/wic/canned-wks' % l for l in '${BBPATH}:${COREBASE}'.split(':'))}"
+WKSUFS_FULL_PATH = "${@wks_search(d.getVar('WKSUFS_FILES').split(), d.getVar('WKSUFS_SEARCH_PATH')) or ''}"
+
+def wks_search(files, search_path):
+ for f in files:
+ if os.path.isabs(f):
+ if os.path.exists(f):
+ return f
+ else:
+ searched = bb.utils.which(search_path, f)
+ if searched:
+ return searched
+
+def wks_checksums(files, search_path):
+ ret = ""
+ for f in files:
+ found, hist = bb.utils.which(search_path, f, history=True)
+ ret = ret + " " + " ".join(h + ":False" for h in hist[:-1])
+ if found:
+ ret = ret + " " + found + ":True"
+ return ret
+
+
+WICUFS_CREATE_EXTRA_ARGS ?= "${WIC_CREATE_EXTRA_ARGS}"
+
+IMAGE_CMD:wic.ufs () {
+ out="${IMGDEPLOYDIR}/${IMAGE_NAME}"
+ build_wic_ufs="${WORKDIR}/build-wic-ufs"
+ tmp_wic_ufs="${WORKDIR}/tmp-wic-ufs"
+ wks="${WKSUFS_FULL_PATH}"
+ if [ -e "$tmp_wic_ufs" ]; then
+ # Ensure we don't have any junk leftover from a previously interrupted
+ # do_image_wic_ufs execution
+ rm -rf "$tmp_wic_ufs"
+ fi
+ if [ -z "$wks" ]; then
+ bbfatal "No kickstart files from WKSUFS_FILES were found: ${WKSUFS_FILES}. Please set WKSUFS_FILE or WKSUFS_FILES appropriately."
+ fi
+ BUILDDIR="${TOPDIR}" PSEUDO_UNLOAD=1 wic create --debug "$wks" --sector-size 4096 --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}-ufs" -o "$build_wic_ufs/" -w "$tmp_wic_ufs" ${WICUFS_CREATE_EXTRA_ARGS}
+
+ # look to see if the user specifies a custom imager
+ IMAGER=direct
+ eval set -- "${WICUFS_CREATE_EXTRA_ARGS} --"
+ while [ 1 ]; do
+ case "$1" in
+ --imager|-i)
+ shift
+ IMAGER=$1
+ ;;
+ --)
+ shift
+ break
+ ;;
+ esac
+ shift
+ done
+ mv "$build_wic_ufs/$(basename "${wks%.wks}")"*.${IMAGER} "$out.wic.ufs"
+}
+IMAGE_CMD:wic.ufs[vardepsexclude] = "WKSUFS_FULL_PATH WKSUFS_FILES TOPDIR"
+SPDX_IMAGE_PURPOSE:wic.ufs = "diskImage"
+do_image_wic_ufs[cleandirs] = "${WORKDIR}/build-wic-ufs"
+
+# Rebuild when the wks file or vars in WICUFSVARS change
+USING_WIC_UFS = "${@bb.utils.contains_any('IMAGE_FSTYPES', 'wic ' + ' '.join('wic.%s' % c for c in '${CONVERSIONTYPES}'.split()), '1', '', d)}"
+WKSUFS_FILE_CHECKSUM = "${@wks_checksums(d.getVar('WKSUFS_FILES').split(), d.getVar('WKSUFS_SEARCH_PATH')) if '${USING_WIC_UFS}' else ''}"
+do_image_wic_ufs[file-checksums] += "${WKSUFS_FILE_CHECKSUM}"
+do_image_wic_ufs[depends] += "${@' '.join('%s-native:do_populate_sysroot' % r for r in ('parted', 'gptfdisk', 'dosfstools', 'mtools'))}"
+
+# We ensure all artfacts are deployed (e.g virtual/bootloader)
+do_image_wic_ufs[recrdeptask] += "do_deploy"
+do_image_wic_ufs[deptask] += "do_image_complete"
+
+WKSUFS_FILE_DEPENDS_DEFAULT = '${@bb.utils.contains_any("BUILD_ARCH", [ 'x86_64', 'i686' ], "syslinux-native", "",d)}'
+WKSUFS_FILE_DEPENDS_DEFAULT += "bmaptool-native cdrtools-native btrfs-tools-native squashfs-tools-native e2fsprogs-native erofs-utils-native"
+# Unified kernel images need objcopy
+WKSUFS_FILE_DEPENDS_DEFAULT += "virtual/cross-binutils"
+WKSUFS_FILE_DEPENDS_BOOTLOADERS = ""
+WKSUFS_FILE_DEPENDS_BOOTLOADERS:aarch64 = "grub-efi systemd-boot"
+WKSUFS_FILE_DEPENDS_BOOTLOADERS:arm = "systemd-boot"
+WKSUFS_FILE_DEPENDS_BOOTLOADERS:x86 = "syslinux grub-efi systemd-boot"
+WKSUFS_FILE_DEPENDS_BOOTLOADERS:x86-64 = "syslinux grub-efi systemd-boot"
+WKSUFS_FILE_DEPENDS_BOOTLOADERS:x86-x32 = "syslinux grub-efi"
+
+WKSUFS_FILE_DEPENDS ??= "${WKSUFS_FILE_DEPENDS_DEFAULT} ${WKSUFS_FILE_DEPENDS_BOOTLOADERS}"
+
+DEPENDS += "${@ '${WKSUFS_FILE_DEPENDS}' if d.getVar('USING_WIC_UFS') else '' }"
+
+python do_write_wksufs_template () {
+ """Write out expanded template contents to WKSUFS_FULL_PATH."""
+ import re
+
+ template_body = d.getVar('_WKSUFS_TEMPLATE')
+
+ # Remove any remnant variable references left behind by the expansion
+ # due to undefined variables
+ expand_var_regexp = re.compile(r"\${[^{}@\n\t :]+}")
+ while True:
+ new_body = re.sub(expand_var_regexp, '', template_body)
+ if new_body == template_body:
+ break
+ else:
+ template_body = new_body
+
+ wks_file = d.getVar('WKSUFS_FULL_PATH')
+ with open(wks_file, 'w') as f:
+ f.write(template_body)
+ f.close()
+ # Copy the finalized wks file to the deploy directory for later use
+ depdir = d.getVar('IMGDEPLOYDIR')
+ basename = d.getVar('IMAGE_BASENAME') + '-ufs'
+ bb.utils.copyfile(wks_file, "%s/%s" % (depdir, basename + '-' + os.path.basename(wks_file)))
+}
+
+do_flush_pseudodb() {
+ ${FAKEROOTENV} ${FAKEROOTCMD} -S
+}
+
+python () {
+ if d.getVar('USING_WIC_UFS'):
+ wksufs_file_u = d.getVar('WKSUFS_FULL_PATH', False)
+ wksufs_file = d.expand(wksufs_file_u)
+ base, ext = os.path.splitext(wksufs_file)
+ if ext == '.in' and os.path.exists(wksufs_file):
+ wksufs_out_file = os.path.join(d.getVar('WORKDIR'), os.path.basename(base))
+ d.setVar('WKSUFS_FULL_PATH', wksufs_out_file)
+ d.setVar('WKSUFS_TEMPLATE_PATH', wksufs_file_u)
+ d.setVar('WKSUFS_FILE_CHECKSUM', '${WKSUFS_TEMPLATE_PATH}:True')
+
+ # We need to re-parse each time the file changes, and bitbake
+ # needs to be told about that explicitly.
+ bb.parse.mark_dependency(d, wksufs_file)
+
+ try:
+ with open(wksufs_file, 'r') as f:
+ body = f.read()
+ except (IOError, OSError) as exc:
+ pass
+ else:
+ # Previously, I used expandWithRefs to get the dependency list
+ # and add it to WICUFSVARS, but there's no point re-parsing the
+ # file in process_wks_template as well, so just put it in
+ # a variable and let the metadata deal with the deps.
+ d.setVar('_WKSUFS_TEMPLATE', body)
+ bb.build.addtask('do_write_wksufs_template', 'do_image_wic_ufs', 'do_image', d)
+ bb.build.addtask('do_image_wic_ufs', 'do_image_complete', 'do_image_wic', d)
+}
+
+#
+# Write environment variables used by wic
+# to tmp/sysroots/<machine>/imgdata/<image>-ufs.env
+#
+python do_rootfs_wicufsenv () {
+ wicufsvars = d.getVar('WICUFSVARS')
+ if not wicufsvars:
+ return
+
+ stdir = d.getVar('STAGING_DIR')
+ outdir = os.path.join(stdir, d.getVar('MACHINE'), 'imgdata')
+ bb.utils.mkdirhier(outdir)
+ basename = d.getVar('IMAGE_BASENAME') + '-ufs'
+ with open(os.path.join(outdir, basename) + '.env', 'w') as envf:
+ for var in wicufsvars.split():
+ value = d.getVar(var)
+ if value:
+ envf.write('%s="%s"\n' % (var, value.strip()))
+ envf.close()
+ # Copy .env file to deploy directory for later use with stand alone wic
+ depdir = d.getVar('IMGDEPLOYDIR')
+ bb.utils.copyfile(os.path.join(outdir, basename) + '.env', os.path.join(depdir, basename) + '.env')
+}
+addtask do_flush_pseudodb after do_rootfs before do_image do_image_qa
+addtask do_rootfs_wicufsenv after do_image before do_image_wic_ufs
+do_rootfs_wicufsenv[vardeps] += "${WICUFSVARS}"
+do_rootfs_wicufsenv[prefuncs] = 'set_image_size'
diff --git a/scripts/lib/wic/canned-wks/mkdisk-ufs.wks b/scripts/lib/wic/canned-wks/mkdisk-ufs.wks
new file mode 100644
index 000000000000..580f0a2045de
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/mkdisk-ufs.wks
@@ -0,0 +1,5 @@
+bootloader --ptable msdos
+
+part /boot --source rootfs --fstype=vfat --label boot --change-directory boot/
+part / --source rootfs --fstype=ext4 --label root --exclude-path boot/
+part swap --fstype=swap --label swap --size 1M
--
2.51.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [OE-core] [PATCH 2/2] ufs image class: add
2026-02-13 16:36 ` [PATCH 2/2] ufs image class: add Trevor Woerner
@ 2026-02-15 16:41 ` Mathieu Dubois-Briand
2026-02-16 2:37 ` Trevor Woerner
0 siblings, 1 reply; 4+ messages in thread
From: Mathieu Dubois-Briand @ 2026-02-15 16:41 UTC (permalink / raw)
To: twoerner, openembedded-core
On Fri Feb 13, 2026 at 5:36 PM CET, Trevor Woerner via lists.openembedded.org wrote:
> Add an image class and wks file that demonstrates generating a wic image
> with a 4096-byte sector size.
>
> Signed-off-by: Trevor Woerner <twoerner@gmail.com>
> ---
Thanks for your patch.
> +
> +IMAGE_CMD:wic.ufs () {
> + out="${IMGDEPLOYDIR}/${IMAGE_NAME}"
IMAGE_CMD:wic_ufs ?
At least I believe this is the source of this warning:
WARNING: core-image-minimal-1.0-r0 do_image_wic_ufs: Function do_image_wic_ufs doesn't exist
https://autobuilder.yoctoproject.org/valkyrie/#/builders/68/builds/3267
https://autobuilder.yoctoproject.org/valkyrie/#/builders/30/builds/3157
https://autobuilder.yoctoproject.org/valkyrie/#/builders/22/builds/3210
Thanks,
Mathieu
--
Mathieu Dubois-Briand, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [OE-core] [PATCH 2/2] ufs image class: add
2026-02-15 16:41 ` [OE-core] " Mathieu Dubois-Briand
@ 2026-02-16 2:37 ` Trevor Woerner
0 siblings, 0 replies; 4+ messages in thread
From: Trevor Woerner @ 2026-02-16 2:37 UTC (permalink / raw)
To: Mathieu Dubois-Briand; +Cc: openembedded-core
Hello!
On Sun 2026-02-15 @ 05:41:17 PM, Mathieu Dubois-Briand wrote:
> On Fri Feb 13, 2026 at 5:36 PM CET, Trevor Woerner via lists.openembedded.org wrote:
> > Add an image class and wks file that demonstrates generating a wic image
> > with a 4096-byte sector size.
> >
> > Signed-off-by: Trevor Woerner <twoerner@gmail.com>
> > ---
>
> Thanks for your patch.
>
>
> > +
> > +IMAGE_CMD:wic.ufs () {
> > + out="${IMGDEPLOYDIR}/${IMAGE_NAME}"
>
> IMAGE_CMD:wic_ufs ?
>
> At least I believe this is the source of this warning:
>
> WARNING: core-image-minimal-1.0-r0 do_image_wic_ufs: Function do_image_wic_ufs doesn't exist
>
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/68/builds/3267
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/30/builds/3157
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/22/builds/3210
Thank you for your report! I had not noticed this warning since it only
appears when wic.ufs is *not* requested (and since I was adding this
new IMAGE_FSTYPE it did not occur to me to test without it).
I thought my v2 had fixed the problem, but it didn't; please ignore.
But I believe my v3 found the problem and fixed it.
Thanks and best regards,
Trevor
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-02-16 2:37 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-13 16:36 [PATCH 1/2] wic: re-implement sector-size support Trevor Woerner
2026-02-13 16:36 ` [PATCH 2/2] ufs image class: add Trevor Woerner
2026-02-15 16:41 ` [OE-core] " Mathieu Dubois-Briand
2026-02-16 2:37 ` Trevor Woerner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox