diff options
Diffstat (limited to 'import-layers/yocto-poky/scripts/lib/wic')
20 files changed, 831 insertions, 228 deletions
diff --git a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/common.wks.inc b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/common.wks.inc index 5cf2fd1f3..89880b417 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/common.wks.inc +++ b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/common.wks.inc @@ -1,3 +1,3 @@ # This file is included into 3 canned wks files from this directory part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 -part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 +part / --source rootfs --use-uuid --fstype=ext4 --label platform --align 1024 diff --git a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg index d5a07d204..c58e74a85 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg +++ b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg @@ -12,16 +12,16 @@ DEFAULT Graphics console boot LABEL Graphics console boot KERNEL /vmlinuz -APPEND label=boot root=/dev/sda2 rootwait +APPEND label=boot rootwait LABEL Serial console boot KERNEL /vmlinuz -APPEND label=boot root=/dev/sda2 rootwait console=ttyS0,115200 +APPEND label=boot rootwait console=ttyS0,115200 LABEL Graphics console install KERNEL /vmlinuz -APPEND label=install root=/dev/sda2 rootwait +APPEND label=install rootwait LABEL Serial console install KERNEL /vmlinuz -APPEND label=install root=/dev/sda2 rootwait console=ttyS0,115200 +APPEND label=install rootwait console=ttyS0,115200 diff --git a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/qemux86-directdisk.wks b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/qemux86-directdisk.wks index db30bbced..1f8466af2 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/qemux86-directdisk.wks +++ b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/qemux86-directdisk.wks @@ -4,5 +4,5 @@ include common.wks.inc -bootloader --timeout=0 --append="vga=0 uvesafb.mode_option=640x480-32 root=/dev/sda2 rw mem=256M ip=192.168.7.2::192.168.7.1:255.255.255.0 oprofile.timer=1 rootfstype=ext4 " +bootloader --timeout=0 --append="vga=0 uvesafb.mode_option=640x480-32 rw mem=256M ip=192.168.7.2::192.168.7.1:255.255.255.0 oprofile.timer=1 rootfstype=ext4 " diff --git a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/systemd-bootdisk.wks b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/systemd-bootdisk.wks index 4bd9d6a65..95d7b97a6 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/canned-wks/systemd-bootdisk.wks +++ b/import-layers/yocto-poky/scripts/lib/wic/canned-wks/systemd-bootdisk.wks @@ -2,10 +2,10 @@ # long-description: Creates a partitioned EFI disk image that the user # can directly dd to boot media. The selected bootloader is systemd-boot. -part /boot --source bootimg-efi --sourceparams="loader=systemd-boot" --ondisk sda --label msdos --active --align 1024 +part /boot --source bootimg-efi --sourceparams="loader=systemd-boot" --ondisk sda --label msdos --active --align 1024 --use-uuid part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid -part swap --ondisk sda --size 44 --label swap1 --fstype=swap +part swap --ondisk sda --size 44 --label swap1 --fstype=swap --use-uuid bootloader --ptable gpt --timeout=5 --append="rootwait rootfstype=ext4 console=ttyS0,115200 console=tty0" diff --git a/import-layers/yocto-poky/scripts/lib/wic/engine.py b/import-layers/yocto-poky/scripts/lib/wic/engine.py index f59821fea..edcfab39e 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/engine.py +++ b/import-layers/yocto-poky/scripts/lib/wic/engine.py @@ -30,10 +30,17 @@ import logging import os +import tempfile +import json +import subprocess + +from collections import namedtuple, OrderedDict +from distutils.spawn import find_executable from wic import WicError +from wic.filemap import sparse_copy from wic.pluginbase import PluginMgr -from wic.utils.misc import get_bitbake_var +from wic.misc import get_bitbake_var, exec_cmd logger = logging.getLogger('wic') @@ -201,17 +208,18 @@ def wic_list(args, scripts_path): """ Print the list of images or source plugins. """ - if len(args) < 1: + if args.list_type is None: return False - if args == ["images"]: + if args.list_type == "images": + list_canned_images(scripts_path) return True - elif args == ["source-plugins"]: + elif args.list_type == "source-plugins": list_source_plugins() return True - elif len(args) == 2 and args[1] == "help": - wks_file = args[0] + elif len(args.help_for) == 1 and args.help_for[0] == 'help': + wks_file = args.list_type fullpath = find_canned_image(scripts_path, wks_file) if not fullpath: raise WicError("No image named %s found, exiting. " @@ -224,6 +232,306 @@ def wic_list(args, scripts_path): return False + +class Disk: + def __init__(self, imagepath, native_sysroot, fstypes=('fat', 'ext')): + self.imagepath = imagepath + self.native_sysroot = native_sysroot + self.fstypes = fstypes + self._partitions = None + self._partimages = {} + self._lsector_size = None + self._psector_size = None + self._ptable_format = None + + # find parted + self.paths = "/bin:/usr/bin:/usr/sbin:/sbin/" + if native_sysroot: + for path in self.paths.split(':'): + self.paths = "%s%s:%s" % (native_sysroot, path, self.paths) + + self.parted = find_executable("parted", self.paths) + if not self.parted: + raise WicError("Can't find executable parted") + + self.partitions = self.get_partitions() + + def __del__(self): + for path in self._partimages.values(): + os.unlink(path) + + def get_partitions(self): + if self._partitions is None: + self._partitions = OrderedDict() + out = exec_cmd("%s -sm %s unit B print" % (self.parted, self.imagepath)) + parttype = namedtuple("Part", "pnum start end size fstype") + splitted = out.splitlines() + lsector_size, psector_size, self._ptable_format = splitted[1].split(":")[3:6] + self._lsector_size = int(lsector_size) + self._psector_size = int(psector_size) + for line in splitted[2:]: + pnum, start, end, size, fstype = line.split(':')[:5] + partition = parttype(int(pnum), int(start[:-1]), int(end[:-1]), + int(size[:-1]), fstype) + self._partitions[pnum] = partition + + return self._partitions + + def __getattr__(self, name): + """Get path to the executable in a lazy way.""" + if name in ("mdir", "mcopy", "mdel", "mdeltree", "sfdisk", "e2fsck", + "resize2fs", "mkswap", "mkdosfs", "debugfs"): + aname = "_%s" % name + if aname not in self.__dict__: + setattr(self, aname, find_executable(name, self.paths)) + if aname not in self.__dict__: + raise WicError("Can't find executable {}".format(name)) + return self.__dict__[aname] + return self.__dict__[name] + + def _get_part_image(self, pnum): + if pnum not in self.partitions: + raise WicError("Partition %s is not in the image") + part = self.partitions[pnum] + # check if fstype is supported + for fstype in self.fstypes: + if part.fstype.startswith(fstype): + break + else: + raise WicError("Not supported fstype: {}".format(part.fstype)) + if pnum not in self._partimages: + tmpf = tempfile.NamedTemporaryFile(prefix="wic-part") + dst_fname = tmpf.name + tmpf.close() + sparse_copy(self.imagepath, dst_fname, skip=part.start, length=part.size) + self._partimages[pnum] = dst_fname + + return self._partimages[pnum] + + def _put_part_image(self, pnum): + """Put partition image into partitioned image.""" + sparse_copy(self._partimages[pnum], self.imagepath, + seek=self.partitions[pnum].start) + + def dir(self, pnum, path): + if self.partitions[pnum].fstype.startswith('ext'): + return exec_cmd("{} {} -R 'ls -l {}'".format(self.debugfs, + self._get_part_image(pnum), + path), as_shell=True) + else: # fat + return exec_cmd("{} -i {} ::{}".format(self.mdir, + self._get_part_image(pnum), + path)) + + def copy(self, src, pnum, path): + """Copy partition image into wic image.""" + if self.partitions[pnum].fstype.startswith('ext'): + cmd = "echo -e 'cd {}\nwrite {} {}' | {} -w {}".\ + format(path, src, os.path.basename(src), + self.debugfs, self._get_part_image(pnum)) + else: # fat + cmd = "{} -i {} -snop {} ::{}".format(self.mcopy, + self._get_part_image(pnum), + src, path) + exec_cmd(cmd, as_shell=True) + self._put_part_image(pnum) + + def remove(self, pnum, path): + """Remove files/dirs from the partition.""" + partimg = self._get_part_image(pnum) + if self.partitions[pnum].fstype.startswith('ext'): + exec_cmd("{} {} -wR 'rm {}'".format(self.debugfs, + self._get_part_image(pnum), + path), as_shell=True) + else: # fat + cmd = "{} -i {} ::{}".format(self.mdel, partimg, path) + try: + exec_cmd(cmd) + except WicError as err: + if "not found" in str(err) or "non empty" in str(err): + # mdel outputs 'File ... not found' or 'directory .. non empty" + # try to use mdeltree as path could be a directory + cmd = "{} -i {} ::{}".format(self.mdeltree, + partimg, path) + exec_cmd(cmd) + else: + raise err + self._put_part_image(pnum) + + def write(self, target, expand): + """Write disk image to the media or file.""" + def write_sfdisk_script(outf, parts): + for key, val in parts['partitiontable'].items(): + if key in ("partitions", "device", "firstlba", "lastlba"): + continue + if key == "id": + key = "label-id" + outf.write("{}: {}\n".format(key, val)) + outf.write("\n") + for part in parts['partitiontable']['partitions']: + line = '' + for name in ('attrs', 'name', 'size', 'type', 'uuid'): + if name == 'size' and part['type'] == 'f': + # don't write size for extended partition + continue + val = part.get(name) + if val: + line += '{}={}, '.format(name, val) + if line: + line = line[:-2] # strip ', ' + if part.get('bootable'): + line += ' ,bootable' + outf.write("{}\n".format(line)) + outf.flush() + + def read_ptable(path): + out = exec_cmd("{} -dJ {}".format(self.sfdisk, path)) + return json.loads(out) + + def write_ptable(parts, target): + with tempfile.NamedTemporaryFile(prefix="wic-sfdisk-", mode='w') as outf: + write_sfdisk_script(outf, parts) + cmd = "{} --no-reread {} < {} 2>/dev/null".format(self.sfdisk, target, outf.name) + try: + subprocess.check_output(cmd, shell=True) + except subprocess.CalledProcessError as err: + raise WicError("Can't run '{}' command: {}".format(cmd, err)) + + if expand is None: + sparse_copy(self.imagepath, target) + else: + # copy first sectors that may contain bootloader + sparse_copy(self.imagepath, target, length=2048 * self._lsector_size) + + # copy source partition table to the target + parts = read_ptable(self.imagepath) + write_ptable(parts, target) + + # get size of unpartitioned space + free = None + for line in exec_cmd("{} -F {}".format(self.sfdisk, target)).splitlines(): + if line.startswith("Unpartitioned space ") and line.endswith("sectors"): + free = int(line.split()[-2]) + if free is None: + raise WicError("Can't get size of unpartitioned space") + + # calculate expanded partitions sizes + sizes = {} + for num, part in enumerate(parts['partitiontable']['partitions'], 1): + if num in expand: + if expand[num] != 0: # don't resize partition if size is set to 0 + sectors = expand[num] // self._lsector_size + free -= sectors - part['size'] + part['size'] = sectors + sizes[num] = sectors + elif part['type'] != 'f': + sizes[num] = -1 + + for num, part in enumerate(parts['partitiontable']['partitions'], 1): + if sizes.get(num) == -1: + part['size'] += free // len(sizes) + + # write resized partition table to the target + write_ptable(parts, target) + + # read resized partition table + parts = read_ptable(target) + + # copy partitions content + for num, part in enumerate(parts['partitiontable']['partitions'], 1): + pnum = str(num) + fstype = self.partitions[pnum].fstype + + # copy unchanged partition + if part['size'] == self.partitions[pnum].size // self._lsector_size: + logger.info("copying unchanged partition {}".format(pnum)) + sparse_copy(self._get_part_image(pnum), target, seek=part['start'] * self._lsector_size) + continue + + # resize or re-create partitions + if fstype.startswith('ext') or fstype.startswith('fat') or \ + fstype.startswith('linux-swap'): + + partfname = None + with tempfile.NamedTemporaryFile(prefix="wic-part{}-".format(pnum)) as partf: + partfname = partf.name + + if fstype.startswith('ext'): + logger.info("resizing ext partition {}".format(pnum)) + partimg = self._get_part_image(pnum) + sparse_copy(partimg, partfname) + exec_cmd("{} -pf {}".format(self.e2fsck, partfname)) + exec_cmd("{} {} {}s".format(\ + self.resize2fs, partfname, part['size'])) + elif fstype.startswith('fat'): + logger.info("copying content of the fat partition {}".format(pnum)) + with tempfile.TemporaryDirectory(prefix='wic-fatdir-') as tmpdir: + # copy content to the temporary directory + cmd = "{} -snompi {} :: {}".format(self.mcopy, + self._get_part_image(pnum), + tmpdir) + exec_cmd(cmd) + # create new msdos partition + label = part.get("name") + label_str = "-n {}".format(label) if label else '' + + cmd = "{} {} -C {} {}".format(self.mkdosfs, label_str, partfname, + part['size']) + exec_cmd(cmd) + # copy content from the temporary directory to the new partition + cmd = "{} -snompi {} {}/* ::".format(self.mcopy, partfname, tmpdir) + exec_cmd(cmd, as_shell=True) + elif fstype.startswith('linux-swap'): + logger.info("creating swap partition {}".format(pnum)) + label = part.get("name") + label_str = "-L {}".format(label) if label else '' + uuid = part.get("uuid") + uuid_str = "-U {}".format(uuid) if uuid else '' + with open(partfname, 'w') as sparse: + os.ftruncate(sparse.fileno(), part['size'] * self._lsector_size) + exec_cmd("{} {} {} {}".format(self.mkswap, label_str, uuid_str, partfname)) + sparse_copy(partfname, target, seek=part['start'] * self._lsector_size) + os.unlink(partfname) + elif part['type'] != 'f': + logger.warn("skipping partition {}: unsupported fstype {}".format(pnum, fstype)) + +def wic_ls(args, native_sysroot): + """List contents of partitioned image or vfat partition.""" + disk = Disk(args.path.image, native_sysroot) + if not args.path.part: + if disk.partitions: + print('Num Start End Size Fstype') + for part in disk.partitions.values(): + print("{:2d} {:12d} {:12d} {:12d} {}".format(\ + part.pnum, part.start, part.end, + part.size, part.fstype)) + else: + path = args.path.path or '/' + print(disk.dir(args.path.part, path)) + +def wic_cp(args, native_sysroot): + """ + Copy local file or directory to the vfat partition of + partitioned image. + """ + disk = Disk(args.dest.image, native_sysroot) + disk.copy(args.src, args.dest.part, args.dest.path) + +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.remove(args.path.part, args.path.path) + +def wic_write(args, native_sysroot): + """ + Write image to a target device. + """ + disk = Disk(args.image, native_sysroot, ('fat', 'ext', 'swap')) + disk.write(args.target, args.expand) + def find_canned(scripts_path, file_name): """ Find a file either by its path or by name in the canned files dir. diff --git a/import-layers/yocto-poky/scripts/lib/wic/filemap.py b/import-layers/yocto-poky/scripts/lib/wic/filemap.py index 1f1aacc52..77e32b9ad 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/filemap.py +++ b/import-layers/yocto-poky/scripts/lib/wic/filemap.py @@ -34,13 +34,9 @@ def get_block_size(file_obj): Returns block size for file object 'file_obj'. Errors are indicated by the 'IOError' exception. """ - - from fcntl import ioctl - import struct - # Get the block size of the host file-system for the image file by calling # the FIGETBSZ ioctl (number 2). - binary_data = ioctl(file_obj, 2, struct.pack('I', 0)) + binary_data = fcntl.ioctl(file_obj, 2, struct.pack('I', 0)) return struct.unpack('I', binary_data)[0] class ErrorNotSupp(Exception): @@ -228,7 +224,7 @@ class FilemapSeek(_FilemapBase): try: tmp_obj = tempfile.TemporaryFile("w+", dir=directory) except IOError as err: - raise ErrorNotSupp("cannot create a temporary in \"%s\": %s" + raise ErrorNotSupp("cannot create a temporary in \"%s\": %s" \ % (directory, err)) try: @@ -530,8 +526,18 @@ def filemap(image, log=None): except ErrorNotSupp: return FilemapSeek(image, log) -def sparse_copy(src_fname, dst_fname, offset=0, skip=0, api=None): - """Efficiently copy sparse file to or into another file.""" +def sparse_copy(src_fname, dst_fname, skip=0, seek=0, + length=0, api=None): + """ + Efficiently copy sparse file to or into another file. + + src_fname: path to source file + dst_fname: path to destination file + skip: skip N bytes at thestart of src + seek: seek N bytes from the start of dst + length: read N bytes from src and write them to dst + api: FilemapFiemap or FilemapSeek object + """ if not api: api = filemap fmap = api(src_fname) @@ -539,17 +545,32 @@ def sparse_copy(src_fname, dst_fname, offset=0, skip=0, api=None): dst_file = open(dst_fname, 'r+b') except IOError: dst_file = open(dst_fname, 'wb') - dst_file.truncate(os.path.getsize(src_fname)) + if length: + dst_size = length + seek + else: + dst_size = os.path.getsize(src_fname) + seek - skip + dst_file.truncate(dst_size) + written = 0 for first, last in fmap.get_mapped_ranges(0, fmap.blocks_cnt): start = first * fmap.block_size end = (last + 1) * fmap.block_size + if skip >= end: + continue + if start < skip < end: - fmap._f_image.seek(skip, os.SEEK_SET) - else: - fmap._f_image.seek(start, os.SEEK_SET) - dst_file.seek(offset + start, os.SEEK_SET) + start = skip + + fmap._f_image.seek(start, os.SEEK_SET) + + written += start - skip - written + if length and written >= length: + dst_file.seek(seek + length, os.SEEK_SET) + dst_file.close() + return + + dst_file.seek(seek + start - skip, os.SEEK_SET) chunk_size = 1024 * 1024 to_read = end - start @@ -558,7 +579,14 @@ def sparse_copy(src_fname, dst_fname, offset=0, skip=0, api=None): while read < to_read: if read + chunk_size > to_read: chunk_size = to_read - read - chunk = fmap._f_image.read(chunk_size) + size = chunk_size + if length and written + size > length: + size = length - written + chunk = fmap._f_image.read(size) dst_file.write(chunk) - read += chunk_size + read += size + written += size + if written == length: + dst_file.close() + return dst_file.close() diff --git a/import-layers/yocto-poky/scripts/lib/wic/help.py b/import-layers/yocto-poky/scripts/lib/wic/help.py index d6e027d25..2ac45e052 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/help.py +++ b/import-layers/yocto-poky/scripts/lib/wic/help.py @@ -56,7 +56,7 @@ def wic_help(args, usage_str, subcommands): """ Subcommand help dispatcher. """ - if len(args) == 1 or not display_help(args[1], subcommands): + if args.help_topic == None or not display_help(args.help_topic, subcommands): print(usage_str) @@ -82,19 +82,20 @@ def invoke_subcommand(args, parser, main_command_usage, subcommands): Dispatch to subcommand handler borrowed from combo-layer. Should use argparse, but has to work in 2.6. """ - if not args: + if not args.command: logger.error("No subcommand specified, exiting") parser.print_help() return 1 - elif args[0] == "help": + elif args.command == "help": wic_help(args, main_command_usage, subcommands) - elif args[0] not in subcommands: - logger.error("Unsupported subcommand %s, exiting\n", args[0]) + elif args.command not in subcommands: + logger.error("Unsupported subcommand %s, exiting\n", args.command) parser.print_help() return 1 else: - usage = subcommands.get(args[0], subcommand_error)[1] - subcommands.get(args[0], subcommand_error)[0](args[1:], usage) + subcmd = subcommands.get(args.command, subcommand_error) + usage = subcmd[1] + subcmd[0](args, usage) ## @@ -130,10 +131,10 @@ wic_create_usage = """ Create a new OpenEmbedded image usage: wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>] - [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] [-e | --image-name] [-s, --skip-build-check] [-D, --debug] [-r, --rootfs-dir] [-b, --bootimg-dir] [-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs] + [-c, --compress-with] [-m, --bmap] This command creates an OpenEmbedded image based on the 'OE kickstart commands' found in the <wks file>. @@ -154,7 +155,7 @@ SYNOPSIS [-e | --image-name] [-s, --skip-build-check] [-D, --debug] [-r, --rootfs-dir] [-b, --bootimg-dir] [-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs] - [-c, --compress-with] [-m, --bmap] + [-c, --compress-with] [-m, --bmap] [--no-fstab-update] DESCRIPTION This command creates an OpenEmbedded image based on the 'OE @@ -226,6 +227,11 @@ DESCRIPTION The -m option is used to produce .bmap file for the image. This file can be used to flash image using bmaptool utility. + + The --no-fstab-update option is used to doesn't change fstab file. When + using this option the final fstab file will be same that in rootfs and + wic doesn't update file, e.g adding a new mount point. User can control + the fstab file content in base-files recipe. """ wic_list_usage = """ @@ -283,6 +289,230 @@ DESCRIPTION details. """ +wic_ls_usage = """ + + List content of a partitioned image + + usage: wic ls <image>[:<partition>[<path>]] [--native-sysroot <path>] + + This command outputs either list of image partitions or directory contents + of vfat and ext* partitions. + + See 'wic help ls' for more detailed instructions. + +""" + +wic_ls_help = """ + +NAME + wic ls - List contents of partitioned image or partition + +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> + +DESCRIPTION + This command lists either partitions of the image or directory contents + of vfat or ext* partitions. + + The first form it lists partitions of the image. + For example: + $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic + Num Start End Size Fstype + 1 1048576 24438783 23390208 fat16 + 2 25165824 50315263 25149440 ext4 + + Second and third form list directory content of the partition: + $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 + Volume in drive : is boot + Volume Serial Number is 2DF2-5F02 + Directory for ::/ + + efi <DIR> 2017-05-11 10:54 + startup nsh 26 2017-05-11 10:54 + vmlinuz 6922288 2017-05-11 10:54 + 3 files 6 922 314 bytes + 15 818 752 bytes free + + + $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/EFI/boot/ + Volume in drive : is boot + Volume Serial Number is 2DF2-5F02 + Directory for ::/EFI/boot + + . <DIR> 2017-05-11 10:54 + .. <DIR> 2017-05-11 10:54 + grub cfg 679 2017-05-11 10:54 + bootx64 efi 571392 2017-05-11 10:54 + 4 files 572 071 bytes + 15 818 752 bytes free + + The -n option is used to specify the path to the native sysroot + containing the tools(parted and mtools) to use. + +""" + +wic_cp_usage = """ + + Copy files and directories to the vfat or ext* partition + + usage: wic cp <src> <image>:<partition>[<path>] [--native-sysroot <path>] + + This command copies local files or directories to the vfat or ext* partitions +of partitioned image. + + See 'wic help cp' for more detailed instructions. + +""" + +wic_cp_help = """ + +NAME + wic cp - copy files and directories to the vfat or ext* partitions + +SYNOPSIS + wic cp <src> <image>:<partition> + wic cp <src> <image>:<partition><path> + wic cp <src> <image>:<partition><path> --native-sysroot <path> + +DESCRIPTION + This command copies files and directories to the vfat or ext* partition of + the partitioned image. + + The first form of it copies file or directory to the root directory of + the partition: + $ wic cp test.wks tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 + $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 + Volume in drive : is boot + Volume Serial Number is DB4C-FD4C + Directory for ::/ + + efi <DIR> 2017-05-24 18:15 + loader <DIR> 2017-05-24 18:15 + startup nsh 26 2017-05-24 18:15 + vmlinuz 6926384 2017-05-24 18:15 + test wks 628 2017-05-24 21:22 + 5 files 6 927 038 bytes + 15 677 440 bytes free + + The second form of the command copies file or directory to the specified directory + on the partition: + $ wic cp test tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/efi/ + $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/efi/ + Volume in drive : is boot + Volume Serial Number is DB4C-FD4C + Directory for ::/efi + + . <DIR> 2017-05-24 18:15 + .. <DIR> 2017-05-24 18:15 + boot <DIR> 2017-05-24 18:15 + test <DIR> 2017-05-24 21:27 + 4 files 0 bytes + 15 675 392 bytes free + + The -n option is used to specify the path to the native sysroot + containing the tools(parted and mtools) to use. +""" + +wic_rm_usage = """ + + Remove files or directories from the vfat or ext* partitions + + usage: wic rm <image>:<partition><path> [--native-sysroot <path>] + + This command removes files or directories from the vfat or ext* partitions of + the partitioned image. + + See 'wic help rm' for more detailed instructions. + +""" + +wic_rm_help = """ + +NAME + wic rm - remove files or directories from the vfat or ext* partitions + +SYNOPSIS + wic rm <src> <image>:<partition><path> + wic rm <src> <image>:<partition><path> --native-sysroot <path> + +DESCRIPTION + This command removes files or directories from the vfat or ext* partition of the + partitioned image: + + $ wic ls ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 + Volume in drive : is boot + Volume Serial Number is 11D0-DE21 + Directory for ::/ + + libcom32 c32 186500 2017-06-02 15:15 + libutil c32 24148 2017-06-02 15:15 + syslinux cfg 209 2017-06-02 15:15 + vesamenu c32 27104 2017-06-02 15:15 + vmlinuz 6926384 2017-06-02 15:15 + 5 files 7 164 345 bytes + 16 582 656 bytes free + + $ wic rm ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/libutil.c32 + + $ wic ls ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 + Volume in drive : is boot + Volume Serial Number is 11D0-DE21 + Directory for ::/ + + libcom32 c32 186500 2017-06-02 15:15 + syslinux cfg 209 2017-06-02 15:15 + vesamenu c32 27104 2017-06-02 15:15 + vmlinuz 6926384 2017-06-02 15:15 + 4 files 7 140 197 bytes + 16 607 232 bytes free + + The -n option is used to specify the path to the native sysroot + containing the tools(parted and mtools) to use. +""" + +wic_write_usage = """ + + Write image to a device + + usage: wic write <image> <target device> [--expand [rules]] [--native-sysroot <path>] + + This command writes partitioned image to a target device (USB stick, SD card etc). + + See 'wic help write' for more detailed instructions. + +""" + +wic_write_help = """ + +NAME + wic write - write an image to a device + +SYNOPSIS + wic write <image> <target> + wic write <image> <target> --expand auto + wic write <image> <target> --expand 1:100M-2:300M + wic write <image> <target> --native-sysroot <path> + +DESCRIPTION + This command writes an image to a target device (USB stick, SD card etc) + + $ wic write ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic /dev/sdb + + The --expand option is used to resize image partitions. + --expand auto expands partitions to occupy all free space available on the target device. + It's also possible to specify expansion rules in a format + <partition>:<size>[-<partition>:<size>...] for one or more partitions. + Specifying size 0 will keep partition unmodified. + Note: Resizing boot partition can result in non-bootable image for non-EFI images. It is + recommended to use size 0 for boot partition to keep image bootable. + + The --native-sysroot option is used to specify the path to the native sysroot + containing the tools(parted, resize2fs) to use. +""" + wic_plugins_help = """ NAME @@ -740,6 +970,8 @@ DESCRIPTION This option cannot be used with --fixed-size option. + --part-name: This option is specific to wic. It specifies name for GPT partitions. + --part-type: This option is specific to wic. It specifies partition type GUID for GPT partitions. List of partition type GUIDS can be found here: @@ -758,6 +990,12 @@ DESCRIPTION for the harware that requires non-default partition system ids. The parameter in one byte long hex number either with 0x prefix or without it. + --mkfs-extraopts: This option specifies extra options to pass to mkfs utility. + NOTE, that wic uses default options for some filesystems, for example + '-S 512' for mkfs.fat or '-F -i 8192' for mkfs.ext. Those options will + not take effect when --mkfs-extraopts is used. This should be taken into + account when using --mkfs-extraopts. + * bootloader This command allows the user to specify various bootloader @@ -795,3 +1033,11 @@ DESCRIPTION .wks files. """ + +wic_help_help = """ +NAME + wic help - display a help topic + +DESCRIPTION + Specify a help topic to display it. Topics are shown above. +""" diff --git a/import-layers/yocto-poky/scripts/lib/wic/ksparser.py b/import-layers/yocto-poky/scripts/lib/wic/ksparser.py index d026caad0..7850e81d2 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/ksparser.py +++ b/import-layers/yocto-poky/scripts/lib/wic/ksparser.py @@ -114,7 +114,7 @@ def systemidtype(arg): return arg class KickStart(): - """"Kickstart parser implementation.""" + """Kickstart parser implementation.""" DEFAULT_EXTRA_SPACE = 10*1024 DEFAULT_OVERHEAD_FACTOR = 1.3 @@ -139,10 +139,12 @@ class KickStart(): part.add_argument('--fstype', default='vfat', choices=('ext2', 'ext3', 'ext4', 'btrfs', 'squashfs', 'vfat', 'msdos', 'swap')) + part.add_argument('--mkfs-extraopts', default='') part.add_argument('--label') part.add_argument('--no-table', action='store_true') part.add_argument('--ondisk', '--ondrive', dest='disk', default='sda') part.add_argument("--overhead-factor", type=overheadtype) + part.add_argument('--part-name') part.add_argument('--part-type') part.add_argument('--rootfs-dir') diff --git a/import-layers/yocto-poky/scripts/lib/wic/utils/misc.py b/import-layers/yocto-poky/scripts/lib/wic/misc.py index 37e0ad6a3..ee888b478 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/utils/misc.py +++ b/import-layers/yocto-poky/scripts/lib/wic/misc.py @@ -29,12 +29,12 @@ import logging import os import re +import subprocess from collections import defaultdict from distutils import spawn from wic import WicError -from wic.utils import runner logger = logging.getLogger('wic') @@ -43,6 +43,9 @@ NATIVE_RECIPES = {"bmaptool": "bmap-tools", "grub-mkimage": "grub-efi", "isohybrid": "syslinux", "mcopy": "mtools", + "mdel" : "mtools", + "mdeltree" : "mtools", + "mdir" : "mtools", "mkdosfs": "dosfstools", "mkisofs": "cdrtools", "mkfs.btrfs": "btrfs-tools", @@ -52,13 +55,46 @@ NATIVE_RECIPES = {"bmaptool": "bmap-tools", "mkfs.vfat": "dosfstools", "mksquashfs": "squashfs-tools", "mkswap": "util-linux", - "mmd": "syslinux", + "mmd": "mtools", "parted": "parted", "sfdisk": "util-linux", "sgdisk": "gptfdisk", "syslinux": "syslinux" } +def runtool(cmdln_or_args): + """ wrapper for most of the subprocess calls + input: + cmdln_or_args: can be both args and cmdln str (shell=True) + return: + rc, output + """ + if isinstance(cmdln_or_args, list): + cmd = cmdln_or_args[0] + shell = False + else: + import shlex + cmd = shlex.split(cmdln_or_args)[0] + shell = True + + sout = subprocess.PIPE + serr = subprocess.STDOUT + + try: + process = subprocess.Popen(cmdln_or_args, stdout=sout, + stderr=serr, shell=shell) + sout, serr = process.communicate() + # combine stdout and stderr, filter None out and decode + out = ''.join([out.decode('utf-8') for out in [sout, serr] if out]) + except OSError as err: + if err.errno == 2: + # [Errno 2] No such file or directory + raise WicError('Cannot run command: %s, lost dependency?' % cmd) + else: + raise # relay + + return process.returncode, out + def _exec_cmd(cmd_and_args, as_shell=False): """ Execute command, catching stderr, stdout @@ -70,9 +106,9 @@ def _exec_cmd(cmd_and_args, as_shell=False): logger.debug(args) if as_shell: - ret, out = runner.runtool(cmd_and_args) + ret, out = runtool(cmd_and_args) else: - ret, out = runner.runtool(args) + ret, out = runtool(args) out = out.strip() if ret != 0: raise WicError("_exec_cmd: %s returned '%s' instead of 0\noutput: %s" % \ @@ -106,14 +142,11 @@ def exec_native_cmd(cmd_and_args, native_sysroot, pseudo=""): if pseudo: cmd_and_args = pseudo + cmd_and_args - wtools_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", "wic-tools") + native_paths = "%s/sbin:%s/usr/sbin:%s/usr/bin" % \ + (native_sysroot, native_sysroot, native_sysroot) - native_paths = \ - "%s/sbin:%s/usr/sbin:%s/usr/bin:%s/sbin:%s/usr/sbin:%s/usr/bin" % \ - (wtools_sysroot, wtools_sysroot, wtools_sysroot, - native_sysroot, native_sysroot, native_sysroot) native_cmd_and_args = "export PATH=%s:$PATH;%s" % \ - (native_paths, cmd_and_args) + (native_paths, cmd_and_args) logger.debug("exec_native_cmd: %s", native_cmd_and_args) # If the command isn't in the native sysroot say we failed. @@ -131,8 +164,8 @@ def exec_native_cmd(cmd_and_args, native_sysroot, pseudo=""): "was not found (see details above).\n\n" % prog recipe = NATIVE_RECIPES.get(prog) if recipe: - msg += "Please make sure wic-tools have %s-native in its DEPENDS, bake it with 'bitbake wic-tools' "\ - "and try again.\n" % recipe + msg += "Please make sure wic-tools have %s-native in its DEPENDS, "\ + "build it with 'bitbake wic-tools' and try again.\n" % recipe else: msg += "Wic failed to find a recipe to build native %s. Please "\ "file a bug against wic.\n" % prog diff --git a/import-layers/yocto-poky/scripts/lib/wic/partition.py b/import-layers/yocto-poky/scripts/lib/wic/partition.py index 939e66731..84fe85d62 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/partition.py +++ b/import-layers/yocto-poky/scripts/lib/wic/partition.py @@ -26,10 +26,9 @@ import logging import os -import tempfile from wic import WicError -from wic.utils.misc import exec_cmd, exec_native_cmd, get_bitbake_var +from wic.misc import exec_cmd, exec_native_cmd, get_bitbake_var from wic.pluginbase import PluginMgr logger = logging.getLogger('wic') @@ -47,10 +46,12 @@ class Partition(): self.fsopts = args.fsopts self.fstype = args.fstype self.label = args.label + self.mkfs_extraopts = args.mkfs_extraopts self.mountpoint = args.mountpoint self.no_table = args.no_table self.num = None self.overhead_factor = args.overhead_factor + self.part_name = args.part_name self.part_type = args.part_type self.rootfs_dir = args.rootfs_dir self.size = args.size @@ -205,7 +206,7 @@ class Partition(): """ p_prefix = os.environ.get("PSEUDO_PREFIX", "%s/usr" % native_sysroot) p_localstatedir = os.environ.get("PSEUDO_LOCALSTATEDIR", - "%s/../pseudo" % rootfs_dir) + "%s/../pseudo" % get_bitbake_var("IMAGE_ROOTFS")) p_passwd = os.environ.get("PSEUDO_PASSWD", rootfs_dir) p_nosymlinkexp = os.environ.get("PSEUDO_NOSYMLINKEXP", "1") pseudo = "export PSEUDO_PREFIX=%s;" % p_prefix @@ -257,14 +258,14 @@ class Partition(): with open(rootfs, 'w') as sparse: os.ftruncate(sparse.fileno(), rootfs_size * 1024) - extra_imagecmd = "-i 8192" + extraopts = self.mkfs_extraopts or "-F -i 8192" label_str = "" if self.label: label_str = "-L %s" % self.label - mkfs_cmd = "mkfs.%s -F %s %s %s -d %s" % \ - (self.fstype, extra_imagecmd, rootfs, label_str, rootfs_dir) + mkfs_cmd = "mkfs.%s %s %s %s -d %s" % \ + (self.fstype, extraopts, rootfs, label_str, rootfs_dir) exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo) mkfs_cmd = "fsck.%s -pvfD %s" % (self.fstype, rootfs) @@ -290,8 +291,9 @@ class Partition(): if self.label: label_str = "-L %s" % self.label - mkfs_cmd = "mkfs.%s -b %d -r %s %s %s" % \ - (self.fstype, rootfs_size * 1024, rootfs_dir, label_str, rootfs) + mkfs_cmd = "mkfs.%s -b %d -r %s %s %s %s" % \ + (self.fstype, rootfs_size * 1024, rootfs_dir, label_str, + self.mkfs_extraopts, rootfs) exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo) def prepare_rootfs_msdos(self, rootfs, oe_builddir, rootfs_dir, @@ -313,8 +315,10 @@ class Partition(): if self.fstype == 'msdos': size_str = "-F 16" # FAT 16 - dosfs_cmd = "mkdosfs %s -S 512 %s -C %s %d" % (label_str, size_str, - rootfs, rootfs_size) + extraopts = self.mkfs_extraopts or '-S 512' + + dosfs_cmd = "mkdosfs %s %s %s -C %s %d" % \ + (label_str, size_str, extraopts, rootfs, rootfs_size) exec_native_cmd(dosfs_cmd, native_sysroot) mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (rootfs, rootfs_dir) @@ -330,8 +334,9 @@ class Partition(): """ Prepare content for a squashfs rootfs partition. """ - squashfs_cmd = "mksquashfs %s %s -noappend" % \ - (rootfs_dir, rootfs) + extraopts = self.mkfs_extraopts or '-noappend' + squashfs_cmd = "mksquashfs %s %s %s" % \ + (rootfs_dir, rootfs, extraopts) exec_native_cmd(squashfs_cmd, native_sysroot, pseudo=pseudo) def prepare_empty_partition_ext(self, rootfs, oe_builddir, @@ -343,14 +348,14 @@ class Partition(): with open(rootfs, 'w') as sparse: os.ftruncate(sparse.fileno(), size * 1024) - extra_imagecmd = "-i 8192" + extraopts = self.mkfs_extraopts or "-i 8192" label_str = "" if self.label: label_str = "-L %s" % self.label mkfs_cmd = "mkfs.%s -F %s %s %s" % \ - (self.fstype, extra_imagecmd, label_str, rootfs) + (self.fstype, extraopts, label_str, rootfs) exec_native_cmd(mkfs_cmd, native_sysroot) def prepare_empty_partition_btrfs(self, rootfs, oe_builddir, @@ -366,8 +371,9 @@ class Partition(): if self.label: label_str = "-L %s" % self.label - mkfs_cmd = "mkfs.%s -b %d %s %s" % \ - (self.fstype, self.size * 1024, label_str, rootfs) + mkfs_cmd = "mkfs.%s -b %d %s %s %s" % \ + (self.fstype, self.size * 1024, label_str, + self.mkfs_extraopts, rootfs) exec_native_cmd(mkfs_cmd, native_sysroot) def prepare_empty_partition_msdos(self, rootfs, oe_builddir, @@ -385,8 +391,11 @@ class Partition(): if self.fstype == 'msdos': size_str = "-F 16" # FAT 16 - dosfs_cmd = "mkdosfs %s -S 512 %s -C %s %d" % (label_str, size_str, - rootfs, blocks) + extraopts = self.mkfs_extraopts or '-S 512' + + dosfs_cmd = "mkdosfs %s %s %s -C %s %d" % \ + (label_str, extraopts, size_str, rootfs, blocks) + exec_native_cmd(dosfs_cmd, native_sysroot) chmod_cmd = "chmod 644 %s" % rootfs diff --git a/import-layers/yocto-poky/scripts/lib/wic/pluginbase.py b/import-layers/yocto-poky/scripts/lib/wic/pluginbase.py index fb3d179c2..c009820ad 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/pluginbase.py +++ b/import-layers/yocto-poky/scripts/lib/wic/pluginbase.py @@ -24,7 +24,7 @@ from collections import defaultdict from importlib.machinery import SourceFileLoader from wic import WicError -from wic.utils.misc import get_bitbake_var +from wic.misc import get_bitbake_var PLUGIN_TYPES = ["imager", "source"] @@ -137,4 +137,3 @@ class SourcePlugin(metaclass=PluginMeta): 'prepares' the partition to be incorporated into the image. """ logger.debug("SourcePlugin: do_prepare_partition: part: %s", part) - diff --git a/import-layers/yocto-poky/scripts/lib/wic/plugins/imager/direct.py b/import-layers/yocto-poky/scripts/lib/wic/plugins/imager/direct.py index f2e612733..da1c06106 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/plugins/imager/direct.py +++ b/import-layers/yocto-poky/scripts/lib/wic/plugins/imager/direct.py @@ -26,17 +26,20 @@ import logging import os +import random import shutil import tempfile import uuid from time import strftime +from oe.path import copyhardlinktree + from wic import WicError from wic.filemap import sparse_copy from wic.ksparser import KickStart, KickStartError from wic.pluginbase import PluginMgr, ImagerPlugin -from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd +from wic.misc import get_bitbake_var, exec_cmd, exec_native_cmd logger = logging.getLogger('wic') @@ -68,6 +71,7 @@ class DirectPlugin(ImagerPlugin): self.outdir = options.outdir self.compressor = options.compressor self.bmap = options.bmap + self.no_fstab_update = options.no_fstab_update self.name = "%s-%s" % (os.path.splitext(os.path.basename(wks_file))[0], strftime("%Y%m%d%H%M")) @@ -115,24 +119,33 @@ class DirectPlugin(ImagerPlugin): fstab_lines = fstab.readlines() if self._update_fstab(fstab_lines, self.parts): - shutil.copyfile(fstab_path, fstab_path + ".orig") + # copy rootfs dir to workdir to update fstab + # as rootfs can be used by other tasks and can't be modified + new_rootfs = os.path.realpath(os.path.join(self.workdir, "rootfs_copy")) + copyhardlinktree(image_rootfs, new_rootfs) + fstab_path = os.path.join(new_rootfs, 'etc/fstab') + + os.unlink(fstab_path) with open(fstab_path, "w") as fstab: fstab.writelines(fstab_lines) - return fstab_path + return new_rootfs def _update_fstab(self, fstab_lines, parts): """Assume partition order same as in wks""" updated = False for part in parts: if not part.realnum or not part.mountpoint \ - or part.mountpoint in ("/", "/boot"): + or part.mountpoint == "/": continue - # mmc device partitions are named mmcblk0p1, mmcblk0p2.. - prefix = 'p' if part.disk.startswith('mmcblk') else '' - device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum) + if part.use_uuid: + device_name = "PARTUUID=%s" % part.uuid + else: + # mmc device partitions are named mmcblk0p1, mmcblk0p2.. + prefix = 'p' if part.disk.startswith('mmcblk') else '' + device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum) opts = part.fsopts if part.fsopts else "defaults" line = "\t".join([device_name, part.mountpoint, part.fstype, @@ -156,7 +169,13 @@ class DirectPlugin(ImagerPlugin): filesystems from the artifacts directly and combine them into a partitioned image. """ - fstab_path = self._write_fstab(self.rootfs_dir.get("ROOTFS_DIR")) + if self.no_fstab_update: + new_rootfs = None + else: + new_rootfs = self._write_fstab(self.rootfs_dir.get("ROOTFS_DIR")) + if new_rootfs: + # rootfs was copied to update fstab + self.rootfs_dir['ROOTFS_DIR'] = new_rootfs for part in self.parts: # get rootfs size from bitbake variable if it's not set in .ks file @@ -173,10 +192,6 @@ class DirectPlugin(ImagerPlugin): part.size = int(round(float(rsize_bb))) self._image.prepare(self) - - if fstab_path: - shutil.move(fstab_path + ".orig", fstab_path) - self._image.layout_partitions() self._image.create() @@ -205,8 +220,10 @@ class DirectPlugin(ImagerPlugin): # Generate .bmap if self.bmap: logger.debug("Generating bmap file for %s", disk_name) - exec_native_cmd("bmaptool create %s -o %s.bmap" % (full_path, full_path), - self.native_sysroot) + python = os.path.join(self.native_sysroot, 'usr/bin/python3-native/python3') + bmaptool = os.path.join(self.native_sysroot, 'usr/bin/bmaptool') + exec_native_cmd("%s %s create %s -o %s.bmap" % \ + (python, bmaptool, full_path, full_path), self.native_sysroot) # Compress the image if self.compressor: logger.debug("Compressing disk %s with %s", disk_name, self.compressor) @@ -296,7 +313,7 @@ class PartitionedImage(): # all partitions (in bytes) self.ptable_format = ptable_format # Partition table format # Disk system identifier - self.identifier = int.from_bytes(os.urandom(4), 'little') + self.identifier = random.SystemRandom().randint(1, 0xffffffff) self.partitions = partitions self.partimages = [] @@ -312,7 +329,7 @@ class PartitionedImage(): part.realnum = 0 else: realnum += 1 - if self.ptable_format == 'msdos' and realnum > 3: + if self.ptable_format == 'msdos' and realnum > 3 and len(partitions) > 4: part.realnum = realnum + 1 continue part.realnum = realnum @@ -352,6 +369,10 @@ class PartitionedImage(): for num in range(len(self.partitions)): part = self.partitions[num] + if self.ptable_format == 'msdos' and part.part_name: + raise WicError("setting custom partition name is not " \ + "implemented for msdos partitions") + if self.ptable_format == 'msdos' and part.part_type: # The --part-type can also be implemented for MBR partitions, # in which case it would map to the 1-byte "partition type" @@ -505,6 +526,13 @@ class PartitionedImage(): self._create_partition(self.path, part.type, parted_fs_type, part.start, part.size_sec) + if part.part_name: + logger.debug("partition %d: set name to %s", + part.num, part.part_name) + exec_native_cmd("sgdisk --change-name=%d:%s %s" % \ + (part.num, part.part_name, + self.path), self.native_sysroot) + if part.part_type: logger.debug("partition %d: set type UID to %s", part.num, part.part_type) @@ -550,7 +578,7 @@ class PartitionedImage(): source = part.source_file if source: # install source_file contents into a partition - sparse_copy(source, self.path, part.start * self.sector_size) + sparse_copy(source, self.path, seek=part.start * self.sector_size) logger.debug("Installed %s in partition %d, sectors %d-%d, " "size %d sectors", source, part.num, part.start, diff --git a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-efi.py b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-efi.py index 9879cb9fc..4c4f36a32 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-efi.py +++ b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-efi.py @@ -31,8 +31,8 @@ import shutil from wic import WicError from wic.engine import get_custom_config from wic.pluginbase import SourcePlugin -from wic.utils.misc import (exec_cmd, exec_native_cmd, get_bitbake_var, - BOOTDD_EXTRA_SPACE) +from wic.misc import (exec_cmd, exec_native_cmd, + get_bitbake_var, BOOTDD_EXTRA_SPACE) logger = logging.getLogger('wic') diff --git a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-partition.py b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-partition.py index 13fddbd47..67e5498d5 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-partition.py +++ b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-partition.py @@ -31,7 +31,7 @@ from glob import glob from wic import WicError from wic.pluginbase import SourcePlugin -from wic.utils.misc import exec_cmd, get_bitbake_var +from wic.misc import exec_cmd, get_bitbake_var logger = logging.getLogger('wic') @@ -54,7 +54,7 @@ class BootimgPartitionPlugin(SourcePlugin): - sets up a vfat partition - copies all files listed in IMAGE_BOOT_FILES variable """ - hdddir = "%s/boot" % cr_workdir + hdddir = "%s/boot.%d" % (cr_workdir, part.lineno) install_cmd = "install -d %s" % hdddir exec_cmd(install_cmd) @@ -65,10 +65,19 @@ class BootimgPartitionPlugin(SourcePlugin): logger.debug('Kernel dir: %s', bootimg_dir) - boot_files = get_bitbake_var("IMAGE_BOOT_FILES") + boot_files = None + for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)): + if fmt: + var = fmt % id + else: + var = "" + + boot_files = get_bitbake_var("IMAGE_BOOT_FILES" + var) + if boot_files is not None: + break - if not boot_files: - raise WicError('No boot files defined, IMAGE_BOOT_FILES unset') + if boot_files is None: + raise WicError('No boot files defined, IMAGE_BOOT_FILES unset for entry #%d' % part.lineno) logger.debug('Boot files: %s', boot_files) diff --git a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-pcbios.py b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-pcbios.py index 5890c1267..56da468fb 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-pcbios.py +++ b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/bootimg-pcbios.py @@ -29,10 +29,9 @@ import os from wic import WicError from wic.engine import get_custom_config -from wic.utils import runner from wic.pluginbase import SourcePlugin -from wic.utils.misc import (exec_cmd, exec_native_cmd, - get_bitbake_var, BOOTDD_EXTRA_SPACE) +from wic.misc import (exec_cmd, exec_native_cmd, + get_bitbake_var, BOOTDD_EXTRA_SPACE) logger = logging.getLogger('wic') @@ -46,10 +45,9 @@ class BootimgPcbiosPlugin(SourcePlugin): @classmethod def _get_bootimg_dir(cls, bootimg_dir, dirname): """ - Check if dirname exists in default bootimg_dir or - in wic-tools STAGING_DIR. + Check if dirname exists in default bootimg_dir or in STAGING_DIR. """ - for result in (bootimg_dir, get_bitbake_var("STAGING_DATADIR", "wic-tools")): + for result in (bootimg_dir, get_bitbake_var("STAGING_DATADIR")): if os.path.exists("%s/%s" % (result, dirname)): return result @@ -186,7 +184,7 @@ class BootimgPcbiosPlugin(SourcePlugin): extra_blocks, part.mountpoint, blocks) # dosfs image, created by mkdosfs - bootimg = "%s/boot.img" % cr_workdir + bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno) dosfs_cmd = "mkdosfs -n boot -S 512 -C %s %d" % (bootimg, blocks) exec_native_cmd(dosfs_cmd, native_sysroot) diff --git a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/isoimage-isohybrid.py b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/isoimage-isohybrid.py index 1ceba62be..d6bd3bff7 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/isoimage-isohybrid.py +++ b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/isoimage-isohybrid.py @@ -29,7 +29,7 @@ import shutil from wic import WicError from wic.engine import get_custom_config from wic.pluginbase import SourcePlugin -from wic.utils.misc import exec_cmd, exec_native_cmd, get_bitbake_var +from wic.misc import exec_cmd, exec_native_cmd, get_bitbake_var logger = logging.getLogger('wic') @@ -95,7 +95,7 @@ class IsoImagePlugin(SourcePlugin): cfg.write(syslinux_conf) @classmethod - def do_configure_grubefi(cls, part, creator, cr_workdir): + def do_configure_grubefi(cls, part, creator, target_dir): """ Create loader-specific (grub-efi) config """ @@ -109,7 +109,7 @@ class IsoImagePlugin(SourcePlugin): raise WicError("configfile is specified " "but failed to get it from %s", configfile) else: - splash = os.path.join(cr_workdir, "EFI/boot/splash.jpg") + splash = os.path.join(target_dir, "splash.jpg") if os.path.exists(splash): splashline = "menu background splash.jpg" else: @@ -137,9 +137,10 @@ class IsoImagePlugin(SourcePlugin): if splashline: grubefi_conf += "%s\n" % splashline - logger.debug("Writing grubefi config %s/EFI/BOOT/grub.cfg", cr_workdir) + cfg_path = os.path.join(target_dir, "grub.cfg") + logger.debug("Writing grubefi config %s", cfg_path) - with open("%s/EFI/BOOT/grub.cfg" % cr_workdir, "w") as cfg: + with open(cfg_path, "w") as cfg: cfg.write(grubefi_conf) @staticmethod @@ -162,13 +163,14 @@ class IsoImagePlugin(SourcePlugin): if not image_type: raise WicError("Couldn't find INITRAMFS_FSTYPES, exiting.") - target_arch = get_bitbake_var("TRANSLATED_TARGET_ARCH") - if not target_arch: - raise WicError("Couldn't find TRANSLATED_TARGET_ARCH, exiting.") + machine = os.path.basename(initrd_dir) - initrd = glob.glob('%s/%s*%s.%s' % (initrd_dir, image_name, target_arch, image_type))[0] + pattern = '%s/%s*%s.%s' % (initrd_dir, image_name, machine, image_type) + files = glob.glob(pattern) + if files: + initrd = files[0] - if not os.path.exists(initrd): + if not initrd or not os.path.exists(initrd): # Create initrd from rootfs directory initrd = "%s/initrd.cpio.gz" % cr_workdir initrd_dir = "%s/INITRD" % cr_workdir @@ -206,8 +208,8 @@ class IsoImagePlugin(SourcePlugin): """ isodir = "%s/ISO/" % cr_workdir - if os.path.exists(cr_workdir): - shutil.rmtree(cr_workdir) + if os.path.exists(isodir): + shutil.rmtree(isodir) install_cmd = "install -d %s " % isodir exec_cmd(install_cmd) @@ -312,20 +314,13 @@ class IsoImagePlugin(SourcePlugin): #Create bootloader for efi boot try: - if source_params['loader'] == 'grub-efi': - # Builds grub.cfg if ISODIR didn't exist or - # didn't contains grub.cfg - bootimg_dir = img_iso_dir - if not os.path.exists("%s/EFI/BOOT" % bootimg_dir): - bootimg_dir = "%s/bootimg" % cr_workdir - if os.path.exists(bootimg_dir): - shutil.rmtree(bootimg_dir) - install_cmd = "install -d %s/EFI/BOOT" % bootimg_dir - exec_cmd(install_cmd) - - if not os.path.isfile("%s/EFI/BOOT/boot.cfg" % bootimg_dir): - cls.do_configure_grubefi(part, creator, bootimg_dir) + target_dir = "%s/EFI/BOOT" % isodir + if os.path.exists(target_dir): + shutil.rmtree(target_dir) + + os.makedirs(target_dir) + if source_params['loader'] == 'grub-efi': # Builds bootx64.efi/bootia32.efi if ISODIR didn't exist or # didn't contains it target_arch = get_bitbake_var("TARGET_SYS") @@ -333,37 +328,23 @@ class IsoImagePlugin(SourcePlugin): raise WicError("Coludn't find target architecture") if re.match("x86_64", target_arch): - grub_target = 'x86_64-efi' - grub_image = "bootx64.efi" + grub_image = "grub-efi-bootx64.efi" elif re.match('i.86', target_arch): - grub_target = 'i386-efi' - grub_image = "bootia32.efi" + grub_image = "grub-efi-bootia32.efi" else: raise WicError("grub-efi is incompatible with target %s" % target_arch) - if not os.path.isfile("%s/EFI/BOOT/%s" \ - % (bootimg_dir, grub_image)): - grub_path = get_bitbake_var("STAGING_LIBDIR", "wic-tools") - if not grub_path: - raise WicError("Couldn't find STAGING_LIBDIR, exiting.") - - grub_core = "%s/grub/%s" % (grub_path, grub_target) - if not os.path.exists(grub_core): - raise WicError("Please build grub-efi first") - - grub_cmd = "grub-mkimage -p '/EFI/BOOT' " - grub_cmd += "-d %s " % grub_core - grub_cmd += "-O %s -o %s/EFI/BOOT/%s " \ - % (grub_target, bootimg_dir, grub_image) - grub_cmd += "part_gpt part_msdos ntfs ntfscomp fat ext2 " - grub_cmd += "normal chain boot configfile linux multiboot " - grub_cmd += "search efi_gop efi_uga font gfxterm gfxmenu " - grub_cmd += "terminal minicmd test iorw loadenv echo help " - grub_cmd += "reboot serial terminfo iso9660 loopback tar " - grub_cmd += "memdisk ls search_fs_uuid udf btrfs xfs lvm " - grub_cmd += "reiserfs ata " - exec_native_cmd(grub_cmd, native_sysroot) + grub_target = os.path.join(target_dir, grub_image) + if not os.path.isfile(grub_target): + grub_src = os.path.join(deploy_dir, grub_image) + if not os.path.exists(grub_src): + raise WicError("Grub loader %s is not found in %s. " + "Please build grub-efi first" % (grub_image, deploy_dir)) + shutil.copy(grub_src, grub_target) + + if not os.path.isfile(os.path.join(target_dir, "boot.cfg")): + cls.do_configure_grubefi(part, creator, target_dir) else: raise WicError("unrecognized bootimg-efi loader: %s" % @@ -371,15 +352,6 @@ class IsoImagePlugin(SourcePlugin): except KeyError: raise WicError("bootimg-efi requires a loader, none specified") - if os.path.exists("%s/EFI/BOOT" % isodir): - shutil.rmtree("%s/EFI/BOOT" % isodir) - - shutil.copytree(bootimg_dir+"/EFI/BOOT", isodir+"/EFI/BOOT") - - # If exists, remove cr_workdir/bootimg temporary folder - if os.path.exists("%s/bootimg" % cr_workdir): - shutil.rmtree("%s/bootimg" % cr_workdir) - # Create efi.img that contains bootloader files for EFI booting # if ISODIR didn't exist or didn't contains it if os.path.isfile("%s/efi.img" % img_iso_dir): @@ -413,7 +385,7 @@ class IsoImagePlugin(SourcePlugin): exec_cmd(chmod_cmd) # Prepare files for legacy boot - syslinux_dir = get_bitbake_var("STAGING_DATADIR", "wic-tools") + syslinux_dir = get_bitbake_var("STAGING_DATADIR") if not syslinux_dir: raise WicError("Couldn't find STAGING_DATADIR, exiting.") diff --git a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rawcopy.py b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rawcopy.py index e1c4f5e7d..e86398ac8 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rawcopy.py +++ b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rawcopy.py @@ -20,7 +20,7 @@ import os from wic import WicError from wic.pluginbase import SourcePlugin -from wic.utils.misc import exec_cmd, get_bitbake_var +from wic.misc import exec_cmd, get_bitbake_var from wic.filemap import sparse_copy logger = logging.getLogger('wic') @@ -32,6 +32,25 @@ class RawCopyPlugin(SourcePlugin): name = 'rawcopy' + @staticmethod + def do_image_label(fstype, dst, label): + if fstype.startswith('ext'): + cmd = 'tune2fs -L %s %s' % (label, dst) + elif fstype in ('msdos', 'vfat'): + cmd = 'dosfslabel %s %s' % (dst, label) + elif fstype == 'btrfs': + cmd = 'btrfs filesystem label %s %s' % (dst, label) + elif fstype == 'swap': + cmd = 'mkswap -L %s %s' % (label, dst) + elif fstype == 'squashfs': + raise WicError("It's not possible to update a squashfs " + "filesystem label '%s'" % (label)) + else: + raise WicError("Cannot update filesystem label: " + "Unknown fstype: '%s'" % (fstype)) + + exec_cmd(cmd) + @classmethod def do_prepare_partition(cls, part, source_params, cr, cr_workdir, oe_builddir, bootimg_dir, kernel_dir, @@ -66,4 +85,7 @@ class RawCopyPlugin(SourcePlugin): if filesize > part.size: part.size = filesize + if part.label: + RawCopyPlugin.do_image_label(part.fstype, dst, part.label) + part.source_file = dst diff --git a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rootfs.py b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rootfs.py index f2e2ca8a2..aec720fb2 100644 --- a/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rootfs.py +++ b/import-layers/yocto-poky/scripts/lib/wic/plugins/source/rootfs.py @@ -28,12 +28,13 @@ import logging import os import shutil +import sys from oe.path import copyhardlinktree from wic import WicError from wic.pluginbase import SourcePlugin -from wic.utils.misc import get_bitbake_var, exec_cmd +from wic.misc import get_bitbake_var logger = logging.getLogger('wic') @@ -47,7 +48,7 @@ class RootfsPlugin(SourcePlugin): @staticmethod def __get_rootfs_dir(rootfs_dir): if os.path.isdir(rootfs_dir): - return rootfs_dir + return os.path.realpath(rootfs_dir) image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir) if not os.path.isdir(image_rootfs_dir): @@ -55,7 +56,7 @@ class RootfsPlugin(SourcePlugin): "named %s has been found at %s, exiting." % (rootfs_dir, image_rootfs_dir)) - return image_rootfs_dir + return os.path.realpath(image_rootfs_dir) @classmethod def do_prepare_partition(cls, part, source_params, cr, cr_workdir, @@ -80,25 +81,25 @@ class RootfsPlugin(SourcePlugin): raise WicError("Couldn't find --rootfs-dir=%s connection or " "it is not a valid path, exiting" % part.rootfs_dir) - real_rootfs_dir = cls.__get_rootfs_dir(rootfs_dir) + part.rootfs_dir = cls.__get_rootfs_dir(rootfs_dir) + new_rootfs = None # Handle excluded paths. if part.exclude_path is not None: # We need a new rootfs directory we can delete files from. Copy to # workdir. - new_rootfs = os.path.realpath(os.path.join(cr_workdir, "rootfs")) + new_rootfs = os.path.realpath(os.path.join(cr_workdir, "rootfs%d" % part.lineno)) if os.path.lexists(new_rootfs): shutil.rmtree(os.path.join(new_rootfs)) - copyhardlinktree(real_rootfs_dir, new_rootfs) - - real_rootfs_dir = new_rootfs + copyhardlinktree(part.rootfs_dir, new_rootfs) for orig_path in part.exclude_path: path = orig_path if os.path.isabs(path): - msger.error("Must be relative: --exclude-path=%s" % orig_path) + logger.error("Must be relative: --exclude-path=%s" % orig_path) + sys.exit(1) full_path = os.path.realpath(os.path.join(new_rootfs, path)) @@ -106,7 +107,8 @@ class RootfsPlugin(SourcePlugin): # because doing so could be quite disastrous (we will delete the # directory). if not full_path.startswith(new_rootfs): - msger.error("'%s' points to a path outside the rootfs" % orig_path) + logger.error("'%s' points to a path outside the rootfs" % orig_path) + sys.exit(1) if path.endswith(os.sep): # Delete content only. @@ -120,6 +122,5 @@ class RootfsPlugin(SourcePlugin): # Delete whole directory. shutil.rmtree(full_path) - part.rootfs_dir = real_rootfs_dir part.prepare_rootfs(cr_workdir, oe_builddir, - real_rootfs_dir, native_sysroot) + new_rootfs or part.rootfs_dir, native_sysroot) diff --git a/import-layers/yocto-poky/scripts/lib/wic/utils/__init__.py b/import-layers/yocto-poky/scripts/lib/wic/utils/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/import-layers/yocto-poky/scripts/lib/wic/utils/__init__.py +++ /dev/null diff --git a/import-layers/yocto-poky/scripts/lib/wic/utils/runner.py b/import-layers/yocto-poky/scripts/lib/wic/utils/runner.py deleted file mode 100644 index 4aa00fbe2..000000000 --- a/import-layers/yocto-poky/scripts/lib/wic/utils/runner.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -tt -# -# Copyright (c) 2011 Intel, Inc. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation; version 2 of the License -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., 59 -# Temple Place - Suite 330, Boston, MA 02111-1307, USA. -import subprocess - -from wic import WicError - -def runtool(cmdln_or_args): - """ wrapper for most of the subprocess calls - input: - cmdln_or_args: can be both args and cmdln str (shell=True) - return: - rc, output - """ - if isinstance(cmdln_or_args, list): - cmd = cmdln_or_args[0] - shell = False - else: - import shlex - cmd = shlex.split(cmdln_or_args)[0] - shell = True - - sout = subprocess.PIPE - serr = subprocess.STDOUT - - try: - process = subprocess.Popen(cmdln_or_args, stdout=sout, - stderr=serr, shell=shell) - sout, serr = process.communicate() - # combine stdout and stderr, filter None out and decode - out = ''.join([out.decode('utf-8') for out in [sout, serr] if out]) - except OSError as err: - if err.errno == 2: - # [Errno 2] No such file or directory - raise WicError('Cannot run command: %s, lost dependency?' % cmd) - else: - raise # relay - - return process.returncode, out |