finally:
os.remove(wks_file)
- def test_wic_sector_size(self):
- """Test generation image sector size"""
+ def test_wic_sector_size_env(self):
+ """Test generation image sector size via environment (obsolete)"""
oldpath = os.environ['PATH']
os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
# list partitions
result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
print(result.output)
- # 4 lines of output: header + 3 partition
- self.assertEqual(4, len(result.output.split('\n')))
+ # Deprecated message + 4 lines of output: header + 3 partitions
+ self.assertEqual(5, 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]),
finally:
os.environ['PATH'] = oldpath
+ 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:
+ bitbake('core-image-minimal')
+
+ with NamedTemporaryFile("w", suffix=".wks") as wks:
+ wks.writelines(
+ ['bootloader --ptable gpt\n',
+ 'part --fstype vfat --fstype vfat --label emptyfat --size 1M\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 --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))
+ self.assertEqual(1, len(images))
+
+ sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
+ # list partitions
+ result = runCmd("wic ls %s -n %s --sector-size 4096" % (images[0], sysroot))
+ print(result.output)
+ # 4 lines of output: header + 3 partitions
+ self.assertEqual(4, len(result.output.split('\n')))
+
+ # 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)
+
+ print(res.output)
+ # 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:1187839B:1048576B::emptyfat:msftdata;
+ # 2:1187840B:149270527B:148082688B:ext4:rofs-a:;
+ # 3:149270528B:297353215B:148082688B:ext4:primary:;
+ 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(4096, sector_size_logical, "Logical sector size is not 4096.")
+ self.assertEqual(4096, sector_size_physical, "Physical sector size is not 4096.")
+
+ # It is a known issue with parsed that a 4K FAT partition does
+ # not have a recognized filesystem type of *fat.
+ part_info = res.output.splitlines()[2]
+ partname = part_info.split(":")[5]
+ parttype = part_info.split(":")[6]
+ self.assertEqual('emptyfat', partname)
+ self.assertEqual('msftdata;', parttype)
+
+ part_info = res.output.splitlines()[3]
+ parttype = part_info.split(":")[4]
+ partname = part_info.split(":")[5]
+ self.assertEqual('ext4', parttype)
+ self.assertEqual('rofs-a', partname)
+
+ part_info = res.output.splitlines()[4]
+ parttype = part_info.split(":")[4]
+ partname = part_info.split(":")[5]
+ self.assertEqual('ext4', parttype)
+ self.assertEqual('primary', partname)
+
+ finally:
+ os.environ['PATH'] = oldpath
+
class Wic2(WicTestCase):
def test_bmap_short(self):
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
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
if self._partitions is None:
self._partitions = OrderedDict()
- if self.sector_size is not None:
- out = exec_cmd("export PARTED_SECTOR_SIZE=%d; %s -sm %s unit B print" % \
- (self.sector_size, self.parted, self.imagepath), True)
- else:
- out = exec_cmd("%s -sm %s unit B print" % (self.parted, self.imagepath))
+ out = exec_cmd("export PARTED_SECTOR_SIZE=%d; %s -sm %s unit B print" % \
+ (self.sector_size, self.parted, self.imagepath), True)
parttype = namedtuple("Part", "pnum start end size fstype")
splitted = out.splitlines()
if pnum not in self.partitions:
raise WicError("Partition %s is not in the image" % pnum)
part = self.partitions[pnum]
+
# check if fstype is supported
+ """
+ NOTE:
+ ^^^^
+ wic uses "parted -m ..." to determine partition types (the "-m" is used
+ so its output is easy to parse in a script). However there appears to
+ be a bug in parted whereby it is unable to identify dos/vfat partition
+ types if the sector-size is not 512 bytes. Therefore if sector-size=512
+ then accept parted's assessment of fileysystem type (including None).
+ But if sector-size!=512 then accept parted's assessment of filesystem
+ type, unless it says None, in which case assume "fat".
+ """
+ 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
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)
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))
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)
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):
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
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.
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.
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
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).
"""
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>]
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
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.
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.
Provide old get_bitbake_var API by wrapping
get_var method of BB_VARS singleton.
"""
+ if var == "WIC_SECTOR_SIZE":
+ env_val = os.environ.get("WIC_SECTOR_SIZE")
+ if env_val is not None:
+ logger.warning("DEPRECATED: Using WIC_SECTOR_SIZE from environment; prefer --sector-size to avoid surprises.")
+ return env_val
+
return BB_VARS.get_var(var, image, cache)
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.
+
+ Generate cmdline options for mkdosfs. A user can supply options
+ they want to see used via a wks file. Check that the user has
+ not supplied an "-S <logical-sector-size>" option, if they
+ have not, we define "-S <logical-sector-size>" option based on
+ the value of self.sector_size.
+ """
+ extraopts = self.mkfs_extraopts
+ for token in extraopts.split():
+ if token == '-S' or (token.startswith('-S') and token[2:].isdigit()):
+ # Nothing to do, the user already supplied a value
+ return extraopts
+
+ # Supply the default based on the partitioning sector_size
+ return extraopts + " -S %d" % (self.sector_size)
+
+ def _mkfs_ext_extraopts(self, base_opts):
+ """
+ Build mkfs.ext* extra options ensuring the CLI sector size is applied.
+
+ Generate cmdline options for mkfs.ext*. Different parts of the
+ code want to provide different default sets of ext* options for
+ mkfs. But the user can provide their own options via the wks
+ file; in which case the user options completely replace the
+ in-code default options. If the user does not specify an option
+ as long as sector-size != 512 we supply an additional
+ "-b <block-size>" argument to align the filesystem with the
+ overall block-size.
+
+ NOTE: the default sector/block size for an ext* filesystem depends on
+ a number of factors and it is generally best to allow the tools to
+ determine the sector/block size heuristically. Therefore only specify
+ the size explicitly when it is not the default. Specifying a sector-size
+ of 512, for example, for an ext4 filesystem will result in:
+ ERROR: mkfs.ext4: invalid block size - 512
+ See the mkfs.ext4 man page for details on the -b option.
+ """
+ extraopts = self.mkfs_extraopts or base_opts
+ for token in extraopts.split():
+ if token == '-b' or (token.startswith('-b') and token[2:].isdigit()):
+ # Nothing to do, the user already supplied a value
+ return extraopts
+
+ if self.sector_size != 512:
+ # Supply the default based on the partitioning sector_size
+ return extraopts + " -b %d" % (self.sector_size)
+ return extraopts
def get_extra_block_count(self, current_blocks):
"""
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)
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
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)
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,
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)
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,
logger.warn("%s Inodes (of size %d) are too small." %
(get_err_str(self), size))
break
-
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
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:
# 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
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
self.native_sysroot = native_sysroot
num_real_partitions = len([p for p in self.partitions if not p.no_table])
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)
+ 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)))
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'])
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
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
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)
+ 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)
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)
+ 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
namespace.__dict__['rootfs_dir'][key] = rootfs_dir
+def _apply_sector_size_default(args):
+ """
+ Populate args.sector_size.
+ Prefer --sector-size if given, WIC_SECTOR_SIZE if not, otherwise fall back to 512.
+ """
+ if not hasattr(args, "sector_size"):
+ return
+
+ BB_VARS.vars_dir = args.vars_dir
+ try:
+ tmp_val = get_bitbake_var("WIC_SECTOR_SIZE", args.image_name)
+ if tmp_val:
+ try:
+ env_val = int(tmp_val)
+ except ValueError:
+ raise WicError("Invalid WIC_SECTOR_SIZE value '%s'; please provide an integer or use --sector-size." % tmp_val)
+
+ logger.warning("DEPRECATED: WIC_SECTOR_SIZE is deprecated, use the --sector-size command-line argument instead.")
+ if args.sector_size is not None:
+ logger.warning("WIC_SECTOR_SIZE (%d) and --sector-size (%d) were both provided; --sector-size used.", env_val, args.sector_size)
+ else:
+ args.sector_size = env_val
+ return
+ except:
+ pass
+
+ if args.sector_size is not None:
+ return
+ args.sector_size = 512
+
+
def wic_create_subcommand(options, usage_str):
"""
Command-line handling for image creation. The real work is done
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=None,
+ help="sector size in bytes (default: 512)")
return
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=None,
+ 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",
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=None,
+ 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",
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=None,
+ 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,
if args.debug:
logger.setLevel(logging.DEBUG)
+ _apply_sector_size_default(args)
+
if "command" in vars(args):
if args.command == "help":
if args.help_topic is None: