diff options
Diffstat (limited to 'scripts/lib/wic')
31 files changed, 0 insertions, 6473 deletions
diff --git a/scripts/lib/wic/__init__.py b/scripts/lib/wic/__init__.py deleted file mode 100644 index 85567934ae..0000000000 --- a/scripts/lib/wic/__init__.py +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | # | ||
| 3 | # Copyright (c) 2007 Red Hat, Inc. | ||
| 4 | # Copyright (c) 2011 Intel, Inc. | ||
| 5 | # | ||
| 6 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 7 | # | ||
| 8 | |||
| 9 | class WicError(Exception): | ||
| 10 | pass | ||
diff --git a/scripts/lib/wic/canned-wks/common.wks.inc b/scripts/lib/wic/canned-wks/common.wks.inc deleted file mode 100644 index 89880b417b..0000000000 --- a/scripts/lib/wic/canned-wks/common.wks.inc +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | # This file is included into 3 canned wks files from this directory | ||
| 2 | part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 | ||
| 3 | part / --source rootfs --use-uuid --fstype=ext4 --label platform --align 1024 | ||
diff --git a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg deleted file mode 100644 index c58e74a853..0000000000 --- a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg +++ /dev/null | |||
| @@ -1,27 +0,0 @@ | |||
| 1 | # This is an example configuration file for syslinux. | ||
| 2 | TIMEOUT 50 | ||
| 3 | ALLOWOPTIONS 1 | ||
| 4 | SERIAL 0 115200 | ||
| 5 | PROMPT 0 | ||
| 6 | |||
| 7 | UI vesamenu.c32 | ||
| 8 | menu title Select boot options | ||
| 9 | menu tabmsg Press [Tab] to edit, [Return] to select | ||
| 10 | |||
| 11 | DEFAULT Graphics console boot | ||
| 12 | |||
| 13 | LABEL Graphics console boot | ||
| 14 | KERNEL /vmlinuz | ||
| 15 | APPEND label=boot rootwait | ||
| 16 | |||
| 17 | LABEL Serial console boot | ||
| 18 | KERNEL /vmlinuz | ||
| 19 | APPEND label=boot rootwait console=ttyS0,115200 | ||
| 20 | |||
| 21 | LABEL Graphics console install | ||
| 22 | KERNEL /vmlinuz | ||
| 23 | APPEND label=install rootwait | ||
| 24 | |||
| 25 | LABEL Serial console install | ||
| 26 | KERNEL /vmlinuz | ||
| 27 | APPEND label=install rootwait console=ttyS0,115200 | ||
diff --git a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks deleted file mode 100644 index 3529e05c87..0000000000 --- a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | # short-description: Create a 'pcbios' direct disk image with custom bootloader config | ||
| 2 | # long-description: Creates a partitioned legacy BIOS disk image that the user | ||
| 3 | # can directly dd to boot media. The bootloader configuration source is a user file. | ||
| 4 | |||
| 5 | include common.wks.inc | ||
| 6 | |||
| 7 | bootloader --configfile="directdisk-bootloader-config.cfg" | ||
| 8 | |||
diff --git a/scripts/lib/wic/canned-wks/directdisk-gpt.wks b/scripts/lib/wic/canned-wks/directdisk-gpt.wks deleted file mode 100644 index 8d7d8de6ea..0000000000 --- a/scripts/lib/wic/canned-wks/directdisk-gpt.wks +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | # short-description: Create a 'pcbios' direct disk image | ||
| 2 | # long-description: Creates a partitioned legacy BIOS disk image that the user | ||
| 3 | # can directly dd to boot media. | ||
| 4 | |||
| 5 | |||
| 6 | part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 | ||
| 7 | part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid | ||
| 8 | |||
| 9 | bootloader --ptable gpt --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8" | ||
| 10 | |||
diff --git a/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks b/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks deleted file mode 100644 index f61d941d6d..0000000000 --- a/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | # short-description: Create multi rootfs image using rootfs plugin | ||
| 2 | # long-description: Creates a partitioned disk image with two rootfs partitions | ||
| 3 | # using rootfs plugin. | ||
| 4 | # | ||
| 5 | # Partitions can use either | ||
| 6 | # - indirect rootfs references to image recipe(s): | ||
| 7 | # wic create directdisk-multi-indirect-recipes -e core-image-minimal \ | ||
| 8 | # --rootfs-dir rootfs1=core-image-minimal | ||
| 9 | # --rootfs-dir rootfs2=core-image-minimal-dev | ||
| 10 | # | ||
| 11 | # - or paths to rootfs directories: | ||
| 12 | # wic create directdisk-multi-rootfs \ | ||
| 13 | # --rootfs-dir rootfs1=tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0-r0/rootfs/ | ||
| 14 | # --rootfs-dir rootfs2=tmp/work/qemux86_64-poky-linux/core-image-minimal-dev/1.0-r0/rootfs/ | ||
| 15 | # | ||
| 16 | # - or any combinations of -r and --rootfs command line options | ||
| 17 | |||
| 18 | part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 | ||
| 19 | part / --source rootfs --rootfs-dir=rootfs1 --ondisk sda --fstype=ext4 --label platform --align 1024 | ||
| 20 | part /rescue --source rootfs --rootfs-dir=rootfs2 --ondisk sda --fstype=ext4 --label secondary --align 1024 | ||
| 21 | |||
| 22 | bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8" | ||
| 23 | |||
diff --git a/scripts/lib/wic/canned-wks/directdisk.wks b/scripts/lib/wic/canned-wks/directdisk.wks deleted file mode 100644 index 8c8e06b02c..0000000000 --- a/scripts/lib/wic/canned-wks/directdisk.wks +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | # short-description: Create a 'pcbios' direct disk image | ||
| 2 | # long-description: Creates a partitioned legacy BIOS disk image that the user | ||
| 3 | # can directly dd to boot media. | ||
| 4 | |||
| 5 | include common.wks.inc | ||
| 6 | |||
| 7 | bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8" | ||
| 8 | |||
diff --git a/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in b/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in deleted file mode 100644 index 2fd286ff98..0000000000 --- a/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | bootloader --ptable gpt | ||
| 2 | part /boot --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --label boot --active --align 1024 --use-uuid --overhead-factor 1.1 | ||
| 3 | part / --source rootfs --fstype=ext4 --label root --align 1024 --exclude-path boot/ | ||
diff --git a/scripts/lib/wic/canned-wks/mkefidisk.wks b/scripts/lib/wic/canned-wks/mkefidisk.wks deleted file mode 100644 index 9f534fe184..0000000000 --- a/scripts/lib/wic/canned-wks/mkefidisk.wks +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | # short-description: Create an EFI disk image | ||
| 2 | # long-description: Creates a partitioned EFI disk image that the user | ||
| 3 | # can directly dd to boot media. | ||
| 4 | |||
| 5 | part /boot --source bootimg-efi --sourceparams="loader=grub-efi" --ondisk sda --label msdos --active --align 1024 | ||
| 6 | |||
| 7 | part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid | ||
| 8 | |||
| 9 | part swap --ondisk sda --size 44 --label swap1 --fstype=swap | ||
| 10 | |||
| 11 | bootloader --ptable gpt --timeout=5 --append="rootfstype=ext4 console=ttyS0,115200 console=tty0" | ||
diff --git a/scripts/lib/wic/canned-wks/mkhybridiso.wks b/scripts/lib/wic/canned-wks/mkhybridiso.wks deleted file mode 100644 index 48c5ac4791..0000000000 --- a/scripts/lib/wic/canned-wks/mkhybridiso.wks +++ /dev/null | |||
| @@ -1,7 +0,0 @@ | |||
| 1 | # short-description: Create a hybrid ISO image | ||
| 2 | # long-description: Creates an EFI and legacy bootable hybrid ISO image | ||
| 3 | # which can be used on optical media as well as USB media. | ||
| 4 | |||
| 5 | part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi,image_name=HYBRID_ISO_IMG" --ondisk cd --label HYBRIDISO | ||
| 6 | |||
| 7 | bootloader --timeout=15 --append="" | ||
diff --git a/scripts/lib/wic/canned-wks/qemuloongarch.wks b/scripts/lib/wic/canned-wks/qemuloongarch.wks deleted file mode 100644 index 8465c7a8c0..0000000000 --- a/scripts/lib/wic/canned-wks/qemuloongarch.wks +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | # short-description: Create qcow2 image for LoongArch QEMU machines | ||
| 2 | |||
| 3 | part / --source rootfs --fstype=ext4 --label root --align 4096 --size 5G | ||
diff --git a/scripts/lib/wic/canned-wks/qemuriscv.wks b/scripts/lib/wic/canned-wks/qemuriscv.wks deleted file mode 100644 index 12c68b7069..0000000000 --- a/scripts/lib/wic/canned-wks/qemuriscv.wks +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | # short-description: Create qcow2 image for RISC-V QEMU machines | ||
| 2 | |||
| 3 | part / --source rootfs --fstype=ext4 --label root --align 4096 --size 5G | ||
diff --git a/scripts/lib/wic/canned-wks/qemux86-directdisk.wks b/scripts/lib/wic/canned-wks/qemux86-directdisk.wks deleted file mode 100644 index 808997611a..0000000000 --- a/scripts/lib/wic/canned-wks/qemux86-directdisk.wks +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | # short-description: Create a qemu machine 'pcbios' direct disk image | ||
| 2 | # long-description: Creates a partitioned legacy BIOS disk image that the user | ||
| 3 | # can directly use to boot a qemu machine. | ||
| 4 | |||
| 5 | include common.wks.inc | ||
| 6 | |||
| 7 | bootloader --timeout=0 --append="rw oprofile.timer=1 rootfstype=ext4 console=tty console=ttyS0 " | ||
| 8 | |||
diff --git a/scripts/lib/wic/canned-wks/sdimage-bootpart.wks b/scripts/lib/wic/canned-wks/sdimage-bootpart.wks deleted file mode 100644 index 63bc4dab6a..0000000000 --- a/scripts/lib/wic/canned-wks/sdimage-bootpart.wks +++ /dev/null | |||
| @@ -1,6 +0,0 @@ | |||
| 1 | # short-description: Create SD card image with a boot partition | ||
| 2 | # long-description: Creates a partitioned SD card image. Boot files | ||
| 3 | # are located in the first vfat partition. | ||
| 4 | |||
| 5 | part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4 --size 16 | ||
| 6 | part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root --align 4 | ||
diff --git a/scripts/lib/wic/canned-wks/systemd-bootdisk.wks b/scripts/lib/wic/canned-wks/systemd-bootdisk.wks deleted file mode 100644 index 95d7b97a60..0000000000 --- a/scripts/lib/wic/canned-wks/systemd-bootdisk.wks +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | # short-description: Create an EFI disk image with systemd-boot | ||
| 2 | # long-description: Creates a partitioned EFI disk image that the user | ||
| 3 | # can directly dd to boot media. The selected bootloader is systemd-boot. | ||
| 4 | |||
| 5 | part /boot --source bootimg-efi --sourceparams="loader=systemd-boot" --ondisk sda --label msdos --active --align 1024 --use-uuid | ||
| 6 | |||
| 7 | part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid | ||
| 8 | |||
| 9 | part swap --ondisk sda --size 44 --label swap1 --fstype=swap --use-uuid | ||
| 10 | |||
| 11 | bootloader --ptable gpt --timeout=5 --append="rootwait rootfstype=ext4 console=ttyS0,115200 console=tty0" | ||
diff --git a/scripts/lib/wic/engine.py b/scripts/lib/wic/engine.py deleted file mode 100644 index 674ccfc244..0000000000 --- a/scripts/lib/wic/engine.py +++ /dev/null | |||
| @@ -1,628 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2013, Intel Corporation. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | |||
| 8 | # This module implements the image creation engine used by 'wic' to | ||
| 9 | # create images. The engine parses through the OpenEmbedded kickstart | ||
| 10 | # (wks) file specified and generates images that can then be directly | ||
| 11 | # written onto media. | ||
| 12 | # | ||
| 13 | # AUTHORS | ||
| 14 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 15 | # | ||
| 16 | |||
| 17 | import logging | ||
| 18 | import os | ||
| 19 | import tempfile | ||
| 20 | import json | ||
| 21 | import subprocess | ||
| 22 | import shutil | ||
| 23 | import re | ||
| 24 | |||
| 25 | from collections import namedtuple, OrderedDict | ||
| 26 | |||
| 27 | from wic import WicError | ||
| 28 | from wic.filemap import sparse_copy | ||
| 29 | from wic.pluginbase import PluginMgr | ||
| 30 | from wic.misc import get_bitbake_var, exec_cmd | ||
| 31 | |||
| 32 | logger = logging.getLogger('wic') | ||
| 33 | |||
| 34 | def verify_build_env(): | ||
| 35 | """ | ||
| 36 | Verify that the build environment is sane. | ||
| 37 | |||
| 38 | Returns True if it is, false otherwise | ||
| 39 | """ | ||
| 40 | if not os.environ.get("BUILDDIR"): | ||
| 41 | raise WicError("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)") | ||
| 42 | |||
| 43 | return True | ||
| 44 | |||
| 45 | |||
| 46 | CANNED_IMAGE_DIR = "lib/wic/canned-wks" # relative to scripts | ||
| 47 | SCRIPTS_CANNED_IMAGE_DIR = "scripts/" + CANNED_IMAGE_DIR | ||
| 48 | WIC_DIR = "wic" | ||
| 49 | |||
| 50 | def build_canned_image_list(path): | ||
| 51 | layers_path = get_bitbake_var("BBLAYERS") | ||
| 52 | canned_wks_layer_dirs = [] | ||
| 53 | |||
| 54 | if layers_path is not None: | ||
| 55 | for layer_path in layers_path.split(): | ||
| 56 | for wks_path in (WIC_DIR, SCRIPTS_CANNED_IMAGE_DIR): | ||
| 57 | cpath = os.path.join(layer_path, wks_path) | ||
| 58 | if os.path.isdir(cpath): | ||
| 59 | canned_wks_layer_dirs.append(cpath) | ||
| 60 | |||
| 61 | cpath = os.path.join(path, CANNED_IMAGE_DIR) | ||
| 62 | canned_wks_layer_dirs.append(cpath) | ||
| 63 | |||
| 64 | return canned_wks_layer_dirs | ||
| 65 | |||
| 66 | def find_canned_image(scripts_path, wks_file): | ||
| 67 | """ | ||
| 68 | Find a .wks file with the given name in the canned files dir. | ||
| 69 | |||
| 70 | Return False if not found | ||
| 71 | """ | ||
| 72 | layers_canned_wks_dir = build_canned_image_list(scripts_path) | ||
| 73 | |||
| 74 | for canned_wks_dir in layers_canned_wks_dir: | ||
| 75 | for root, dirs, files in os.walk(canned_wks_dir): | ||
| 76 | for fname in files: | ||
| 77 | if fname.endswith("~") or fname.endswith("#"): | ||
| 78 | continue | ||
| 79 | if ((fname.endswith(".wks") and wks_file + ".wks" == fname) or \ | ||
| 80 | (fname.endswith(".wks.in") and wks_file + ".wks.in" == fname)): | ||
| 81 | fullpath = os.path.join(canned_wks_dir, fname) | ||
| 82 | return fullpath | ||
| 83 | return None | ||
| 84 | |||
| 85 | |||
| 86 | def list_canned_images(scripts_path): | ||
| 87 | """ | ||
| 88 | List the .wks files in the canned image dir, minus the extension. | ||
| 89 | """ | ||
| 90 | layers_canned_wks_dir = build_canned_image_list(scripts_path) | ||
| 91 | |||
| 92 | for canned_wks_dir in layers_canned_wks_dir: | ||
| 93 | for root, dirs, files in os.walk(canned_wks_dir): | ||
| 94 | for fname in files: | ||
| 95 | if fname.endswith("~") or fname.endswith("#"): | ||
| 96 | continue | ||
| 97 | if fname.endswith(".wks") or fname.endswith(".wks.in"): | ||
| 98 | fullpath = os.path.join(canned_wks_dir, fname) | ||
| 99 | with open(fullpath) as wks: | ||
| 100 | for line in wks: | ||
| 101 | desc = "" | ||
| 102 | idx = line.find("short-description:") | ||
| 103 | if idx != -1: | ||
| 104 | desc = line[idx + len("short-description:"):].strip() | ||
| 105 | break | ||
| 106 | basename = fname.split('.')[0] | ||
| 107 | print(" %s\t\t%s" % (basename.ljust(30), desc)) | ||
| 108 | |||
| 109 | |||
| 110 | def list_canned_image_help(scripts_path, fullpath): | ||
| 111 | """ | ||
| 112 | List the help and params in the specified canned image. | ||
| 113 | """ | ||
| 114 | found = False | ||
| 115 | with open(fullpath) as wks: | ||
| 116 | for line in wks: | ||
| 117 | if not found: | ||
| 118 | idx = line.find("long-description:") | ||
| 119 | if idx != -1: | ||
| 120 | print() | ||
| 121 | print(line[idx + len("long-description:"):].strip()) | ||
| 122 | found = True | ||
| 123 | continue | ||
| 124 | if not line.strip(): | ||
| 125 | break | ||
| 126 | idx = line.find("#") | ||
| 127 | if idx != -1: | ||
| 128 | print(line[idx + len("#:"):].rstrip()) | ||
| 129 | else: | ||
| 130 | break | ||
| 131 | |||
| 132 | |||
| 133 | def list_source_plugins(): | ||
| 134 | """ | ||
| 135 | List the available source plugins i.e. plugins available for --source. | ||
| 136 | """ | ||
| 137 | plugins = PluginMgr.get_plugins('source') | ||
| 138 | |||
| 139 | for plugin in plugins: | ||
| 140 | print(" %s" % plugin) | ||
| 141 | |||
| 142 | |||
| 143 | def wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir, | ||
| 144 | native_sysroot, options): | ||
| 145 | """ | ||
| 146 | Create image | ||
| 147 | |||
| 148 | wks_file - user-defined OE kickstart file | ||
| 149 | rootfs_dir - absolute path to the build's /rootfs dir | ||
| 150 | bootimg_dir - absolute path to the build's boot artifacts directory | ||
| 151 | kernel_dir - absolute path to the build's kernel directory | ||
| 152 | native_sysroot - absolute path to the build's native sysroots dir | ||
| 153 | image_output_dir - dirname to create for image | ||
| 154 | options - wic command line options (debug, bmap, etc) | ||
| 155 | |||
| 156 | Normally, the values for the build artifacts values are determined | ||
| 157 | by 'wic -e' from the output of the 'bitbake -e' command given an | ||
| 158 | image name e.g. 'core-image-minimal' and a given machine set in | ||
| 159 | local.conf. If that's the case, the variables get the following | ||
| 160 | values from the output of 'bitbake -e': | ||
| 161 | |||
| 162 | rootfs_dir: IMAGE_ROOTFS | ||
| 163 | kernel_dir: DEPLOY_DIR_IMAGE | ||
| 164 | native_sysroot: STAGING_DIR_NATIVE | ||
| 165 | |||
| 166 | In the above case, bootimg_dir remains unset and the | ||
| 167 | plugin-specific image creation code is responsible for finding the | ||
| 168 | bootimg artifacts. | ||
| 169 | |||
| 170 | In the case where the values are passed in explicitly i.e 'wic -e' | ||
| 171 | is not used but rather the individual 'wic' options are used to | ||
| 172 | explicitly specify these values. | ||
| 173 | """ | ||
| 174 | try: | ||
| 175 | oe_builddir = os.environ["BUILDDIR"] | ||
| 176 | except KeyError: | ||
| 177 | raise WicError("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)") | ||
| 178 | |||
| 179 | if not os.path.exists(options.outdir): | ||
| 180 | os.makedirs(options.outdir) | ||
| 181 | |||
| 182 | pname = options.imager | ||
| 183 | plugin_class = PluginMgr.get_plugins('imager').get(pname) | ||
| 184 | if not plugin_class: | ||
| 185 | raise WicError('Unknown plugin: %s' % pname) | ||
| 186 | |||
| 187 | plugin = plugin_class(wks_file, rootfs_dir, bootimg_dir, kernel_dir, | ||
| 188 | native_sysroot, oe_builddir, options) | ||
| 189 | |||
| 190 | plugin.do_create() | ||
| 191 | |||
| 192 | logger.info("The image(s) were created using OE kickstart file:\n %s", wks_file) | ||
| 193 | |||
| 194 | |||
| 195 | def wic_list(args, scripts_path): | ||
| 196 | """ | ||
| 197 | Print the list of images or source plugins. | ||
| 198 | """ | ||
| 199 | if args.list_type is None: | ||
| 200 | return False | ||
| 201 | |||
| 202 | if args.list_type == "images": | ||
| 203 | |||
| 204 | list_canned_images(scripts_path) | ||
| 205 | return True | ||
| 206 | elif args.list_type == "source-plugins": | ||
| 207 | list_source_plugins() | ||
| 208 | return True | ||
| 209 | elif len(args.help_for) == 1 and args.help_for[0] == 'help': | ||
| 210 | wks_file = args.list_type | ||
| 211 | fullpath = find_canned_image(scripts_path, wks_file) | ||
| 212 | if not fullpath: | ||
| 213 | raise WicError("No image named %s found, exiting. " | ||
| 214 | "(Use 'wic list images' to list available images, " | ||
| 215 | "or specify a fully-qualified OE kickstart (.wks) " | ||
| 216 | "filename)" % wks_file) | ||
| 217 | |||
| 218 | list_canned_image_help(scripts_path, fullpath) | ||
| 219 | return True | ||
| 220 | |||
| 221 | return False | ||
| 222 | |||
| 223 | |||
| 224 | class Disk: | ||
| 225 | def __init__(self, imagepath, native_sysroot, fstypes=('fat', 'ext')): | ||
| 226 | self.imagepath = imagepath | ||
| 227 | self.native_sysroot = native_sysroot | ||
| 228 | self.fstypes = fstypes | ||
| 229 | self._partitions = None | ||
| 230 | self._partimages = {} | ||
| 231 | self._lsector_size = None | ||
| 232 | self._psector_size = None | ||
| 233 | self._ptable_format = None | ||
| 234 | |||
| 235 | # find parted | ||
| 236 | # read paths from $PATH environment variable | ||
| 237 | # if it fails, use hardcoded paths | ||
| 238 | pathlist = "/bin:/usr/bin:/usr/sbin:/sbin/" | ||
| 239 | try: | ||
| 240 | self.paths = os.environ['PATH'] + ":" + pathlist | ||
| 241 | except KeyError: | ||
| 242 | self.paths = pathlist | ||
| 243 | |||
| 244 | if native_sysroot: | ||
| 245 | for path in pathlist.split(':'): | ||
| 246 | self.paths = "%s%s:%s" % (native_sysroot, path, self.paths) | ||
| 247 | |||
| 248 | self.parted = shutil.which("parted", path=self.paths) | ||
| 249 | if not self.parted: | ||
| 250 | raise WicError("Can't find executable parted") | ||
| 251 | |||
| 252 | self.partitions = self.get_partitions() | ||
| 253 | |||
| 254 | def __del__(self): | ||
| 255 | for path in self._partimages.values(): | ||
| 256 | os.unlink(path) | ||
| 257 | |||
| 258 | def get_partitions(self): | ||
| 259 | if self._partitions is None: | ||
| 260 | self._partitions = OrderedDict() | ||
| 261 | out = exec_cmd("%s -sm %s unit B print" % (self.parted, self.imagepath)) | ||
| 262 | parttype = namedtuple("Part", "pnum start end size fstype") | ||
| 263 | splitted = out.splitlines() | ||
| 264 | # skip over possible errors in exec_cmd output | ||
| 265 | try: | ||
| 266 | idx =splitted.index("BYT;") | ||
| 267 | except ValueError: | ||
| 268 | raise WicError("Error getting partition information from %s" % (self.parted)) | ||
| 269 | lsector_size, psector_size, self._ptable_format = splitted[idx + 1].split(":")[3:6] | ||
| 270 | self._lsector_size = int(lsector_size) | ||
| 271 | self._psector_size = int(psector_size) | ||
| 272 | for line in splitted[idx + 2:]: | ||
| 273 | pnum, start, end, size, fstype = line.split(':')[:5] | ||
| 274 | partition = parttype(int(pnum), int(start[:-1]), int(end[:-1]), | ||
| 275 | int(size[:-1]), fstype) | ||
| 276 | self._partitions[pnum] = partition | ||
| 277 | |||
| 278 | return self._partitions | ||
| 279 | |||
| 280 | def __getattr__(self, name): | ||
| 281 | """Get path to the executable in a lazy way.""" | ||
| 282 | if name in ("mdir", "mcopy", "mdel", "mdeltree", "sfdisk", "e2fsck", | ||
| 283 | "resize2fs", "mkswap", "mkdosfs", "debugfs","blkid"): | ||
| 284 | aname = "_%s" % name | ||
| 285 | if aname not in self.__dict__: | ||
| 286 | setattr(self, aname, shutil.which(name, path=self.paths)) | ||
| 287 | if aname not in self.__dict__ or self.__dict__[aname] is None: | ||
| 288 | raise WicError("Can't find executable '{}'".format(name)) | ||
| 289 | return self.__dict__[aname] | ||
| 290 | return self.__dict__[name] | ||
| 291 | |||
| 292 | def _get_part_image(self, pnum): | ||
| 293 | if pnum not in self.partitions: | ||
| 294 | raise WicError("Partition %s is not in the image" % pnum) | ||
| 295 | part = self.partitions[pnum] | ||
| 296 | # check if fstype is supported | ||
| 297 | for fstype in self.fstypes: | ||
| 298 | if part.fstype.startswith(fstype): | ||
| 299 | break | ||
| 300 | else: | ||
| 301 | raise WicError("Not supported fstype: {}".format(part.fstype)) | ||
| 302 | if pnum not in self._partimages: | ||
| 303 | tmpf = tempfile.NamedTemporaryFile(prefix="wic-part") | ||
| 304 | dst_fname = tmpf.name | ||
| 305 | tmpf.close() | ||
| 306 | sparse_copy(self.imagepath, dst_fname, skip=part.start, length=part.size) | ||
| 307 | self._partimages[pnum] = dst_fname | ||
| 308 | |||
| 309 | return self._partimages[pnum] | ||
| 310 | |||
| 311 | def _put_part_image(self, pnum): | ||
| 312 | """Put partition image into partitioned image.""" | ||
| 313 | sparse_copy(self._partimages[pnum], self.imagepath, | ||
| 314 | seek=self.partitions[pnum].start) | ||
| 315 | |||
| 316 | def dir(self, pnum, path): | ||
| 317 | if pnum not in self.partitions: | ||
| 318 | raise WicError("Partition %s is not in the image" % pnum) | ||
| 319 | |||
| 320 | if self.partitions[pnum].fstype.startswith('ext'): | ||
| 321 | return exec_cmd("{} {} -R 'ls -l {}'".format(self.debugfs, | ||
| 322 | self._get_part_image(pnum), | ||
| 323 | path), as_shell=True) | ||
| 324 | else: # fat | ||
| 325 | return exec_cmd("{} -i {} ::{}".format(self.mdir, | ||
| 326 | self._get_part_image(pnum), | ||
| 327 | path)) | ||
| 328 | |||
| 329 | def copy(self, src, dest): | ||
| 330 | """Copy partition image into wic image.""" | ||
| 331 | pnum = dest.part if isinstance(src, str) else src.part | ||
| 332 | |||
| 333 | if self.partitions[pnum].fstype.startswith('ext'): | ||
| 334 | if isinstance(src, str): | ||
| 335 | cmd = "printf 'cd {}\nwrite {} {}\n' | {} -w {}".\ | ||
| 336 | format(os.path.dirname(dest.path), src, os.path.basename(src), | ||
| 337 | self.debugfs, self._get_part_image(pnum)) | ||
| 338 | else: # copy from wic | ||
| 339 | # run both dump and rdump to support both files and directory | ||
| 340 | cmd = "printf 'cd {}\ndump /{} {}\nrdump /{} {}\n' | {} {}".\ | ||
| 341 | format(os.path.dirname(src.path), src.path, | ||
| 342 | dest, src.path, dest, self.debugfs, | ||
| 343 | self._get_part_image(pnum)) | ||
| 344 | else: # fat | ||
| 345 | if isinstance(src, str): | ||
| 346 | cmd = "{} -i {} -snop {} ::{}".format(self.mcopy, | ||
| 347 | self._get_part_image(pnum), | ||
| 348 | src, dest.path) | ||
| 349 | else: | ||
| 350 | cmd = "{} -i {} -snop ::{} {}".format(self.mcopy, | ||
| 351 | self._get_part_image(pnum), | ||
| 352 | src.path, dest) | ||
| 353 | |||
| 354 | exec_cmd(cmd, as_shell=True) | ||
| 355 | self._put_part_image(pnum) | ||
| 356 | |||
| 357 | def remove_ext(self, pnum, path, recursive): | ||
| 358 | """ | ||
| 359 | Remove files/dirs and their contents from the partition. | ||
| 360 | This only applies to ext* partition. | ||
| 361 | """ | ||
| 362 | abs_path = re.sub('\/\/+', '/', path) | ||
| 363 | cmd = "{} {} -wR 'rm \"{}\"'".format(self.debugfs, | ||
| 364 | self._get_part_image(pnum), | ||
| 365 | abs_path) | ||
| 366 | out = exec_cmd(cmd , as_shell=True) | ||
| 367 | for line in out.splitlines(): | ||
| 368 | if line.startswith("rm:"): | ||
| 369 | if "file is a directory" in line: | ||
| 370 | if recursive: | ||
| 371 | # loop through content and delete them one by one if | ||
| 372 | # flaged with -r | ||
| 373 | subdirs = iter(self.dir(pnum, abs_path).splitlines()) | ||
| 374 | next(subdirs) | ||
| 375 | for subdir in subdirs: | ||
| 376 | dir = subdir.split(':')[1].split(" ", 1)[1] | ||
| 377 | if not dir == "." and not dir == "..": | ||
| 378 | self.remove_ext(pnum, "%s/%s" % (abs_path, dir), recursive) | ||
| 379 | |||
| 380 | rmdir_out = exec_cmd("{} {} -wR 'rmdir \"{}\"'".format(self.debugfs, | ||
| 381 | self._get_part_image(pnum), | ||
| 382 | abs_path.rstrip('/')) | ||
| 383 | , as_shell=True) | ||
| 384 | |||
| 385 | for rmdir_line in rmdir_out.splitlines(): | ||
| 386 | if "directory not empty" in rmdir_line: | ||
| 387 | raise WicError("Could not complete operation: \n%s \n" | ||
| 388 | "use -r to remove non-empty directory" % rmdir_line) | ||
| 389 | if rmdir_line.startswith("rmdir:"): | ||
| 390 | raise WicError("Could not complete operation: \n%s " | ||
| 391 | "\n%s" % (str(line), rmdir_line)) | ||
| 392 | |||
| 393 | else: | ||
| 394 | raise WicError("Could not complete operation: \n%s " | ||
| 395 | "\nUnable to remove %s" % (str(line), abs_path)) | ||
| 396 | |||
| 397 | def remove(self, pnum, path, recursive): | ||
| 398 | """Remove files/dirs from the partition.""" | ||
| 399 | partimg = self._get_part_image(pnum) | ||
| 400 | if self.partitions[pnum].fstype.startswith('ext'): | ||
| 401 | self.remove_ext(pnum, path, recursive) | ||
| 402 | |||
| 403 | else: # fat | ||
| 404 | cmd = "{} -i {} ::{}".format(self.mdel, partimg, path) | ||
| 405 | try: | ||
| 406 | exec_cmd(cmd) | ||
| 407 | except WicError as err: | ||
| 408 | if "not found" in str(err) or "non empty" in str(err): | ||
| 409 | # mdel outputs 'File ... not found' or 'directory .. non empty" | ||
| 410 | # try to use mdeltree as path could be a directory | ||
| 411 | cmd = "{} -i {} ::{}".format(self.mdeltree, | ||
| 412 | partimg, path) | ||
| 413 | exec_cmd(cmd) | ||
| 414 | else: | ||
| 415 | raise err | ||
| 416 | self._put_part_image(pnum) | ||
| 417 | |||
| 418 | def write(self, target, expand): | ||
| 419 | """Write disk image to the media or file.""" | ||
| 420 | def write_sfdisk_script(outf, parts): | ||
| 421 | for key, val in parts['partitiontable'].items(): | ||
| 422 | if key in ("partitions", "device", "firstlba", "lastlba"): | ||
| 423 | continue | ||
| 424 | if key == "id": | ||
| 425 | key = "label-id" | ||
| 426 | outf.write("{}: {}\n".format(key, val)) | ||
| 427 | outf.write("\n") | ||
| 428 | for part in parts['partitiontable']['partitions']: | ||
| 429 | line = '' | ||
| 430 | for name in ('attrs', 'name', 'size', 'type', 'uuid'): | ||
| 431 | if name == 'size' and part['type'] == 'f': | ||
| 432 | # don't write size for extended partition | ||
| 433 | continue | ||
| 434 | val = part.get(name) | ||
| 435 | if val: | ||
| 436 | line += '{}={}, '.format(name, val) | ||
| 437 | if line: | ||
| 438 | line = line[:-2] # strip ', ' | ||
| 439 | if part.get('bootable'): | ||
| 440 | line += ' ,bootable' | ||
| 441 | outf.write("{}\n".format(line)) | ||
| 442 | outf.flush() | ||
| 443 | |||
| 444 | def read_ptable(path): | ||
| 445 | out = exec_cmd("{} -J {}".format(self.sfdisk, path)) | ||
| 446 | return json.loads(out) | ||
| 447 | |||
| 448 | def write_ptable(parts, target): | ||
| 449 | with tempfile.NamedTemporaryFile(prefix="wic-sfdisk-", mode='w') as outf: | ||
| 450 | write_sfdisk_script(outf, parts) | ||
| 451 | cmd = "{} --no-reread {} < {} ".format(self.sfdisk, target, outf.name) | ||
| 452 | exec_cmd(cmd, as_shell=True) | ||
| 453 | |||
| 454 | if expand is None: | ||
| 455 | sparse_copy(self.imagepath, target) | ||
| 456 | else: | ||
| 457 | # copy first sectors that may contain bootloader | ||
| 458 | sparse_copy(self.imagepath, target, length=2048 * self._lsector_size) | ||
| 459 | |||
| 460 | # copy source partition table to the target | ||
| 461 | parts = read_ptable(self.imagepath) | ||
| 462 | write_ptable(parts, target) | ||
| 463 | |||
| 464 | # get size of unpartitioned space | ||
| 465 | free = None | ||
| 466 | for line in exec_cmd("{} -F {}".format(self.sfdisk, target)).splitlines(): | ||
| 467 | if line.startswith("Unpartitioned space ") and line.endswith("sectors"): | ||
| 468 | free = int(line.split()[-2]) | ||
| 469 | # Align free space to a 2048 sector boundary. YOCTO #12840. | ||
| 470 | free = free - (free % 2048) | ||
| 471 | if free is None: | ||
| 472 | raise WicError("Can't get size of unpartitioned space") | ||
| 473 | |||
| 474 | # calculate expanded partitions sizes | ||
| 475 | sizes = {} | ||
| 476 | num_auto_resize = 0 | ||
| 477 | for num, part in enumerate(parts['partitiontable']['partitions'], 1): | ||
| 478 | if num in expand: | ||
| 479 | if expand[num] != 0: # don't resize partition if size is set to 0 | ||
| 480 | sectors = expand[num] // self._lsector_size | ||
| 481 | free -= sectors - part['size'] | ||
| 482 | part['size'] = sectors | ||
| 483 | sizes[num] = sectors | ||
| 484 | elif part['type'] != 'f': | ||
| 485 | sizes[num] = -1 | ||
| 486 | num_auto_resize += 1 | ||
| 487 | |||
| 488 | for num, part in enumerate(parts['partitiontable']['partitions'], 1): | ||
| 489 | if sizes.get(num) == -1: | ||
| 490 | part['size'] += free // num_auto_resize | ||
| 491 | |||
| 492 | # write resized partition table to the target | ||
| 493 | write_ptable(parts, target) | ||
| 494 | |||
| 495 | # read resized partition table | ||
| 496 | parts = read_ptable(target) | ||
| 497 | |||
| 498 | # copy partitions content | ||
| 499 | for num, part in enumerate(parts['partitiontable']['partitions'], 1): | ||
| 500 | pnum = str(num) | ||
| 501 | fstype = self.partitions[pnum].fstype | ||
| 502 | |||
| 503 | # copy unchanged partition | ||
| 504 | if part['size'] == self.partitions[pnum].size // self._lsector_size: | ||
| 505 | logger.info("copying unchanged partition {}".format(pnum)) | ||
| 506 | sparse_copy(self._get_part_image(pnum), target, seek=part['start'] * self._lsector_size) | ||
| 507 | continue | ||
| 508 | |||
| 509 | # resize or re-create partitions | ||
| 510 | if fstype.startswith('ext') or fstype.startswith('fat') or \ | ||
| 511 | fstype.startswith('linux-swap'): | ||
| 512 | |||
| 513 | partfname = None | ||
| 514 | with tempfile.NamedTemporaryFile(prefix="wic-part{}-".format(pnum)) as partf: | ||
| 515 | partfname = partf.name | ||
| 516 | |||
| 517 | if fstype.startswith('ext'): | ||
| 518 | logger.info("resizing ext partition {}".format(pnum)) | ||
| 519 | partimg = self._get_part_image(pnum) | ||
| 520 | sparse_copy(partimg, partfname) | ||
| 521 | exec_cmd("{} -pf {}".format(self.e2fsck, partfname)) | ||
| 522 | exec_cmd("{} {} {}s".format(\ | ||
| 523 | self.resize2fs, partfname, part['size'])) | ||
| 524 | elif fstype.startswith('fat'): | ||
| 525 | logger.info("copying content of the fat partition {}".format(pnum)) | ||
| 526 | with tempfile.TemporaryDirectory(prefix='wic-fatdir-') as tmpdir: | ||
| 527 | # copy content to the temporary directory | ||
| 528 | cmd = "{} -snompi {} :: {}".format(self.mcopy, | ||
| 529 | self._get_part_image(pnum), | ||
| 530 | tmpdir) | ||
| 531 | exec_cmd(cmd) | ||
| 532 | # create new msdos partition | ||
| 533 | label = part.get("name") | ||
| 534 | label_str = "-n {}".format(label) if label else '' | ||
| 535 | |||
| 536 | cmd = "{} {} -C {} {}".format(self.mkdosfs, label_str, partfname, | ||
| 537 | part['size']) | ||
| 538 | exec_cmd(cmd) | ||
| 539 | # copy content from the temporary directory to the new partition | ||
| 540 | cmd = "{} -snompi {} {}/* ::".format(self.mcopy, partfname, tmpdir) | ||
| 541 | exec_cmd(cmd, as_shell=True) | ||
| 542 | elif fstype.startswith('linux-swap'): | ||
| 543 | logger.info("creating swap partition {}".format(pnum)) | ||
| 544 | label = part.get("name") | ||
| 545 | label_str = "-L {}".format(label) if label else '' | ||
| 546 | out = exec_cmd("{} --probe {}".format(self.blkid, self._get_part_image(pnum))) | ||
| 547 | uuid = out[out.index("UUID=\"")+6:out.index("UUID=\"")+42] | ||
| 548 | uuid_str = "-U {}".format(uuid) if uuid else '' | ||
| 549 | with open(partfname, 'w') as sparse: | ||
| 550 | os.ftruncate(sparse.fileno(), part['size'] * self._lsector_size) | ||
| 551 | exec_cmd("{} {} {} {}".format(self.mkswap, label_str, uuid_str, partfname)) | ||
| 552 | sparse_copy(partfname, target, seek=part['start'] * self._lsector_size) | ||
| 553 | os.unlink(partfname) | ||
| 554 | elif part['type'] != 'f': | ||
| 555 | logger.warning("skipping partition {}: unsupported fstype {}".format(pnum, fstype)) | ||
| 556 | |||
| 557 | def wic_ls(args, native_sysroot): | ||
| 558 | """List contents of partitioned image or vfat partition.""" | ||
| 559 | disk = Disk(args.path.image, native_sysroot) | ||
| 560 | if not args.path.part: | ||
| 561 | if disk.partitions: | ||
| 562 | print('Num Start End Size Fstype') | ||
| 563 | for part in disk.partitions.values(): | ||
| 564 | print("{:2d} {:12d} {:12d} {:12d} {}".format(\ | ||
| 565 | part.pnum, part.start, part.end, | ||
| 566 | part.size, part.fstype)) | ||
| 567 | else: | ||
| 568 | path = args.path.path or '/' | ||
| 569 | print(disk.dir(args.path.part, path)) | ||
| 570 | |||
| 571 | def wic_cp(args, native_sysroot): | ||
| 572 | """ | ||
| 573 | Copy file or directory to/from the vfat/ext partition of | ||
| 574 | partitioned image. | ||
| 575 | """ | ||
| 576 | if isinstance(args.dest, str): | ||
| 577 | disk = Disk(args.src.image, native_sysroot) | ||
| 578 | else: | ||
| 579 | disk = Disk(args.dest.image, native_sysroot) | ||
| 580 | disk.copy(args.src, args.dest) | ||
| 581 | |||
| 582 | |||
| 583 | def wic_rm(args, native_sysroot): | ||
| 584 | """ | ||
| 585 | Remove files or directories from the vfat partition of | ||
| 586 | partitioned image. | ||
| 587 | """ | ||
| 588 | disk = Disk(args.path.image, native_sysroot) | ||
| 589 | disk.remove(args.path.part, args.path.path, args.recursive_delete) | ||
| 590 | |||
| 591 | def wic_write(args, native_sysroot): | ||
| 592 | """ | ||
| 593 | Write image to a target device. | ||
| 594 | """ | ||
| 595 | disk = Disk(args.image, native_sysroot, ('fat', 'ext', 'linux-swap')) | ||
| 596 | disk.write(args.target, args.expand) | ||
| 597 | |||
| 598 | def find_canned(scripts_path, file_name): | ||
| 599 | """ | ||
| 600 | Find a file either by its path or by name in the canned files dir. | ||
| 601 | |||
| 602 | Return None if not found | ||
| 603 | """ | ||
| 604 | if os.path.exists(file_name): | ||
| 605 | return file_name | ||
| 606 | |||
| 607 | layers_canned_wks_dir = build_canned_image_list(scripts_path) | ||
| 608 | for canned_wks_dir in layers_canned_wks_dir: | ||
| 609 | for root, dirs, files in os.walk(canned_wks_dir): | ||
| 610 | for fname in files: | ||
| 611 | if fname == file_name: | ||
| 612 | fullpath = os.path.join(canned_wks_dir, fname) | ||
| 613 | return fullpath | ||
| 614 | |||
| 615 | def get_custom_config(boot_file): | ||
| 616 | """ | ||
| 617 | Get the custom configuration to be used for the bootloader. | ||
| 618 | |||
| 619 | Return None if the file can't be found. | ||
| 620 | """ | ||
| 621 | # Get the scripts path of poky | ||
| 622 | scripts_path = os.path.abspath("%s/../.." % os.path.dirname(__file__)) | ||
| 623 | |||
| 624 | cfg_file = find_canned(scripts_path, boot_file) | ||
| 625 | if cfg_file: | ||
| 626 | with open(cfg_file, "r") as f: | ||
| 627 | config = f.read() | ||
| 628 | return config | ||
diff --git a/scripts/lib/wic/filemap.py b/scripts/lib/wic/filemap.py deleted file mode 100644 index 85b39d5d74..0000000000 --- a/scripts/lib/wic/filemap.py +++ /dev/null | |||
| @@ -1,583 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2012 Intel, Inc. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | |||
| 7 | """ | ||
| 8 | This module implements python implements a way to get file block. Two methods | ||
| 9 | are supported - the FIEMAP ioctl and the 'SEEK_HOLE / SEEK_DATA' features of | ||
| 10 | the file seek syscall. The former is implemented by the 'FilemapFiemap' class, | ||
| 11 | the latter is implemented by the 'FilemapSeek' class. Both classes provide the | ||
| 12 | same API. The 'filemap' function automatically selects which class can be used | ||
| 13 | and returns an instance of the class. | ||
| 14 | """ | ||
| 15 | |||
| 16 | # Disable the following pylint recommendations: | ||
| 17 | # * Too many instance attributes (R0902) | ||
| 18 | # pylint: disable=R0902 | ||
| 19 | |||
| 20 | import errno | ||
| 21 | import os | ||
| 22 | import struct | ||
| 23 | import array | ||
| 24 | import fcntl | ||
| 25 | import tempfile | ||
| 26 | import logging | ||
| 27 | |||
| 28 | def get_block_size(file_obj): | ||
| 29 | """ | ||
| 30 | Returns block size for file object 'file_obj'. Errors are indicated by the | ||
| 31 | 'IOError' exception. | ||
| 32 | """ | ||
| 33 | # Get the block size of the host file-system for the image file by calling | ||
| 34 | # the FIGETBSZ ioctl (number 2). | ||
| 35 | try: | ||
| 36 | binary_data = fcntl.ioctl(file_obj, 2, struct.pack('I', 0)) | ||
| 37 | bsize = struct.unpack('I', binary_data)[0] | ||
| 38 | except OSError: | ||
| 39 | bsize = None | ||
| 40 | |||
| 41 | # If ioctl causes OSError or give bsize to zero failback to os.fstat | ||
| 42 | if not bsize: | ||
| 43 | import os | ||
| 44 | stat = os.fstat(file_obj.fileno()) | ||
| 45 | if hasattr(stat, 'st_blksize'): | ||
| 46 | bsize = stat.st_blksize | ||
| 47 | else: | ||
| 48 | raise IOError("Unable to determine block size") | ||
| 49 | |||
| 50 | # The logic in this script only supports a maximum of a 4KB | ||
| 51 | # block size | ||
| 52 | max_block_size = 4 * 1024 | ||
| 53 | if bsize > max_block_size: | ||
| 54 | bsize = max_block_size | ||
| 55 | |||
| 56 | return bsize | ||
| 57 | |||
| 58 | class ErrorNotSupp(Exception): | ||
| 59 | """ | ||
| 60 | An exception of this type is raised when the 'FIEMAP' or 'SEEK_HOLE' feature | ||
| 61 | is not supported either by the kernel or the file-system. | ||
| 62 | """ | ||
| 63 | pass | ||
| 64 | |||
| 65 | class Error(Exception): | ||
| 66 | """A class for all the other exceptions raised by this module.""" | ||
| 67 | pass | ||
| 68 | |||
| 69 | |||
| 70 | class _FilemapBase(object): | ||
| 71 | """ | ||
| 72 | This is a base class for a couple of other classes in this module. This | ||
| 73 | class simply performs the common parts of the initialization process: opens | ||
| 74 | the image file, gets its size, etc. The 'log' parameter is the logger object | ||
| 75 | to use for printing messages. | ||
| 76 | """ | ||
| 77 | |||
| 78 | def __init__(self, image, log=None): | ||
| 79 | """ | ||
| 80 | Initialize a class instance. The 'image' argument is full path to the | ||
| 81 | file or file object to operate on. | ||
| 82 | """ | ||
| 83 | |||
| 84 | self._log = log | ||
| 85 | if self._log is None: | ||
| 86 | self._log = logging.getLogger(__name__) | ||
| 87 | |||
| 88 | self._f_image_needs_close = False | ||
| 89 | |||
| 90 | if hasattr(image, "fileno"): | ||
| 91 | self._f_image = image | ||
| 92 | self._image_path = image.name | ||
| 93 | else: | ||
| 94 | self._image_path = image | ||
| 95 | self._open_image_file() | ||
| 96 | |||
| 97 | try: | ||
| 98 | self.image_size = os.fstat(self._f_image.fileno()).st_size | ||
| 99 | except IOError as err: | ||
| 100 | raise Error("cannot get information about file '%s': %s" | ||
| 101 | % (self._f_image.name, err)) | ||
| 102 | |||
| 103 | try: | ||
| 104 | self.block_size = get_block_size(self._f_image) | ||
| 105 | except IOError as err: | ||
| 106 | raise Error("cannot get block size for '%s': %s" | ||
| 107 | % (self._image_path, err)) | ||
| 108 | |||
| 109 | self.blocks_cnt = self.image_size + self.block_size - 1 | ||
| 110 | self.blocks_cnt //= self.block_size | ||
| 111 | |||
| 112 | try: | ||
| 113 | self._f_image.flush() | ||
| 114 | except IOError as err: | ||
| 115 | raise Error("cannot flush image file '%s': %s" | ||
| 116 | % (self._image_path, err)) | ||
| 117 | |||
| 118 | try: | ||
| 119 | os.fsync(self._f_image.fileno()), | ||
| 120 | except OSError as err: | ||
| 121 | raise Error("cannot synchronize image file '%s': %s " | ||
| 122 | % (self._image_path, err.strerror)) | ||
| 123 | |||
| 124 | self._log.debug("opened image \"%s\"" % self._image_path) | ||
| 125 | self._log.debug("block size %d, blocks count %d, image size %d" | ||
| 126 | % (self.block_size, self.blocks_cnt, self.image_size)) | ||
| 127 | |||
| 128 | def __del__(self): | ||
| 129 | """The class destructor which just closes the image file.""" | ||
| 130 | if self._f_image_needs_close: | ||
| 131 | self._f_image.close() | ||
| 132 | |||
| 133 | def _open_image_file(self): | ||
| 134 | """Open the image file.""" | ||
| 135 | try: | ||
| 136 | self._f_image = open(self._image_path, 'rb') | ||
| 137 | except IOError as err: | ||
| 138 | raise Error("cannot open image file '%s': %s" | ||
| 139 | % (self._image_path, err)) | ||
| 140 | |||
| 141 | self._f_image_needs_close = True | ||
| 142 | |||
| 143 | def block_is_mapped(self, block): # pylint: disable=W0613,R0201 | ||
| 144 | """ | ||
| 145 | This method has has to be implemented by child classes. It returns | ||
| 146 | 'True' if block number 'block' of the image file is mapped and 'False' | ||
| 147 | otherwise. | ||
| 148 | """ | ||
| 149 | |||
| 150 | raise Error("the method is not implemented") | ||
| 151 | |||
| 152 | def get_mapped_ranges(self, start, count): # pylint: disable=W0613,R0201 | ||
| 153 | """ | ||
| 154 | This method has has to be implemented by child classes. This is a | ||
| 155 | generator which yields ranges of mapped blocks in the file. The ranges | ||
| 156 | are tuples of 2 elements: [first, last], where 'first' is the first | ||
| 157 | mapped block and 'last' is the last mapped block. | ||
| 158 | |||
| 159 | The ranges are yielded for the area of the file of size 'count' blocks, | ||
| 160 | starting from block 'start'. | ||
| 161 | """ | ||
| 162 | |||
| 163 | raise Error("the method is not implemented") | ||
| 164 | |||
| 165 | |||
| 166 | # The 'SEEK_HOLE' and 'SEEK_DATA' options of the file seek system call | ||
| 167 | _SEEK_DATA = 3 | ||
| 168 | _SEEK_HOLE = 4 | ||
| 169 | |||
| 170 | def _lseek(file_obj, offset, whence): | ||
| 171 | """This is a helper function which invokes 'os.lseek' for file object | ||
| 172 | 'file_obj' and with specified 'offset' and 'whence'. The 'whence' | ||
| 173 | argument is supposed to be either '_SEEK_DATA' or '_SEEK_HOLE'. When | ||
| 174 | there is no more data or hole starting from 'offset', this function | ||
| 175 | returns '-1'. Otherwise the data or hole position is returned.""" | ||
| 176 | |||
| 177 | try: | ||
| 178 | return os.lseek(file_obj.fileno(), offset, whence) | ||
| 179 | except OSError as err: | ||
| 180 | # The 'lseek' system call returns the ENXIO if there is no data or | ||
| 181 | # hole starting from the specified offset. | ||
| 182 | if err.errno == errno.ENXIO: | ||
| 183 | return -1 | ||
| 184 | elif err.errno == errno.EINVAL: | ||
| 185 | raise ErrorNotSupp("the kernel or file-system does not support " | ||
| 186 | "\"SEEK_HOLE\" and \"SEEK_DATA\"") | ||
| 187 | else: | ||
| 188 | raise | ||
| 189 | |||
| 190 | class FilemapSeek(_FilemapBase): | ||
| 191 | """ | ||
| 192 | This class uses the 'SEEK_HOLE' and 'SEEK_DATA' to find file block mapping. | ||
| 193 | Unfortunately, the current implementation requires the caller to have write | ||
| 194 | access to the image file. | ||
| 195 | """ | ||
| 196 | |||
| 197 | def __init__(self, image, log=None): | ||
| 198 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 199 | |||
| 200 | # Call the base class constructor first | ||
| 201 | _FilemapBase.__init__(self, image, log) | ||
| 202 | self._log.debug("FilemapSeek: initializing") | ||
| 203 | |||
| 204 | self._probe_seek_hole() | ||
| 205 | |||
| 206 | def _probe_seek_hole(self): | ||
| 207 | """ | ||
| 208 | Check whether the system implements 'SEEK_HOLE' and 'SEEK_DATA'. | ||
| 209 | Unfortunately, there seems to be no clean way for detecting this, | ||
| 210 | because often the system just fakes them by just assuming that all | ||
| 211 | files are fully mapped, so 'SEEK_HOLE' always returns EOF and | ||
| 212 | 'SEEK_DATA' always returns the requested offset. | ||
| 213 | |||
| 214 | I could not invent a better way of detecting the fake 'SEEK_HOLE' | ||
| 215 | implementation than just to create a temporary file in the same | ||
| 216 | directory where the image file resides. It would be nice to change this | ||
| 217 | to something better. | ||
| 218 | """ | ||
| 219 | |||
| 220 | directory = os.path.dirname(self._image_path) | ||
| 221 | |||
| 222 | try: | ||
| 223 | tmp_obj = tempfile.TemporaryFile("w+", dir=directory) | ||
| 224 | except IOError as err: | ||
| 225 | raise ErrorNotSupp("cannot create a temporary in \"%s\": %s" \ | ||
| 226 | % (directory, err)) | ||
| 227 | |||
| 228 | try: | ||
| 229 | os.ftruncate(tmp_obj.fileno(), self.block_size) | ||
| 230 | except OSError as err: | ||
| 231 | raise ErrorNotSupp("cannot truncate temporary file in \"%s\": %s" | ||
| 232 | % (directory, err)) | ||
| 233 | |||
| 234 | offs = _lseek(tmp_obj, 0, _SEEK_HOLE) | ||
| 235 | if offs != 0: | ||
| 236 | # We are dealing with the stub 'SEEK_HOLE' implementation which | ||
| 237 | # always returns EOF. | ||
| 238 | self._log.debug("lseek(0, SEEK_HOLE) returned %d" % offs) | ||
| 239 | raise ErrorNotSupp("the file-system does not support " | ||
| 240 | "\"SEEK_HOLE\" and \"SEEK_DATA\" but only " | ||
| 241 | "provides a stub implementation") | ||
| 242 | |||
| 243 | tmp_obj.close() | ||
| 244 | |||
| 245 | def block_is_mapped(self, block): | ||
| 246 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 247 | offs = _lseek(self._f_image, block * self.block_size, _SEEK_DATA) | ||
| 248 | if offs == -1: | ||
| 249 | result = False | ||
| 250 | else: | ||
| 251 | result = (offs // self.block_size == block) | ||
| 252 | |||
| 253 | self._log.debug("FilemapSeek: block_is_mapped(%d) returns %s" | ||
| 254 | % (block, result)) | ||
| 255 | return result | ||
| 256 | |||
| 257 | def _get_ranges(self, start, count, whence1, whence2): | ||
| 258 | """ | ||
| 259 | This function implements 'get_mapped_ranges()' depending | ||
| 260 | on what is passed in the 'whence1' and 'whence2' arguments. | ||
| 261 | """ | ||
| 262 | |||
| 263 | assert whence1 != whence2 | ||
| 264 | end = start * self.block_size | ||
| 265 | limit = end + count * self.block_size | ||
| 266 | |||
| 267 | while True: | ||
| 268 | start = _lseek(self._f_image, end, whence1) | ||
| 269 | if start == -1 or start >= limit or start == self.image_size: | ||
| 270 | break | ||
| 271 | |||
| 272 | end = _lseek(self._f_image, start, whence2) | ||
| 273 | if end == -1 or end == self.image_size: | ||
| 274 | end = self.blocks_cnt * self.block_size | ||
| 275 | if end > limit: | ||
| 276 | end = limit | ||
| 277 | |||
| 278 | start_blk = start // self.block_size | ||
| 279 | end_blk = end // self.block_size - 1 | ||
| 280 | self._log.debug("FilemapSeek: yielding range (%d, %d)" | ||
| 281 | % (start_blk, end_blk)) | ||
| 282 | yield (start_blk, end_blk) | ||
| 283 | |||
| 284 | def get_mapped_ranges(self, start, count): | ||
| 285 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 286 | self._log.debug("FilemapSeek: get_mapped_ranges(%d, %d(%d))" | ||
| 287 | % (start, count, start + count - 1)) | ||
| 288 | return self._get_ranges(start, count, _SEEK_DATA, _SEEK_HOLE) | ||
| 289 | |||
| 290 | |||
| 291 | # Below goes the FIEMAP ioctl implementation, which is not very readable | ||
| 292 | # because it deals with the rather complex FIEMAP ioctl. To understand the | ||
| 293 | # code, you need to know the FIEMAP interface, which is documented in the | ||
| 294 | # "Documentation/filesystems/fiemap.txt" file in the Linux kernel sources. | ||
| 295 | |||
| 296 | # Format string for 'struct fiemap' | ||
| 297 | _FIEMAP_FORMAT = "=QQLLLL" | ||
| 298 | # sizeof(struct fiemap) | ||
| 299 | _FIEMAP_SIZE = struct.calcsize(_FIEMAP_FORMAT) | ||
| 300 | # Format string for 'struct fiemap_extent' | ||
| 301 | _FIEMAP_EXTENT_FORMAT = "=QQQQQLLLL" | ||
| 302 | # sizeof(struct fiemap_extent) | ||
| 303 | _FIEMAP_EXTENT_SIZE = struct.calcsize(_FIEMAP_EXTENT_FORMAT) | ||
| 304 | # The FIEMAP ioctl number | ||
| 305 | _FIEMAP_IOCTL = 0xC020660B | ||
| 306 | # This FIEMAP ioctl flag which instructs the kernel to sync the file before | ||
| 307 | # reading the block map | ||
| 308 | _FIEMAP_FLAG_SYNC = 0x00000001 | ||
| 309 | # Size of the buffer for 'struct fiemap_extent' elements which will be used | ||
| 310 | # when invoking the FIEMAP ioctl. The larger is the buffer, the less times the | ||
| 311 | # FIEMAP ioctl will be invoked. | ||
| 312 | _FIEMAP_BUFFER_SIZE = 256 * 1024 | ||
| 313 | |||
| 314 | class FilemapFiemap(_FilemapBase): | ||
| 315 | """ | ||
| 316 | This class provides API to the FIEMAP ioctl. Namely, it allows to iterate | ||
| 317 | over all mapped blocks and over all holes. | ||
| 318 | |||
| 319 | This class synchronizes the image file every time it invokes the FIEMAP | ||
| 320 | ioctl in order to work-around early FIEMAP implementation kernel bugs. | ||
| 321 | """ | ||
| 322 | |||
| 323 | def __init__(self, image, log=None): | ||
| 324 | """ | ||
| 325 | Initialize a class instance. The 'image' argument is full the file | ||
| 326 | object to operate on. | ||
| 327 | """ | ||
| 328 | |||
| 329 | # Call the base class constructor first | ||
| 330 | _FilemapBase.__init__(self, image, log) | ||
| 331 | self._log.debug("FilemapFiemap: initializing") | ||
| 332 | |||
| 333 | self._buf_size = _FIEMAP_BUFFER_SIZE | ||
| 334 | |||
| 335 | # Calculate how many 'struct fiemap_extent' elements fit the buffer | ||
| 336 | self._buf_size -= _FIEMAP_SIZE | ||
| 337 | self._fiemap_extent_cnt = self._buf_size // _FIEMAP_EXTENT_SIZE | ||
| 338 | assert self._fiemap_extent_cnt > 0 | ||
| 339 | self._buf_size = self._fiemap_extent_cnt * _FIEMAP_EXTENT_SIZE | ||
| 340 | self._buf_size += _FIEMAP_SIZE | ||
| 341 | |||
| 342 | # Allocate a mutable buffer for the FIEMAP ioctl | ||
| 343 | self._buf = array.array('B', [0] * self._buf_size) | ||
| 344 | |||
| 345 | # Check if the FIEMAP ioctl is supported | ||
| 346 | self.block_is_mapped(0) | ||
| 347 | |||
| 348 | def _invoke_fiemap(self, block, count): | ||
| 349 | """ | ||
| 350 | Invoke the FIEMAP ioctl for 'count' blocks of the file starting from | ||
| 351 | block number 'block'. | ||
| 352 | |||
| 353 | The full result of the operation is stored in 'self._buf' on exit. | ||
| 354 | Returns the unpacked 'struct fiemap' data structure in form of a python | ||
| 355 | list (just like 'struct.upack()'). | ||
| 356 | """ | ||
| 357 | |||
| 358 | if self.blocks_cnt != 0 and (block < 0 or block >= self.blocks_cnt): | ||
| 359 | raise Error("bad block number %d, should be within [0, %d]" | ||
| 360 | % (block, self.blocks_cnt)) | ||
| 361 | |||
| 362 | # Initialize the 'struct fiemap' part of the buffer. We use the | ||
| 363 | # '_FIEMAP_FLAG_SYNC' flag in order to make sure the file is | ||
| 364 | # synchronized. The reason for this is that early FIEMAP | ||
| 365 | # implementations had many bugs related to cached dirty data, and | ||
| 366 | # synchronizing the file is a necessary work-around. | ||
| 367 | struct.pack_into(_FIEMAP_FORMAT, self._buf, 0, block * self.block_size, | ||
| 368 | count * self.block_size, _FIEMAP_FLAG_SYNC, 0, | ||
| 369 | self._fiemap_extent_cnt, 0) | ||
| 370 | |||
| 371 | try: | ||
| 372 | fcntl.ioctl(self._f_image, _FIEMAP_IOCTL, self._buf, 1) | ||
| 373 | except IOError as err: | ||
| 374 | # Note, the FIEMAP ioctl is supported by the Linux kernel starting | ||
| 375 | # from version 2.6.28 (year 2008). | ||
| 376 | if err.errno == errno.EOPNOTSUPP: | ||
| 377 | errstr = "FilemapFiemap: the FIEMAP ioctl is not supported " \ | ||
| 378 | "by the file-system" | ||
| 379 | self._log.debug(errstr) | ||
| 380 | raise ErrorNotSupp(errstr) | ||
| 381 | if err.errno == errno.ENOTTY: | ||
| 382 | errstr = "FilemapFiemap: the FIEMAP ioctl is not supported " \ | ||
| 383 | "by the kernel" | ||
| 384 | self._log.debug(errstr) | ||
| 385 | raise ErrorNotSupp(errstr) | ||
| 386 | raise Error("the FIEMAP ioctl failed for '%s': %s" | ||
| 387 | % (self._image_path, err)) | ||
| 388 | |||
| 389 | return struct.unpack(_FIEMAP_FORMAT, self._buf[:_FIEMAP_SIZE]) | ||
| 390 | |||
| 391 | def block_is_mapped(self, block): | ||
| 392 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 393 | struct_fiemap = self._invoke_fiemap(block, 1) | ||
| 394 | |||
| 395 | # The 3rd element of 'struct_fiemap' is the 'fm_mapped_extents' field. | ||
| 396 | # If it contains zero, the block is not mapped, otherwise it is | ||
| 397 | # mapped. | ||
| 398 | result = bool(struct_fiemap[3]) | ||
| 399 | self._log.debug("FilemapFiemap: block_is_mapped(%d) returns %s" | ||
| 400 | % (block, result)) | ||
| 401 | return result | ||
| 402 | |||
| 403 | def _unpack_fiemap_extent(self, index): | ||
| 404 | """ | ||
| 405 | Unpack a 'struct fiemap_extent' structure object number 'index' from | ||
| 406 | the internal 'self._buf' buffer. | ||
| 407 | """ | ||
| 408 | |||
| 409 | offset = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE * index | ||
| 410 | return struct.unpack(_FIEMAP_EXTENT_FORMAT, | ||
| 411 | self._buf[offset : offset + _FIEMAP_EXTENT_SIZE]) | ||
| 412 | |||
| 413 | def _do_get_mapped_ranges(self, start, count): | ||
| 414 | """ | ||
| 415 | Implements most the functionality for the 'get_mapped_ranges()' | ||
| 416 | generator: invokes the FIEMAP ioctl, walks through the mapped extents | ||
| 417 | and yields mapped block ranges. However, the ranges may be consecutive | ||
| 418 | (e.g., (1, 100), (100, 200)) and 'get_mapped_ranges()' simply merges | ||
| 419 | them. | ||
| 420 | """ | ||
| 421 | |||
| 422 | block = start | ||
| 423 | while block < start + count: | ||
| 424 | struct_fiemap = self._invoke_fiemap(block, count) | ||
| 425 | |||
| 426 | mapped_extents = struct_fiemap[3] | ||
| 427 | if mapped_extents == 0: | ||
| 428 | # No more mapped blocks | ||
| 429 | return | ||
| 430 | |||
| 431 | extent = 0 | ||
| 432 | while extent < mapped_extents: | ||
| 433 | fiemap_extent = self._unpack_fiemap_extent(extent) | ||
| 434 | |||
| 435 | # Start of the extent | ||
| 436 | extent_start = fiemap_extent[0] | ||
| 437 | # Starting block number of the extent | ||
| 438 | extent_block = extent_start // self.block_size | ||
| 439 | # Length of the extent | ||
| 440 | extent_len = fiemap_extent[2] | ||
| 441 | # Count of blocks in the extent | ||
| 442 | extent_count = extent_len // self.block_size | ||
| 443 | |||
| 444 | # Extent length and offset have to be block-aligned | ||
| 445 | assert extent_start % self.block_size == 0 | ||
| 446 | assert extent_len % self.block_size == 0 | ||
| 447 | |||
| 448 | if extent_block > start + count - 1: | ||
| 449 | return | ||
| 450 | |||
| 451 | first = max(extent_block, block) | ||
| 452 | last = min(extent_block + extent_count, start + count) - 1 | ||
| 453 | yield (first, last) | ||
| 454 | |||
| 455 | extent += 1 | ||
| 456 | |||
| 457 | block = extent_block + extent_count | ||
| 458 | |||
| 459 | def get_mapped_ranges(self, start, count): | ||
| 460 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 461 | self._log.debug("FilemapFiemap: get_mapped_ranges(%d, %d(%d))" | ||
| 462 | % (start, count, start + count - 1)) | ||
| 463 | iterator = self._do_get_mapped_ranges(start, count) | ||
| 464 | first_prev, last_prev = next(iterator) | ||
| 465 | |||
| 466 | for first, last in iterator: | ||
| 467 | if last_prev == first - 1: | ||
| 468 | last_prev = last | ||
| 469 | else: | ||
| 470 | self._log.debug("FilemapFiemap: yielding range (%d, %d)" | ||
| 471 | % (first_prev, last_prev)) | ||
| 472 | yield (first_prev, last_prev) | ||
| 473 | first_prev, last_prev = first, last | ||
| 474 | |||
| 475 | self._log.debug("FilemapFiemap: yielding range (%d, %d)" | ||
| 476 | % (first_prev, last_prev)) | ||
| 477 | yield (first_prev, last_prev) | ||
| 478 | |||
| 479 | class FilemapNobmap(_FilemapBase): | ||
| 480 | """ | ||
| 481 | This class is used when both the 'SEEK_DATA/HOLE' and FIEMAP are not | ||
| 482 | supported by the filesystem or kernel. | ||
| 483 | """ | ||
| 484 | |||
| 485 | def __init__(self, image, log=None): | ||
| 486 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 487 | |||
| 488 | # Call the base class constructor first | ||
| 489 | _FilemapBase.__init__(self, image, log) | ||
| 490 | self._log.debug("FilemapNobmap: initializing") | ||
| 491 | |||
| 492 | def block_is_mapped(self, block): | ||
| 493 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 494 | return True | ||
| 495 | |||
| 496 | def get_mapped_ranges(self, start, count): | ||
| 497 | """Refer the '_FilemapBase' class for the documentation.""" | ||
| 498 | self._log.debug("FilemapNobmap: get_mapped_ranges(%d, %d(%d))" | ||
| 499 | % (start, count, start + count - 1)) | ||
| 500 | yield (start, start + count -1) | ||
| 501 | |||
| 502 | def filemap(image, log=None): | ||
| 503 | """ | ||
| 504 | Create and return an instance of a Filemap class - 'FilemapFiemap' or | ||
| 505 | 'FilemapSeek', depending on what the system we run on supports. If the | ||
| 506 | FIEMAP ioctl is supported, an instance of the 'FilemapFiemap' class is | ||
| 507 | returned. Otherwise, if 'SEEK_HOLE' is supported an instance of the | ||
| 508 | 'FilemapSeek' class is returned. If none of these are supported, the | ||
| 509 | function generates an 'Error' type exception. | ||
| 510 | """ | ||
| 511 | |||
| 512 | try: | ||
| 513 | return FilemapFiemap(image, log) | ||
| 514 | except ErrorNotSupp: | ||
| 515 | try: | ||
| 516 | return FilemapSeek(image, log) | ||
| 517 | except ErrorNotSupp: | ||
| 518 | return FilemapNobmap(image, log) | ||
| 519 | |||
| 520 | def sparse_copy(src_fname, dst_fname, skip=0, seek=0, | ||
| 521 | length=0, api=None): | ||
| 522 | """ | ||
| 523 | Efficiently copy sparse file to or into another file. | ||
| 524 | |||
| 525 | src_fname: path to source file | ||
| 526 | dst_fname: path to destination file | ||
| 527 | skip: skip N bytes at thestart of src | ||
| 528 | seek: seek N bytes from the start of dst | ||
| 529 | length: read N bytes from src and write them to dst | ||
| 530 | api: FilemapFiemap or FilemapSeek object | ||
| 531 | """ | ||
| 532 | if not api: | ||
| 533 | api = filemap | ||
| 534 | fmap = api(src_fname) | ||
| 535 | try: | ||
| 536 | dst_file = open(dst_fname, 'r+b') | ||
| 537 | except IOError: | ||
| 538 | dst_file = open(dst_fname, 'wb') | ||
| 539 | if length: | ||
| 540 | dst_size = length + seek | ||
| 541 | else: | ||
| 542 | dst_size = os.path.getsize(src_fname) + seek - skip | ||
| 543 | dst_file.truncate(dst_size) | ||
| 544 | |||
| 545 | written = 0 | ||
| 546 | for first, last in fmap.get_mapped_ranges(0, fmap.blocks_cnt): | ||
| 547 | start = first * fmap.block_size | ||
| 548 | end = (last + 1) * fmap.block_size | ||
| 549 | |||
| 550 | if skip >= end: | ||
| 551 | continue | ||
| 552 | |||
| 553 | if start < skip < end: | ||
| 554 | start = skip | ||
| 555 | |||
| 556 | fmap._f_image.seek(start, os.SEEK_SET) | ||
| 557 | |||
| 558 | written += start - skip - written | ||
| 559 | if length and written >= length: | ||
| 560 | dst_file.seek(seek + length, os.SEEK_SET) | ||
| 561 | dst_file.close() | ||
| 562 | return | ||
| 563 | |||
| 564 | dst_file.seek(seek + start - skip, os.SEEK_SET) | ||
| 565 | |||
| 566 | chunk_size = 1024 * 1024 | ||
| 567 | to_read = end - start | ||
| 568 | read = 0 | ||
| 569 | |||
| 570 | while read < to_read: | ||
| 571 | if read + chunk_size > to_read: | ||
| 572 | chunk_size = to_read - read | ||
| 573 | size = chunk_size | ||
| 574 | if length and written + size > length: | ||
| 575 | size = length - written | ||
| 576 | chunk = fmap._f_image.read(size) | ||
| 577 | dst_file.write(chunk) | ||
| 578 | read += size | ||
| 579 | written += size | ||
| 580 | if written == length: | ||
| 581 | dst_file.close() | ||
| 582 | return | ||
| 583 | dst_file.close() | ||
diff --git a/scripts/lib/wic/help.py b/scripts/lib/wic/help.py deleted file mode 100644 index 163535e431..0000000000 --- a/scripts/lib/wic/help.py +++ /dev/null | |||
| @@ -1,1148 +0,0 @@ | |||
| 1 | # Copyright (c) 2013, Intel Corporation. | ||
| 2 | # | ||
| 3 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 4 | # | ||
| 5 | # DESCRIPTION | ||
| 6 | # This module implements some basic help invocation functions along | ||
| 7 | # with the bulk of the help topic text for the OE Core Image Tools. | ||
| 8 | # | ||
| 9 | # AUTHORS | ||
| 10 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 11 | # | ||
| 12 | |||
| 13 | import subprocess | ||
| 14 | import logging | ||
| 15 | |||
| 16 | from wic.pluginbase import PluginMgr, PLUGIN_TYPES | ||
| 17 | |||
| 18 | logger = logging.getLogger('wic') | ||
| 19 | |||
| 20 | def subcommand_error(args): | ||
| 21 | logger.info("invalid subcommand %s", args[0]) | ||
| 22 | |||
| 23 | |||
| 24 | def display_help(subcommand, subcommands): | ||
| 25 | """ | ||
| 26 | Display help for subcommand. | ||
| 27 | """ | ||
| 28 | if subcommand not in subcommands: | ||
| 29 | return False | ||
| 30 | |||
| 31 | hlp = subcommands.get(subcommand, subcommand_error)[2] | ||
| 32 | if callable(hlp): | ||
| 33 | hlp = hlp() | ||
| 34 | pager = subprocess.Popen('less', stdin=subprocess.PIPE) | ||
| 35 | pager.communicate(hlp.encode('utf-8')) | ||
| 36 | |||
| 37 | return True | ||
| 38 | |||
| 39 | |||
| 40 | def wic_help(args, usage_str, subcommands): | ||
| 41 | """ | ||
| 42 | Subcommand help dispatcher. | ||
| 43 | """ | ||
| 44 | if args.help_topic == None or not display_help(args.help_topic, subcommands): | ||
| 45 | print(usage_str) | ||
| 46 | |||
| 47 | |||
| 48 | def get_wic_plugins_help(): | ||
| 49 | """ | ||
| 50 | Combine wic_plugins_help with the help for every known | ||
| 51 | source plugin. | ||
| 52 | """ | ||
| 53 | result = wic_plugins_help | ||
| 54 | for plugin_type in PLUGIN_TYPES: | ||
| 55 | result += '\n\n%s PLUGINS\n\n' % plugin_type.upper() | ||
| 56 | for name, plugin in PluginMgr.get_plugins(plugin_type).items(): | ||
| 57 | result += "\n %s plugin:\n" % name | ||
| 58 | if plugin.__doc__: | ||
| 59 | result += plugin.__doc__ | ||
| 60 | else: | ||
| 61 | result += "\n %s is missing docstring\n" % plugin | ||
| 62 | return result | ||
| 63 | |||
| 64 | |||
| 65 | def invoke_subcommand(args, parser, main_command_usage, subcommands): | ||
| 66 | """ | ||
| 67 | Dispatch to subcommand handler borrowed from combo-layer. | ||
| 68 | Should use argparse, but has to work in 2.6. | ||
| 69 | """ | ||
| 70 | if not args.command: | ||
| 71 | logger.error("No subcommand specified, exiting") | ||
| 72 | parser.print_help() | ||
| 73 | return 1 | ||
| 74 | elif args.command == "help": | ||
| 75 | wic_help(args, main_command_usage, subcommands) | ||
| 76 | elif args.command not in subcommands: | ||
| 77 | logger.error("Unsupported subcommand %s, exiting\n", args.command) | ||
| 78 | parser.print_help() | ||
| 79 | return 1 | ||
| 80 | else: | ||
| 81 | subcmd = subcommands.get(args.command, subcommand_error) | ||
| 82 | usage = subcmd[1] | ||
| 83 | subcmd[0](args, usage) | ||
| 84 | |||
| 85 | |||
| 86 | ## | ||
| 87 | # wic help and usage strings | ||
| 88 | ## | ||
| 89 | |||
| 90 | wic_usage = """ | ||
| 91 | |||
| 92 | Create a customized OpenEmbedded image | ||
| 93 | |||
| 94 | usage: wic [--version] | [--help] | [COMMAND [ARGS]] | ||
| 95 | |||
| 96 | Current 'wic' commands are: | ||
| 97 | help Show help for command or one of the topics (see below) | ||
| 98 | create Create a new OpenEmbedded image | ||
| 99 | list List available canned images and source plugins | ||
| 100 | |||
| 101 | Help topics: | ||
| 102 | overview wic overview - General overview of wic | ||
| 103 | plugins wic plugins - Overview and API | ||
| 104 | kickstart wic kickstart - wic kickstart reference | ||
| 105 | """ | ||
| 106 | |||
| 107 | wic_help_usage = """ | ||
| 108 | |||
| 109 | usage: wic help <subcommand> | ||
| 110 | |||
| 111 | This command displays detailed help for the specified subcommand. | ||
| 112 | """ | ||
| 113 | |||
| 114 | wic_create_usage = """ | ||
| 115 | |||
| 116 | Create a new OpenEmbedded image | ||
| 117 | |||
| 118 | usage: wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>] | ||
| 119 | [-e | --image-name] [-s, --skip-build-check] [-D, --debug] | ||
| 120 | [-r, --rootfs-dir] [-b, --bootimg-dir] | ||
| 121 | [-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs] | ||
| 122 | [-c, --compress-with] [-m, --bmap] | ||
| 123 | |||
| 124 | This command creates an OpenEmbedded image based on the 'OE kickstart | ||
| 125 | commands' found in the <wks file>. | ||
| 126 | |||
| 127 | The -o option can be used to place the image in a directory with a | ||
| 128 | different name and location. | ||
| 129 | |||
| 130 | See 'wic help create' for more detailed instructions. | ||
| 131 | """ | ||
| 132 | |||
| 133 | wic_create_help = """ | ||
| 134 | |||
| 135 | NAME | ||
| 136 | wic create - Create a new OpenEmbedded image | ||
| 137 | |||
| 138 | SYNOPSIS | ||
| 139 | wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>] | ||
| 140 | [-e | --image-name] [-s, --skip-build-check] [-D, --debug] | ||
| 141 | [-r, --rootfs-dir] [-b, --bootimg-dir] | ||
| 142 | [-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs] | ||
| 143 | [-c, --compress-with] [-m, --bmap] [--no-fstab-update] | ||
| 144 | |||
| 145 | DESCRIPTION | ||
| 146 | This command creates an OpenEmbedded image based on the 'OE | ||
| 147 | kickstart commands' found in the <wks file>. | ||
| 148 | |||
| 149 | In order to do this, wic needs to know the locations of the | ||
| 150 | various build artifacts required to build the image. | ||
| 151 | |||
| 152 | Users can explicitly specify the build artifact locations using | ||
| 153 | the -r, -b, -k, and -n options. See below for details on where | ||
| 154 | the corresponding artifacts are typically found in a normal | ||
| 155 | OpenEmbedded build. | ||
| 156 | |||
| 157 | Alternatively, users can use the -e option to have 'wic' determine | ||
| 158 | those locations for a given image. If the -e option is used, the | ||
| 159 | user needs to have set the appropriate MACHINE variable in | ||
| 160 | local.conf, and have sourced the build environment. | ||
| 161 | |||
| 162 | The -e option is used to specify the name of the image to use the | ||
| 163 | artifacts from e.g. core-image-sato. | ||
| 164 | |||
| 165 | The -r option is used to specify the path to the /rootfs dir to | ||
| 166 | use as the .wks rootfs source. | ||
| 167 | |||
| 168 | The -b option is used to specify the path to the dir containing | ||
| 169 | the boot artifacts (e.g. /EFI or /syslinux dirs) to use as the | ||
| 170 | .wks bootimg source. | ||
| 171 | |||
| 172 | The -k option is used to specify the path to the dir containing | ||
| 173 | the kernel to use in the .wks bootimg. | ||
| 174 | |||
| 175 | The -n option is used to specify the path to the native sysroot | ||
| 176 | containing the tools to use to build the image. | ||
| 177 | |||
| 178 | The -f option is used to build rootfs by running "bitbake <image>" | ||
| 179 | |||
| 180 | The -s option is used to skip the build check. The build check is | ||
| 181 | a simple sanity check used to determine whether the user has | ||
| 182 | sourced the build environment so that the -e option can operate | ||
| 183 | correctly. If the user has specified the build artifact locations | ||
| 184 | explicitly, 'wic' assumes the user knows what he or she is doing | ||
| 185 | and skips the build check. | ||
| 186 | |||
| 187 | The -D option is used to display debug information detailing | ||
| 188 | exactly what happens behind the scenes when a create request is | ||
| 189 | fulfilled (or not, as the case may be). It enumerates and | ||
| 190 | displays the command sequence used, and should be included in any | ||
| 191 | bug report describing unexpected results. | ||
| 192 | |||
| 193 | When 'wic -e' is used, the locations for the build artifacts | ||
| 194 | values are determined by 'wic -e' from the output of the 'bitbake | ||
| 195 | -e' command given an image name e.g. 'core-image-minimal' and a | ||
| 196 | given machine set in local.conf. In that case, the image is | ||
| 197 | created as if the following 'bitbake -e' variables were used: | ||
| 198 | |||
| 199 | -r: IMAGE_ROOTFS | ||
| 200 | -k: STAGING_KERNEL_DIR | ||
| 201 | -n: STAGING_DIR_NATIVE | ||
| 202 | -b: empty (plugin-specific handlers must determine this) | ||
| 203 | |||
| 204 | If 'wic -e' is not used, the user needs to select the appropriate | ||
| 205 | value for -b (as well as -r, -k, and -n). | ||
| 206 | |||
| 207 | The -o option can be used to place the image in a directory with a | ||
| 208 | different name and location. | ||
| 209 | |||
| 210 | The -c option is used to specify compressor utility to compress | ||
| 211 | an image. gzip, bzip2 and xz compressors are supported. | ||
| 212 | |||
| 213 | The -m option is used to produce .bmap file for the image. This file | ||
| 214 | can be used to flash image using bmaptool utility. | ||
| 215 | |||
| 216 | The --no-fstab-update option is used to doesn't change fstab file. When | ||
| 217 | using this option the final fstab file will be same that in rootfs and | ||
| 218 | wic doesn't update file, e.g adding a new mount point. User can control | ||
| 219 | the fstab file content in base-files recipe. | ||
| 220 | """ | ||
| 221 | |||
| 222 | wic_list_usage = """ | ||
| 223 | |||
| 224 | List available OpenEmbedded images and source plugins | ||
| 225 | |||
| 226 | usage: wic list images | ||
| 227 | wic list <image> help | ||
| 228 | wic list source-plugins | ||
| 229 | |||
| 230 | This command enumerates the set of available canned images as well as | ||
| 231 | help for those images. It also can be used to list of available source | ||
| 232 | plugins. | ||
| 233 | |||
| 234 | The first form enumerates all the available 'canned' images. | ||
| 235 | |||
| 236 | The second form lists the detailed help information for a specific | ||
| 237 | 'canned' image. | ||
| 238 | |||
| 239 | The third form enumerates all the available --sources (source | ||
| 240 | plugins). | ||
| 241 | |||
| 242 | See 'wic help list' for more details. | ||
| 243 | """ | ||
| 244 | |||
| 245 | wic_list_help = """ | ||
| 246 | |||
| 247 | NAME | ||
| 248 | wic list - List available OpenEmbedded images and source plugins | ||
| 249 | |||
| 250 | SYNOPSIS | ||
| 251 | wic list images | ||
| 252 | wic list <image> help | ||
| 253 | wic list source-plugins | ||
| 254 | |||
| 255 | DESCRIPTION | ||
| 256 | This command enumerates the set of available canned images as well | ||
| 257 | as help for those images. It also can be used to list available | ||
| 258 | source plugins. | ||
| 259 | |||
| 260 | The first form enumerates all the available 'canned' images. | ||
| 261 | These are actually just the set of .wks files that have been moved | ||
| 262 | into the /scripts/lib/wic/canned-wks directory). | ||
| 263 | |||
| 264 | The second form lists the detailed help information for a specific | ||
| 265 | 'canned' image. | ||
| 266 | |||
| 267 | The third form enumerates all the available --sources (source | ||
| 268 | plugins). The contents of a given partition are driven by code | ||
| 269 | defined in 'source plugins'. Users specify a specific plugin via | ||
| 270 | the --source parameter of the partition .wks command. Normally | ||
| 271 | this is the 'rootfs' plugin but can be any of the more specialized | ||
| 272 | sources listed by the 'list source-plugins' command. Users can | ||
| 273 | also add their own source plugins - see 'wic help plugins' for | ||
| 274 | details. | ||
| 275 | """ | ||
| 276 | |||
| 277 | wic_ls_usage = """ | ||
| 278 | |||
| 279 | List content of a partitioned image | ||
| 280 | |||
| 281 | usage: wic ls <image>[:<partition>[<path>]] [--native-sysroot <path>] | ||
| 282 | |||
| 283 | This command outputs either list of image partitions or directory contents | ||
| 284 | of vfat and ext* partitions. | ||
| 285 | |||
| 286 | See 'wic help ls' for more detailed instructions. | ||
| 287 | |||
| 288 | """ | ||
| 289 | |||
| 290 | wic_ls_help = """ | ||
| 291 | |||
| 292 | NAME | ||
| 293 | wic ls - List contents of partitioned image or partition | ||
| 294 | |||
| 295 | SYNOPSIS | ||
| 296 | wic ls <image> | ||
| 297 | wic ls <image>:<vfat or ext* partition> | ||
| 298 | wic ls <image>:<vfat or ext* partition><path> | ||
| 299 | wic ls <image>:<vfat or ext* partition><path> --native-sysroot <path> | ||
| 300 | |||
| 301 | DESCRIPTION | ||
| 302 | This command lists either partitions of the image or directory contents | ||
| 303 | of vfat or ext* partitions. | ||
| 304 | |||
| 305 | The first form it lists partitions of the image. | ||
| 306 | For example: | ||
| 307 | $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic | ||
| 308 | Num Start End Size Fstype | ||
| 309 | 1 1048576 24438783 23390208 fat16 | ||
| 310 | 2 25165824 50315263 25149440 ext4 | ||
| 311 | |||
| 312 | Second and third form list directory content of the partition: | ||
| 313 | $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 | ||
| 314 | Volume in drive : is boot | ||
| 315 | Volume Serial Number is 2DF2-5F02 | ||
| 316 | Directory for ::/ | ||
| 317 | |||
| 318 | efi <DIR> 2017-05-11 10:54 | ||
| 319 | startup nsh 26 2017-05-11 10:54 | ||
| 320 | vmlinuz 6922288 2017-05-11 10:54 | ||
| 321 | 3 files 6 922 314 bytes | ||
| 322 | 15 818 752 bytes free | ||
| 323 | |||
| 324 | |||
| 325 | $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/EFI/boot/ | ||
| 326 | Volume in drive : is boot | ||
| 327 | Volume Serial Number is 2DF2-5F02 | ||
| 328 | Directory for ::/EFI/boot | ||
| 329 | |||
| 330 | . <DIR> 2017-05-11 10:54 | ||
| 331 | .. <DIR> 2017-05-11 10:54 | ||
| 332 | grub cfg 679 2017-05-11 10:54 | ||
| 333 | bootx64 efi 571392 2017-05-11 10:54 | ||
| 334 | 4 files 572 071 bytes | ||
| 335 | 15 818 752 bytes free | ||
| 336 | |||
| 337 | The -n option is used to specify the path to the native sysroot | ||
| 338 | containing the tools(parted and mtools) to use. | ||
| 339 | |||
| 340 | """ | ||
| 341 | |||
| 342 | wic_cp_usage = """ | ||
| 343 | |||
| 344 | Copy files and directories to/from the vfat or ext* partition | ||
| 345 | |||
| 346 | usage: wic cp <src> <dest> [--native-sysroot <path>] | ||
| 347 | |||
| 348 | source/destination image in format <image>:<partition>[<path>] | ||
| 349 | |||
| 350 | This command copies files or directories either | ||
| 351 | - from local to vfat or ext* partitions of partitioned image | ||
| 352 | - from vfat or ext* partitions of partitioned image to local | ||
| 353 | |||
| 354 | See 'wic help cp' for more detailed instructions. | ||
| 355 | |||
| 356 | """ | ||
| 357 | |||
| 358 | wic_cp_help = """ | ||
| 359 | |||
| 360 | NAME | ||
| 361 | wic cp - copy files and directories to/from the vfat or ext* partitions | ||
| 362 | |||
| 363 | SYNOPSIS | ||
| 364 | wic cp <src> <dest>:<partition> | ||
| 365 | wic cp <src>:<partition> <dest> | ||
| 366 | wic cp <src> <dest-image>:<partition><path> | ||
| 367 | wic cp <src> <dest-image>:<partition><path> --native-sysroot <path> | ||
| 368 | |||
| 369 | DESCRIPTION | ||
| 370 | This command copies files or directories either | ||
| 371 | - from local to vfat or ext* partitions of partitioned image | ||
| 372 | - from vfat or ext* partitions of partitioned image to local | ||
| 373 | |||
| 374 | The first form of it copies file or directory to the root directory of | ||
| 375 | the partition: | ||
| 376 | $ wic cp test.wks tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 | ||
| 377 | $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 | ||
| 378 | Volume in drive : is boot | ||
| 379 | Volume Serial Number is DB4C-FD4C | ||
| 380 | Directory for ::/ | ||
| 381 | |||
| 382 | efi <DIR> 2017-05-24 18:15 | ||
| 383 | loader <DIR> 2017-05-24 18:15 | ||
| 384 | startup nsh 26 2017-05-24 18:15 | ||
| 385 | vmlinuz 6926384 2017-05-24 18:15 | ||
| 386 | test wks 628 2017-05-24 21:22 | ||
| 387 | 5 files 6 927 038 bytes | ||
| 388 | 15 677 440 bytes free | ||
| 389 | |||
| 390 | The second form of the command copies file or directory to the specified directory | ||
| 391 | on the partition: | ||
| 392 | $ wic cp test tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/efi/ | ||
| 393 | $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/efi/ | ||
| 394 | Volume in drive : is boot | ||
| 395 | Volume Serial Number is DB4C-FD4C | ||
| 396 | Directory for ::/efi | ||
| 397 | |||
| 398 | . <DIR> 2017-05-24 18:15 | ||
| 399 | .. <DIR> 2017-05-24 18:15 | ||
| 400 | boot <DIR> 2017-05-24 18:15 | ||
| 401 | test <DIR> 2017-05-24 21:27 | ||
| 402 | 4 files 0 bytes | ||
| 403 | 15 675 392 bytes free | ||
| 404 | |||
| 405 | The third form of the command copies file or directory from the specified directory | ||
| 406 | on the partition to local: | ||
| 407 | $ wic cp tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/vmlinuz test | ||
| 408 | |||
| 409 | The -n option is used to specify the path to the native sysroot | ||
| 410 | containing the tools(parted and mtools) to use. | ||
| 411 | """ | ||
| 412 | |||
| 413 | wic_rm_usage = """ | ||
| 414 | |||
| 415 | Remove files or directories from the vfat or ext* partitions | ||
| 416 | |||
| 417 | usage: wic rm <image>:<partition><path> [--native-sysroot <path>] | ||
| 418 | |||
| 419 | This command removes files or directories from the vfat or ext* partitions of | ||
| 420 | the partitioned image. | ||
| 421 | |||
| 422 | See 'wic help rm' for more detailed instructions. | ||
| 423 | |||
| 424 | """ | ||
| 425 | |||
| 426 | wic_rm_help = """ | ||
| 427 | |||
| 428 | NAME | ||
| 429 | wic rm - remove files or directories from the vfat or ext* partitions | ||
| 430 | |||
| 431 | SYNOPSIS | ||
| 432 | wic rm <src> <image>:<partition><path> | ||
| 433 | wic rm <src> <image>:<partition><path> --native-sysroot <path> | ||
| 434 | wic rm -r <image>:<partition><path> | ||
| 435 | |||
| 436 | DESCRIPTION | ||
| 437 | This command removes files or directories from the vfat or ext* partition of the | ||
| 438 | partitioned image: | ||
| 439 | |||
| 440 | $ wic ls ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 | ||
| 441 | Volume in drive : is boot | ||
| 442 | Volume Serial Number is 11D0-DE21 | ||
| 443 | Directory for ::/ | ||
| 444 | |||
| 445 | libcom32 c32 186500 2017-06-02 15:15 | ||
| 446 | libutil c32 24148 2017-06-02 15:15 | ||
| 447 | syslinux cfg 209 2017-06-02 15:15 | ||
| 448 | vesamenu c32 27104 2017-06-02 15:15 | ||
| 449 | vmlinuz 6926384 2017-06-02 15:15 | ||
| 450 | 5 files 7 164 345 bytes | ||
| 451 | 16 582 656 bytes free | ||
| 452 | |||
| 453 | $ wic rm ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/libutil.c32 | ||
| 454 | |||
| 455 | $ wic ls ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1 | ||
| 456 | Volume in drive : is boot | ||
| 457 | Volume Serial Number is 11D0-DE21 | ||
| 458 | Directory for ::/ | ||
| 459 | |||
| 460 | libcom32 c32 186500 2017-06-02 15:15 | ||
| 461 | syslinux cfg 209 2017-06-02 15:15 | ||
| 462 | vesamenu c32 27104 2017-06-02 15:15 | ||
| 463 | vmlinuz 6926384 2017-06-02 15:15 | ||
| 464 | 4 files 7 140 197 bytes | ||
| 465 | 16 607 232 bytes free | ||
| 466 | |||
| 467 | The -n option is used to specify the path to the native sysroot | ||
| 468 | containing the tools(parted and mtools) to use. | ||
| 469 | |||
| 470 | The -r option is used to remove directories and their contents | ||
| 471 | recursively,this only applies to ext* partition. | ||
| 472 | """ | ||
| 473 | |||
| 474 | wic_write_usage = """ | ||
| 475 | |||
| 476 | Write image to a device | ||
| 477 | |||
| 478 | usage: wic write <image> <target device> [--expand [rules]] [--native-sysroot <path>] | ||
| 479 | |||
| 480 | This command writes partitioned image to a target device (USB stick, SD card etc). | ||
| 481 | |||
| 482 | See 'wic help write' for more detailed instructions. | ||
| 483 | |||
| 484 | """ | ||
| 485 | |||
| 486 | wic_write_help = """ | ||
| 487 | |||
| 488 | NAME | ||
| 489 | wic write - write an image to a device | ||
| 490 | |||
| 491 | SYNOPSIS | ||
| 492 | wic write <image> <target> | ||
| 493 | wic write <image> <target> --expand auto | ||
| 494 | wic write <image> <target> --expand 1:100M,2:300M | ||
| 495 | wic write <image> <target> --native-sysroot <path> | ||
| 496 | |||
| 497 | DESCRIPTION | ||
| 498 | This command writes an image to a target device (USB stick, SD card etc) | ||
| 499 | |||
| 500 | $ wic write ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic /dev/sdb | ||
| 501 | |||
| 502 | The --expand option is used to resize image partitions. | ||
| 503 | --expand auto expands partitions to occupy all free space available on the target device. | ||
| 504 | It's also possible to specify expansion rules in a format | ||
| 505 | <partition>:<size>[,<partition>:<size>...] for one or more partitions. | ||
| 506 | Specifying size 0 will keep partition unmodified. | ||
| 507 | Note: Resizing boot partition can result in non-bootable image for non-EFI images. It is | ||
| 508 | recommended to use size 0 for boot partition to keep image bootable. | ||
| 509 | |||
| 510 | The --native-sysroot option is used to specify the path to the native sysroot | ||
| 511 | containing the tools(parted, resize2fs) to use. | ||
| 512 | """ | ||
| 513 | |||
| 514 | wic_plugins_help = """ | ||
| 515 | |||
| 516 | NAME | ||
| 517 | wic plugins - Overview and API | ||
| 518 | |||
| 519 | DESCRIPTION | ||
| 520 | plugins allow wic functionality to be extended and specialized by | ||
| 521 | users. This section documents the plugin interface, which is | ||
| 522 | currently restricted to 'source' plugins. | ||
| 523 | |||
| 524 | 'Source' plugins provide a mechanism to customize various aspects | ||
| 525 | of the image generation process in wic, mainly the contents of | ||
| 526 | partitions. | ||
| 527 | |||
| 528 | Source plugins provide a mechanism for mapping values specified in | ||
| 529 | .wks files using the --source keyword to a particular plugin | ||
| 530 | implementation that populates a corresponding partition. | ||
| 531 | |||
| 532 | A source plugin is created as a subclass of SourcePlugin (see | ||
| 533 | scripts/lib/wic/pluginbase.py) and the plugin file containing it | ||
| 534 | is added to scripts/lib/wic/plugins/source/ to make the plugin | ||
| 535 | implementation available to the wic implementation. | ||
| 536 | |||
| 537 | Source plugins can also be implemented and added by external | ||
| 538 | layers - any plugins found in a scripts/lib/wic/plugins/source/ | ||
| 539 | or lib/wic/plugins/source/ directory in an external layer will | ||
| 540 | also be made available. | ||
| 541 | |||
| 542 | When the wic implementation needs to invoke a partition-specific | ||
| 543 | implementation, it looks for the plugin that has the same name as | ||
| 544 | the --source param given to that partition. For example, if the | ||
| 545 | partition is set up like this: | ||
| 546 | |||
| 547 | part /boot --source bootimg-pcbios ... | ||
| 548 | |||
| 549 | then the methods defined as class members of the plugin having the | ||
| 550 | matching bootimg-pcbios .name class member would be used. | ||
| 551 | |||
| 552 | To be more concrete, here's the plugin definition that would match | ||
| 553 | a '--source bootimg-pcbios' usage, along with an example method | ||
| 554 | that would be called by the wic implementation when it needed to | ||
| 555 | invoke an implementation-specific partition-preparation function: | ||
| 556 | |||
| 557 | class BootimgPcbiosPlugin(SourcePlugin): | ||
| 558 | name = 'bootimg-pcbios' | ||
| 559 | |||
| 560 | @classmethod | ||
| 561 | def do_prepare_partition(self, part, ...) | ||
| 562 | |||
| 563 | If the subclass itself doesn't implement a function, a 'default' | ||
| 564 | version in a superclass will be located and used, which is why all | ||
| 565 | plugins must be derived from SourcePlugin. | ||
| 566 | |||
| 567 | The SourcePlugin class defines the following methods, which is the | ||
| 568 | current set of methods that can be implemented/overridden by | ||
| 569 | --source plugins. Any methods not implemented by a SourcePlugin | ||
| 570 | subclass inherit the implementations present in the SourcePlugin | ||
| 571 | class (see the SourcePlugin source for details): | ||
| 572 | |||
| 573 | do_prepare_partition() | ||
| 574 | Called to do the actual content population for a | ||
| 575 | partition. In other words, it 'prepares' the final partition | ||
| 576 | image which will be incorporated into the disk image. | ||
| 577 | |||
| 578 | do_post_partition() | ||
| 579 | Called after the partition is created. It is useful to add post | ||
| 580 | operations e.g. signing the partition. | ||
| 581 | |||
| 582 | do_configure_partition() | ||
| 583 | Called before do_prepare_partition(), typically used to | ||
| 584 | create custom configuration files for a partition, for | ||
| 585 | example syslinux or grub config files. | ||
| 586 | |||
| 587 | do_install_disk() | ||
| 588 | Called after all partitions have been prepared and assembled | ||
| 589 | into a disk image. This provides a hook to allow | ||
| 590 | finalization of a disk image, for example to write an MBR to | ||
| 591 | it. | ||
| 592 | |||
| 593 | do_stage_partition() | ||
| 594 | Special content-staging hook called before | ||
| 595 | do_prepare_partition(), normally empty. | ||
| 596 | |||
| 597 | Typically, a partition will just use the passed-in | ||
| 598 | parameters, for example the unmodified value of bootimg_dir. | ||
| 599 | In some cases however, things may need to be more tailored. | ||
| 600 | As an example, certain files may additionally need to be | ||
| 601 | take from bootimg_dir + /boot. This hook allows those files | ||
| 602 | to be staged in a customized fashion. Note that | ||
| 603 | get_bitbake_var() allows you to access non-standard | ||
| 604 | variables that you might want to use for these types of | ||
| 605 | situations. | ||
| 606 | |||
| 607 | This scheme is extensible - adding more hooks is a simple matter | ||
| 608 | of adding more plugin methods to SourcePlugin and derived classes. | ||
| 609 | Please see the implementation for details. | ||
| 610 | """ | ||
| 611 | |||
| 612 | wic_overview_help = """ | ||
| 613 | |||
| 614 | NAME | ||
| 615 | wic overview - General overview of wic | ||
| 616 | |||
| 617 | DESCRIPTION | ||
| 618 | The 'wic' command generates partitioned images from existing | ||
| 619 | OpenEmbedded build artifacts. Image generation is driven by | ||
| 620 | partitioning commands contained in an 'Openembedded kickstart' | ||
| 621 | (.wks) file (see 'wic help kickstart') specified either directly | ||
| 622 | on the command-line or as one of a selection of canned .wks files | ||
| 623 | (see 'wic list images'). When applied to a given set of build | ||
| 624 | artifacts, the result is an image or set of images that can be | ||
| 625 | directly written onto media and used on a particular system. | ||
| 626 | |||
| 627 | The 'wic' command and the infrastructure it's based on is by | ||
| 628 | definition incomplete - its purpose is to allow the generation of | ||
| 629 | customized images, and as such was designed to be completely | ||
| 630 | extensible via a plugin interface (see 'wic help plugins'). | ||
| 631 | |||
| 632 | Background and Motivation | ||
| 633 | |||
| 634 | wic is meant to be a completely independent standalone utility | ||
| 635 | that initially provides easier-to-use and more flexible | ||
| 636 | replacements for a couple bits of existing functionality in | ||
| 637 | oe-core: directdisk.bbclass and mkefidisk.sh. The difference | ||
| 638 | between wic and those examples is that with wic the functionality | ||
| 639 | of those scripts is implemented by a general-purpose partitioning | ||
| 640 | 'language' based on Red Hat kickstart syntax). | ||
| 641 | |||
| 642 | The initial motivation and design considerations that lead to the | ||
| 643 | current tool are described exhaustively in Yocto Bug #3847 | ||
| 644 | (https://bugzilla.yoctoproject.org/show_bug.cgi?id=3847). | ||
| 645 | |||
| 646 | Implementation and Examples | ||
| 647 | |||
| 648 | wic can be used in two different modes, depending on how much | ||
| 649 | control the user needs in specifying the Openembedded build | ||
| 650 | artifacts that will be used in creating the image: 'raw' and | ||
| 651 | 'cooked'. | ||
| 652 | |||
| 653 | If used in 'raw' mode, artifacts are explicitly specified via | ||
| 654 | command-line arguments (see example below). | ||
| 655 | |||
| 656 | The more easily usable 'cooked' mode uses the current MACHINE | ||
| 657 | setting and a specified image name to automatically locate the | ||
| 658 | artifacts used to create the image. | ||
| 659 | |||
| 660 | OE kickstart files (.wks) can of course be specified directly on | ||
| 661 | the command-line, but the user can also choose from a set of | ||
| 662 | 'canned' .wks files available via the 'wic list images' command | ||
| 663 | (example below). | ||
| 664 | |||
| 665 | In any case, the prerequisite for generating any image is to have | ||
| 666 | the build artifacts already available. The below examples assume | ||
| 667 | the user has already build a 'core-image-minimal' for a specific | ||
| 668 | machine (future versions won't require this redundant step, but | ||
| 669 | for now that's typically how build artifacts get generated). | ||
| 670 | |||
| 671 | The other prerequisite is to source the build environment: | ||
| 672 | |||
| 673 | $ source oe-init-build-env | ||
| 674 | |||
| 675 | To start out with, we'll generate an image from one of the canned | ||
| 676 | .wks files. The following generates a list of availailable | ||
| 677 | images: | ||
| 678 | |||
| 679 | $ wic list images | ||
| 680 | mkefidisk Create an EFI disk image | ||
| 681 | directdisk Create a 'pcbios' direct disk image | ||
| 682 | |||
| 683 | You can get more information about any of the available images by | ||
| 684 | typing 'wic list xxx help', where 'xxx' is one of the image names: | ||
| 685 | |||
| 686 | $ wic list mkefidisk help | ||
| 687 | |||
| 688 | Creates a partitioned EFI disk image that the user can directly dd | ||
| 689 | to boot media. | ||
| 690 | |||
| 691 | At any time, you can get help on the 'wic' command or any | ||
| 692 | subcommand (currently 'list' and 'create'). For instance, to get | ||
| 693 | the description of 'wic create' command and its parameters: | ||
| 694 | |||
| 695 | $ wic create | ||
| 696 | |||
| 697 | Usage: | ||
| 698 | |||
| 699 | Create a new OpenEmbedded image | ||
| 700 | |||
| 701 | usage: wic create <wks file or image name> [-o <DIRNAME> | ...] | ||
| 702 | [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] | ||
| 703 | [-e | --image-name] [-s, --skip-build-check] [-D, --debug] | ||
| 704 | [-r, --rootfs-dir] [-b, --bootimg-dir] [-k, --kernel-dir] | ||
| 705 | [-n, --native-sysroot] [-f, --build-rootfs] | ||
| 706 | |||
| 707 | This command creates an OpenEmbedded image based on the 'OE | ||
| 708 | kickstart commands' found in the <wks file>. | ||
| 709 | |||
| 710 | The -o option can be used to place the image in a directory | ||
| 711 | with a different name and location. | ||
| 712 | |||
| 713 | See 'wic help create' for more detailed instructions. | ||
| 714 | ... | ||
| 715 | |||
| 716 | As mentioned in the command, you can get even more detailed | ||
| 717 | information by adding 'help' to the above: | ||
| 718 | |||
| 719 | $ wic help create | ||
| 720 | |||
| 721 | So, the easiest way to create an image is to use the -e option | ||
| 722 | with a canned .wks file. To use the -e option, you need to | ||
| 723 | specify the image used to generate the artifacts and you actually | ||
| 724 | need to have the MACHINE used to build them specified in your | ||
| 725 | local.conf (these requirements aren't necessary if you aren't | ||
| 726 | using the -e options.) Below, we generate a directdisk image, | ||
| 727 | pointing the process at the core-image-minimal artifacts for the | ||
| 728 | current MACHINE: | ||
| 729 | |||
| 730 | $ wic create directdisk -e core-image-minimal | ||
| 731 | |||
| 732 | Checking basic build environment... | ||
| 733 | Done. | ||
| 734 | |||
| 735 | Creating image(s)... | ||
| 736 | |||
| 737 | Info: The new image(s) can be found here: | ||
| 738 | /var/tmp/wic/build/directdisk-201309252350-sda.direct | ||
| 739 | |||
| 740 | The following build artifacts were used to create the image(s): | ||
| 741 | |||
| 742 | ROOTFS_DIR: ... | ||
| 743 | BOOTIMG_DIR: ... | ||
| 744 | KERNEL_DIR: ... | ||
| 745 | NATIVE_SYSROOT: ... | ||
| 746 | |||
| 747 | The image(s) were created using OE kickstart file: | ||
| 748 | .../scripts/lib/wic/canned-wks/directdisk.wks | ||
| 749 | |||
| 750 | The output shows the name and location of the image created, and | ||
| 751 | so that you know exactly what was used to generate the image, each | ||
| 752 | of the artifacts and the kickstart file used. | ||
| 753 | |||
| 754 | Similarly, you can create a 'mkefidisk' image in the same way | ||
| 755 | (notice that this example uses a different machine - because it's | ||
| 756 | using the -e option, you need to change the MACHINE in your | ||
| 757 | local.conf): | ||
| 758 | |||
| 759 | $ wic create mkefidisk -e core-image-minimal | ||
| 760 | Checking basic build environment... | ||
| 761 | Done. | ||
| 762 | |||
| 763 | Creating image(s)... | ||
| 764 | |||
| 765 | Info: The new image(s) can be found here: | ||
| 766 | /var/tmp/wic/build/mkefidisk-201309260027-sda.direct | ||
| 767 | |||
| 768 | ... | ||
| 769 | |||
| 770 | Here's an example that doesn't take the easy way out and manually | ||
| 771 | specifies each build artifact, along with a non-canned .wks file, | ||
| 772 | and also uses the -o option to have wic create the output | ||
| 773 | somewhere other than the default /var/tmp/wic: | ||
| 774 | |||
| 775 | $ wic create ./test.wks -o ./out --rootfs-dir | ||
| 776 | tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0-r0/rootfs | ||
| 777 | --bootimg-dir tmp/sysroots/qemux86-64/usr/share | ||
| 778 | --kernel-dir tmp/deploy/images/qemux86-64 | ||
| 779 | --native-sysroot tmp/sysroots/x86_64-linux | ||
| 780 | |||
| 781 | Creating image(s)... | ||
| 782 | |||
| 783 | Info: The new image(s) can be found here: | ||
| 784 | out/build/test-201507211313-sda.direct | ||
| 785 | |||
| 786 | The following build artifacts were used to create the image(s): | ||
| 787 | ROOTFS_DIR: tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0-r0/rootfs | ||
| 788 | BOOTIMG_DIR: tmp/sysroots/qemux86-64/usr/share | ||
| 789 | KERNEL_DIR: tmp/deploy/images/qemux86-64 | ||
| 790 | NATIVE_SYSROOT: tmp/sysroots/x86_64-linux | ||
| 791 | |||
| 792 | The image(s) were created using OE kickstart file: | ||
| 793 | ./test.wks | ||
| 794 | |||
| 795 | Here is a content of test.wks: | ||
| 796 | |||
| 797 | part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 | ||
| 798 | part / --source rootfs --ondisk sda --fstype=ext3 --label platform --align 1024 | ||
| 799 | |||
| 800 | bootloader --timeout=0 --append="rootwait rootfstype=ext3 video=vesafb vga=0x318 console=tty0" | ||
| 801 | |||
| 802 | |||
| 803 | Finally, here's an example of the actual partition language | ||
| 804 | commands used to generate the mkefidisk image i.e. these are the | ||
| 805 | contents of the mkefidisk.wks OE kickstart file: | ||
| 806 | |||
| 807 | # short-description: Create an EFI disk image | ||
| 808 | # long-description: Creates a partitioned EFI disk image that the user | ||
| 809 | # can directly dd to boot media. | ||
| 810 | |||
| 811 | part /boot --source bootimg-efi --ondisk sda --fstype=efi --active | ||
| 812 | |||
| 813 | part / --source rootfs --ondisk sda --fstype=ext3 --label platform | ||
| 814 | |||
| 815 | part swap --ondisk sda --size 44 --label swap1 --fstype=swap | ||
| 816 | |||
| 817 | bootloader --timeout=10 --append="rootwait console=ttyPCH0,115200" | ||
| 818 | |||
| 819 | You can get a complete listing and description of all the | ||
| 820 | kickstart commands available for use in .wks files from 'wic help | ||
| 821 | kickstart'. | ||
| 822 | """ | ||
| 823 | |||
| 824 | wic_kickstart_help = """ | ||
| 825 | |||
| 826 | NAME | ||
| 827 | wic kickstart - wic kickstart reference | ||
| 828 | |||
| 829 | DESCRIPTION | ||
| 830 | This section provides the definitive reference to the wic | ||
| 831 | kickstart language. It also provides documentation on the list of | ||
| 832 | --source plugins available for use from the 'part' command (see | ||
| 833 | the 'Platform-specific Plugins' section below). | ||
| 834 | |||
| 835 | The current wic implementation supports only the basic kickstart | ||
| 836 | partitioning commands: partition (or part for short) and | ||
| 837 | bootloader. | ||
| 838 | |||
| 839 | The following is a listing of the commands, their syntax, and | ||
| 840 | meanings. The commands are based on the Fedora kickstart | ||
| 841 | documentation but with modifications to reflect wic capabilities. | ||
| 842 | |||
| 843 | https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#part-or-partition | ||
| 844 | https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#bootloader | ||
| 845 | |||
| 846 | Commands | ||
| 847 | |||
| 848 | * 'part' or 'partition' | ||
| 849 | |||
| 850 | This command creates a partition on the system and uses the | ||
| 851 | following syntax: | ||
| 852 | |||
| 853 | part [<mountpoint>] | ||
| 854 | |||
| 855 | The <mountpoint> is where the partition will be mounted and | ||
| 856 | must take of one of the following forms: | ||
| 857 | |||
| 858 | /<path>: For example: /, /usr, or /home | ||
| 859 | |||
| 860 | swap: The partition will be used as swap space. | ||
| 861 | |||
| 862 | If a <mountpoint> is not specified the partition will be created | ||
| 863 | but will not be mounted. | ||
| 864 | |||
| 865 | Partitions with a <mountpoint> specified will be automatically mounted. | ||
| 866 | This is achieved by wic adding entries to the fstab during image | ||
| 867 | generation. In order for a valid fstab to be generated one of the | ||
| 868 | --ondrive, --ondisk, --use-uuid or --use-label partition options must | ||
| 869 | be used for each partition that specifies a mountpoint. Note that with | ||
| 870 | --use-{uuid,label} and non-root <mountpoint>, including swap, the mount | ||
| 871 | program must understand the PARTUUID or LABEL syntax. This currently | ||
| 872 | excludes the busybox versions of these applications. | ||
| 873 | |||
| 874 | |||
| 875 | The following are supported 'part' options: | ||
| 876 | |||
| 877 | --size: The minimum partition size. Specify an integer value | ||
| 878 | such as 500. Multipliers k, M ang G can be used. If | ||
| 879 | not specified, the size is in MB. | ||
| 880 | You do not need this option if you use --source. | ||
| 881 | |||
| 882 | --fixed-size: Exact partition size. Value format is the same | ||
| 883 | as for --size option. This option cannot be | ||
| 884 | specified along with --size. If partition data | ||
| 885 | is larger than --fixed-size and error will be | ||
| 886 | raised when assembling disk image. | ||
| 887 | |||
| 888 | --source: This option is a wic-specific option that names the | ||
| 889 | source of the data that will populate the | ||
| 890 | partition. The most common value for this option | ||
| 891 | is 'rootfs', but can be any value which maps to a | ||
| 892 | valid 'source plugin' (see 'wic help plugins'). | ||
| 893 | |||
| 894 | If '--source rootfs' is used, it tells the wic | ||
| 895 | command to create a partition as large as needed | ||
| 896 | and to fill it with the contents of the root | ||
| 897 | filesystem pointed to by the '-r' wic command-line | ||
| 898 | option (or the equivalent rootfs derived from the | ||
| 899 | '-e' command-line option). The filesystem type | ||
| 900 | that will be used to create the partition is driven | ||
| 901 | by the value of the --fstype option specified for | ||
| 902 | the partition (see --fstype below). | ||
| 903 | |||
| 904 | If --source <plugin-name>' is used, it tells the | ||
| 905 | wic command to create a partition as large as | ||
| 906 | needed and to fill with the contents of the | ||
| 907 | partition that will be generated by the specified | ||
| 908 | plugin name using the data pointed to by the '-r' | ||
| 909 | wic command-line option (or the equivalent rootfs | ||
| 910 | derived from the '-e' command-line option). | ||
| 911 | Exactly what those contents and filesystem type end | ||
| 912 | up being are dependent on the given plugin | ||
| 913 | implementation. | ||
| 914 | |||
| 915 | If --source option is not used, the wic command | ||
| 916 | will create empty partition. --size parameter has | ||
| 917 | to be used to specify size of empty partition. | ||
| 918 | |||
| 919 | --ondisk or --ondrive: Forces the partition to be created on | ||
| 920 | a particular disk. | ||
| 921 | |||
| 922 | --fstype: Sets the file system type for the partition. These | ||
| 923 | apply to partitions created using '--source rootfs' (see | ||
| 924 | --source above). Valid values are: | ||
| 925 | |||
| 926 | vfat | ||
| 927 | msdos | ||
| 928 | ext2 | ||
| 929 | ext3 | ||
| 930 | ext4 | ||
| 931 | btrfs | ||
| 932 | squashfs | ||
| 933 | erofs | ||
| 934 | swap | ||
| 935 | |||
| 936 | --fsoptions: Specifies a free-form string of options to be | ||
| 937 | used when mounting the filesystem. This string | ||
| 938 | will be copied into the /etc/fstab file of the | ||
| 939 | installed system and should be enclosed in | ||
| 940 | quotes. If not specified, the default string is | ||
| 941 | "defaults". | ||
| 942 | |||
| 943 | --fspassno: Specifies the order in which filesystem checks are done | ||
| 944 | at boot time by fsck. See fs_passno parameter of | ||
| 945 | fstab(5). This parameter will be copied into the | ||
| 946 | /etc/fstab file of the installed system. If not | ||
| 947 | specified the default value of "0" will be used. | ||
| 948 | |||
| 949 | --label label: Specifies the label to give to the filesystem | ||
| 950 | to be made on the partition. If the given | ||
| 951 | label is already in use by another filesystem, | ||
| 952 | a new label is created for the partition. | ||
| 953 | |||
| 954 | --use-label: This option is specific to wic. It makes wic to use the | ||
| 955 | label in /etc/fstab to specify a partition. If the | ||
| 956 | --use-label and --use-uuid are used at the same time, | ||
| 957 | we prefer the uuid because it is less likely to cause | ||
| 958 | name confliction. We don't support using this parameter | ||
| 959 | on the root partition since it requires an initramfs to | ||
| 960 | parse this value and we do not currently support that. | ||
| 961 | |||
| 962 | --active: Marks the partition as active. | ||
| 963 | |||
| 964 | --align (in KBytes): This option is specific to wic and says | ||
| 965 | to start a partition on an x KBytes | ||
| 966 | boundary. | ||
| 967 | |||
| 968 | --no-table: This option is specific to wic. Space will be | ||
| 969 | reserved for the partition and it will be | ||
| 970 | populated but it will not be added to the | ||
| 971 | partition table. It may be useful for | ||
| 972 | bootloaders. | ||
| 973 | |||
| 974 | --exclude-path: This option is specific to wic. It excludes the given | ||
| 975 | relative path from the resulting image. If the path | ||
| 976 | ends with a slash, only the content of the directory | ||
| 977 | is omitted, not the directory itself. This option only | ||
| 978 | has an effect with the rootfs source plugin. | ||
| 979 | |||
| 980 | --include-path: This option is specific to wic. It adds the contents | ||
| 981 | of the given path or a rootfs to the resulting image. | ||
| 982 | The option contains two fields, the origin and the | ||
| 983 | destination. When the origin is a rootfs, it follows | ||
| 984 | the same logic as the rootfs-dir argument and the | ||
| 985 | permissions and owners are kept. When the origin is a | ||
| 986 | path, it is relative to the directory in which wic is | ||
| 987 | running not the rootfs itself so use of an absolute | ||
| 988 | path is recommended, and the owner and group is set to | ||
| 989 | root:root. If no destination is given it is | ||
| 990 | automatically set to the root of the rootfs. This | ||
| 991 | option only has an effect with the rootfs source | ||
| 992 | plugin. | ||
| 993 | |||
| 994 | --change-directory: This option is specific to wic. It changes to the | ||
| 995 | given directory before copying the files. This | ||
| 996 | option is useful when we want to split a rootfs in | ||
| 997 | multiple partitions and we want to keep the right | ||
| 998 | permissions and usernames in all the partitions. | ||
| 999 | |||
| 1000 | --no-fstab-update: This option is specific to wic. It does not update the | ||
| 1001 | '/etc/fstab' stock file for the given partition. | ||
| 1002 | |||
| 1003 | --extra-space: This option is specific to wic. It adds extra | ||
| 1004 | space after the space filled by the content | ||
| 1005 | of the partition. The final size can go | ||
| 1006 | beyond the size specified by --size. | ||
| 1007 | By default, 10MB. This option cannot be used | ||
| 1008 | with --fixed-size option. | ||
| 1009 | |||
| 1010 | --overhead-factor: This option is specific to wic. The | ||
| 1011 | size of the partition is multiplied by | ||
| 1012 | this factor. It has to be greater than or | ||
| 1013 | equal to 1. The default value is 1.3. | ||
| 1014 | This option cannot be used with --fixed-size | ||
| 1015 | option. | ||
| 1016 | |||
| 1017 | --part-name: This option is specific to wic. It specifies name for GPT partitions. | ||
| 1018 | |||
| 1019 | --part-type: This option is specific to wic. It specifies partition | ||
| 1020 | type GUID for GPT partitions. | ||
| 1021 | List of partition type GUIDS can be found here: | ||
| 1022 | http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs | ||
| 1023 | |||
| 1024 | --use-uuid: This option is specific to wic. It makes wic to generate | ||
| 1025 | random globally unique identifier (GUID) for the partition | ||
| 1026 | and use it in bootloader configuration to specify root partition. | ||
| 1027 | |||
| 1028 | --uuid: This option is specific to wic. It specifies partition UUID. | ||
| 1029 | It's useful if preconfigured partition UUID is added to kernel command line | ||
| 1030 | in bootloader configuration before running wic. In this case .wks file can | ||
| 1031 | be generated or modified to set preconfigured parition UUID using this option. | ||
| 1032 | |||
| 1033 | --fsuuid: This option is specific to wic. It specifies filesystem UUID. | ||
| 1034 | It's useful if preconfigured filesystem UUID is added to kernel command line | ||
| 1035 | in bootloader configuration before running wic. In this case .wks file can | ||
| 1036 | be generated or modified to set preconfigured filesystem UUID using this option. | ||
| 1037 | |||
| 1038 | --system-id: This option is specific to wic. It specifies partition system id. It's useful | ||
| 1039 | for the harware that requires non-default partition system ids. The parameter | ||
| 1040 | in one byte long hex number either with 0x prefix or without it. | ||
| 1041 | |||
| 1042 | --mkfs-extraopts: This option specifies extra options to pass to mkfs utility. | ||
| 1043 | NOTE, that wic uses default options for some filesystems, for example | ||
| 1044 | '-S 512' for mkfs.fat or '-F -i 8192' for mkfs.ext. Those options will | ||
| 1045 | not take effect when --mkfs-extraopts is used. This should be taken into | ||
| 1046 | account when using --mkfs-extraopts. | ||
| 1047 | |||
| 1048 | * bootloader | ||
| 1049 | |||
| 1050 | This command allows the user to specify various bootloader | ||
| 1051 | options. The following are supported 'bootloader' options: | ||
| 1052 | |||
| 1053 | --timeout: Specifies the number of seconds before the | ||
| 1054 | bootloader times out and boots the default option. | ||
| 1055 | |||
| 1056 | --append: Specifies kernel parameters. These will be added to | ||
| 1057 | bootloader command-line - for example, the syslinux | ||
| 1058 | APPEND or grub kernel command line. | ||
| 1059 | |||
| 1060 | --configfile: Specifies a user defined configuration file for | ||
| 1061 | the bootloader. This file must be located in the | ||
| 1062 | canned-wks folder or could be the full path to the | ||
| 1063 | file. Using this option will override any other | ||
| 1064 | bootloader option. | ||
| 1065 | |||
| 1066 | Note that bootloader functionality and boot partitions are | ||
| 1067 | implemented by the various --source plugins that implement | ||
| 1068 | bootloader functionality; the bootloader command essentially | ||
| 1069 | provides a means of modifying bootloader configuration. | ||
| 1070 | |||
| 1071 | * include | ||
| 1072 | |||
| 1073 | This command allows the user to include the content of .wks file | ||
| 1074 | into original .wks file. | ||
| 1075 | |||
| 1076 | Command uses the following syntax: | ||
| 1077 | |||
| 1078 | include <file> | ||
| 1079 | |||
| 1080 | The <file> is either path to the file or its name. If name is | ||
| 1081 | specified wic will try to find file in the directories with canned | ||
| 1082 | .wks files. | ||
| 1083 | |||
| 1084 | """ | ||
| 1085 | |||
| 1086 | wic_help_help = """ | ||
| 1087 | NAME | ||
| 1088 | wic help - display a help topic | ||
| 1089 | |||
| 1090 | DESCRIPTION | ||
| 1091 | Specify a help topic to display it. Topics are shown above. | ||
| 1092 | """ | ||
| 1093 | |||
| 1094 | |||
| 1095 | wic_help = """ | ||
| 1096 | Creates a customized OpenEmbedded image. | ||
| 1097 | |||
| 1098 | Usage: wic [--version] | ||
| 1099 | wic help [COMMAND or TOPIC] | ||
| 1100 | wic COMMAND [ARGS] | ||
| 1101 | |||
| 1102 | usage 1: Returns the current version of Wic | ||
| 1103 | usage 2: Returns detailed help for a COMMAND or TOPIC | ||
| 1104 | usage 3: Executes COMMAND | ||
| 1105 | |||
| 1106 | |||
| 1107 | COMMAND: | ||
| 1108 | |||
| 1109 | list - List available canned images and source plugins | ||
| 1110 | ls - List contents of partitioned image or partition | ||
| 1111 | rm - Remove files or directories from the vfat or ext* partitions | ||
| 1112 | help - Show help for a wic COMMAND or TOPIC | ||
| 1113 | write - Write an image to a device | ||
| 1114 | cp - Copy files and directories to the vfat or ext* partitions | ||
| 1115 | create - Create a new OpenEmbedded image | ||
| 1116 | |||
| 1117 | |||
| 1118 | TOPIC: | ||
| 1119 | overview - Presents an overall overview of Wic | ||
| 1120 | plugins - Presents an overview and API for Wic plugins | ||
| 1121 | kickstart - Presents a Wic kickstart file reference | ||
| 1122 | |||
| 1123 | |||
| 1124 | Examples: | ||
| 1125 | |||
| 1126 | $ wic --version | ||
| 1127 | |||
| 1128 | Returns the current version of Wic | ||
| 1129 | |||
| 1130 | |||
| 1131 | $ wic help cp | ||
| 1132 | |||
| 1133 | Returns the SYNOPSIS and DESCRIPTION for the Wic "cp" command. | ||
| 1134 | |||
| 1135 | |||
| 1136 | $ wic list images | ||
| 1137 | |||
| 1138 | Returns the list of canned images (i.e. *.wks files located in | ||
| 1139 | the /scripts/lib/wic/canned-wks directory. | ||
| 1140 | |||
| 1141 | |||
| 1142 | $ wic create mkefidisk -e core-image-minimal | ||
| 1143 | |||
| 1144 | Creates an EFI disk image from artifacts used in a previous | ||
| 1145 | core-image-minimal build in standard BitBake locations | ||
| 1146 | (e.g. Cooked Mode). | ||
| 1147 | |||
| 1148 | """ | ||
diff --git a/scripts/lib/wic/ksparser.py b/scripts/lib/wic/ksparser.py deleted file mode 100644 index 7ef3dc83dd..0000000000 --- a/scripts/lib/wic/ksparser.py +++ /dev/null | |||
| @@ -1,298 +0,0 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | # | ||
| 3 | # Copyright (c) 2016 Intel, Inc. | ||
| 4 | # | ||
| 5 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 6 | # | ||
| 7 | # DESCRIPTION | ||
| 8 | # This module provides parser for kickstart format | ||
| 9 | # | ||
| 10 | # AUTHORS | ||
| 11 | # Ed Bartosh <ed.bartosh> (at] linux.intel.com> | ||
| 12 | |||
| 13 | """Kickstart parser module.""" | ||
| 14 | |||
| 15 | import os | ||
| 16 | import shlex | ||
| 17 | import logging | ||
| 18 | import re | ||
| 19 | |||
| 20 | from argparse import ArgumentParser, ArgumentError, ArgumentTypeError | ||
| 21 | |||
| 22 | from wic.engine import find_canned | ||
| 23 | from wic.partition import Partition | ||
| 24 | from wic.misc import get_bitbake_var | ||
| 25 | |||
| 26 | logger = logging.getLogger('wic') | ||
| 27 | |||
| 28 | __expand_var_regexp__ = re.compile(r"\${[^{}@\n\t :]+}") | ||
| 29 | |||
| 30 | def expand_line(line): | ||
| 31 | while True: | ||
| 32 | m = __expand_var_regexp__.search(line) | ||
| 33 | if not m: | ||
| 34 | return line | ||
| 35 | key = m.group()[2:-1] | ||
| 36 | val = get_bitbake_var(key) | ||
| 37 | if val is None: | ||
| 38 | logger.warning("cannot expand variable %s" % key) | ||
| 39 | return line | ||
| 40 | line = line[:m.start()] + val + line[m.end():] | ||
| 41 | |||
| 42 | class KickStartError(Exception): | ||
| 43 | """Custom exception.""" | ||
| 44 | pass | ||
| 45 | |||
| 46 | class KickStartParser(ArgumentParser): | ||
| 47 | """ | ||
| 48 | This class overwrites error method to throw exception | ||
| 49 | instead of producing usage message(default argparse behavior). | ||
| 50 | """ | ||
| 51 | def error(self, message): | ||
| 52 | raise ArgumentError(None, message) | ||
| 53 | |||
| 54 | def sizetype(default, size_in_bytes=False): | ||
| 55 | def f(arg): | ||
| 56 | """ | ||
| 57 | Custom type for ArgumentParser | ||
| 58 | Converts size string in <num>[S|s|K|k|M|G] format into the integer value | ||
| 59 | """ | ||
| 60 | try: | ||
| 61 | suffix = default | ||
| 62 | size = int(arg) | ||
| 63 | except ValueError: | ||
| 64 | try: | ||
| 65 | suffix = arg[-1:] | ||
| 66 | size = int(arg[:-1]) | ||
| 67 | except ValueError: | ||
| 68 | raise ArgumentTypeError("Invalid size: %r" % arg) | ||
| 69 | |||
| 70 | |||
| 71 | if size_in_bytes: | ||
| 72 | if suffix == 's' or suffix == 'S': | ||
| 73 | return size * 512 | ||
| 74 | mult = 1024 | ||
| 75 | else: | ||
| 76 | mult = 1 | ||
| 77 | |||
| 78 | if suffix == "k" or suffix == "K": | ||
| 79 | return size * mult | ||
| 80 | if suffix == "M": | ||
| 81 | return size * mult * 1024 | ||
| 82 | if suffix == "G": | ||
| 83 | return size * mult * 1024 * 1024 | ||
| 84 | |||
| 85 | raise ArgumentTypeError("Invalid size: %r" % arg) | ||
| 86 | return f | ||
| 87 | |||
| 88 | def overheadtype(arg): | ||
| 89 | """ | ||
| 90 | Custom type for ArgumentParser | ||
| 91 | Converts overhead string to float and checks if it's bigger than 1.0 | ||
| 92 | """ | ||
| 93 | try: | ||
| 94 | result = float(arg) | ||
| 95 | except ValueError: | ||
| 96 | raise ArgumentTypeError("Invalid value: %r" % arg) | ||
| 97 | |||
| 98 | if result < 1.0: | ||
| 99 | raise ArgumentTypeError("Overhead factor should be > 1.0" % arg) | ||
| 100 | |||
| 101 | return result | ||
| 102 | |||
| 103 | def cannedpathtype(arg): | ||
| 104 | """ | ||
| 105 | Custom type for ArgumentParser | ||
| 106 | Tries to find file in the list of canned wks paths | ||
| 107 | """ | ||
| 108 | scripts_path = os.path.abspath(os.path.dirname(__file__) + '../../..') | ||
| 109 | result = find_canned(scripts_path, arg) | ||
| 110 | if not result: | ||
| 111 | raise ArgumentTypeError("file not found: %s" % arg) | ||
| 112 | return result | ||
| 113 | |||
| 114 | def systemidtype(arg): | ||
| 115 | """ | ||
| 116 | Custom type for ArgumentParser | ||
| 117 | Checks if the argument sutisfies system id requirements, | ||
| 118 | i.e. if it's one byte long integer > 0 | ||
| 119 | """ | ||
| 120 | error = "Invalid system type: %s. must be hex "\ | ||
| 121 | "between 0x1 and 0xFF" % arg | ||
| 122 | try: | ||
| 123 | result = int(arg, 16) | ||
| 124 | except ValueError: | ||
| 125 | raise ArgumentTypeError(error) | ||
| 126 | |||
| 127 | if result <= 0 or result > 0xff: | ||
| 128 | raise ArgumentTypeError(error) | ||
| 129 | |||
| 130 | return arg | ||
| 131 | |||
| 132 | class KickStart(): | ||
| 133 | """Kickstart parser implementation.""" | ||
| 134 | |||
| 135 | DEFAULT_EXTRA_SPACE = 10*1024 | ||
| 136 | DEFAULT_OVERHEAD_FACTOR = 1.3 | ||
| 137 | |||
| 138 | def __init__(self, confpath): | ||
| 139 | |||
| 140 | self.partitions = [] | ||
| 141 | self.bootloader = None | ||
| 142 | self.lineno = 0 | ||
| 143 | self.partnum = 0 | ||
| 144 | |||
| 145 | parser = KickStartParser() | ||
| 146 | subparsers = parser.add_subparsers() | ||
| 147 | |||
| 148 | part = subparsers.add_parser('part') | ||
| 149 | part.add_argument('mountpoint', nargs='?') | ||
| 150 | part.add_argument('--active', action='store_true') | ||
| 151 | part.add_argument('--align', type=int) | ||
| 152 | part.add_argument('--offset', type=sizetype("K", True)) | ||
| 153 | part.add_argument('--exclude-path', nargs='+') | ||
| 154 | part.add_argument('--include-path', nargs='+', action='append') | ||
| 155 | part.add_argument('--change-directory') | ||
| 156 | part.add_argument("--extra-space", type=sizetype("M")) | ||
| 157 | part.add_argument('--fsoptions', dest='fsopts') | ||
| 158 | part.add_argument('--fspassno', dest='fspassno') | ||
| 159 | part.add_argument('--fstype', default='vfat', | ||
| 160 | choices=('ext2', 'ext3', 'ext4', 'btrfs', | ||
| 161 | 'squashfs', 'vfat', 'msdos', 'erofs', | ||
| 162 | 'swap', 'none')) | ||
| 163 | part.add_argument('--mkfs-extraopts', default='') | ||
| 164 | part.add_argument('--label') | ||
| 165 | part.add_argument('--use-label', action='store_true') | ||
| 166 | part.add_argument('--no-table', action='store_true') | ||
| 167 | part.add_argument('--ondisk', '--ondrive', dest='disk', default='sda') | ||
| 168 | part.add_argument("--overhead-factor", type=overheadtype) | ||
| 169 | part.add_argument('--part-name') | ||
| 170 | part.add_argument('--part-type') | ||
| 171 | part.add_argument('--rootfs-dir') | ||
| 172 | part.add_argument('--type', default='primary', | ||
| 173 | choices = ('primary', 'logical')) | ||
| 174 | part.add_argument('--hidden', action='store_true') | ||
| 175 | |||
| 176 | # --size and --fixed-size cannot be specified together; options | ||
| 177 | # ----extra-space and --overhead-factor should also raise a parser | ||
| 178 | # --error, but since nesting mutually exclusive groups does not work, | ||
| 179 | # ----extra-space/--overhead-factor are handled later | ||
| 180 | sizeexcl = part.add_mutually_exclusive_group() | ||
| 181 | sizeexcl.add_argument('--size', type=sizetype("M"), default=0) | ||
| 182 | sizeexcl.add_argument('--fixed-size', type=sizetype("M"), default=0) | ||
| 183 | |||
| 184 | part.add_argument('--source') | ||
| 185 | part.add_argument('--sourceparams') | ||
| 186 | part.add_argument('--system-id', type=systemidtype) | ||
| 187 | part.add_argument('--use-uuid', action='store_true') | ||
| 188 | part.add_argument('--uuid') | ||
| 189 | part.add_argument('--fsuuid') | ||
| 190 | part.add_argument('--no-fstab-update', action='store_true') | ||
| 191 | part.add_argument('--mbr', action='store_true') | ||
| 192 | |||
| 193 | bootloader = subparsers.add_parser('bootloader') | ||
| 194 | bootloader.add_argument('--append') | ||
| 195 | bootloader.add_argument('--configfile') | ||
| 196 | bootloader.add_argument('--ptable', choices=('msdos', 'gpt', 'gpt-hybrid'), | ||
| 197 | default='msdos') | ||
| 198 | bootloader.add_argument('--timeout', type=int) | ||
| 199 | bootloader.add_argument('--source') | ||
| 200 | |||
| 201 | include = subparsers.add_parser('include') | ||
| 202 | include.add_argument('path', type=cannedpathtype) | ||
| 203 | |||
| 204 | self._parse(parser, confpath) | ||
| 205 | if not self.bootloader: | ||
| 206 | logger.warning('bootloader config not specified, using defaults\n') | ||
| 207 | self.bootloader = bootloader.parse_args([]) | ||
| 208 | |||
| 209 | def _parse(self, parser, confpath): | ||
| 210 | """ | ||
| 211 | Parse file in .wks format using provided parser. | ||
| 212 | """ | ||
| 213 | with open(confpath) as conf: | ||
| 214 | lineno = 0 | ||
| 215 | for line in conf: | ||
| 216 | line = line.strip() | ||
| 217 | lineno += 1 | ||
| 218 | if line and line[0] != '#': | ||
| 219 | line = expand_line(line) | ||
| 220 | try: | ||
| 221 | line_args = shlex.split(line) | ||
| 222 | parsed = parser.parse_args(line_args) | ||
| 223 | except ArgumentError as err: | ||
| 224 | raise KickStartError('%s:%d: %s' % \ | ||
| 225 | (confpath, lineno, err)) | ||
| 226 | if line.startswith('part'): | ||
| 227 | # SquashFS does not support filesystem UUID | ||
| 228 | if parsed.fstype == 'squashfs': | ||
| 229 | if parsed.fsuuid: | ||
| 230 | err = "%s:%d: SquashFS does not support UUID" \ | ||
| 231 | % (confpath, lineno) | ||
| 232 | raise KickStartError(err) | ||
| 233 | if parsed.label: | ||
| 234 | err = "%s:%d: SquashFS does not support LABEL" \ | ||
| 235 | % (confpath, lineno) | ||
| 236 | raise KickStartError(err) | ||
| 237 | # erofs does not support filesystem labels | ||
| 238 | if parsed.fstype == 'erofs' and parsed.label: | ||
| 239 | err = "%s:%d: erofs does not support LABEL" % (confpath, lineno) | ||
| 240 | raise KickStartError(err) | ||
| 241 | if parsed.fstype == 'msdos' or parsed.fstype == 'vfat': | ||
| 242 | if parsed.fsuuid: | ||
| 243 | if parsed.fsuuid.upper().startswith('0X'): | ||
| 244 | if len(parsed.fsuuid) > 10: | ||
| 245 | err = "%s:%d: fsuuid %s given in wks kickstart file " \ | ||
| 246 | "exceeds the length limit for %s filesystem. " \ | ||
| 247 | "It should be in the form of a 32 bit hexadecimal" \ | ||
| 248 | "number (for example, 0xABCD1234)." \ | ||
| 249 | % (confpath, lineno, parsed.fsuuid, parsed.fstype) | ||
| 250 | raise KickStartError(err) | ||
| 251 | elif len(parsed.fsuuid) > 8: | ||
| 252 | err = "%s:%d: fsuuid %s given in wks kickstart file " \ | ||
| 253 | "exceeds the length limit for %s filesystem. " \ | ||
| 254 | "It should be in the form of a 32 bit hexadecimal" \ | ||
| 255 | "number (for example, 0xABCD1234)." \ | ||
| 256 | % (confpath, lineno, parsed.fsuuid, parsed.fstype) | ||
| 257 | raise KickStartError(err) | ||
| 258 | if parsed.use_label and not parsed.label: | ||
| 259 | err = "%s:%d: Must set the label with --label" \ | ||
| 260 | % (confpath, lineno) | ||
| 261 | raise KickStartError(err) | ||
| 262 | # using ArgumentParser one cannot easily tell if option | ||
| 263 | # was passed as argument, if said option has a default | ||
| 264 | # value; --overhead-factor/--extra-space cannot be used | ||
| 265 | # with --fixed-size, so at least detect when these were | ||
| 266 | # passed with non-0 values ... | ||
| 267 | if parsed.fixed_size: | ||
| 268 | if parsed.overhead_factor or parsed.extra_space: | ||
| 269 | err = "%s:%d: arguments --overhead-factor and --extra-space not "\ | ||
| 270 | "allowed with argument --fixed-size" \ | ||
| 271 | % (confpath, lineno) | ||
| 272 | raise KickStartError(err) | ||
| 273 | else: | ||
| 274 | # ... and provide defaults if not using | ||
| 275 | # --fixed-size iff given option was not used | ||
| 276 | # (again, one cannot tell if option was passed but | ||
| 277 | # with value equal to 0) | ||
| 278 | if '--overhead-factor' not in line_args: | ||
| 279 | parsed.overhead_factor = self.DEFAULT_OVERHEAD_FACTOR | ||
| 280 | if '--extra-space' not in line_args: | ||
| 281 | parsed.extra_space = self.DEFAULT_EXTRA_SPACE | ||
| 282 | |||
| 283 | self.partnum += 1 | ||
| 284 | self.partitions.append(Partition(parsed, self.partnum)) | ||
| 285 | elif line.startswith('include'): | ||
| 286 | self._parse(parser, parsed.path) | ||
| 287 | elif line.startswith('bootloader'): | ||
| 288 | if not self.bootloader: | ||
| 289 | self.bootloader = parsed | ||
| 290 | # Concatenate the strings set in APPEND | ||
| 291 | append_var = get_bitbake_var("APPEND") | ||
| 292 | if append_var: | ||
| 293 | self.bootloader.append = ' '.join(filter(None, \ | ||
| 294 | (self.bootloader.append, append_var))) | ||
| 295 | else: | ||
| 296 | err = "%s:%d: more than one bootloader specified" \ | ||
| 297 | % (confpath, lineno) | ||
| 298 | raise KickStartError(err) | ||
diff --git a/scripts/lib/wic/misc.py b/scripts/lib/wic/misc.py deleted file mode 100644 index 1a7c140fa6..0000000000 --- a/scripts/lib/wic/misc.py +++ /dev/null | |||
| @@ -1,266 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2013, Intel Corporation. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This module provides a place to collect various wic-related utils | ||
| 8 | # for the OpenEmbedded Image Tools. | ||
| 9 | # | ||
| 10 | # AUTHORS | ||
| 11 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 12 | # | ||
| 13 | """Miscellaneous functions.""" | ||
| 14 | |||
| 15 | import logging | ||
| 16 | import os | ||
| 17 | import re | ||
| 18 | import subprocess | ||
| 19 | import shutil | ||
| 20 | |||
| 21 | from collections import defaultdict | ||
| 22 | |||
| 23 | from wic import WicError | ||
| 24 | |||
| 25 | logger = logging.getLogger('wic') | ||
| 26 | |||
| 27 | # executable -> recipe pairs for exec_native_cmd | ||
| 28 | NATIVE_RECIPES = {"bmaptool": "bmaptool", | ||
| 29 | "dumpe2fs": "e2fsprogs", | ||
| 30 | "grub-mkimage": "grub-efi", | ||
| 31 | "isohybrid": "syslinux", | ||
| 32 | "mcopy": "mtools", | ||
| 33 | "mdel" : "mtools", | ||
| 34 | "mdeltree" : "mtools", | ||
| 35 | "mdir" : "mtools", | ||
| 36 | "mkdosfs": "dosfstools", | ||
| 37 | "mkisofs": "cdrtools", | ||
| 38 | "mkfs.btrfs": "btrfs-tools", | ||
| 39 | "mkfs.erofs": "erofs-utils", | ||
| 40 | "mkfs.ext2": "e2fsprogs", | ||
| 41 | "mkfs.ext3": "e2fsprogs", | ||
| 42 | "mkfs.ext4": "e2fsprogs", | ||
| 43 | "mkfs.vfat": "dosfstools", | ||
| 44 | "mksquashfs": "squashfs-tools", | ||
| 45 | "mkswap": "util-linux", | ||
| 46 | "mmd": "mtools", | ||
| 47 | "parted": "parted", | ||
| 48 | "sfdisk": "util-linux", | ||
| 49 | "sgdisk": "gptfdisk", | ||
| 50 | "syslinux": "syslinux", | ||
| 51 | "tar": "tar" | ||
| 52 | } | ||
| 53 | |||
| 54 | def runtool(cmdln_or_args): | ||
| 55 | """ wrapper for most of the subprocess calls | ||
| 56 | input: | ||
| 57 | cmdln_or_args: can be both args and cmdln str (shell=True) | ||
| 58 | return: | ||
| 59 | rc, output | ||
| 60 | """ | ||
| 61 | if isinstance(cmdln_or_args, list): | ||
| 62 | cmd = cmdln_or_args[0] | ||
| 63 | shell = False | ||
| 64 | else: | ||
| 65 | import shlex | ||
| 66 | cmd = shlex.split(cmdln_or_args)[0] | ||
| 67 | shell = True | ||
| 68 | |||
| 69 | sout = subprocess.PIPE | ||
| 70 | serr = subprocess.STDOUT | ||
| 71 | |||
| 72 | try: | ||
| 73 | process = subprocess.Popen(cmdln_or_args, stdout=sout, | ||
| 74 | stderr=serr, shell=shell) | ||
| 75 | sout, serr = process.communicate() | ||
| 76 | # combine stdout and stderr, filter None out and decode | ||
| 77 | out = ''.join([out.decode('utf-8') for out in [sout, serr] if out]) | ||
| 78 | except OSError as err: | ||
| 79 | if err.errno == 2: | ||
| 80 | # [Errno 2] No such file or directory | ||
| 81 | raise WicError('Cannot run command: %s, lost dependency?' % cmd) | ||
| 82 | else: | ||
| 83 | raise # relay | ||
| 84 | |||
| 85 | return process.returncode, out | ||
| 86 | |||
| 87 | def _exec_cmd(cmd_and_args, as_shell=False): | ||
| 88 | """ | ||
| 89 | Execute command, catching stderr, stdout | ||
| 90 | |||
| 91 | Need to execute as_shell if the command uses wildcards | ||
| 92 | """ | ||
| 93 | logger.debug("_exec_cmd: %s", cmd_and_args) | ||
| 94 | args = cmd_and_args.split() | ||
| 95 | logger.debug(args) | ||
| 96 | |||
| 97 | if as_shell: | ||
| 98 | ret, out = runtool(cmd_and_args) | ||
| 99 | else: | ||
| 100 | ret, out = runtool(args) | ||
| 101 | out = out.strip() | ||
| 102 | if ret != 0: | ||
| 103 | raise WicError("_exec_cmd: %s returned '%s' instead of 0\noutput: %s" % \ | ||
| 104 | (cmd_and_args, ret, out)) | ||
| 105 | |||
| 106 | logger.debug("_exec_cmd: output for %s (rc = %d): %s", | ||
| 107 | cmd_and_args, ret, out) | ||
| 108 | |||
| 109 | return ret, out | ||
| 110 | |||
| 111 | |||
| 112 | def exec_cmd(cmd_and_args, as_shell=False): | ||
| 113 | """ | ||
| 114 | Execute command, return output | ||
| 115 | """ | ||
| 116 | return _exec_cmd(cmd_and_args, as_shell)[1] | ||
| 117 | |||
| 118 | def find_executable(cmd, paths): | ||
| 119 | recipe = cmd | ||
| 120 | if recipe in NATIVE_RECIPES: | ||
| 121 | recipe = NATIVE_RECIPES[recipe] | ||
| 122 | provided = get_bitbake_var("ASSUME_PROVIDED") | ||
| 123 | if provided and "%s-native" % recipe in provided: | ||
| 124 | return True | ||
| 125 | |||
| 126 | return shutil.which(cmd, path=paths) | ||
| 127 | |||
| 128 | def exec_native_cmd(cmd_and_args, native_sysroot, pseudo=""): | ||
| 129 | """ | ||
| 130 | Execute native command, catching stderr, stdout | ||
| 131 | |||
| 132 | Need to execute as_shell if the command uses wildcards | ||
| 133 | |||
| 134 | Always need to execute native commands as_shell | ||
| 135 | """ | ||
| 136 | # The reason -1 is used is because there may be "export" commands. | ||
| 137 | args = cmd_and_args.split(';')[-1].split() | ||
| 138 | logger.debug(args) | ||
| 139 | |||
| 140 | if pseudo: | ||
| 141 | cmd_and_args = pseudo + cmd_and_args | ||
| 142 | |||
| 143 | hosttools_dir = get_bitbake_var("HOSTTOOLS_DIR") | ||
| 144 | target_sys = get_bitbake_var("TARGET_SYS") | ||
| 145 | |||
| 146 | native_paths = "%s/sbin:%s/usr/sbin:%s/usr/bin:%s/usr/bin/%s:%s/bin:%s" % \ | ||
| 147 | (native_sysroot, native_sysroot, | ||
| 148 | native_sysroot, native_sysroot, target_sys, | ||
| 149 | native_sysroot, hosttools_dir) | ||
| 150 | |||
| 151 | native_cmd_and_args = "export PATH=%s:$PATH;%s" % \ | ||
| 152 | (native_paths, cmd_and_args) | ||
| 153 | logger.debug("exec_native_cmd: %s", native_cmd_and_args) | ||
| 154 | |||
| 155 | # If the command isn't in the native sysroot say we failed. | ||
| 156 | if find_executable(args[0], native_paths): | ||
| 157 | ret, out = _exec_cmd(native_cmd_and_args, True) | ||
| 158 | else: | ||
| 159 | ret = 127 | ||
| 160 | out = "can't find native executable %s in %s" % (args[0], native_paths) | ||
| 161 | |||
| 162 | prog = args[0] | ||
| 163 | # shell command-not-found | ||
| 164 | if ret == 127 \ | ||
| 165 | or (pseudo and ret == 1 and out == "Can't find '%s' in $PATH." % prog): | ||
| 166 | msg = "A native program %s required to build the image "\ | ||
| 167 | "was not found (see details above).\n\n" % prog | ||
| 168 | recipe = NATIVE_RECIPES.get(prog) | ||
| 169 | if recipe: | ||
| 170 | msg += "Please make sure wic-tools have %s-native in its DEPENDS, "\ | ||
| 171 | "build it with 'bitbake wic-tools' and try again.\n" % recipe | ||
| 172 | else: | ||
| 173 | msg += "Wic failed to find a recipe to build native %s. Please "\ | ||
| 174 | "file a bug against wic.\n" % prog | ||
| 175 | raise WicError(msg) | ||
| 176 | |||
| 177 | return ret, out | ||
| 178 | |||
| 179 | BOOTDD_EXTRA_SPACE = 16384 | ||
| 180 | |||
| 181 | class BitbakeVars(defaultdict): | ||
| 182 | """ | ||
| 183 | Container for Bitbake variables. | ||
| 184 | """ | ||
| 185 | def __init__(self): | ||
| 186 | defaultdict.__init__(self, dict) | ||
| 187 | |||
| 188 | # default_image and vars_dir attributes should be set from outside | ||
| 189 | self.default_image = None | ||
| 190 | self.vars_dir = None | ||
| 191 | |||
| 192 | def _parse_line(self, line, image, matcher=re.compile(r"^([a-zA-Z0-9\-_+./~]+)=(.*)")): | ||
| 193 | """ | ||
| 194 | Parse one line from bitbake -e output or from .env file. | ||
| 195 | Put result key-value pair into the storage. | ||
| 196 | """ | ||
| 197 | if "=" not in line: | ||
| 198 | return | ||
| 199 | match = matcher.match(line) | ||
| 200 | if not match: | ||
| 201 | return | ||
| 202 | key, val = match.groups() | ||
| 203 | self[image][key] = val.strip('"') | ||
| 204 | |||
| 205 | def get_var(self, var, image=None, cache=True): | ||
| 206 | """ | ||
| 207 | Get bitbake variable from 'bitbake -e' output or from .env file. | ||
| 208 | This is a lazy method, i.e. it runs bitbake or parses file only when | ||
| 209 | only when variable is requested. It also caches results. | ||
| 210 | """ | ||
| 211 | if not image: | ||
| 212 | image = self.default_image | ||
| 213 | |||
| 214 | if image not in self: | ||
| 215 | if image and self.vars_dir: | ||
| 216 | fname = os.path.join(self.vars_dir, image + '.env') | ||
| 217 | if os.path.isfile(fname): | ||
| 218 | # parse .env file | ||
| 219 | with open(fname) as varsfile: | ||
| 220 | for line in varsfile: | ||
| 221 | self._parse_line(line, image) | ||
| 222 | else: | ||
| 223 | print("Couldn't get bitbake variable from %s." % fname) | ||
| 224 | print("File %s doesn't exist." % fname) | ||
| 225 | return | ||
| 226 | else: | ||
| 227 | # Get bitbake -e output | ||
| 228 | cmd = "bitbake -e" | ||
| 229 | if image: | ||
| 230 | cmd += " %s" % image | ||
| 231 | |||
| 232 | log_level = logger.getEffectiveLevel() | ||
| 233 | logger.setLevel(logging.INFO) | ||
| 234 | ret, lines = _exec_cmd(cmd) | ||
| 235 | logger.setLevel(log_level) | ||
| 236 | |||
| 237 | if ret: | ||
| 238 | logger.error("Couldn't get '%s' output.", cmd) | ||
| 239 | logger.error("Bitbake failed with error:\n%s\n", lines) | ||
| 240 | return | ||
| 241 | |||
| 242 | # Parse bitbake -e output | ||
| 243 | for line in lines.split('\n'): | ||
| 244 | self._parse_line(line, image) | ||
| 245 | |||
| 246 | # Make first image a default set of variables | ||
| 247 | if cache: | ||
| 248 | images = [key for key in self if key] | ||
| 249 | if len(images) == 1: | ||
| 250 | self[None] = self[image] | ||
| 251 | |||
| 252 | result = self[image].get(var) | ||
| 253 | if not cache: | ||
| 254 | self.pop(image, None) | ||
| 255 | |||
| 256 | return result | ||
| 257 | |||
| 258 | # Create BB_VARS singleton | ||
| 259 | BB_VARS = BitbakeVars() | ||
| 260 | |||
| 261 | def get_bitbake_var(var, image=None, cache=True): | ||
| 262 | """ | ||
| 263 | Provide old get_bitbake_var API by wrapping | ||
| 264 | get_var method of BB_VARS singleton. | ||
| 265 | """ | ||
| 266 | return BB_VARS.get_var(var, image, cache) | ||
diff --git a/scripts/lib/wic/partition.py b/scripts/lib/wic/partition.py deleted file mode 100644 index 795707ec5d..0000000000 --- a/scripts/lib/wic/partition.py +++ /dev/null | |||
| @@ -1,542 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2013-2016 Intel Corporation. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This module provides the OpenEmbedded partition object definitions. | ||
| 8 | # | ||
| 9 | # AUTHORS | ||
| 10 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 11 | # Ed Bartosh <ed.bartosh> (at] linux.intel.com> | ||
| 12 | |||
| 13 | import logging | ||
| 14 | import os | ||
| 15 | import uuid | ||
| 16 | |||
| 17 | from wic import WicError | ||
| 18 | from wic.misc import exec_cmd, exec_native_cmd, get_bitbake_var | ||
| 19 | from wic.pluginbase import PluginMgr | ||
| 20 | |||
| 21 | logger = logging.getLogger('wic') | ||
| 22 | |||
| 23 | class Partition(): | ||
| 24 | |||
| 25 | def __init__(self, args, lineno): | ||
| 26 | self.args = args | ||
| 27 | self.active = args.active | ||
| 28 | self.align = args.align | ||
| 29 | self.disk = args.disk | ||
| 30 | self.device = None | ||
| 31 | self.extra_space = args.extra_space | ||
| 32 | self.exclude_path = args.exclude_path | ||
| 33 | self.include_path = args.include_path | ||
| 34 | self.change_directory = args.change_directory | ||
| 35 | self.fsopts = args.fsopts | ||
| 36 | self.fspassno = args.fspassno | ||
| 37 | self.fstype = args.fstype | ||
| 38 | self.label = args.label | ||
| 39 | self.use_label = args.use_label | ||
| 40 | self.mkfs_extraopts = args.mkfs_extraopts | ||
| 41 | self.mountpoint = args.mountpoint | ||
| 42 | self.no_table = args.no_table | ||
| 43 | self.num = None | ||
| 44 | self.offset = args.offset | ||
| 45 | self.overhead_factor = args.overhead_factor | ||
| 46 | self.part_name = args.part_name | ||
| 47 | self.part_type = args.part_type | ||
| 48 | self.rootfs_dir = args.rootfs_dir | ||
| 49 | self.size = args.size | ||
| 50 | self.fixed_size = args.fixed_size | ||
| 51 | self.source = args.source | ||
| 52 | self.sourceparams = args.sourceparams | ||
| 53 | self.system_id = args.system_id | ||
| 54 | self.use_uuid = args.use_uuid | ||
| 55 | self.uuid = args.uuid | ||
| 56 | self.fsuuid = args.fsuuid | ||
| 57 | self.type = args.type | ||
| 58 | self.no_fstab_update = args.no_fstab_update | ||
| 59 | self.updated_fstab_path = None | ||
| 60 | self.has_fstab = False | ||
| 61 | self.update_fstab_in_rootfs = False | ||
| 62 | self.hidden = args.hidden | ||
| 63 | self.mbr = args.mbr | ||
| 64 | |||
| 65 | self.lineno = lineno | ||
| 66 | self.source_file = "" | ||
| 67 | |||
| 68 | def get_extra_block_count(self, current_blocks): | ||
| 69 | """ | ||
| 70 | The --size param is reflected in self.size (in kB), and we already | ||
| 71 | have current_blocks (1k) blocks, calculate and return the | ||
| 72 | number of (1k) blocks we need to add to get to --size, 0 if | ||
| 73 | we're already there or beyond. | ||
| 74 | """ | ||
| 75 | logger.debug("Requested partition size for %s: %d", | ||
| 76 | self.mountpoint, self.size) | ||
| 77 | |||
| 78 | if not self.size: | ||
| 79 | return 0 | ||
| 80 | |||
| 81 | requested_blocks = self.size | ||
| 82 | |||
| 83 | logger.debug("Requested blocks %d, current_blocks %d", | ||
| 84 | requested_blocks, current_blocks) | ||
| 85 | |||
| 86 | if requested_blocks > current_blocks: | ||
| 87 | return requested_blocks - current_blocks | ||
| 88 | else: | ||
| 89 | return 0 | ||
| 90 | |||
| 91 | def get_rootfs_size(self, actual_rootfs_size=0): | ||
| 92 | """ | ||
| 93 | Calculate the required size of rootfs taking into consideration | ||
| 94 | --size/--fixed-size flags as well as overhead and extra space, as | ||
| 95 | specified in kickstart file. Raises an error if the | ||
| 96 | `actual_rootfs_size` is larger than fixed-size rootfs. | ||
| 97 | |||
| 98 | """ | ||
| 99 | if self.fixed_size: | ||
| 100 | rootfs_size = self.fixed_size | ||
| 101 | if actual_rootfs_size > rootfs_size: | ||
| 102 | raise WicError("Actual rootfs size (%d kB) is larger than " | ||
| 103 | "allowed size %d kB" % | ||
| 104 | (actual_rootfs_size, rootfs_size)) | ||
| 105 | else: | ||
| 106 | extra_blocks = self.get_extra_block_count(actual_rootfs_size) | ||
| 107 | if extra_blocks < self.extra_space: | ||
| 108 | extra_blocks = self.extra_space | ||
| 109 | |||
| 110 | rootfs_size = actual_rootfs_size + extra_blocks | ||
| 111 | rootfs_size = int(rootfs_size * self.overhead_factor) | ||
| 112 | |||
| 113 | logger.debug("Added %d extra blocks to %s to get to %d total blocks", | ||
| 114 | extra_blocks, self.mountpoint, rootfs_size) | ||
| 115 | |||
| 116 | return rootfs_size | ||
| 117 | |||
| 118 | @property | ||
| 119 | def disk_size(self): | ||
| 120 | """ | ||
| 121 | Obtain on-disk size of partition taking into consideration | ||
| 122 | --size/--fixed-size options. | ||
| 123 | |||
| 124 | """ | ||
| 125 | return self.fixed_size if self.fixed_size else self.size | ||
| 126 | |||
| 127 | def prepare(self, creator, cr_workdir, oe_builddir, rootfs_dir, | ||
| 128 | bootimg_dir, kernel_dir, native_sysroot, updated_fstab_path): | ||
| 129 | """ | ||
| 130 | Prepare content for individual partitions, depending on | ||
| 131 | partition command parameters. | ||
| 132 | """ | ||
| 133 | self.updated_fstab_path = updated_fstab_path | ||
| 134 | if self.updated_fstab_path and not (self.fstype.startswith("ext") or self.fstype == "msdos"): | ||
| 135 | self.update_fstab_in_rootfs = True | ||
| 136 | |||
| 137 | if not self.source: | ||
| 138 | if self.fstype == "none" or self.no_table: | ||
| 139 | return | ||
| 140 | if not self.size and not self.fixed_size: | ||
| 141 | raise WicError("The %s partition has a size of zero. Please " | ||
| 142 | "specify a non-zero --size/--fixed-size for that " | ||
| 143 | "partition." % self.mountpoint) | ||
| 144 | |||
| 145 | if self.fstype == "swap": | ||
| 146 | self.prepare_swap_partition(cr_workdir, oe_builddir, | ||
| 147 | native_sysroot) | ||
| 148 | self.source_file = "%s/fs.%s" % (cr_workdir, self.fstype) | ||
| 149 | else: | ||
| 150 | if self.fstype in ('squashfs', 'erofs'): | ||
| 151 | raise WicError("It's not possible to create empty %s " | ||
| 152 | "partition '%s'" % (self.fstype, self.mountpoint)) | ||
| 153 | |||
| 154 | rootfs = "%s/fs_%s.%s.%s" % (cr_workdir, self.label, | ||
| 155 | self.lineno, self.fstype) | ||
| 156 | if os.path.isfile(rootfs): | ||
| 157 | os.remove(rootfs) | ||
| 158 | |||
| 159 | prefix = "ext" if self.fstype.startswith("ext") else self.fstype | ||
| 160 | method = getattr(self, "prepare_empty_partition_" + prefix) | ||
| 161 | method(rootfs, oe_builddir, native_sysroot) | ||
| 162 | self.source_file = rootfs | ||
| 163 | return | ||
| 164 | |||
| 165 | plugins = PluginMgr.get_plugins('source') | ||
| 166 | |||
| 167 | if self.source not in plugins: | ||
| 168 | raise WicError("The '%s' --source specified for %s doesn't exist.\n\t" | ||
| 169 | "See 'wic list source-plugins' for a list of available" | ||
| 170 | " --sources.\n\tSee 'wic help source-plugins' for " | ||
| 171 | "details on adding a new source plugin." % | ||
| 172 | (self.source, self.mountpoint)) | ||
| 173 | |||
| 174 | srcparams_dict = {} | ||
| 175 | if self.sourceparams: | ||
| 176 | # Split sourceparams string of the form key1=val1[,key2=val2,...] | ||
| 177 | # into a dict. Also accepts valueless keys i.e. without = | ||
| 178 | splitted = self.sourceparams.split(',') | ||
| 179 | srcparams_dict = dict((par.split('=', 1) + [None])[:2] for par in splitted if par) | ||
| 180 | |||
| 181 | plugin = PluginMgr.get_plugins('source')[self.source] | ||
| 182 | plugin.do_configure_partition(self, srcparams_dict, creator, | ||
| 183 | cr_workdir, oe_builddir, bootimg_dir, | ||
| 184 | kernel_dir, native_sysroot) | ||
| 185 | plugin.do_stage_partition(self, srcparams_dict, creator, | ||
| 186 | cr_workdir, oe_builddir, bootimg_dir, | ||
| 187 | kernel_dir, native_sysroot) | ||
| 188 | plugin.do_prepare_partition(self, srcparams_dict, creator, | ||
| 189 | cr_workdir, oe_builddir, bootimg_dir, | ||
| 190 | kernel_dir, rootfs_dir, native_sysroot) | ||
| 191 | plugin.do_post_partition(self, srcparams_dict, creator, | ||
| 192 | cr_workdir, oe_builddir, bootimg_dir, | ||
| 193 | kernel_dir, rootfs_dir, native_sysroot) | ||
| 194 | |||
| 195 | # further processing required Partition.size to be an integer, make | ||
| 196 | # sure that it is one | ||
| 197 | if not isinstance(self.size, int): | ||
| 198 | raise WicError("Partition %s internal size is not an integer. " | ||
| 199 | "This a bug in source plugin %s and needs to be fixed." % | ||
| 200 | (self.mountpoint, self.source)) | ||
| 201 | |||
| 202 | if self.fixed_size and self.size > self.fixed_size: | ||
| 203 | raise WicError("File system image of partition %s is " | ||
| 204 | "larger (%d kB) than its allowed size %d kB" % | ||
| 205 | (self.mountpoint, self.size, self.fixed_size)) | ||
| 206 | |||
| 207 | def prepare_rootfs(self, cr_workdir, oe_builddir, rootfs_dir, | ||
| 208 | native_sysroot, real_rootfs = True, pseudo_dir = None): | ||
| 209 | """ | ||
| 210 | Prepare content for a rootfs partition i.e. create a partition | ||
| 211 | and fill it from a /rootfs dir. | ||
| 212 | |||
| 213 | Currently handles ext2/3/4, btrfs, vfat and squashfs. | ||
| 214 | """ | ||
| 215 | |||
| 216 | rootfs = "%s/rootfs_%s.%s.%s" % (cr_workdir, self.label, | ||
| 217 | self.lineno, self.fstype) | ||
| 218 | if os.path.isfile(rootfs): | ||
| 219 | os.remove(rootfs) | ||
| 220 | |||
| 221 | p_prefix = os.environ.get("PSEUDO_PREFIX", "%s/usr" % native_sysroot) | ||
| 222 | if (pseudo_dir): | ||
| 223 | # Canonicalize the ignore paths. This corresponds to | ||
| 224 | # calling oe.path.canonicalize(), which is used in bitbake.conf. | ||
| 225 | ignore_paths = [rootfs] + (get_bitbake_var("PSEUDO_IGNORE_PATHS") or "").split(",") | ||
| 226 | canonical_paths = [] | ||
| 227 | for path in ignore_paths: | ||
| 228 | if "$" not in path: | ||
| 229 | trailing_slash = path.endswith("/") and "/" or "" | ||
| 230 | canonical_paths.append(os.path.realpath(path) + trailing_slash) | ||
| 231 | ignore_paths = ",".join(canonical_paths) | ||
| 232 | |||
| 233 | pseudo = "export PSEUDO_PREFIX=%s;" % p_prefix | ||
| 234 | pseudo += "export PSEUDO_LOCALSTATEDIR=%s;" % pseudo_dir | ||
| 235 | pseudo += "export PSEUDO_PASSWD=%s;" % rootfs_dir | ||
| 236 | pseudo += "export PSEUDO_NOSYMLINKEXP=1;" | ||
| 237 | pseudo += "export PSEUDO_IGNORE_PATHS=%s;" % ignore_paths | ||
| 238 | pseudo += "%s " % get_bitbake_var("FAKEROOTCMD") | ||
| 239 | else: | ||
| 240 | pseudo = None | ||
| 241 | |||
| 242 | if not self.size and real_rootfs: | ||
| 243 | # The rootfs size is not set in .ks file so try to get it | ||
| 244 | # from bitbake variable | ||
| 245 | rsize_bb = get_bitbake_var('ROOTFS_SIZE') | ||
| 246 | rdir = get_bitbake_var('IMAGE_ROOTFS') | ||
| 247 | if rsize_bb and rdir == rootfs_dir: | ||
| 248 | # Bitbake variable ROOTFS_SIZE is calculated in | ||
| 249 | # Image._get_rootfs_size method from meta/lib/oe/image.py | ||
| 250 | # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT, | ||
| 251 | # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE | ||
| 252 | self.size = int(round(float(rsize_bb))) | ||
| 253 | else: | ||
| 254 | # Bitbake variable ROOTFS_SIZE is not defined so compute it | ||
| 255 | # from the rootfs_dir size using the same logic found in | ||
| 256 | # get_rootfs_size() from meta/classes/image.bbclass | ||
| 257 | du_cmd = "du -ks %s" % rootfs_dir | ||
| 258 | out = exec_cmd(du_cmd) | ||
| 259 | self.size = int(out.split()[0]) | ||
| 260 | |||
| 261 | prefix = "ext" if self.fstype.startswith("ext") else self.fstype | ||
| 262 | method = getattr(self, "prepare_rootfs_" + prefix) | ||
| 263 | method(rootfs, cr_workdir, oe_builddir, rootfs_dir, native_sysroot, pseudo) | ||
| 264 | self.source_file = rootfs | ||
| 265 | |||
| 266 | # get the rootfs size in the right units for kickstart (kB) | ||
| 267 | du_cmd = "du -Lbks %s" % rootfs | ||
| 268 | out = exec_cmd(du_cmd) | ||
| 269 | self.size = int(out.split()[0]) | ||
| 270 | |||
| 271 | def prepare_rootfs_ext(self, rootfs, cr_workdir, oe_builddir, rootfs_dir, | ||
| 272 | native_sysroot, pseudo): | ||
| 273 | """ | ||
| 274 | Prepare content for an ext2/3/4 rootfs partition. | ||
| 275 | """ | ||
| 276 | du_cmd = "du -ks %s" % rootfs_dir | ||
| 277 | out = exec_cmd(du_cmd) | ||
| 278 | actual_rootfs_size = int(out.split()[0]) | ||
| 279 | |||
| 280 | rootfs_size = self.get_rootfs_size(actual_rootfs_size) | ||
| 281 | |||
| 282 | with open(rootfs, 'w') as sparse: | ||
| 283 | os.ftruncate(sparse.fileno(), rootfs_size * 1024) | ||
| 284 | |||
| 285 | extraopts = self.mkfs_extraopts or "-F -i 8192" | ||
| 286 | |||
| 287 | if os.getenv('SOURCE_DATE_EPOCH'): | ||
| 288 | sde_time = int(os.getenv('SOURCE_DATE_EPOCH')) | ||
| 289 | if pseudo: | ||
| 290 | pseudo = "export E2FSPROGS_FAKE_TIME=%s;%s " % (sde_time, pseudo) | ||
| 291 | else: | ||
| 292 | pseudo = "export E2FSPROGS_FAKE_TIME=%s; " % sde_time | ||
| 293 | |||
| 294 | # Set hash_seed to generate deterministic directory indexes | ||
| 295 | namespace = uuid.UUID("e7429877-e7b3-4a68-a5c9-2f2fdf33d460") | ||
| 296 | if self.fsuuid: | ||
| 297 | namespace = uuid.UUID(self.fsuuid) | ||
| 298 | hash_seed = str(uuid.uuid5(namespace, str(sde_time))) | ||
| 299 | extraopts += " -E hash_seed=%s" % hash_seed | ||
| 300 | |||
| 301 | label_str = "" | ||
| 302 | if self.label: | ||
| 303 | label_str = "-L %s" % self.label | ||
| 304 | |||
| 305 | mkfs_cmd = "mkfs.%s %s %s %s -U %s -d %s" % \ | ||
| 306 | (self.fstype, extraopts, rootfs, label_str, self.fsuuid, rootfs_dir) | ||
| 307 | exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo) | ||
| 308 | |||
| 309 | if self.updated_fstab_path and self.has_fstab and not self.no_fstab_update: | ||
| 310 | debugfs_script_path = os.path.join(cr_workdir, "debugfs_script") | ||
| 311 | with open(debugfs_script_path, "w") as f: | ||
| 312 | f.write("cd etc\n") | ||
| 313 | f.write("rm fstab\n") | ||
| 314 | f.write("write %s fstab\n" % (self.updated_fstab_path)) | ||
| 315 | debugfs_cmd = "debugfs -w -f %s %s" % (debugfs_script_path, rootfs) | ||
| 316 | exec_native_cmd(debugfs_cmd, native_sysroot) | ||
| 317 | |||
| 318 | mkfs_cmd = "fsck.%s -pvfD %s" % (self.fstype, rootfs) | ||
| 319 | exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo) | ||
| 320 | |||
| 321 | if os.getenv('SOURCE_DATE_EPOCH'): | ||
| 322 | sde_time = hex(int(os.getenv('SOURCE_DATE_EPOCH'))) | ||
| 323 | debugfs_script_path = os.path.join(cr_workdir, "debugfs_script") | ||
| 324 | files = [] | ||
| 325 | for root, dirs, others in os.walk(rootfs_dir): | ||
| 326 | base = root.replace(rootfs_dir, "").rstrip(os.sep) | ||
| 327 | files += [ "/" if base == "" else base ] | ||
| 328 | files += [ base + "/" + n for n in dirs + others ] | ||
| 329 | with open(debugfs_script_path, "w") as f: | ||
| 330 | f.write("set_current_time %s\n" % (sde_time)) | ||
| 331 | if self.updated_fstab_path and self.has_fstab and not self.no_fstab_update: | ||
| 332 | f.write("set_inode_field /etc/fstab mtime %s\n" % (sde_time)) | ||
| 333 | f.write("set_inode_field /etc/fstab mtime_extra 0\n") | ||
| 334 | for file in set(files): | ||
| 335 | for time in ["atime", "ctime", "crtime"]: | ||
| 336 | f.write("set_inode_field \"%s\" %s %s\n" % (file, time, sde_time)) | ||
| 337 | f.write("set_inode_field \"%s\" %s_extra 0\n" % (file, time)) | ||
| 338 | for time in ["wtime", "mkfs_time", "lastcheck"]: | ||
| 339 | f.write("set_super_value %s %s\n" % (time, sde_time)) | ||
| 340 | for time in ["mtime", "first_error_time", "last_error_time"]: | ||
| 341 | f.write("set_super_value %s 0\n" % (time)) | ||
| 342 | debugfs_cmd = "debugfs -w -f %s %s" % (debugfs_script_path, rootfs) | ||
| 343 | exec_native_cmd(debugfs_cmd, native_sysroot) | ||
| 344 | |||
| 345 | self.check_for_Y2038_problem(rootfs, native_sysroot) | ||
| 346 | |||
| 347 | def prepare_rootfs_btrfs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir, | ||
| 348 | native_sysroot, pseudo): | ||
| 349 | """ | ||
| 350 | Prepare content for a btrfs rootfs partition. | ||
| 351 | """ | ||
| 352 | du_cmd = "du -ks %s" % rootfs_dir | ||
| 353 | out = exec_cmd(du_cmd) | ||
| 354 | actual_rootfs_size = int(out.split()[0]) | ||
| 355 | |||
| 356 | rootfs_size = self.get_rootfs_size(actual_rootfs_size) | ||
| 357 | |||
| 358 | with open(rootfs, 'w') as sparse: | ||
| 359 | os.ftruncate(sparse.fileno(), rootfs_size * 1024) | ||
| 360 | |||
| 361 | label_str = "" | ||
| 362 | if self.label: | ||
| 363 | label_str = "-L %s" % self.label | ||
| 364 | |||
| 365 | mkfs_cmd = "mkfs.%s -b %d -r %s %s %s -U %s %s" % \ | ||
| 366 | (self.fstype, rootfs_size * 1024, rootfs_dir, label_str, | ||
| 367 | self.mkfs_extraopts, self.fsuuid, rootfs) | ||
| 368 | exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo) | ||
| 369 | |||
| 370 | def prepare_rootfs_msdos(self, rootfs, cr_workdir, oe_builddir, rootfs_dir, | ||
| 371 | native_sysroot, pseudo): | ||
| 372 | """ | ||
| 373 | Prepare content for a msdos/vfat rootfs partition. | ||
| 374 | """ | ||
| 375 | du_cmd = "du -bks %s" % rootfs_dir | ||
| 376 | out = exec_cmd(du_cmd) | ||
| 377 | blocks = int(out.split()[0]) | ||
| 378 | |||
| 379 | rootfs_size = self.get_rootfs_size(blocks) | ||
| 380 | |||
| 381 | label_str = "-n boot" | ||
| 382 | if self.label: | ||
| 383 | label_str = "-n %s" % self.label | ||
| 384 | |||
| 385 | size_str = "" | ||
| 386 | |||
| 387 | extraopts = self.mkfs_extraopts or '-S 512' | ||
| 388 | |||
| 389 | dosfs_cmd = "mkdosfs %s -i %s %s %s -C %s %d" % \ | ||
| 390 | (label_str, self.fsuuid, size_str, extraopts, rootfs, | ||
| 391 | rootfs_size) | ||
| 392 | exec_native_cmd(dosfs_cmd, native_sysroot) | ||
| 393 | |||
| 394 | mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (rootfs, rootfs_dir) | ||
| 395 | exec_native_cmd(mcopy_cmd, native_sysroot) | ||
| 396 | |||
| 397 | if self.updated_fstab_path and self.has_fstab and not self.no_fstab_update: | ||
| 398 | mcopy_cmd = "mcopy -m -i %s %s ::/etc/fstab" % (rootfs, self.updated_fstab_path) | ||
| 399 | exec_native_cmd(mcopy_cmd, native_sysroot) | ||
| 400 | |||
| 401 | chmod_cmd = "chmod 644 %s" % rootfs | ||
| 402 | exec_cmd(chmod_cmd) | ||
| 403 | |||
| 404 | prepare_rootfs_vfat = prepare_rootfs_msdos | ||
| 405 | |||
| 406 | def prepare_rootfs_squashfs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir, | ||
| 407 | native_sysroot, pseudo): | ||
| 408 | """ | ||
| 409 | Prepare content for a squashfs rootfs partition. | ||
| 410 | """ | ||
| 411 | extraopts = self.mkfs_extraopts or '-noappend' | ||
| 412 | squashfs_cmd = "mksquashfs %s %s %s" % \ | ||
| 413 | (rootfs_dir, rootfs, extraopts) | ||
| 414 | exec_native_cmd(squashfs_cmd, native_sysroot, pseudo=pseudo) | ||
| 415 | |||
| 416 | def prepare_rootfs_erofs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir, | ||
| 417 | native_sysroot, pseudo): | ||
| 418 | """ | ||
| 419 | Prepare content for a erofs rootfs partition. | ||
| 420 | """ | ||
| 421 | extraopts = self.mkfs_extraopts or '' | ||
| 422 | erofs_cmd = "mkfs.erofs %s -U %s %s %s" % \ | ||
| 423 | (extraopts, self.fsuuid, rootfs, rootfs_dir) | ||
| 424 | exec_native_cmd(erofs_cmd, native_sysroot, pseudo=pseudo) | ||
| 425 | |||
| 426 | def prepare_empty_partition_none(self, rootfs, oe_builddir, native_sysroot): | ||
| 427 | pass | ||
| 428 | |||
| 429 | def prepare_empty_partition_ext(self, rootfs, oe_builddir, | ||
| 430 | native_sysroot): | ||
| 431 | """ | ||
| 432 | Prepare an empty ext2/3/4 partition. | ||
| 433 | """ | ||
| 434 | size = self.disk_size | ||
| 435 | with open(rootfs, 'w') as sparse: | ||
| 436 | os.ftruncate(sparse.fileno(), size * 1024) | ||
| 437 | |||
| 438 | extraopts = self.mkfs_extraopts or "-i 8192" | ||
| 439 | |||
| 440 | label_str = "" | ||
| 441 | if self.label: | ||
| 442 | label_str = "-L %s" % self.label | ||
| 443 | |||
| 444 | mkfs_cmd = "mkfs.%s -F %s %s -U %s %s" % \ | ||
| 445 | (self.fstype, extraopts, label_str, self.fsuuid, rootfs) | ||
| 446 | exec_native_cmd(mkfs_cmd, native_sysroot) | ||
| 447 | |||
| 448 | self.check_for_Y2038_problem(rootfs, native_sysroot) | ||
| 449 | |||
| 450 | def prepare_empty_partition_btrfs(self, rootfs, oe_builddir, | ||
| 451 | native_sysroot): | ||
| 452 | """ | ||
| 453 | Prepare an empty btrfs partition. | ||
| 454 | """ | ||
| 455 | size = self.disk_size | ||
| 456 | with open(rootfs, 'w') as sparse: | ||
| 457 | os.ftruncate(sparse.fileno(), size * 1024) | ||
| 458 | |||
| 459 | label_str = "" | ||
| 460 | if self.label: | ||
| 461 | label_str = "-L %s" % self.label | ||
| 462 | |||
| 463 | mkfs_cmd = "mkfs.%s -b %d %s -U %s %s %s" % \ | ||
| 464 | (self.fstype, self.size * 1024, label_str, self.fsuuid, | ||
| 465 | self.mkfs_extraopts, rootfs) | ||
| 466 | exec_native_cmd(mkfs_cmd, native_sysroot) | ||
| 467 | |||
| 468 | def prepare_empty_partition_msdos(self, rootfs, oe_builddir, | ||
| 469 | native_sysroot): | ||
| 470 | """ | ||
| 471 | Prepare an empty vfat partition. | ||
| 472 | """ | ||
| 473 | blocks = self.disk_size | ||
| 474 | |||
| 475 | label_str = "-n boot" | ||
| 476 | if self.label: | ||
| 477 | label_str = "-n %s" % self.label | ||
| 478 | |||
| 479 | size_str = "" | ||
| 480 | |||
| 481 | extraopts = self.mkfs_extraopts or '-S 512' | ||
| 482 | |||
| 483 | dosfs_cmd = "mkdosfs %s -i %s %s %s -C %s %d" % \ | ||
| 484 | (label_str, self.fsuuid, extraopts, size_str, rootfs, | ||
| 485 | blocks) | ||
| 486 | |||
| 487 | exec_native_cmd(dosfs_cmd, native_sysroot) | ||
| 488 | |||
| 489 | chmod_cmd = "chmod 644 %s" % rootfs | ||
| 490 | exec_cmd(chmod_cmd) | ||
| 491 | |||
| 492 | prepare_empty_partition_vfat = prepare_empty_partition_msdos | ||
| 493 | |||
| 494 | def prepare_swap_partition(self, cr_workdir, oe_builddir, native_sysroot): | ||
| 495 | """ | ||
| 496 | Prepare a swap partition. | ||
| 497 | """ | ||
| 498 | path = "%s/fs.%s" % (cr_workdir, self.fstype) | ||
| 499 | |||
| 500 | with open(path, 'w') as sparse: | ||
| 501 | os.ftruncate(sparse.fileno(), self.size * 1024) | ||
| 502 | |||
| 503 | label_str = "" | ||
| 504 | if self.label: | ||
| 505 | label_str = "-L %s" % self.label | ||
| 506 | |||
| 507 | mkswap_cmd = "mkswap %s -U %s %s" % (label_str, self.fsuuid, path) | ||
| 508 | exec_native_cmd(mkswap_cmd, native_sysroot) | ||
| 509 | |||
| 510 | def check_for_Y2038_problem(self, rootfs, native_sysroot): | ||
| 511 | """ | ||
| 512 | Check if the filesystem is affected by the Y2038 problem | ||
| 513 | (Y2038 problem = 32 bit time_t overflow in January 2038) | ||
| 514 | """ | ||
| 515 | def get_err_str(part): | ||
| 516 | err = "The {} filesystem {} has no Y2038 support." | ||
| 517 | if part.mountpoint: | ||
| 518 | args = [part.fstype, "mounted at %s" % part.mountpoint] | ||
| 519 | elif part.label: | ||
| 520 | args = [part.fstype, "labeled '%s'" % part.label] | ||
| 521 | elif part.part_name: | ||
| 522 | args = [part.fstype, "in partition '%s'" % part.part_name] | ||
| 523 | else: | ||
| 524 | args = [part.fstype, "in partition %s" % part.num] | ||
| 525 | return err.format(*args) | ||
| 526 | |||
| 527 | # ext2 and ext3 are always affected by the Y2038 problem | ||
| 528 | if self.fstype in ["ext2", "ext3"]: | ||
| 529 | logger.warn(get_err_str(self)) | ||
| 530 | return | ||
| 531 | |||
| 532 | ret, out = exec_native_cmd("dumpe2fs %s" % rootfs, native_sysroot) | ||
| 533 | |||
| 534 | # if ext4 is affected by the Y2038 problem depends on the inode size | ||
| 535 | for line in out.splitlines(): | ||
| 536 | if line.startswith("Inode size:"): | ||
| 537 | size = int(line.split(":")[1].strip()) | ||
| 538 | if size < 256: | ||
| 539 | logger.warn("%s Inodes (of size %d) are too small." % | ||
| 540 | (get_err_str(self), size)) | ||
| 541 | break | ||
| 542 | |||
diff --git a/scripts/lib/wic/pluginbase.py b/scripts/lib/wic/pluginbase.py deleted file mode 100644 index b64568339b..0000000000 --- a/scripts/lib/wic/pluginbase.py +++ /dev/null | |||
| @@ -1,144 +0,0 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | # | ||
| 3 | # Copyright (c) 2011 Intel, Inc. | ||
| 4 | # | ||
| 5 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 6 | # | ||
| 7 | |||
| 8 | __all__ = ['ImagerPlugin', 'SourcePlugin'] | ||
| 9 | |||
| 10 | import os | ||
| 11 | import logging | ||
| 12 | import types | ||
| 13 | |||
| 14 | from collections import defaultdict | ||
| 15 | import importlib | ||
| 16 | import importlib.util | ||
| 17 | |||
| 18 | from wic import WicError | ||
| 19 | from wic.misc import get_bitbake_var | ||
| 20 | |||
| 21 | PLUGIN_TYPES = ["imager", "source"] | ||
| 22 | |||
| 23 | SCRIPTS_PLUGIN_DIR = ["scripts/lib/wic/plugins", "lib/wic/plugins"] | ||
| 24 | |||
| 25 | logger = logging.getLogger('wic') | ||
| 26 | |||
| 27 | PLUGINS = defaultdict(dict) | ||
| 28 | |||
| 29 | class PluginMgr: | ||
| 30 | _plugin_dirs = [] | ||
| 31 | |||
| 32 | @classmethod | ||
| 33 | def get_plugins(cls, ptype): | ||
| 34 | """Get dictionary of <plugin_name>:<class> pairs.""" | ||
| 35 | if ptype not in PLUGIN_TYPES: | ||
| 36 | raise WicError('%s is not valid plugin type' % ptype) | ||
| 37 | |||
| 38 | # collect plugin directories | ||
| 39 | if not cls._plugin_dirs: | ||
| 40 | cls._plugin_dirs = [os.path.join(os.path.dirname(__file__), 'plugins')] | ||
| 41 | layers = get_bitbake_var("BBLAYERS") or '' | ||
| 42 | for layer_path in layers.split(): | ||
| 43 | for script_plugin_dir in SCRIPTS_PLUGIN_DIR: | ||
| 44 | path = os.path.join(layer_path, script_plugin_dir) | ||
| 45 | path = os.path.abspath(os.path.expanduser(path)) | ||
| 46 | if path not in cls._plugin_dirs and os.path.isdir(path): | ||
| 47 | cls._plugin_dirs.insert(0, path) | ||
| 48 | |||
| 49 | if ptype not in PLUGINS: | ||
| 50 | # load all ptype plugins | ||
| 51 | for pdir in cls._plugin_dirs: | ||
| 52 | ppath = os.path.join(pdir, ptype) | ||
| 53 | if os.path.isdir(ppath): | ||
| 54 | for fname in os.listdir(ppath): | ||
| 55 | if fname.endswith('.py'): | ||
| 56 | mname = fname[:-3] | ||
| 57 | mpath = os.path.join(ppath, fname) | ||
| 58 | logger.debug("loading plugin module %s", mpath) | ||
| 59 | spec = importlib.util.spec_from_file_location(mname, mpath) | ||
| 60 | module = importlib.util.module_from_spec(spec) | ||
| 61 | spec.loader.exec_module(module) | ||
| 62 | |||
| 63 | return PLUGINS.get(ptype) | ||
| 64 | |||
| 65 | class PluginMeta(type): | ||
| 66 | def __new__(cls, name, bases, attrs): | ||
| 67 | class_type = type.__new__(cls, name, bases, attrs) | ||
| 68 | if 'name' in attrs: | ||
| 69 | PLUGINS[class_type.wic_plugin_type][attrs['name']] = class_type | ||
| 70 | |||
| 71 | return class_type | ||
| 72 | |||
| 73 | class ImagerPlugin(metaclass=PluginMeta): | ||
| 74 | wic_plugin_type = "imager" | ||
| 75 | |||
| 76 | def do_create(self): | ||
| 77 | raise WicError("Method %s.do_create is not implemented" % | ||
| 78 | self.__class__.__name__) | ||
| 79 | |||
| 80 | class SourcePlugin(metaclass=PluginMeta): | ||
| 81 | wic_plugin_type = "source" | ||
| 82 | """ | ||
| 83 | The methods that can be implemented by --source plugins. | ||
| 84 | |||
| 85 | Any methods not implemented in a subclass inherit these. | ||
| 86 | """ | ||
| 87 | |||
| 88 | @classmethod | ||
| 89 | def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir, | ||
| 90 | bootimg_dir, kernel_dir, native_sysroot): | ||
| 91 | """ | ||
| 92 | Called after all partitions have been prepared and assembled into a | ||
| 93 | disk image. This provides a hook to allow finalization of a | ||
| 94 | disk image e.g. to write an MBR to it. | ||
| 95 | """ | ||
| 96 | logger.debug("SourcePlugin: do_install_disk: disk: %s", disk_name) | ||
| 97 | |||
| 98 | @classmethod | ||
| 99 | def do_stage_partition(cls, part, source_params, creator, cr_workdir, | ||
| 100 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 101 | native_sysroot): | ||
| 102 | """ | ||
| 103 | Special content staging hook called before do_prepare_partition(), | ||
| 104 | normally empty. | ||
| 105 | |||
| 106 | Typically, a partition will just use the passed-in parame e.g | ||
| 107 | straight bootimg_dir, etc, but in some cases, things need to | ||
| 108 | be more tailored e.g. to use a deploy dir + /boot, etc. This | ||
| 109 | hook allows those files to be staged in a customized fashion. | ||
| 110 | Not that get_bitbake_var() allows you to acces non-standard | ||
| 111 | variables that you might want to use for this. | ||
| 112 | """ | ||
| 113 | logger.debug("SourcePlugin: do_stage_partition: part: %s", part) | ||
| 114 | |||
| 115 | @classmethod | ||
| 116 | def do_configure_partition(cls, part, source_params, creator, cr_workdir, | ||
| 117 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 118 | native_sysroot): | ||
| 119 | """ | ||
| 120 | Called before do_prepare_partition(), typically used to create | ||
| 121 | custom configuration files for a partition, for example | ||
| 122 | syslinux or grub config files. | ||
| 123 | """ | ||
| 124 | logger.debug("SourcePlugin: do_configure_partition: part: %s", part) | ||
| 125 | |||
| 126 | @classmethod | ||
| 127 | def do_prepare_partition(cls, part, source_params, creator, cr_workdir, | ||
| 128 | oe_builddir, bootimg_dir, kernel_dir, rootfs_dir, | ||
| 129 | native_sysroot): | ||
| 130 | """ | ||
| 131 | Called to do the actual content population for a partition i.e. it | ||
| 132 | 'prepares' the partition to be incorporated into the image. | ||
| 133 | """ | ||
| 134 | logger.debug("SourcePlugin: do_prepare_partition: part: %s", part) | ||
| 135 | |||
| 136 | @classmethod | ||
| 137 | def do_post_partition(cls, part, source_params, creator, cr_workdir, | ||
| 138 | oe_builddir, bootimg_dir, kernel_dir, rootfs_dir, | ||
| 139 | native_sysroot): | ||
| 140 | """ | ||
| 141 | Called after the partition is created. It is useful to add post | ||
| 142 | operations e.g. security signing the partition. | ||
| 143 | """ | ||
| 144 | logger.debug("SourcePlugin: do_post_partition: part: %s", part) | ||
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py deleted file mode 100644 index a1d152659b..0000000000 --- a/scripts/lib/wic/plugins/imager/direct.py +++ /dev/null | |||
| @@ -1,694 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2013, Intel Corporation. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This implements the 'direct' imager plugin class for 'wic' | ||
| 8 | # | ||
| 9 | # AUTHORS | ||
| 10 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 11 | # | ||
| 12 | |||
| 13 | import logging | ||
| 14 | import os | ||
| 15 | import random | ||
| 16 | import shutil | ||
| 17 | import tempfile | ||
| 18 | import uuid | ||
| 19 | |||
| 20 | from time import strftime | ||
| 21 | |||
| 22 | from oe.path import copyhardlinktree | ||
| 23 | |||
| 24 | from wic import WicError | ||
| 25 | from wic.filemap import sparse_copy | ||
| 26 | from wic.ksparser import KickStart, KickStartError | ||
| 27 | from wic.pluginbase import PluginMgr, ImagerPlugin | ||
| 28 | from wic.misc import get_bitbake_var, exec_cmd, exec_native_cmd | ||
| 29 | |||
| 30 | logger = logging.getLogger('wic') | ||
| 31 | |||
| 32 | class DirectPlugin(ImagerPlugin): | ||
| 33 | """ | ||
| 34 | Install a system into a file containing a partitioned disk image. | ||
| 35 | |||
| 36 | An image file is formatted with a partition table, each partition | ||
| 37 | created from a rootfs or other OpenEmbedded build artifact and dd'ed | ||
| 38 | into the virtual disk. The disk image can subsequently be dd'ed onto | ||
| 39 | media and used on actual hardware. | ||
| 40 | """ | ||
| 41 | name = 'direct' | ||
| 42 | |||
| 43 | def __init__(self, wks_file, rootfs_dir, bootimg_dir, kernel_dir, | ||
| 44 | native_sysroot, oe_builddir, options): | ||
| 45 | try: | ||
| 46 | self.ks = KickStart(wks_file) | ||
| 47 | except KickStartError as err: | ||
| 48 | raise WicError(str(err)) | ||
| 49 | |||
| 50 | # parse possible 'rootfs=name' items | ||
| 51 | self.rootfs_dir = dict(rdir.split('=') for rdir in rootfs_dir.split(' ')) | ||
| 52 | self.bootimg_dir = bootimg_dir | ||
| 53 | self.kernel_dir = kernel_dir | ||
| 54 | self.native_sysroot = native_sysroot | ||
| 55 | self.oe_builddir = oe_builddir | ||
| 56 | |||
| 57 | self.debug = options.debug | ||
| 58 | self.outdir = options.outdir | ||
| 59 | self.compressor = options.compressor | ||
| 60 | self.bmap = options.bmap | ||
| 61 | self.no_fstab_update = options.no_fstab_update | ||
| 62 | self.updated_fstab_path = None | ||
| 63 | |||
| 64 | self.name = "%s-%s" % (os.path.splitext(os.path.basename(wks_file))[0], | ||
| 65 | strftime("%Y%m%d%H%M")) | ||
| 66 | self.workdir = self.setup_workdir(options.workdir) | ||
| 67 | self._image = None | ||
| 68 | self.ptable_format = self.ks.bootloader.ptable | ||
| 69 | self.parts = self.ks.partitions | ||
| 70 | |||
| 71 | # as a convenience, set source to the boot partition source | ||
| 72 | # instead of forcing it to be set via bootloader --source | ||
| 73 | for part in self.parts: | ||
| 74 | if not self.ks.bootloader.source and part.mountpoint == "/boot": | ||
| 75 | self.ks.bootloader.source = part.source | ||
| 76 | break | ||
| 77 | |||
| 78 | image_path = self._full_path(self.workdir, self.parts[0].disk, "direct") | ||
| 79 | self._image = PartitionedImage(image_path, self.ptable_format, | ||
| 80 | self.parts, self.native_sysroot, | ||
| 81 | options.extra_space) | ||
| 82 | |||
| 83 | def setup_workdir(self, workdir): | ||
| 84 | if workdir: | ||
| 85 | if os.path.exists(workdir): | ||
| 86 | raise WicError("Internal workdir '%s' specified in wic arguments already exists!" % (workdir)) | ||
| 87 | |||
| 88 | os.makedirs(workdir) | ||
| 89 | return workdir | ||
| 90 | else: | ||
| 91 | return tempfile.mkdtemp(dir=self.outdir, prefix='tmp.wic.') | ||
| 92 | |||
| 93 | def do_create(self): | ||
| 94 | """ | ||
| 95 | Plugin entry point. | ||
| 96 | """ | ||
| 97 | try: | ||
| 98 | self.create() | ||
| 99 | self.assemble() | ||
| 100 | self.finalize() | ||
| 101 | self.print_info() | ||
| 102 | finally: | ||
| 103 | self.cleanup() | ||
| 104 | |||
| 105 | def update_fstab(self, image_rootfs): | ||
| 106 | """Assume partition order same as in wks""" | ||
| 107 | if not image_rootfs: | ||
| 108 | return | ||
| 109 | |||
| 110 | fstab_path = image_rootfs + "/etc/fstab" | ||
| 111 | if not os.path.isfile(fstab_path): | ||
| 112 | return | ||
| 113 | |||
| 114 | with open(fstab_path) as fstab: | ||
| 115 | fstab_lines = fstab.readlines() | ||
| 116 | |||
| 117 | updated = False | ||
| 118 | for part in self.parts: | ||
| 119 | if not part.realnum or not part.mountpoint \ | ||
| 120 | or part.mountpoint == "/" or not (part.mountpoint.startswith('/') or part.mountpoint == "swap"): | ||
| 121 | continue | ||
| 122 | |||
| 123 | if part.use_uuid: | ||
| 124 | if part.fsuuid: | ||
| 125 | # FAT UUID is different from others | ||
| 126 | if len(part.fsuuid) == 10: | ||
| 127 | device_name = "UUID=%s-%s" % \ | ||
| 128 | (part.fsuuid[2:6], part.fsuuid[6:]) | ||
| 129 | else: | ||
| 130 | device_name = "UUID=%s" % part.fsuuid | ||
| 131 | else: | ||
| 132 | device_name = "PARTUUID=%s" % part.uuid | ||
| 133 | elif part.use_label: | ||
| 134 | device_name = "LABEL=%s" % part.label | ||
| 135 | else: | ||
| 136 | # mmc device partitions are named mmcblk0p1, mmcblk0p2.. | ||
| 137 | prefix = 'p' if part.disk.startswith('mmcblk') else '' | ||
| 138 | device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum) | ||
| 139 | |||
| 140 | opts = part.fsopts if part.fsopts else "defaults" | ||
| 141 | passno = part.fspassno if part.fspassno else "0" | ||
| 142 | line = "\t".join([device_name, part.mountpoint, part.fstype, | ||
| 143 | opts, "0", passno]) + "\n" | ||
| 144 | |||
| 145 | fstab_lines.append(line) | ||
| 146 | updated = True | ||
| 147 | |||
| 148 | if updated: | ||
| 149 | self.updated_fstab_path = os.path.join(self.workdir, "fstab") | ||
| 150 | with open(self.updated_fstab_path, "w") as f: | ||
| 151 | f.writelines(fstab_lines) | ||
| 152 | if os.getenv('SOURCE_DATE_EPOCH'): | ||
| 153 | fstab_time = int(os.getenv('SOURCE_DATE_EPOCH')) | ||
| 154 | os.utime(self.updated_fstab_path, (fstab_time, fstab_time)) | ||
| 155 | |||
| 156 | def _full_path(self, path, name, extention): | ||
| 157 | """ Construct full file path to a file we generate. """ | ||
| 158 | return os.path.join(path, "%s-%s.%s" % (self.name, name, extention)) | ||
| 159 | |||
| 160 | # | ||
| 161 | # Actual implemention | ||
| 162 | # | ||
| 163 | def create(self): | ||
| 164 | """ | ||
| 165 | For 'wic', we already have our build artifacts - we just create | ||
| 166 | filesystems from the artifacts directly and combine them into | ||
| 167 | a partitioned image. | ||
| 168 | """ | ||
| 169 | if not self.no_fstab_update: | ||
| 170 | self.update_fstab(self.rootfs_dir.get("ROOTFS_DIR")) | ||
| 171 | |||
| 172 | for part in self.parts: | ||
| 173 | # get rootfs size from bitbake variable if it's not set in .ks file | ||
| 174 | if not part.size: | ||
| 175 | # and if rootfs name is specified for the partition | ||
| 176 | image_name = self.rootfs_dir.get(part.rootfs_dir) | ||
| 177 | if image_name and os.path.sep not in image_name: | ||
| 178 | # Bitbake variable ROOTFS_SIZE is calculated in | ||
| 179 | # Image._get_rootfs_size method from meta/lib/oe/image.py | ||
| 180 | # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT, | ||
| 181 | # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE | ||
| 182 | rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name) | ||
| 183 | if rsize_bb: | ||
| 184 | part.size = int(round(float(rsize_bb))) | ||
| 185 | |||
| 186 | self._image.prepare(self) | ||
| 187 | self._image.layout_partitions() | ||
| 188 | self._image.create() | ||
| 189 | |||
| 190 | def assemble(self): | ||
| 191 | """ | ||
| 192 | Assemble partitions into disk image | ||
| 193 | """ | ||
| 194 | self._image.assemble() | ||
| 195 | |||
| 196 | def finalize(self): | ||
| 197 | """ | ||
| 198 | Finalize the disk image. | ||
| 199 | |||
| 200 | For example, prepare the image to be bootable by e.g. | ||
| 201 | creating and installing a bootloader configuration. | ||
| 202 | """ | ||
| 203 | source_plugin = self.ks.bootloader.source | ||
| 204 | disk_name = self.parts[0].disk | ||
| 205 | if source_plugin: | ||
| 206 | plugin = PluginMgr.get_plugins('source')[source_plugin] | ||
| 207 | plugin.do_install_disk(self._image, disk_name, self, self.workdir, | ||
| 208 | self.oe_builddir, self.bootimg_dir, | ||
| 209 | self.kernel_dir, self.native_sysroot) | ||
| 210 | |||
| 211 | full_path = self._image.path | ||
| 212 | # Generate .bmap | ||
| 213 | if self.bmap: | ||
| 214 | logger.debug("Generating bmap file for %s", disk_name) | ||
| 215 | python = os.path.join(self.native_sysroot, 'usr/bin/python3-native/python3') | ||
| 216 | bmaptool = os.path.join(self.native_sysroot, 'usr/bin/bmaptool') | ||
| 217 | exec_native_cmd("%s %s create %s -o %s.bmap" % \ | ||
| 218 | (python, bmaptool, full_path, full_path), self.native_sysroot) | ||
| 219 | # Compress the image | ||
| 220 | if self.compressor: | ||
| 221 | logger.debug("Compressing disk %s with %s", disk_name, self.compressor) | ||
| 222 | exec_cmd("%s %s" % (self.compressor, full_path)) | ||
| 223 | |||
| 224 | def print_info(self): | ||
| 225 | """ | ||
| 226 | Print the image(s) and artifacts used, for the user. | ||
| 227 | """ | ||
| 228 | msg = "The new image(s) can be found here:\n" | ||
| 229 | |||
| 230 | extension = "direct" + {"gzip": ".gz", | ||
| 231 | "bzip2": ".bz2", | ||
| 232 | "xz": ".xz", | ||
| 233 | None: ""}.get(self.compressor) | ||
| 234 | full_path = self._full_path(self.outdir, self.parts[0].disk, extension) | ||
| 235 | msg += ' %s\n\n' % full_path | ||
| 236 | |||
| 237 | msg += 'The following build artifacts were used to create the image(s):\n' | ||
| 238 | for part in self.parts: | ||
| 239 | if part.rootfs_dir is None: | ||
| 240 | continue | ||
| 241 | if part.mountpoint == '/': | ||
| 242 | suffix = ':' | ||
| 243 | else: | ||
| 244 | suffix = '["%s"]:' % (part.mountpoint or part.label) | ||
| 245 | rootdir = part.rootfs_dir | ||
| 246 | msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), rootdir) | ||
| 247 | |||
| 248 | msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir | ||
| 249 | msg += ' KERNEL_DIR: %s\n' % self.kernel_dir | ||
| 250 | msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot | ||
| 251 | |||
| 252 | logger.info(msg) | ||
| 253 | |||
| 254 | @property | ||
| 255 | def rootdev(self): | ||
| 256 | """ | ||
| 257 | Get root device name to use as a 'root' parameter | ||
| 258 | in kernel command line. | ||
| 259 | |||
| 260 | Assume partition order same as in wks | ||
| 261 | """ | ||
| 262 | for part in self.parts: | ||
| 263 | if part.mountpoint == "/": | ||
| 264 | if part.uuid: | ||
| 265 | return "PARTUUID=%s" % part.uuid | ||
| 266 | elif part.label and self.ptable_format != 'msdos': | ||
| 267 | return "PARTLABEL=%s" % part.label | ||
| 268 | else: | ||
| 269 | suffix = 'p' if part.disk.startswith('mmcblk') else '' | ||
| 270 | return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum) | ||
| 271 | |||
| 272 | def cleanup(self): | ||
| 273 | if self._image: | ||
| 274 | self._image.cleanup() | ||
| 275 | |||
| 276 | # Move results to the output dir | ||
| 277 | if not os.path.exists(self.outdir): | ||
| 278 | os.makedirs(self.outdir) | ||
| 279 | |||
| 280 | for fname in os.listdir(self.workdir): | ||
| 281 | path = os.path.join(self.workdir, fname) | ||
| 282 | if os.path.isfile(path): | ||
| 283 | shutil.move(path, os.path.join(self.outdir, fname)) | ||
| 284 | |||
| 285 | # remove work directory when it is not in debugging mode | ||
| 286 | if not self.debug: | ||
| 287 | shutil.rmtree(self.workdir, ignore_errors=True) | ||
| 288 | |||
| 289 | # Overhead of the MBR partitioning scheme (just one sector) | ||
| 290 | MBR_OVERHEAD = 1 | ||
| 291 | |||
| 292 | # Overhead of the GPT partitioning scheme | ||
| 293 | GPT_OVERHEAD = 34 | ||
| 294 | |||
| 295 | # Size of a sector in bytes | ||
| 296 | SECTOR_SIZE = 512 | ||
| 297 | |||
| 298 | class PartitionedImage(): | ||
| 299 | """ | ||
| 300 | Partitioned image in a file. | ||
| 301 | """ | ||
| 302 | |||
| 303 | def __init__(self, path, ptable_format, partitions, native_sysroot=None, extra_space=0): | ||
| 304 | self.path = path # Path to the image file | ||
| 305 | self.numpart = 0 # Number of allocated partitions | ||
| 306 | self.realpart = 0 # Number of partitions in the partition table | ||
| 307 | self.primary_part_num = 0 # Number of primary partitions (msdos) | ||
| 308 | self.extendedpart = 0 # Create extended partition before this logical partition (msdos) | ||
| 309 | self.extended_size_sec = 0 # Size of exteded partition (msdos) | ||
| 310 | self.logical_part_cnt = 0 # Number of total logical paritions (msdos) | ||
| 311 | self.offset = 0 # Offset of next partition (in sectors) | ||
| 312 | self.min_size = 0 # Minimum required disk size to fit | ||
| 313 | # all partitions (in bytes) | ||
| 314 | self.ptable_format = ptable_format # Partition table format | ||
| 315 | # Disk system identifier | ||
| 316 | if os.getenv('SOURCE_DATE_EPOCH'): | ||
| 317 | self.identifier = random.Random(int(os.getenv('SOURCE_DATE_EPOCH'))).randint(1, 0xffffffff) | ||
| 318 | else: | ||
| 319 | self.identifier = random.SystemRandom().randint(1, 0xffffffff) | ||
| 320 | |||
| 321 | self.partitions = partitions | ||
| 322 | self.partimages = [] | ||
| 323 | # Size of a sector used in calculations | ||
| 324 | self.sector_size = SECTOR_SIZE | ||
| 325 | self.native_sysroot = native_sysroot | ||
| 326 | num_real_partitions = len([p for p in self.partitions if not p.no_table]) | ||
| 327 | self.extra_space = extra_space | ||
| 328 | |||
| 329 | # calculate the real partition number, accounting for partitions not | ||
| 330 | # in the partition table and logical partitions | ||
| 331 | realnum = 0 | ||
| 332 | for part in self.partitions: | ||
| 333 | if part.no_table: | ||
| 334 | part.realnum = 0 | ||
| 335 | else: | ||
| 336 | realnum += 1 | ||
| 337 | if self.ptable_format == 'msdos' and realnum > 3 and num_real_partitions > 4: | ||
| 338 | part.realnum = realnum + 1 | ||
| 339 | continue | ||
| 340 | part.realnum = realnum | ||
| 341 | |||
| 342 | # generate parition and filesystem UUIDs | ||
| 343 | for part in self.partitions: | ||
| 344 | if not part.uuid and part.use_uuid: | ||
| 345 | if self.ptable_format in ('gpt', 'gpt-hybrid'): | ||
| 346 | part.uuid = str(uuid.uuid4()) | ||
| 347 | else: # msdos partition table | ||
| 348 | part.uuid = '%08x-%02d' % (self.identifier, part.realnum) | ||
| 349 | if not part.fsuuid: | ||
| 350 | if part.fstype == 'vfat' or part.fstype == 'msdos': | ||
| 351 | part.fsuuid = '0x' + str(uuid.uuid4())[:8].upper() | ||
| 352 | else: | ||
| 353 | part.fsuuid = str(uuid.uuid4()) | ||
| 354 | else: | ||
| 355 | #make sure the fsuuid for vfat/msdos align with format 0xYYYYYYYY | ||
| 356 | if part.fstype == 'vfat' or part.fstype == 'msdos': | ||
| 357 | if part.fsuuid.upper().startswith("0X"): | ||
| 358 | part.fsuuid = '0x' + part.fsuuid.upper()[2:].rjust(8,"0") | ||
| 359 | else: | ||
| 360 | part.fsuuid = '0x' + part.fsuuid.upper().rjust(8,"0") | ||
| 361 | |||
| 362 | def prepare(self, imager): | ||
| 363 | """Prepare an image. Call prepare method of all image partitions.""" | ||
| 364 | for part in self.partitions: | ||
| 365 | # need to create the filesystems in order to get their | ||
| 366 | # sizes before we can add them and do the layout. | ||
| 367 | part.prepare(imager, imager.workdir, imager.oe_builddir, | ||
| 368 | imager.rootfs_dir, imager.bootimg_dir, | ||
| 369 | imager.kernel_dir, imager.native_sysroot, | ||
| 370 | imager.updated_fstab_path) | ||
| 371 | |||
| 372 | # Converting kB to sectors for parted | ||
| 373 | part.size_sec = part.disk_size * 1024 // self.sector_size | ||
| 374 | |||
| 375 | def layout_partitions(self): | ||
| 376 | """ Layout the partitions, meaning calculate the position of every | ||
| 377 | partition on the disk. The 'ptable_format' parameter defines the | ||
| 378 | partition table format and may be "msdos". """ | ||
| 379 | |||
| 380 | logger.debug("Assigning %s partitions to disks", self.ptable_format) | ||
| 381 | |||
| 382 | # The number of primary and logical partitions. Extended partition and | ||
| 383 | # partitions not listed in the table are not included. | ||
| 384 | num_real_partitions = len([p for p in self.partitions if not p.no_table]) | ||
| 385 | |||
| 386 | # Go through partitions in the order they are added in .ks file | ||
| 387 | for num in range(len(self.partitions)): | ||
| 388 | part = self.partitions[num] | ||
| 389 | |||
| 390 | if self.ptable_format == 'msdos' and part.part_name: | ||
| 391 | raise WicError("setting custom partition name is not " \ | ||
| 392 | "implemented for msdos partitions") | ||
| 393 | |||
| 394 | if self.ptable_format == 'msdos' and part.part_type: | ||
| 395 | # The --part-type can also be implemented for MBR partitions, | ||
| 396 | # in which case it would map to the 1-byte "partition type" | ||
| 397 | # filed at offset 3 of the partition entry. | ||
| 398 | raise WicError("setting custom partition type is not " \ | ||
| 399 | "implemented for msdos partitions") | ||
| 400 | |||
| 401 | if part.mbr and self.ptable_format != 'gpt-hybrid': | ||
| 402 | raise WicError("Partition may only be included in MBR with " \ | ||
| 403 | "a gpt-hybrid partition table") | ||
| 404 | |||
| 405 | # Get the disk where the partition is located | ||
| 406 | self.numpart += 1 | ||
| 407 | if not part.no_table: | ||
| 408 | self.realpart += 1 | ||
| 409 | |||
| 410 | if self.numpart == 1: | ||
| 411 | if self.ptable_format == "msdos": | ||
| 412 | overhead = MBR_OVERHEAD | ||
| 413 | elif self.ptable_format in ("gpt", "gpt-hybrid"): | ||
| 414 | overhead = GPT_OVERHEAD | ||
| 415 | |||
| 416 | # Skip one sector required for the partitioning scheme overhead | ||
| 417 | self.offset += overhead | ||
| 418 | |||
| 419 | if self.ptable_format == "msdos": | ||
| 420 | if self.primary_part_num > 3 or \ | ||
| 421 | (self.extendedpart == 0 and self.primary_part_num >= 3 and num_real_partitions > 4): | ||
| 422 | part.type = 'logical' | ||
| 423 | # Reserve a sector for EBR for every logical partition | ||
| 424 | # before alignment is performed. | ||
| 425 | if part.type == 'logical': | ||
| 426 | self.offset += 2 | ||
| 427 | |||
| 428 | align_sectors = 0 | ||
| 429 | if part.align: | ||
| 430 | # If not first partition and we do have alignment set we need | ||
| 431 | # to align the partition. | ||
| 432 | # FIXME: This leaves a empty spaces to the disk. To fill the | ||
| 433 | # gaps we could enlargea the previous partition? | ||
| 434 | |||
| 435 | # Calc how much the alignment is off. | ||
| 436 | align_sectors = self.offset % (part.align * 1024 // self.sector_size) | ||
| 437 | |||
| 438 | if align_sectors: | ||
| 439 | # If partition is not aligned as required, we need | ||
| 440 | # to move forward to the next alignment point | ||
| 441 | align_sectors = (part.align * 1024 // self.sector_size) - align_sectors | ||
| 442 | |||
| 443 | logger.debug("Realignment for %s%s with %s sectors, original" | ||
| 444 | " offset %s, target alignment is %sK.", | ||
| 445 | part.disk, self.numpart, align_sectors, | ||
| 446 | self.offset, part.align) | ||
| 447 | |||
| 448 | # increase the offset so we actually start the partition on right alignment | ||
| 449 | self.offset += align_sectors | ||
| 450 | |||
| 451 | if part.offset is not None: | ||
| 452 | offset = part.offset // self.sector_size | ||
| 453 | |||
| 454 | if offset * self.sector_size != part.offset: | ||
| 455 | raise WicError("Could not place %s%s at offset %d with sector size %d" % (part.disk, self.numpart, part.offset, self.sector_size)) | ||
| 456 | |||
| 457 | delta = offset - self.offset | ||
| 458 | if delta < 0: | ||
| 459 | raise WicError("Could not place %s%s at offset %d: next free sector is %d (delta: %d)" % (part.disk, self.numpart, part.offset, self.offset, delta)) | ||
| 460 | |||
| 461 | logger.debug("Skipping %d sectors to place %s%s at offset %dK", | ||
| 462 | delta, part.disk, self.numpart, part.offset) | ||
| 463 | |||
| 464 | self.offset = offset | ||
| 465 | |||
| 466 | part.start = self.offset | ||
| 467 | self.offset += part.size_sec | ||
| 468 | |||
| 469 | if not part.no_table: | ||
| 470 | part.num = self.realpart | ||
| 471 | else: | ||
| 472 | part.num = 0 | ||
| 473 | |||
| 474 | if self.ptable_format == "msdos" and not part.no_table: | ||
| 475 | if part.type == 'logical': | ||
| 476 | self.logical_part_cnt += 1 | ||
| 477 | part.num = self.logical_part_cnt + 4 | ||
| 478 | if self.extendedpart == 0: | ||
| 479 | # Create extended partition as a primary partition | ||
| 480 | self.primary_part_num += 1 | ||
| 481 | self.extendedpart = part.num | ||
| 482 | else: | ||
| 483 | self.extended_size_sec += align_sectors | ||
| 484 | self.extended_size_sec += part.size_sec + 2 | ||
| 485 | else: | ||
| 486 | self.primary_part_num += 1 | ||
| 487 | part.num = self.primary_part_num | ||
| 488 | |||
| 489 | logger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " | ||
| 490 | "sectors (%d bytes).", part.mountpoint, part.disk, | ||
| 491 | part.num, part.start, self.offset - 1, part.size_sec, | ||
| 492 | part.size_sec * self.sector_size) | ||
| 493 | |||
| 494 | # Once all the partitions have been layed out, we can calculate the | ||
| 495 | # minumim disk size | ||
| 496 | self.min_size = self.offset | ||
| 497 | if self.ptable_format in ("gpt", "gpt-hybrid"): | ||
| 498 | self.min_size += GPT_OVERHEAD | ||
| 499 | |||
| 500 | self.min_size *= self.sector_size | ||
| 501 | self.min_size += self.extra_space | ||
| 502 | |||
| 503 | def _create_partition(self, device, parttype, fstype, start, size): | ||
| 504 | """ Create a partition on an image described by the 'device' object. """ | ||
| 505 | |||
| 506 | # Start is included to the size so we need to substract one from the end. | ||
| 507 | end = start + size - 1 | ||
| 508 | logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors", | ||
| 509 | parttype, start, end, size) | ||
| 510 | |||
| 511 | cmd = "parted -s %s unit s mkpart %s" % (device, parttype) | ||
| 512 | if fstype: | ||
| 513 | cmd += " %s" % fstype | ||
| 514 | cmd += " %d %d" % (start, end) | ||
| 515 | |||
| 516 | return exec_native_cmd(cmd, self.native_sysroot) | ||
| 517 | |||
| 518 | def _write_identifier(self, device, identifier): | ||
| 519 | logger.debug("Set disk identifier %x", identifier) | ||
| 520 | with open(device, 'r+b') as img: | ||
| 521 | img.seek(0x1B8) | ||
| 522 | img.write(identifier.to_bytes(4, 'little')) | ||
| 523 | |||
| 524 | def _make_disk(self, device, ptable_format, min_size): | ||
| 525 | logger.debug("Creating sparse file %s", device) | ||
| 526 | with open(device, 'w') as sparse: | ||
| 527 | os.ftruncate(sparse.fileno(), min_size) | ||
| 528 | |||
| 529 | logger.debug("Initializing partition table for %s", device) | ||
| 530 | exec_native_cmd("parted -s %s mklabel %s" % (device, ptable_format), | ||
| 531 | self.native_sysroot) | ||
| 532 | |||
| 533 | def _write_disk_guid(self): | ||
| 534 | if self.ptable_format in ('gpt', 'gpt-hybrid'): | ||
| 535 | if os.getenv('SOURCE_DATE_EPOCH'): | ||
| 536 | self.disk_guid = uuid.UUID(int=int(os.getenv('SOURCE_DATE_EPOCH'))) | ||
| 537 | else: | ||
| 538 | self.disk_guid = uuid.uuid4() | ||
| 539 | |||
| 540 | logger.debug("Set disk guid %s", self.disk_guid) | ||
| 541 | sfdisk_cmd = "sfdisk --disk-id %s %s" % (self.path, self.disk_guid) | ||
| 542 | exec_native_cmd(sfdisk_cmd, self.native_sysroot) | ||
| 543 | |||
| 544 | def create(self): | ||
| 545 | self._make_disk(self.path, | ||
| 546 | "gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format, | ||
| 547 | self.min_size) | ||
| 548 | |||
| 549 | self._write_identifier(self.path, self.identifier) | ||
| 550 | self._write_disk_guid() | ||
| 551 | |||
| 552 | if self.ptable_format == "gpt-hybrid": | ||
| 553 | mbr_path = self.path + ".mbr" | ||
| 554 | self._make_disk(mbr_path, "msdos", self.min_size) | ||
| 555 | self._write_identifier(mbr_path, self.identifier) | ||
| 556 | |||
| 557 | logger.debug("Creating partitions") | ||
| 558 | |||
| 559 | hybrid_mbr_part_num = 0 | ||
| 560 | |||
| 561 | for part in self.partitions: | ||
| 562 | if part.num == 0: | ||
| 563 | continue | ||
| 564 | |||
| 565 | if self.ptable_format == "msdos" and part.num == self.extendedpart: | ||
| 566 | # Create an extended partition (note: extended | ||
| 567 | # partition is described in MBR and contains all | ||
| 568 | # logical partitions). The logical partitions save a | ||
| 569 | # sector for an EBR just before the start of a | ||
| 570 | # partition. The extended partition must start one | ||
| 571 | # sector before the start of the first logical | ||
| 572 | # partition. This way the first EBR is inside of the | ||
| 573 | # extended partition. Since the extended partitions | ||
| 574 | # starts a sector before the first logical partition, | ||
| 575 | # add a sector at the back, so that there is enough | ||
| 576 | # room for all logical partitions. | ||
| 577 | self._create_partition(self.path, "extended", | ||
| 578 | None, part.start - 2, | ||
| 579 | self.extended_size_sec) | ||
| 580 | |||
| 581 | if part.fstype == "swap": | ||
| 582 | parted_fs_type = "linux-swap" | ||
| 583 | elif part.fstype == "vfat": | ||
| 584 | parted_fs_type = "fat32" | ||
| 585 | elif part.fstype == "msdos": | ||
| 586 | parted_fs_type = "fat16" | ||
| 587 | if not part.system_id: | ||
| 588 | part.system_id = '0x6' # FAT16 | ||
| 589 | else: | ||
| 590 | # Type for ext2/ext3/ext4/btrfs | ||
| 591 | parted_fs_type = "ext2" | ||
| 592 | |||
| 593 | # Boot ROM of OMAP boards require vfat boot partition to have an | ||
| 594 | # even number of sectors. | ||
| 595 | if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \ | ||
| 596 | and part.size_sec % 2: | ||
| 597 | logger.debug("Subtracting one sector from '%s' partition to " | ||
| 598 | "get even number of sectors for the partition", | ||
| 599 | part.mountpoint) | ||
| 600 | part.size_sec -= 1 | ||
| 601 | |||
| 602 | self._create_partition(self.path, part.type, | ||
| 603 | parted_fs_type, part.start, part.size_sec) | ||
| 604 | |||
| 605 | if self.ptable_format == "gpt-hybrid" and part.mbr: | ||
| 606 | hybrid_mbr_part_num += 1 | ||
| 607 | if hybrid_mbr_part_num > 4: | ||
| 608 | raise WicError("Extended MBR partitions are not supported in hybrid MBR") | ||
| 609 | self._create_partition(mbr_path, "primary", | ||
| 610 | parted_fs_type, part.start, part.size_sec) | ||
| 611 | |||
| 612 | if self.ptable_format in ("gpt", "gpt-hybrid") and (part.part_name or part.label): | ||
| 613 | partition_label = part.part_name if part.part_name else part.label | ||
| 614 | logger.debug("partition %d: set name to %s", | ||
| 615 | part.num, partition_label) | ||
| 616 | exec_native_cmd("sgdisk --change-name=%d:%s %s" % \ | ||
| 617 | (part.num, partition_label, | ||
| 618 | self.path), self.native_sysroot) | ||
| 619 | |||
| 620 | if part.part_type: | ||
| 621 | logger.debug("partition %d: set type UID to %s", | ||
| 622 | part.num, part.part_type) | ||
| 623 | exec_native_cmd("sgdisk --typecode=%d:%s %s" % \ | ||
| 624 | (part.num, part.part_type, | ||
| 625 | self.path), self.native_sysroot) | ||
| 626 | |||
| 627 | if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"): | ||
| 628 | logger.debug("partition %d: set UUID to %s", | ||
| 629 | part.num, part.uuid) | ||
| 630 | exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ | ||
| 631 | (part.num, part.uuid, self.path), | ||
| 632 | self.native_sysroot) | ||
| 633 | |||
| 634 | if part.active: | ||
| 635 | flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot" | ||
| 636 | logger.debug("Set '%s' flag for partition '%s' on disk '%s'", | ||
| 637 | flag_name, part.num, self.path) | ||
| 638 | exec_native_cmd("parted -s %s set %d %s on" % \ | ||
| 639 | (self.path, part.num, flag_name), | ||
| 640 | self.native_sysroot) | ||
| 641 | if self.ptable_format == 'gpt-hybrid' and part.mbr: | ||
| 642 | exec_native_cmd("parted -s %s set %d %s on" % \ | ||
| 643 | (mbr_path, hybrid_mbr_part_num, "boot"), | ||
| 644 | self.native_sysroot) | ||
| 645 | if part.system_id: | ||
| 646 | exec_native_cmd("sfdisk --part-type %s %s %s" % \ | ||
| 647 | (self.path, part.num, part.system_id), | ||
| 648 | self.native_sysroot) | ||
| 649 | |||
| 650 | if part.hidden and self.ptable_format == "gpt": | ||
| 651 | logger.debug("Set hidden attribute for partition '%s' on disk '%s'", | ||
| 652 | part.num, self.path) | ||
| 653 | exec_native_cmd("sfdisk --part-attrs %s %s RequiredPartition" % \ | ||
| 654 | (self.path, part.num), | ||
| 655 | self.native_sysroot) | ||
| 656 | |||
| 657 | if self.ptable_format == "gpt-hybrid": | ||
| 658 | # Write a protective GPT partition | ||
| 659 | hybrid_mbr_part_num += 1 | ||
| 660 | if hybrid_mbr_part_num > 4: | ||
| 661 | raise WicError("Extended MBR partitions are not supported in hybrid MBR") | ||
| 662 | |||
| 663 | # parted cannot directly create a protective GPT partition, so | ||
| 664 | # create with an arbitrary type, then change it to the correct type | ||
| 665 | # with sfdisk | ||
| 666 | self._create_partition(mbr_path, "primary", "fat32", 1, GPT_OVERHEAD) | ||
| 667 | exec_native_cmd("sfdisk --part-type %s %d 0xee" % (mbr_path, hybrid_mbr_part_num), | ||
| 668 | self.native_sysroot) | ||
| 669 | |||
| 670 | # Copy hybrid MBR | ||
| 671 | with open(mbr_path, "rb") as mbr_file: | ||
| 672 | with open(self.path, "r+b") as image_file: | ||
| 673 | mbr = mbr_file.read(512) | ||
| 674 | image_file.write(mbr) | ||
| 675 | |||
| 676 | def cleanup(self): | ||
| 677 | pass | ||
| 678 | |||
| 679 | def assemble(self): | ||
| 680 | logger.debug("Installing partitions") | ||
| 681 | |||
| 682 | for part in self.partitions: | ||
| 683 | source = part.source_file | ||
| 684 | if source: | ||
| 685 | # install source_file contents into a partition | ||
| 686 | sparse_copy(source, self.path, seek=part.start * self.sector_size) | ||
| 687 | |||
| 688 | logger.debug("Installed %s in partition %d, sectors %d-%d, " | ||
| 689 | "size %d sectors", source, part.num, part.start, | ||
| 690 | part.start + part.size_sec - 1, part.size_sec) | ||
| 691 | |||
| 692 | partimage = self.path + '.p%d' % part.num | ||
| 693 | os.rename(source, partimage) | ||
| 694 | self.partimages.append(partimage) | ||
diff --git a/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py b/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py deleted file mode 100644 index 5bd7390680..0000000000 --- a/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py +++ /dev/null | |||
| @@ -1,213 +0,0 @@ | |||
| 1 | # | ||
| 2 | # This program is free software; you can redistribute it and/or modify | ||
| 3 | # it under the terms of the GNU General Public License version 2 as | ||
| 4 | # published by the Free Software Foundation. | ||
| 5 | # | ||
| 6 | # This program is distributed in the hope that it will be useful, | ||
| 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 9 | # GNU General Public License for more details. | ||
| 10 | # | ||
| 11 | # You should have received a copy of the GNU General Public License along | ||
| 12 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
| 13 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 14 | # | ||
| 15 | # DESCRIPTION | ||
| 16 | # This implements the 'bootimg-biosplusefi' source plugin class for 'wic' | ||
| 17 | # | ||
| 18 | # AUTHORS | ||
| 19 | # William Bourque <wbourque [at) gmail.com> | ||
| 20 | |||
| 21 | import types | ||
| 22 | |||
| 23 | from wic.pluginbase import SourcePlugin | ||
| 24 | from importlib.machinery import SourceFileLoader | ||
| 25 | |||
| 26 | class BootimgBiosPlusEFIPlugin(SourcePlugin): | ||
| 27 | """ | ||
| 28 | Create MBR + EFI boot partition | ||
| 29 | |||
| 30 | This plugin creates a boot partition that contains both | ||
| 31 | legacy BIOS and EFI content. It will be able to boot from both. | ||
| 32 | This is useful when managing PC fleet with some older machines | ||
| 33 | without EFI support. | ||
| 34 | |||
| 35 | Note it is possible to create an image that can boot from both | ||
| 36 | legacy BIOS and EFI by defining two partitions : one with arg | ||
| 37 | --source bootimg-efi and another one with --source bootimg-pcbios. | ||
| 38 | However, this method has the obvious downside that it requires TWO | ||
| 39 | partitions to be created on the storage device. | ||
| 40 | Both partitions will also be marked as "bootable" which does not work on | ||
| 41 | most BIOS, has BIOS often uses the "bootable" flag to determine | ||
| 42 | what to boot. If you have such a BIOS, you need to manually remove the | ||
| 43 | "bootable" flag from the EFI partition for the drive to be bootable. | ||
| 44 | Having two partitions also seems to confuse wic : the content of | ||
| 45 | the first partition will be duplicated into the second, even though it | ||
| 46 | will not be used at all. | ||
| 47 | |||
| 48 | Also, unlike "isoimage-isohybrid" that also does BIOS and EFI, this plugin | ||
| 49 | allows you to have more than only a single rootfs partitions and does | ||
| 50 | not turn the rootfs into an initramfs RAM image. | ||
| 51 | |||
| 52 | This plugin is made to put everything into a single /boot partition so it | ||
| 53 | does not have the limitations listed above. | ||
| 54 | |||
| 55 | The plugin is made so it does tries not to reimplement what's already | ||
| 56 | been done in other plugins; as such it imports "bootimg-pcbios" | ||
| 57 | and "bootimg-efi". | ||
| 58 | Plugin "bootimg-pcbios" is used to generate legacy BIOS boot. | ||
| 59 | Plugin "bootimg-efi" is used to generate the UEFI boot. Note that it | ||
| 60 | requires a --sourceparams argument to know which loader to use; refer | ||
| 61 | to "bootimg-efi" code/documentation for the list of loader. | ||
| 62 | |||
| 63 | Imports are handled with "SourceFileLoader" from importlib as it is | ||
| 64 | otherwise very difficult to import module that has hyphen "-" in their | ||
| 65 | filename. | ||
| 66 | The SourcePlugin() methods used in the plugins (do_install_disk, | ||
| 67 | do_configure_partition, do_prepare_partition) are then called on both, | ||
| 68 | beginning by "bootimg-efi". | ||
| 69 | |||
| 70 | Plugin options, such as "--sourceparams" can still be passed to a | ||
| 71 | plugin, as long they does not cause issue in the other plugin. | ||
| 72 | |||
| 73 | Example wic configuration: | ||
| 74 | part /boot --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\\ | ||
| 75 | --ondisk sda --label os_boot --active --align 1024 --use-uuid | ||
| 76 | """ | ||
| 77 | |||
| 78 | name = 'bootimg-biosplusefi' | ||
| 79 | |||
| 80 | __PCBIOS_MODULE_NAME = "bootimg-pcbios" | ||
| 81 | __EFI_MODULE_NAME = "bootimg-efi" | ||
| 82 | |||
| 83 | __imgEFIObj = None | ||
| 84 | __imgBiosObj = None | ||
| 85 | |||
| 86 | @classmethod | ||
| 87 | def __init__(cls): | ||
| 88 | """ | ||
| 89 | Constructor (init) | ||
| 90 | """ | ||
| 91 | |||
| 92 | # XXX | ||
| 93 | # For some reasons, __init__ constructor is never called. | ||
| 94 | # Something to do with how pluginbase works? | ||
| 95 | cls.__instanciateSubClasses() | ||
| 96 | |||
| 97 | @classmethod | ||
| 98 | def __instanciateSubClasses(cls): | ||
| 99 | """ | ||
| 100 | |||
| 101 | """ | ||
| 102 | |||
| 103 | # Import bootimg-pcbios (class name "BootimgPcbiosPlugin") | ||
| 104 | modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), | ||
| 105 | cls.__PCBIOS_MODULE_NAME + ".py") | ||
| 106 | loader = SourceFileLoader(cls.__PCBIOS_MODULE_NAME, modulePath) | ||
| 107 | mod = types.ModuleType(loader.name) | ||
| 108 | loader.exec_module(mod) | ||
| 109 | cls.__imgBiosObj = mod.BootimgPcbiosPlugin() | ||
| 110 | |||
| 111 | # Import bootimg-efi (class name "BootimgEFIPlugin") | ||
| 112 | modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), | ||
| 113 | cls.__EFI_MODULE_NAME + ".py") | ||
| 114 | loader = SourceFileLoader(cls.__EFI_MODULE_NAME, modulePath) | ||
| 115 | mod = types.ModuleType(loader.name) | ||
| 116 | loader.exec_module(mod) | ||
| 117 | cls.__imgEFIObj = mod.BootimgEFIPlugin() | ||
| 118 | |||
| 119 | @classmethod | ||
| 120 | def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir, | ||
| 121 | bootimg_dir, kernel_dir, native_sysroot): | ||
| 122 | """ | ||
| 123 | Called after all partitions have been prepared and assembled into a | ||
| 124 | disk image. | ||
| 125 | """ | ||
| 126 | |||
| 127 | if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ): | ||
| 128 | cls.__instanciateSubClasses() | ||
| 129 | |||
| 130 | cls.__imgEFIObj.do_install_disk( | ||
| 131 | disk, | ||
| 132 | disk_name, | ||
| 133 | creator, | ||
| 134 | workdir, | ||
| 135 | oe_builddir, | ||
| 136 | bootimg_dir, | ||
| 137 | kernel_dir, | ||
| 138 | native_sysroot) | ||
| 139 | |||
| 140 | cls.__imgBiosObj.do_install_disk( | ||
| 141 | disk, | ||
| 142 | disk_name, | ||
| 143 | creator, | ||
| 144 | workdir, | ||
| 145 | oe_builddir, | ||
| 146 | bootimg_dir, | ||
| 147 | kernel_dir, | ||
| 148 | native_sysroot) | ||
| 149 | |||
| 150 | @classmethod | ||
| 151 | def do_configure_partition(cls, part, source_params, creator, cr_workdir, | ||
| 152 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 153 | native_sysroot): | ||
| 154 | """ | ||
| 155 | Called before do_prepare_partition() | ||
| 156 | """ | ||
| 157 | |||
| 158 | if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ): | ||
| 159 | cls.__instanciateSubClasses() | ||
| 160 | |||
| 161 | cls.__imgEFIObj.do_configure_partition( | ||
| 162 | part, | ||
| 163 | source_params, | ||
| 164 | creator, | ||
| 165 | cr_workdir, | ||
| 166 | oe_builddir, | ||
| 167 | bootimg_dir, | ||
| 168 | kernel_dir, | ||
| 169 | native_sysroot) | ||
| 170 | |||
| 171 | cls.__imgBiosObj.do_configure_partition( | ||
| 172 | part, | ||
| 173 | source_params, | ||
| 174 | creator, | ||
| 175 | cr_workdir, | ||
| 176 | oe_builddir, | ||
| 177 | bootimg_dir, | ||
| 178 | kernel_dir, | ||
| 179 | native_sysroot) | ||
| 180 | |||
| 181 | @classmethod | ||
| 182 | def do_prepare_partition(cls, part, source_params, creator, cr_workdir, | ||
| 183 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 184 | rootfs_dir, native_sysroot): | ||
| 185 | """ | ||
| 186 | Called to do the actual content population for a partition i.e. it | ||
| 187 | 'prepares' the partition to be incorporated into the image. | ||
| 188 | """ | ||
| 189 | |||
| 190 | if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ): | ||
| 191 | cls.__instanciateSubClasses() | ||
| 192 | |||
| 193 | cls.__imgEFIObj.do_prepare_partition( | ||
| 194 | part, | ||
| 195 | source_params, | ||
| 196 | creator, | ||
| 197 | cr_workdir, | ||
| 198 | oe_builddir, | ||
| 199 | bootimg_dir, | ||
| 200 | kernel_dir, | ||
| 201 | rootfs_dir, | ||
| 202 | native_sysroot) | ||
| 203 | |||
| 204 | cls.__imgBiosObj.do_prepare_partition( | ||
| 205 | part, | ||
| 206 | source_params, | ||
| 207 | creator, | ||
| 208 | cr_workdir, | ||
| 209 | oe_builddir, | ||
| 210 | bootimg_dir, | ||
| 211 | kernel_dir, | ||
| 212 | rootfs_dir, | ||
| 213 | native_sysroot) | ||
diff --git a/scripts/lib/wic/plugins/source/bootimg-efi.py b/scripts/lib/wic/plugins/source/bootimg-efi.py deleted file mode 100644 index 13a9cddf4e..0000000000 --- a/scripts/lib/wic/plugins/source/bootimg-efi.py +++ /dev/null | |||
| @@ -1,507 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2014, Intel Corporation. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This implements the 'bootimg-efi' source plugin class for 'wic' | ||
| 8 | # | ||
| 9 | # AUTHORS | ||
| 10 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 11 | # | ||
| 12 | |||
| 13 | import logging | ||
| 14 | import os | ||
| 15 | import tempfile | ||
| 16 | import shutil | ||
| 17 | import re | ||
| 18 | |||
| 19 | from glob import glob | ||
| 20 | |||
| 21 | from wic import WicError | ||
| 22 | from wic.engine import get_custom_config | ||
| 23 | from wic.pluginbase import SourcePlugin | ||
| 24 | from wic.misc import (exec_cmd, exec_native_cmd, | ||
| 25 | get_bitbake_var, BOOTDD_EXTRA_SPACE) | ||
| 26 | |||
| 27 | logger = logging.getLogger('wic') | ||
| 28 | |||
| 29 | class BootimgEFIPlugin(SourcePlugin): | ||
| 30 | """ | ||
| 31 | Create EFI boot partition. | ||
| 32 | This plugin supports GRUB 2 and systemd-boot bootloaders. | ||
| 33 | """ | ||
| 34 | |||
| 35 | name = 'bootimg-efi' | ||
| 36 | |||
| 37 | @classmethod | ||
| 38 | def _copy_additional_files(cls, hdddir, initrd, dtb): | ||
| 39 | bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 40 | if not bootimg_dir: | ||
| 41 | raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") | ||
| 42 | |||
| 43 | if initrd: | ||
| 44 | initrds = initrd.split(';') | ||
| 45 | for rd in initrds: | ||
| 46 | cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir) | ||
| 47 | exec_cmd(cp_cmd, True) | ||
| 48 | else: | ||
| 49 | logger.debug("Ignoring missing initrd") | ||
| 50 | |||
| 51 | if dtb: | ||
| 52 | if ';' in dtb: | ||
| 53 | raise WicError("Only one DTB supported, exiting") | ||
| 54 | cp_cmd = "cp %s/%s %s" % (bootimg_dir, dtb, hdddir) | ||
| 55 | exec_cmd(cp_cmd, True) | ||
| 56 | |||
| 57 | @classmethod | ||
| 58 | def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params): | ||
| 59 | """ | ||
| 60 | Create loader-specific (grub-efi) config | ||
| 61 | """ | ||
| 62 | configfile = creator.ks.bootloader.configfile | ||
| 63 | custom_cfg = None | ||
| 64 | if configfile: | ||
| 65 | custom_cfg = get_custom_config(configfile) | ||
| 66 | if custom_cfg: | ||
| 67 | # Use a custom configuration for grub | ||
| 68 | grubefi_conf = custom_cfg | ||
| 69 | logger.debug("Using custom configuration file " | ||
| 70 | "%s for grub.cfg", configfile) | ||
| 71 | else: | ||
| 72 | raise WicError("configfile is specified but failed to " | ||
| 73 | "get it from %s." % configfile) | ||
| 74 | |||
| 75 | initrd = source_params.get('initrd') | ||
| 76 | dtb = source_params.get('dtb') | ||
| 77 | |||
| 78 | cls._copy_additional_files(hdddir, initrd, dtb) | ||
| 79 | |||
| 80 | if not custom_cfg: | ||
| 81 | # Create grub configuration using parameters from wks file | ||
| 82 | bootloader = creator.ks.bootloader | ||
| 83 | title = source_params.get('title') | ||
| 84 | |||
| 85 | grubefi_conf = "" | ||
| 86 | grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n" | ||
| 87 | grubefi_conf += "default=boot\n" | ||
| 88 | grubefi_conf += "timeout=%s\n" % bootloader.timeout | ||
| 89 | grubefi_conf += "menuentry '%s'{\n" % (title if title else "boot") | ||
| 90 | |||
| 91 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 92 | if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": | ||
| 93 | if get_bitbake_var("INITRAMFS_IMAGE"): | ||
| 94 | kernel = "%s-%s.bin" % \ | ||
| 95 | (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) | ||
| 96 | |||
| 97 | label = source_params.get('label') | ||
| 98 | label_conf = "root=%s" % creator.rootdev | ||
| 99 | if label: | ||
| 100 | label_conf = "LABEL=%s" % label | ||
| 101 | |||
| 102 | grubefi_conf += "linux /%s %s rootwait %s\n" \ | ||
| 103 | % (kernel, label_conf, bootloader.append) | ||
| 104 | |||
| 105 | if initrd: | ||
| 106 | initrds = initrd.split(';') | ||
| 107 | grubefi_conf += "initrd" | ||
| 108 | for rd in initrds: | ||
| 109 | grubefi_conf += " /%s" % rd | ||
| 110 | grubefi_conf += "\n" | ||
| 111 | |||
| 112 | if dtb: | ||
| 113 | grubefi_conf += "devicetree /%s\n" % dtb | ||
| 114 | |||
| 115 | grubefi_conf += "}\n" | ||
| 116 | |||
| 117 | logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg", | ||
| 118 | cr_workdir) | ||
| 119 | cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w") | ||
| 120 | cfg.write(grubefi_conf) | ||
| 121 | cfg.close() | ||
| 122 | |||
| 123 | @classmethod | ||
| 124 | def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params): | ||
| 125 | """ | ||
| 126 | Create loader-specific systemd-boot/gummiboot config | ||
| 127 | """ | ||
| 128 | install_cmd = "install -d %s/loader" % hdddir | ||
| 129 | exec_cmd(install_cmd) | ||
| 130 | |||
| 131 | install_cmd = "install -d %s/loader/entries" % hdddir | ||
| 132 | exec_cmd(install_cmd) | ||
| 133 | |||
| 134 | bootloader = creator.ks.bootloader | ||
| 135 | |||
| 136 | unified_image = source_params.get('create-unified-kernel-image') == "true" | ||
| 137 | |||
| 138 | loader_conf = "" | ||
| 139 | if not unified_image: | ||
| 140 | loader_conf += "default boot\n" | ||
| 141 | loader_conf += "timeout %d\n" % bootloader.timeout | ||
| 142 | |||
| 143 | initrd = source_params.get('initrd') | ||
| 144 | dtb = source_params.get('dtb') | ||
| 145 | |||
| 146 | if not unified_image: | ||
| 147 | cls._copy_additional_files(hdddir, initrd, dtb) | ||
| 148 | |||
| 149 | logger.debug("Writing systemd-boot config " | ||
| 150 | "%s/hdd/boot/loader/loader.conf", cr_workdir) | ||
| 151 | cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w") | ||
| 152 | cfg.write(loader_conf) | ||
| 153 | cfg.close() | ||
| 154 | |||
| 155 | configfile = creator.ks.bootloader.configfile | ||
| 156 | custom_cfg = None | ||
| 157 | if configfile: | ||
| 158 | custom_cfg = get_custom_config(configfile) | ||
| 159 | if custom_cfg: | ||
| 160 | # Use a custom configuration for systemd-boot | ||
| 161 | boot_conf = custom_cfg | ||
| 162 | logger.debug("Using custom configuration file " | ||
| 163 | "%s for systemd-boots's boot.conf", configfile) | ||
| 164 | else: | ||
| 165 | raise WicError("configfile is specified but failed to " | ||
| 166 | "get it from %s.", configfile) | ||
| 167 | |||
| 168 | if not custom_cfg: | ||
| 169 | # Create systemd-boot configuration using parameters from wks file | ||
| 170 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 171 | if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": | ||
| 172 | if get_bitbake_var("INITRAMFS_IMAGE"): | ||
| 173 | kernel = "%s-%s.bin" % \ | ||
| 174 | (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) | ||
| 175 | |||
| 176 | title = source_params.get('title') | ||
| 177 | |||
| 178 | boot_conf = "" | ||
| 179 | boot_conf += "title %s\n" % (title if title else "boot") | ||
| 180 | boot_conf += "linux /%s\n" % kernel | ||
| 181 | |||
| 182 | label = source_params.get('label') | ||
| 183 | label_conf = "LABEL=Boot root=%s" % creator.rootdev | ||
| 184 | if label: | ||
| 185 | label_conf = "LABEL=%s" % label | ||
| 186 | |||
| 187 | boot_conf += "options %s %s\n" % \ | ||
| 188 | (label_conf, bootloader.append) | ||
| 189 | |||
| 190 | if initrd: | ||
| 191 | initrds = initrd.split(';') | ||
| 192 | for rd in initrds: | ||
| 193 | boot_conf += "initrd /%s\n" % rd | ||
| 194 | |||
| 195 | if dtb: | ||
| 196 | boot_conf += "devicetree /%s\n" % dtb | ||
| 197 | |||
| 198 | if not unified_image: | ||
| 199 | logger.debug("Writing systemd-boot config " | ||
| 200 | "%s/hdd/boot/loader/entries/boot.conf", cr_workdir) | ||
| 201 | cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w") | ||
| 202 | cfg.write(boot_conf) | ||
| 203 | cfg.close() | ||
| 204 | |||
| 205 | |||
| 206 | @classmethod | ||
| 207 | def do_configure_partition(cls, part, source_params, creator, cr_workdir, | ||
| 208 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 209 | native_sysroot): | ||
| 210 | """ | ||
| 211 | Called before do_prepare_partition(), creates loader-specific config | ||
| 212 | """ | ||
| 213 | hdddir = "%s/hdd/boot" % cr_workdir | ||
| 214 | |||
| 215 | install_cmd = "install -d %s/EFI/BOOT" % hdddir | ||
| 216 | exec_cmd(install_cmd) | ||
| 217 | |||
| 218 | try: | ||
| 219 | if source_params['loader'] == 'grub-efi': | ||
| 220 | cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params) | ||
| 221 | elif source_params['loader'] == 'systemd-boot': | ||
| 222 | cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params) | ||
| 223 | elif source_params['loader'] == 'uefi-kernel': | ||
| 224 | pass | ||
| 225 | else: | ||
| 226 | raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader']) | ||
| 227 | except KeyError: | ||
| 228 | raise WicError("bootimg-efi requires a loader, none specified") | ||
| 229 | |||
| 230 | if get_bitbake_var("IMAGE_EFI_BOOT_FILES") is None: | ||
| 231 | logger.debug('No boot files defined in IMAGE_EFI_BOOT_FILES') | ||
| 232 | else: | ||
| 233 | boot_files = None | ||
| 234 | for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)): | ||
| 235 | if fmt: | ||
| 236 | var = fmt % id | ||
| 237 | else: | ||
| 238 | var = "" | ||
| 239 | |||
| 240 | boot_files = get_bitbake_var("IMAGE_EFI_BOOT_FILES" + var) | ||
| 241 | if boot_files: | ||
| 242 | break | ||
| 243 | |||
| 244 | logger.debug('Boot files: %s', boot_files) | ||
| 245 | |||
| 246 | # list of tuples (src_name, dst_name) | ||
| 247 | deploy_files = [] | ||
| 248 | for src_entry in re.findall(r'[\w;\-\./\*]+', boot_files): | ||
| 249 | if ';' in src_entry: | ||
| 250 | dst_entry = tuple(src_entry.split(';')) | ||
| 251 | if not dst_entry[0] or not dst_entry[1]: | ||
| 252 | raise WicError('Malformed boot file entry: %s' % src_entry) | ||
| 253 | else: | ||
| 254 | dst_entry = (src_entry, src_entry) | ||
| 255 | |||
| 256 | logger.debug('Destination entry: %r', dst_entry) | ||
| 257 | deploy_files.append(dst_entry) | ||
| 258 | |||
| 259 | cls.install_task = []; | ||
| 260 | for deploy_entry in deploy_files: | ||
| 261 | src, dst = deploy_entry | ||
| 262 | if '*' in src: | ||
| 263 | # by default install files under their basename | ||
| 264 | entry_name_fn = os.path.basename | ||
| 265 | if dst != src: | ||
| 266 | # unless a target name was given, then treat name | ||
| 267 | # as a directory and append a basename | ||
| 268 | entry_name_fn = lambda name: \ | ||
| 269 | os.path.join(dst, | ||
| 270 | os.path.basename(name)) | ||
| 271 | |||
| 272 | srcs = glob(os.path.join(kernel_dir, src)) | ||
| 273 | |||
| 274 | logger.debug('Globbed sources: %s', ', '.join(srcs)) | ||
| 275 | for entry in srcs: | ||
| 276 | src = os.path.relpath(entry, kernel_dir) | ||
| 277 | entry_dst_name = entry_name_fn(entry) | ||
| 278 | cls.install_task.append((src, entry_dst_name)) | ||
| 279 | else: | ||
| 280 | cls.install_task.append((src, dst)) | ||
| 281 | |||
| 282 | @classmethod | ||
| 283 | def do_prepare_partition(cls, part, source_params, creator, cr_workdir, | ||
| 284 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 285 | rootfs_dir, native_sysroot): | ||
| 286 | """ | ||
| 287 | Called to do the actual content population for a partition i.e. it | ||
| 288 | 'prepares' the partition to be incorporated into the image. | ||
| 289 | In this case, prepare content for an EFI (grub) boot partition. | ||
| 290 | """ | ||
| 291 | if not kernel_dir: | ||
| 292 | kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 293 | if not kernel_dir: | ||
| 294 | raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") | ||
| 295 | |||
| 296 | staging_kernel_dir = kernel_dir | ||
| 297 | |||
| 298 | hdddir = "%s/hdd/boot" % cr_workdir | ||
| 299 | |||
| 300 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 301 | if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": | ||
| 302 | if get_bitbake_var("INITRAMFS_IMAGE"): | ||
| 303 | kernel = "%s-%s.bin" % \ | ||
| 304 | (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) | ||
| 305 | |||
| 306 | if source_params.get('create-unified-kernel-image') == "true": | ||
| 307 | initrd = source_params.get('initrd') | ||
| 308 | if not initrd: | ||
| 309 | raise WicError("initrd= must be specified when create-unified-kernel-image=true, exiting") | ||
| 310 | |||
| 311 | deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 312 | efi_stub = glob("%s/%s" % (deploy_dir, "linux*.efi.stub")) | ||
| 313 | if len(efi_stub) == 0: | ||
| 314 | raise WicError("Unified Kernel Image EFI stub not found, exiting") | ||
| 315 | efi_stub = efi_stub[0] | ||
| 316 | |||
| 317 | with tempfile.TemporaryDirectory() as tmp_dir: | ||
| 318 | label = source_params.get('label') | ||
| 319 | label_conf = "root=%s" % creator.rootdev | ||
| 320 | if label: | ||
| 321 | label_conf = "LABEL=%s" % label | ||
| 322 | |||
| 323 | bootloader = creator.ks.bootloader | ||
| 324 | cmdline = open("%s/cmdline" % tmp_dir, "w") | ||
| 325 | cmdline.write("%s %s" % (label_conf, bootloader.append)) | ||
| 326 | cmdline.close() | ||
| 327 | |||
| 328 | initrds = initrd.split(';') | ||
| 329 | initrd = open("%s/initrd" % tmp_dir, "wb") | ||
| 330 | for f in initrds: | ||
| 331 | with open("%s/%s" % (deploy_dir, f), 'rb') as in_file: | ||
| 332 | shutil.copyfileobj(in_file, initrd) | ||
| 333 | initrd.close() | ||
| 334 | |||
| 335 | # Searched by systemd-boot: | ||
| 336 | # https://systemd.io/BOOT_LOADER_SPECIFICATION/#type-2-efi-unified-kernel-images | ||
| 337 | install_cmd = "install -d %s/EFI/Linux" % hdddir | ||
| 338 | exec_cmd(install_cmd) | ||
| 339 | |||
| 340 | staging_dir_host = get_bitbake_var("STAGING_DIR_HOST") | ||
| 341 | target_sys = get_bitbake_var("TARGET_SYS") | ||
| 342 | |||
| 343 | objdump_cmd = "%s-objdump" % target_sys | ||
| 344 | objdump_cmd += " -p %s" % efi_stub | ||
| 345 | objdump_cmd += " | awk '{ if ($1 == \"SectionAlignment\"){print $2} }'" | ||
| 346 | |||
| 347 | ret, align_str = exec_native_cmd(objdump_cmd, native_sysroot) | ||
| 348 | align = int(align_str, 16) | ||
| 349 | |||
| 350 | objdump_cmd = "%s-objdump" % target_sys | ||
| 351 | objdump_cmd += " -h %s | tail -2" % efi_stub | ||
| 352 | ret, output = exec_native_cmd(objdump_cmd, native_sysroot) | ||
| 353 | |||
| 354 | offset = int(output.split()[2], 16) + int(output.split()[3], 16) | ||
| 355 | |||
| 356 | osrel_off = offset + align - offset % align | ||
| 357 | osrel_path = "%s/usr/lib/os-release" % staging_dir_host | ||
| 358 | osrel_sz = os.stat(osrel_path).st_size | ||
| 359 | |||
| 360 | cmdline_off = osrel_off + osrel_sz | ||
| 361 | cmdline_off = cmdline_off + align - cmdline_off % align | ||
| 362 | cmdline_sz = os.stat(cmdline.name).st_size | ||
| 363 | |||
| 364 | dtb_off = cmdline_off + cmdline_sz | ||
| 365 | dtb_off = dtb_off + align - dtb_off % align | ||
| 366 | |||
| 367 | dtb = source_params.get('dtb') | ||
| 368 | if dtb: | ||
| 369 | if ';' in dtb: | ||
| 370 | raise WicError("Only one DTB supported, exiting") | ||
| 371 | dtb_path = "%s/%s" % (deploy_dir, dtb) | ||
| 372 | dtb_params = '--add-section .dtb=%s --change-section-vma .dtb=0x%x' % \ | ||
| 373 | (dtb_path, dtb_off) | ||
| 374 | linux_off = dtb_off + os.stat(dtb_path).st_size | ||
| 375 | linux_off = linux_off + align - linux_off % align | ||
| 376 | else: | ||
| 377 | dtb_params = '' | ||
| 378 | linux_off = dtb_off | ||
| 379 | |||
| 380 | linux_path = "%s/%s" % (staging_kernel_dir, kernel) | ||
| 381 | linux_sz = os.stat(linux_path).st_size | ||
| 382 | |||
| 383 | initrd_off = linux_off + linux_sz | ||
| 384 | initrd_off = initrd_off + align - initrd_off % align | ||
| 385 | |||
| 386 | # https://www.freedesktop.org/software/systemd/man/systemd-stub.html | ||
| 387 | objcopy_cmd = "%s-objcopy" % target_sys | ||
| 388 | objcopy_cmd += " --enable-deterministic-archives" | ||
| 389 | objcopy_cmd += " --preserve-dates" | ||
| 390 | objcopy_cmd += " --add-section .osrel=%s" % osrel_path | ||
| 391 | objcopy_cmd += " --change-section-vma .osrel=0x%x" % osrel_off | ||
| 392 | objcopy_cmd += " --add-section .cmdline=%s" % cmdline.name | ||
| 393 | objcopy_cmd += " --change-section-vma .cmdline=0x%x" % cmdline_off | ||
| 394 | objcopy_cmd += dtb_params | ||
| 395 | objcopy_cmd += " --add-section .linux=%s" % linux_path | ||
| 396 | objcopy_cmd += " --change-section-vma .linux=0x%x" % linux_off | ||
| 397 | objcopy_cmd += " --add-section .initrd=%s" % initrd.name | ||
| 398 | objcopy_cmd += " --change-section-vma .initrd=0x%x" % initrd_off | ||
| 399 | objcopy_cmd += " %s %s/EFI/Linux/linux.efi" % (efi_stub, hdddir) | ||
| 400 | |||
| 401 | exec_native_cmd(objcopy_cmd, native_sysroot) | ||
| 402 | else: | ||
| 403 | if source_params.get('install-kernel-into-boot-dir') != 'false': | ||
| 404 | install_cmd = "install -m 0644 %s/%s %s/%s" % \ | ||
| 405 | (staging_kernel_dir, kernel, hdddir, kernel) | ||
| 406 | exec_cmd(install_cmd) | ||
| 407 | |||
| 408 | if get_bitbake_var("IMAGE_EFI_BOOT_FILES"): | ||
| 409 | for src_path, dst_path in cls.install_task: | ||
| 410 | install_cmd = "install -m 0644 -D %s %s" \ | ||
| 411 | % (os.path.join(kernel_dir, src_path), | ||
| 412 | os.path.join(hdddir, dst_path)) | ||
| 413 | exec_cmd(install_cmd) | ||
| 414 | |||
| 415 | try: | ||
| 416 | if source_params['loader'] == 'grub-efi': | ||
| 417 | shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, | ||
| 418 | "%s/grub.cfg" % cr_workdir) | ||
| 419 | for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]: | ||
| 420 | cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:]) | ||
| 421 | exec_cmd(cp_cmd, True) | ||
| 422 | shutil.move("%s/grub.cfg" % cr_workdir, | ||
| 423 | "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir) | ||
| 424 | elif source_params['loader'] == 'systemd-boot': | ||
| 425 | for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]: | ||
| 426 | cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:]) | ||
| 427 | exec_cmd(cp_cmd, True) | ||
| 428 | elif source_params['loader'] == 'uefi-kernel': | ||
| 429 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 430 | if not kernel: | ||
| 431 | raise WicError("Empty KERNEL_IMAGETYPE %s\n" % target) | ||
| 432 | target = get_bitbake_var("TARGET_SYS") | ||
| 433 | if not target: | ||
| 434 | raise WicError("Unknown arch (TARGET_SYS) %s\n" % target) | ||
| 435 | |||
| 436 | if re.match("x86_64", target): | ||
| 437 | kernel_efi_image = "bootx64.efi" | ||
| 438 | elif re.match('i.86', target): | ||
| 439 | kernel_efi_image = "bootia32.efi" | ||
| 440 | elif re.match('aarch64', target): | ||
| 441 | kernel_efi_image = "bootaa64.efi" | ||
| 442 | elif re.match('arm', target): | ||
| 443 | kernel_efi_image = "bootarm.efi" | ||
| 444 | else: | ||
| 445 | raise WicError("UEFI stub kernel is incompatible with target %s" % target) | ||
| 446 | |||
| 447 | for mod in [x for x in os.listdir(kernel_dir) if x.startswith(kernel)]: | ||
| 448 | cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, kernel_efi_image) | ||
| 449 | exec_cmd(cp_cmd, True) | ||
| 450 | else: | ||
| 451 | raise WicError("unrecognized bootimg-efi loader: %s" % | ||
| 452 | source_params['loader']) | ||
| 453 | except KeyError: | ||
| 454 | raise WicError("bootimg-efi requires a loader, none specified") | ||
| 455 | |||
| 456 | startup = os.path.join(kernel_dir, "startup.nsh") | ||
| 457 | if os.path.exists(startup): | ||
| 458 | cp_cmd = "cp %s %s/" % (startup, hdddir) | ||
| 459 | exec_cmd(cp_cmd, True) | ||
| 460 | |||
| 461 | for paths in part.include_path or []: | ||
| 462 | for path in paths: | ||
| 463 | cp_cmd = "cp -r %s %s/" % (path, hdddir) | ||
| 464 | exec_cmd(cp_cmd, True) | ||
| 465 | |||
| 466 | du_cmd = "du -bks %s" % hdddir | ||
| 467 | out = exec_cmd(du_cmd) | ||
| 468 | blocks = int(out.split()[0]) | ||
| 469 | |||
| 470 | extra_blocks = part.get_extra_block_count(blocks) | ||
| 471 | |||
| 472 | if extra_blocks < BOOTDD_EXTRA_SPACE: | ||
| 473 | extra_blocks = BOOTDD_EXTRA_SPACE | ||
| 474 | |||
| 475 | blocks += extra_blocks | ||
| 476 | |||
| 477 | logger.debug("Added %d extra blocks to %s to get to %d total blocks", | ||
| 478 | extra_blocks, part.mountpoint, blocks) | ||
| 479 | |||
| 480 | # required for compatibility with certain devices expecting file system | ||
| 481 | # block count to be equal to partition block count | ||
| 482 | if blocks < part.fixed_size: | ||
| 483 | blocks = part.fixed_size | ||
| 484 | logger.debug("Overriding %s to %d total blocks for compatibility", | ||
| 485 | part.mountpoint, blocks) | ||
| 486 | |||
| 487 | # dosfs image, created by mkdosfs | ||
| 488 | bootimg = "%s/boot.img" % cr_workdir | ||
| 489 | |||
| 490 | label = part.label if part.label else "ESP" | ||
| 491 | |||
| 492 | dosfs_cmd = "mkdosfs -n %s -i %s -C %s %d" % \ | ||
| 493 | (label, part.fsuuid, bootimg, blocks) | ||
| 494 | exec_native_cmd(dosfs_cmd, native_sysroot) | ||
| 495 | |||
| 496 | mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) | ||
| 497 | exec_native_cmd(mcopy_cmd, native_sysroot) | ||
| 498 | |||
| 499 | chmod_cmd = "chmod 644 %s" % bootimg | ||
| 500 | exec_cmd(chmod_cmd) | ||
| 501 | |||
| 502 | du_cmd = "du -Lbks %s" % bootimg | ||
| 503 | out = exec_cmd(du_cmd) | ||
| 504 | bootimg_size = out.split()[0] | ||
| 505 | |||
| 506 | part.size = int(bootimg_size) | ||
| 507 | part.source_file = bootimg | ||
diff --git a/scripts/lib/wic/plugins/source/bootimg-partition.py b/scripts/lib/wic/plugins/source/bootimg-partition.py deleted file mode 100644 index 1071d1af3f..0000000000 --- a/scripts/lib/wic/plugins/source/bootimg-partition.py +++ /dev/null | |||
| @@ -1,197 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright OpenEmbedded Contributors | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This implements the 'bootimg-partition' source plugin class for | ||
| 8 | # 'wic'. The plugin creates an image of boot partition, copying over | ||
| 9 | # files listed in IMAGE_BOOT_FILES bitbake variable. | ||
| 10 | # | ||
| 11 | # AUTHORS | ||
| 12 | # Maciej Borzecki <maciej.borzecki (at] open-rnd.pl> | ||
| 13 | # | ||
| 14 | |||
| 15 | import logging | ||
| 16 | import os | ||
| 17 | import re | ||
| 18 | |||
| 19 | from glob import glob | ||
| 20 | |||
| 21 | from wic import WicError | ||
| 22 | from wic.engine import get_custom_config | ||
| 23 | from wic.pluginbase import SourcePlugin | ||
| 24 | from wic.misc import exec_cmd, get_bitbake_var | ||
| 25 | |||
| 26 | logger = logging.getLogger('wic') | ||
| 27 | |||
| 28 | class BootimgPartitionPlugin(SourcePlugin): | ||
| 29 | """ | ||
| 30 | Create an image of boot partition, copying over files | ||
| 31 | listed in IMAGE_BOOT_FILES bitbake variable. | ||
| 32 | """ | ||
| 33 | |||
| 34 | name = 'bootimg-partition' | ||
| 35 | image_boot_files_var_name = 'IMAGE_BOOT_FILES' | ||
| 36 | |||
| 37 | @classmethod | ||
| 38 | def do_configure_partition(cls, part, source_params, cr, cr_workdir, | ||
| 39 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 40 | native_sysroot): | ||
| 41 | """ | ||
| 42 | Called before do_prepare_partition(), create u-boot specific boot config | ||
| 43 | """ | ||
| 44 | hdddir = "%s/boot.%d" % (cr_workdir, part.lineno) | ||
| 45 | install_cmd = "install -d %s" % hdddir | ||
| 46 | exec_cmd(install_cmd) | ||
| 47 | |||
| 48 | if not kernel_dir: | ||
| 49 | kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 50 | if not kernel_dir: | ||
| 51 | raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") | ||
| 52 | |||
| 53 | boot_files = None | ||
| 54 | for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)): | ||
| 55 | if fmt: | ||
| 56 | var = fmt % id | ||
| 57 | else: | ||
| 58 | var = "" | ||
| 59 | |||
| 60 | boot_files = get_bitbake_var(cls.image_boot_files_var_name + var) | ||
| 61 | if boot_files is not None: | ||
| 62 | break | ||
| 63 | |||
| 64 | if boot_files is None: | ||
| 65 | raise WicError('No boot files defined, %s unset for entry #%d' % (cls.image_boot_files_var_name, part.lineno)) | ||
| 66 | |||
| 67 | logger.debug('Boot files: %s', boot_files) | ||
| 68 | |||
| 69 | # list of tuples (src_name, dst_name) | ||
| 70 | deploy_files = [] | ||
| 71 | for src_entry in re.findall(r'[\w;\-\./\*]+', boot_files): | ||
| 72 | if ';' in src_entry: | ||
| 73 | dst_entry = tuple(src_entry.split(';')) | ||
| 74 | if not dst_entry[0] or not dst_entry[1]: | ||
| 75 | raise WicError('Malformed boot file entry: %s' % src_entry) | ||
| 76 | else: | ||
| 77 | dst_entry = (src_entry, src_entry) | ||
| 78 | |||
| 79 | logger.debug('Destination entry: %r', dst_entry) | ||
| 80 | deploy_files.append(dst_entry) | ||
| 81 | |||
| 82 | cls.install_task = []; | ||
| 83 | for deploy_entry in deploy_files: | ||
| 84 | src, dst = deploy_entry | ||
| 85 | if '*' in src: | ||
| 86 | # by default install files under their basename | ||
| 87 | entry_name_fn = os.path.basename | ||
| 88 | if dst != src: | ||
| 89 | # unless a target name was given, then treat name | ||
| 90 | # as a directory and append a basename | ||
| 91 | entry_name_fn = lambda name: \ | ||
| 92 | os.path.join(dst, | ||
| 93 | os.path.basename(name)) | ||
| 94 | |||
| 95 | srcs = glob(os.path.join(kernel_dir, src)) | ||
| 96 | |||
| 97 | logger.debug('Globbed sources: %s', ', '.join(srcs)) | ||
| 98 | for entry in srcs: | ||
| 99 | src = os.path.relpath(entry, kernel_dir) | ||
| 100 | entry_dst_name = entry_name_fn(entry) | ||
| 101 | cls.install_task.append((src, entry_dst_name)) | ||
| 102 | else: | ||
| 103 | cls.install_task.append((src, dst)) | ||
| 104 | |||
| 105 | if source_params.get('loader') != "u-boot": | ||
| 106 | return | ||
| 107 | |||
| 108 | configfile = cr.ks.bootloader.configfile | ||
| 109 | custom_cfg = None | ||
| 110 | if configfile: | ||
| 111 | custom_cfg = get_custom_config(configfile) | ||
| 112 | if custom_cfg: | ||
| 113 | # Use a custom configuration for extlinux.conf | ||
| 114 | extlinux_conf = custom_cfg | ||
| 115 | logger.debug("Using custom configuration file " | ||
| 116 | "%s for extlinux.conf", configfile) | ||
| 117 | else: | ||
| 118 | raise WicError("configfile is specified but failed to " | ||
| 119 | "get it from %s." % configfile) | ||
| 120 | |||
| 121 | if not custom_cfg: | ||
| 122 | # The kernel types supported by the sysboot of u-boot | ||
| 123 | kernel_types = ["zImage", "Image", "fitImage", "uImage", "vmlinux"] | ||
| 124 | has_dtb = False | ||
| 125 | fdt_dir = '/' | ||
| 126 | kernel_name = None | ||
| 127 | |||
| 128 | # Find the kernel image name, from the highest precedence to lowest | ||
| 129 | for image in kernel_types: | ||
| 130 | for task in cls.install_task: | ||
| 131 | src, dst = task | ||
| 132 | if re.match(image, src): | ||
| 133 | kernel_name = os.path.join('/', dst) | ||
| 134 | break | ||
| 135 | if kernel_name: | ||
| 136 | break | ||
| 137 | |||
| 138 | for task in cls.install_task: | ||
| 139 | src, dst = task | ||
| 140 | # We suppose that all the dtb are in the same directory | ||
| 141 | if re.search(r'\.dtb', src) and fdt_dir == '/': | ||
| 142 | has_dtb = True | ||
| 143 | fdt_dir = os.path.join(fdt_dir, os.path.dirname(dst)) | ||
| 144 | break | ||
| 145 | |||
| 146 | if not kernel_name: | ||
| 147 | raise WicError('No kernel file found') | ||
| 148 | |||
| 149 | # Compose the extlinux.conf | ||
| 150 | extlinux_conf = "default Yocto\n" | ||
| 151 | extlinux_conf += "label Yocto\n" | ||
| 152 | extlinux_conf += " kernel %s\n" % kernel_name | ||
| 153 | if has_dtb: | ||
| 154 | extlinux_conf += " fdtdir %s\n" % fdt_dir | ||
| 155 | bootloader = cr.ks.bootloader | ||
| 156 | extlinux_conf += "append root=%s rootwait %s\n" \ | ||
| 157 | % (cr.rootdev, bootloader.append if bootloader.append else '') | ||
| 158 | |||
| 159 | install_cmd = "install -d %s/extlinux/" % hdddir | ||
| 160 | exec_cmd(install_cmd) | ||
| 161 | cfg = open("%s/extlinux/extlinux.conf" % hdddir, "w") | ||
| 162 | cfg.write(extlinux_conf) | ||
| 163 | cfg.close() | ||
| 164 | |||
| 165 | |||
| 166 | @classmethod | ||
| 167 | def do_prepare_partition(cls, part, source_params, cr, cr_workdir, | ||
| 168 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 169 | rootfs_dir, native_sysroot): | ||
| 170 | """ | ||
| 171 | Called to do the actual content population for a partition i.e. it | ||
| 172 | 'prepares' the partition to be incorporated into the image. | ||
| 173 | In this case, does the following: | ||
| 174 | - sets up a vfat partition | ||
| 175 | - copies all files listed in IMAGE_BOOT_FILES variable | ||
| 176 | """ | ||
| 177 | hdddir = "%s/boot.%d" % (cr_workdir, part.lineno) | ||
| 178 | |||
| 179 | if not kernel_dir: | ||
| 180 | kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 181 | if not kernel_dir: | ||
| 182 | raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") | ||
| 183 | |||
| 184 | logger.debug('Kernel dir: %s', bootimg_dir) | ||
| 185 | |||
| 186 | |||
| 187 | for task in cls.install_task: | ||
| 188 | src_path, dst_path = task | ||
| 189 | logger.debug('Install %s as %s', src_path, dst_path) | ||
| 190 | install_cmd = "install -m 0644 -D %s %s" \ | ||
| 191 | % (os.path.join(kernel_dir, src_path), | ||
| 192 | os.path.join(hdddir, dst_path)) | ||
| 193 | exec_cmd(install_cmd) | ||
| 194 | |||
| 195 | logger.debug('Prepare boot partition using rootfs in %s', hdddir) | ||
| 196 | part.prepare_rootfs(cr_workdir, oe_builddir, hdddir, | ||
| 197 | native_sysroot, False) | ||
diff --git a/scripts/lib/wic/plugins/source/bootimg-pcbios.py b/scripts/lib/wic/plugins/source/bootimg-pcbios.py deleted file mode 100644 index a207a83530..0000000000 --- a/scripts/lib/wic/plugins/source/bootimg-pcbios.py +++ /dev/null | |||
| @@ -1,209 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2014, Intel Corporation. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This implements the 'bootimg-pcbios' source plugin class for 'wic' | ||
| 8 | # | ||
| 9 | # AUTHORS | ||
| 10 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 11 | # | ||
| 12 | |||
| 13 | import logging | ||
| 14 | import os | ||
| 15 | import re | ||
| 16 | |||
| 17 | from wic import WicError | ||
| 18 | from wic.engine import get_custom_config | ||
| 19 | from wic.pluginbase import SourcePlugin | ||
| 20 | from wic.misc import (exec_cmd, exec_native_cmd, | ||
| 21 | get_bitbake_var, BOOTDD_EXTRA_SPACE) | ||
| 22 | |||
| 23 | logger = logging.getLogger('wic') | ||
| 24 | |||
| 25 | class BootimgPcbiosPlugin(SourcePlugin): | ||
| 26 | """ | ||
| 27 | Create MBR boot partition and install syslinux on it. | ||
| 28 | """ | ||
| 29 | |||
| 30 | name = 'bootimg-pcbios' | ||
| 31 | |||
| 32 | @classmethod | ||
| 33 | def _get_bootimg_dir(cls, bootimg_dir, dirname): | ||
| 34 | """ | ||
| 35 | Check if dirname exists in default bootimg_dir or in STAGING_DIR. | ||
| 36 | """ | ||
| 37 | staging_datadir = get_bitbake_var("STAGING_DATADIR") | ||
| 38 | for result in (bootimg_dir, staging_datadir): | ||
| 39 | if os.path.exists("%s/%s" % (result, dirname)): | ||
| 40 | return result | ||
| 41 | |||
| 42 | # STAGING_DATADIR is expanded with MLPREFIX if multilib is enabled | ||
| 43 | # but dependency syslinux is still populated to original STAGING_DATADIR | ||
| 44 | nonarch_datadir = re.sub('/[^/]*recipe-sysroot', '/recipe-sysroot', staging_datadir) | ||
| 45 | if os.path.exists(os.path.join(nonarch_datadir, dirname)): | ||
| 46 | return nonarch_datadir | ||
| 47 | |||
| 48 | raise WicError("Couldn't find correct bootimg_dir, exiting") | ||
| 49 | |||
| 50 | @classmethod | ||
| 51 | def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir, | ||
| 52 | bootimg_dir, kernel_dir, native_sysroot): | ||
| 53 | """ | ||
| 54 | Called after all partitions have been prepared and assembled into a | ||
| 55 | disk image. In this case, we install the MBR. | ||
| 56 | """ | ||
| 57 | bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux') | ||
| 58 | mbrfile = "%s/syslinux/" % bootimg_dir | ||
| 59 | if creator.ptable_format == 'msdos': | ||
| 60 | mbrfile += "mbr.bin" | ||
| 61 | elif creator.ptable_format == 'gpt': | ||
| 62 | mbrfile += "gptmbr.bin" | ||
| 63 | else: | ||
| 64 | raise WicError("Unsupported partition table: %s" % | ||
| 65 | creator.ptable_format) | ||
| 66 | |||
| 67 | if not os.path.exists(mbrfile): | ||
| 68 | raise WicError("Couldn't find %s. If using the -e option, do you " | ||
| 69 | "have the right MACHINE set in local.conf? If not, " | ||
| 70 | "is the bootimg_dir path correct?" % mbrfile) | ||
| 71 | |||
| 72 | full_path = creator._full_path(workdir, disk_name, "direct") | ||
| 73 | logger.debug("Installing MBR on disk %s as %s with size %s bytes", | ||
| 74 | disk_name, full_path, disk.min_size) | ||
| 75 | |||
| 76 | dd_cmd = "dd if=%s of=%s conv=notrunc" % (mbrfile, full_path) | ||
| 77 | exec_cmd(dd_cmd, native_sysroot) | ||
| 78 | |||
| 79 | @classmethod | ||
| 80 | def do_configure_partition(cls, part, source_params, creator, cr_workdir, | ||
| 81 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 82 | native_sysroot): | ||
| 83 | """ | ||
| 84 | Called before do_prepare_partition(), creates syslinux config | ||
| 85 | """ | ||
| 86 | hdddir = "%s/hdd/boot" % cr_workdir | ||
| 87 | |||
| 88 | install_cmd = "install -d %s" % hdddir | ||
| 89 | exec_cmd(install_cmd) | ||
| 90 | |||
| 91 | bootloader = creator.ks.bootloader | ||
| 92 | |||
| 93 | custom_cfg = None | ||
| 94 | if bootloader.configfile: | ||
| 95 | custom_cfg = get_custom_config(bootloader.configfile) | ||
| 96 | if custom_cfg: | ||
| 97 | # Use a custom configuration for grub | ||
| 98 | syslinux_conf = custom_cfg | ||
| 99 | logger.debug("Using custom configuration file %s " | ||
| 100 | "for syslinux.cfg", bootloader.configfile) | ||
| 101 | else: | ||
| 102 | raise WicError("configfile is specified but failed to " | ||
| 103 | "get it from %s." % bootloader.configfile) | ||
| 104 | |||
| 105 | if not custom_cfg: | ||
| 106 | # Create syslinux configuration using parameters from wks file | ||
| 107 | splash = os.path.join(cr_workdir, "/hdd/boot/splash.jpg") | ||
| 108 | if os.path.exists(splash): | ||
| 109 | splashline = "menu background splash.jpg" | ||
| 110 | else: | ||
| 111 | splashline = "" | ||
| 112 | |||
| 113 | syslinux_conf = "" | ||
| 114 | syslinux_conf += "PROMPT 0\n" | ||
| 115 | syslinux_conf += "TIMEOUT " + str(bootloader.timeout) + "\n" | ||
| 116 | syslinux_conf += "\n" | ||
| 117 | syslinux_conf += "ALLOWOPTIONS 1\n" | ||
| 118 | syslinux_conf += "SERIAL 0 115200\n" | ||
| 119 | syslinux_conf += "\n" | ||
| 120 | if splashline: | ||
| 121 | syslinux_conf += "%s\n" % splashline | ||
| 122 | syslinux_conf += "DEFAULT boot\n" | ||
| 123 | syslinux_conf += "LABEL boot\n" | ||
| 124 | |||
| 125 | kernel = "/" + get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 126 | syslinux_conf += "KERNEL " + kernel + "\n" | ||
| 127 | |||
| 128 | syslinux_conf += "APPEND label=boot root=%s %s\n" % \ | ||
| 129 | (creator.rootdev, bootloader.append) | ||
| 130 | |||
| 131 | logger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg", | ||
| 132 | cr_workdir) | ||
| 133 | cfg = open("%s/hdd/boot/syslinux.cfg" % cr_workdir, "w") | ||
| 134 | cfg.write(syslinux_conf) | ||
| 135 | cfg.close() | ||
| 136 | |||
| 137 | @classmethod | ||
| 138 | def do_prepare_partition(cls, part, source_params, creator, cr_workdir, | ||
| 139 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 140 | rootfs_dir, native_sysroot): | ||
| 141 | """ | ||
| 142 | Called to do the actual content population for a partition i.e. it | ||
| 143 | 'prepares' the partition to be incorporated into the image. | ||
| 144 | In this case, prepare content for legacy bios boot partition. | ||
| 145 | """ | ||
| 146 | bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux') | ||
| 147 | |||
| 148 | staging_kernel_dir = kernel_dir | ||
| 149 | |||
| 150 | hdddir = "%s/hdd/boot" % cr_workdir | ||
| 151 | |||
| 152 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 153 | if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": | ||
| 154 | if get_bitbake_var("INITRAMFS_IMAGE"): | ||
| 155 | kernel = "%s-%s.bin" % \ | ||
| 156 | (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) | ||
| 157 | |||
| 158 | cmds = ("install -m 0644 %s/%s %s/%s" % | ||
| 159 | (staging_kernel_dir, kernel, hdddir, get_bitbake_var("KERNEL_IMAGETYPE")), | ||
| 160 | "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" % | ||
| 161 | (bootimg_dir, hdddir), | ||
| 162 | "install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" % | ||
| 163 | (bootimg_dir, hdddir), | ||
| 164 | "install -m 444 %s/syslinux/libcom32.c32 %s/libcom32.c32" % | ||
| 165 | (bootimg_dir, hdddir), | ||
| 166 | "install -m 444 %s/syslinux/libutil.c32 %s/libutil.c32" % | ||
| 167 | (bootimg_dir, hdddir)) | ||
| 168 | |||
| 169 | for install_cmd in cmds: | ||
| 170 | exec_cmd(install_cmd) | ||
| 171 | |||
| 172 | du_cmd = "du -bks %s" % hdddir | ||
| 173 | out = exec_cmd(du_cmd) | ||
| 174 | blocks = int(out.split()[0]) | ||
| 175 | |||
| 176 | extra_blocks = part.get_extra_block_count(blocks) | ||
| 177 | |||
| 178 | if extra_blocks < BOOTDD_EXTRA_SPACE: | ||
| 179 | extra_blocks = BOOTDD_EXTRA_SPACE | ||
| 180 | |||
| 181 | blocks += extra_blocks | ||
| 182 | |||
| 183 | logger.debug("Added %d extra blocks to %s to get to %d total blocks", | ||
| 184 | extra_blocks, part.mountpoint, blocks) | ||
| 185 | |||
| 186 | # dosfs image, created by mkdosfs | ||
| 187 | bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno) | ||
| 188 | |||
| 189 | label = part.label if part.label else "boot" | ||
| 190 | |||
| 191 | dosfs_cmd = "mkdosfs -n %s -i %s -S 512 -C %s %d" % \ | ||
| 192 | (label, part.fsuuid, bootimg, blocks) | ||
| 193 | exec_native_cmd(dosfs_cmd, native_sysroot) | ||
| 194 | |||
| 195 | mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) | ||
| 196 | exec_native_cmd(mcopy_cmd, native_sysroot) | ||
| 197 | |||
| 198 | syslinux_cmd = "syslinux %s" % bootimg | ||
| 199 | exec_native_cmd(syslinux_cmd, native_sysroot) | ||
| 200 | |||
| 201 | chmod_cmd = "chmod 644 %s" % bootimg | ||
| 202 | exec_cmd(chmod_cmd) | ||
| 203 | |||
| 204 | du_cmd = "du -Lbks %s" % bootimg | ||
| 205 | out = exec_cmd(du_cmd) | ||
| 206 | bootimg_size = out.split()[0] | ||
| 207 | |||
| 208 | part.size = int(bootimg_size) | ||
| 209 | part.source_file = bootimg | ||
diff --git a/scripts/lib/wic/plugins/source/empty.py b/scripts/lib/wic/plugins/source/empty.py deleted file mode 100644 index 4178912377..0000000000 --- a/scripts/lib/wic/plugins/source/empty.py +++ /dev/null | |||
| @@ -1,89 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright OpenEmbedded Contributors | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: MIT | ||
| 5 | # | ||
| 6 | |||
| 7 | # The empty wic plugin is used to create unformatted empty partitions for wic | ||
| 8 | # images. | ||
| 9 | # To use it you must pass "empty" as argument for the "--source" parameter in | ||
| 10 | # the wks file. For example: | ||
| 11 | # part foo --source empty --ondisk sda --size="1024" --align 1024 | ||
| 12 | # | ||
| 13 | # The plugin supports writing zeros to the start of the | ||
| 14 | # partition. This is useful to overwrite old content like | ||
| 15 | # filesystem signatures which may be re-recognized otherwise. | ||
| 16 | # This feature can be enabled with | ||
| 17 | # '--sourceparams="[fill|size=<N>[S|s|K|k|M|G]][,][bs=<N>[S|s|K|k|M|G]]"' | ||
| 18 | # Conflicting or missing options throw errors. | ||
| 19 | |||
| 20 | import logging | ||
| 21 | import os | ||
| 22 | |||
| 23 | from wic import WicError | ||
| 24 | from wic.ksparser import sizetype | ||
| 25 | from wic.pluginbase import SourcePlugin | ||
| 26 | |||
| 27 | logger = logging.getLogger('wic') | ||
| 28 | |||
| 29 | class EmptyPartitionPlugin(SourcePlugin): | ||
| 30 | """ | ||
| 31 | Populate unformatted empty partition. | ||
| 32 | |||
| 33 | The following sourceparams are supported: | ||
| 34 | - fill | ||
| 35 | Fill the entire partition with zeros. Requires '--fixed-size' option | ||
| 36 | to be set. | ||
| 37 | - size=<N>[S|s|K|k|M|G] | ||
| 38 | Set the first N bytes of the partition to zero. Default unit is 'K'. | ||
| 39 | - bs=<N>[S|s|K|k|M|G] | ||
| 40 | Write at most N bytes at a time during source file creation. | ||
| 41 | Defaults to '1M'. Default unit is 'K'. | ||
| 42 | """ | ||
| 43 | |||
| 44 | name = 'empty' | ||
| 45 | |||
| 46 | @classmethod | ||
| 47 | def do_prepare_partition(cls, part, source_params, cr, cr_workdir, | ||
| 48 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 49 | rootfs_dir, native_sysroot): | ||
| 50 | """ | ||
| 51 | Called to do the actual content population for a partition i.e. it | ||
| 52 | 'prepares' the partition to be incorporated into the image. | ||
| 53 | """ | ||
| 54 | get_byte_count = sizetype('K', True) | ||
| 55 | size = 0 | ||
| 56 | |||
| 57 | if 'fill' in source_params and 'size' in source_params: | ||
| 58 | raise WicError("Conflicting source parameters 'fill' and 'size' specified, exiting.") | ||
| 59 | |||
| 60 | # Set the size of the zeros to be written to the partition | ||
| 61 | if 'fill' in source_params: | ||
| 62 | if part.fixed_size == 0: | ||
| 63 | raise WicError("Source parameter 'fill' only works with the '--fixed-size' option, exiting.") | ||
| 64 | size = get_byte_count(part.fixed_size) | ||
| 65 | elif 'size' in source_params: | ||
| 66 | size = get_byte_count(source_params['size']) | ||
| 67 | |||
| 68 | if size == 0: | ||
| 69 | # Nothing to do, create empty partition | ||
| 70 | return | ||
| 71 | |||
| 72 | if 'bs' in source_params: | ||
| 73 | bs = get_byte_count(source_params['bs']) | ||
| 74 | else: | ||
| 75 | bs = get_byte_count('1M') | ||
| 76 | |||
| 77 | # Create a binary file of the requested size filled with zeros | ||
| 78 | source_file = os.path.join(cr_workdir, 'empty-plugin-zeros%s.bin' % part.lineno) | ||
| 79 | if not os.path.exists(os.path.dirname(source_file)): | ||
| 80 | os.makedirs(os.path.dirname(source_file)) | ||
| 81 | |||
| 82 | quotient, remainder = divmod(size, bs) | ||
| 83 | with open(source_file, 'wb') as file: | ||
| 84 | for _ in range(quotient): | ||
| 85 | file.write(bytearray(bs)) | ||
| 86 | file.write(bytearray(remainder)) | ||
| 87 | |||
| 88 | part.size = (size + 1024 - 1) // 1024 # size in KB rounded up | ||
| 89 | part.source_file = source_file | ||
diff --git a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py deleted file mode 100644 index 607356ad13..0000000000 --- a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py +++ /dev/null | |||
| @@ -1,463 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright OpenEmbedded Contributors | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This implements the 'isoimage-isohybrid' source plugin class for 'wic' | ||
| 8 | # | ||
| 9 | # AUTHORS | ||
| 10 | # Mihaly Varga <mihaly.varga (at] ni.com> | ||
| 11 | |||
| 12 | import glob | ||
| 13 | import logging | ||
| 14 | import os | ||
| 15 | import re | ||
| 16 | import shutil | ||
| 17 | |||
| 18 | from wic import WicError | ||
| 19 | from wic.engine import get_custom_config | ||
| 20 | from wic.pluginbase import SourcePlugin | ||
| 21 | from wic.misc import exec_cmd, exec_native_cmd, get_bitbake_var | ||
| 22 | |||
| 23 | logger = logging.getLogger('wic') | ||
| 24 | |||
| 25 | class IsoImagePlugin(SourcePlugin): | ||
| 26 | """ | ||
| 27 | Create a bootable ISO image | ||
| 28 | |||
| 29 | This plugin creates a hybrid, legacy and EFI bootable ISO image. The | ||
| 30 | generated image can be used on optical media as well as USB media. | ||
| 31 | |||
| 32 | Legacy boot uses syslinux and EFI boot uses grub or gummiboot (not | ||
| 33 | implemented yet) as bootloader. The plugin creates the directories required | ||
| 34 | by bootloaders and populates them by creating and configuring the | ||
| 35 | bootloader files. | ||
| 36 | |||
| 37 | Example kickstart file: | ||
| 38 | part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi, \\ | ||
| 39 | image_name= IsoImage" --ondisk cd --label LIVECD | ||
| 40 | bootloader --timeout=10 --append=" " | ||
| 41 | |||
| 42 | In --sourceparams "loader" specifies the bootloader used for booting in EFI | ||
| 43 | mode, while "image_name" specifies the name of the generated image. In the | ||
| 44 | example above, wic creates an ISO image named IsoImage-cd.direct (default | ||
| 45 | extension added by direct imeger plugin) and a file named IsoImage-cd.iso | ||
| 46 | """ | ||
| 47 | |||
| 48 | name = 'isoimage-isohybrid' | ||
| 49 | |||
| 50 | @classmethod | ||
| 51 | def do_configure_syslinux(cls, creator, cr_workdir): | ||
| 52 | """ | ||
| 53 | Create loader-specific (syslinux) config | ||
| 54 | """ | ||
| 55 | splash = os.path.join(cr_workdir, "ISO/boot/splash.jpg") | ||
| 56 | if os.path.exists(splash): | ||
| 57 | splashline = "menu background splash.jpg" | ||
| 58 | else: | ||
| 59 | splashline = "" | ||
| 60 | |||
| 61 | bootloader = creator.ks.bootloader | ||
| 62 | |||
| 63 | syslinux_conf = "" | ||
| 64 | syslinux_conf += "PROMPT 0\n" | ||
| 65 | syslinux_conf += "TIMEOUT %s \n" % (bootloader.timeout or 10) | ||
| 66 | syslinux_conf += "\n" | ||
| 67 | syslinux_conf += "ALLOWOPTIONS 1\n" | ||
| 68 | syslinux_conf += "SERIAL 0 115200\n" | ||
| 69 | syslinux_conf += "\n" | ||
| 70 | if splashline: | ||
| 71 | syslinux_conf += "%s\n" % splashline | ||
| 72 | syslinux_conf += "DEFAULT boot\n" | ||
| 73 | syslinux_conf += "LABEL boot\n" | ||
| 74 | |||
| 75 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 76 | if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": | ||
| 77 | if get_bitbake_var("INITRAMFS_IMAGE"): | ||
| 78 | kernel = "%s-%s.bin" % \ | ||
| 79 | (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) | ||
| 80 | |||
| 81 | syslinux_conf += "KERNEL /" + kernel + "\n" | ||
| 82 | syslinux_conf += "APPEND initrd=/initrd LABEL=boot %s\n" \ | ||
| 83 | % bootloader.append | ||
| 84 | |||
| 85 | logger.debug("Writing syslinux config %s/ISO/isolinux/isolinux.cfg", | ||
| 86 | cr_workdir) | ||
| 87 | |||
| 88 | with open("%s/ISO/isolinux/isolinux.cfg" % cr_workdir, "w") as cfg: | ||
| 89 | cfg.write(syslinux_conf) | ||
| 90 | |||
| 91 | @classmethod | ||
| 92 | def do_configure_grubefi(cls, part, creator, target_dir): | ||
| 93 | """ | ||
| 94 | Create loader-specific (grub-efi) config | ||
| 95 | """ | ||
| 96 | configfile = creator.ks.bootloader.configfile | ||
| 97 | if configfile: | ||
| 98 | grubefi_conf = get_custom_config(configfile) | ||
| 99 | if grubefi_conf: | ||
| 100 | logger.debug("Using custom configuration file %s for grub.cfg", | ||
| 101 | configfile) | ||
| 102 | else: | ||
| 103 | raise WicError("configfile is specified " | ||
| 104 | "but failed to get it from %s", configfile) | ||
| 105 | else: | ||
| 106 | splash = os.path.join(target_dir, "splash.jpg") | ||
| 107 | if os.path.exists(splash): | ||
| 108 | splashline = "menu background splash.jpg" | ||
| 109 | else: | ||
| 110 | splashline = "" | ||
| 111 | |||
| 112 | bootloader = creator.ks.bootloader | ||
| 113 | |||
| 114 | grubefi_conf = "" | ||
| 115 | grubefi_conf += "serial --unit=0 --speed=115200 --word=8 " | ||
| 116 | grubefi_conf += "--parity=no --stop=1\n" | ||
| 117 | grubefi_conf += "default=boot\n" | ||
| 118 | grubefi_conf += "timeout=%s\n" % (bootloader.timeout or 10) | ||
| 119 | grubefi_conf += "\n" | ||
| 120 | grubefi_conf += "search --set=root --label %s " % part.label | ||
| 121 | grubefi_conf += "\n" | ||
| 122 | grubefi_conf += "menuentry 'boot'{\n" | ||
| 123 | |||
| 124 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 125 | if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": | ||
| 126 | if get_bitbake_var("INITRAMFS_IMAGE"): | ||
| 127 | kernel = "%s-%s.bin" % \ | ||
| 128 | (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) | ||
| 129 | |||
| 130 | grubefi_conf += "linux /%s rootwait %s\n" \ | ||
| 131 | % (kernel, bootloader.append) | ||
| 132 | grubefi_conf += "initrd /initrd \n" | ||
| 133 | grubefi_conf += "}\n" | ||
| 134 | |||
| 135 | if splashline: | ||
| 136 | grubefi_conf += "%s\n" % splashline | ||
| 137 | |||
| 138 | cfg_path = os.path.join(target_dir, "grub.cfg") | ||
| 139 | logger.debug("Writing grubefi config %s", cfg_path) | ||
| 140 | |||
| 141 | with open(cfg_path, "w") as cfg: | ||
| 142 | cfg.write(grubefi_conf) | ||
| 143 | |||
| 144 | @staticmethod | ||
| 145 | def _build_initramfs_path(rootfs_dir, cr_workdir): | ||
| 146 | """ | ||
| 147 | Create path for initramfs image | ||
| 148 | """ | ||
| 149 | |||
| 150 | initrd = get_bitbake_var("INITRD_LIVE") or get_bitbake_var("INITRD") | ||
| 151 | if not initrd: | ||
| 152 | initrd_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 153 | if not initrd_dir: | ||
| 154 | raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting.") | ||
| 155 | |||
| 156 | image_name = get_bitbake_var("IMAGE_BASENAME") | ||
| 157 | if not image_name: | ||
| 158 | raise WicError("Couldn't find IMAGE_BASENAME, exiting.") | ||
| 159 | |||
| 160 | image_type = get_bitbake_var("INITRAMFS_FSTYPES") | ||
| 161 | if not image_type: | ||
| 162 | raise WicError("Couldn't find INITRAMFS_FSTYPES, exiting.") | ||
| 163 | |||
| 164 | machine = os.path.basename(initrd_dir) | ||
| 165 | |||
| 166 | pattern = '%s/%s*%s.%s' % (initrd_dir, image_name, machine, image_type) | ||
| 167 | files = glob.glob(pattern) | ||
| 168 | if files: | ||
| 169 | initrd = files[0] | ||
| 170 | |||
| 171 | if not initrd or not os.path.exists(initrd): | ||
| 172 | # Create initrd from rootfs directory | ||
| 173 | initrd = "%s/initrd.cpio.gz" % cr_workdir | ||
| 174 | initrd_dir = "%s/INITRD" % cr_workdir | ||
| 175 | shutil.copytree("%s" % rootfs_dir, \ | ||
| 176 | "%s" % initrd_dir, symlinks=True) | ||
| 177 | |||
| 178 | if os.path.isfile("%s/init" % rootfs_dir): | ||
| 179 | shutil.copy2("%s/init" % rootfs_dir, "%s/init" % initrd_dir) | ||
| 180 | elif os.path.lexists("%s/init" % rootfs_dir): | ||
| 181 | os.symlink(os.readlink("%s/init" % rootfs_dir), \ | ||
| 182 | "%s/init" % initrd_dir) | ||
| 183 | elif os.path.isfile("%s/sbin/init" % rootfs_dir): | ||
| 184 | shutil.copy2("%s/sbin/init" % rootfs_dir, \ | ||
| 185 | "%s" % initrd_dir) | ||
| 186 | elif os.path.lexists("%s/sbin/init" % rootfs_dir): | ||
| 187 | os.symlink(os.readlink("%s/sbin/init" % rootfs_dir), \ | ||
| 188 | "%s/init" % initrd_dir) | ||
| 189 | else: | ||
| 190 | raise WicError("Couldn't find or build initrd, exiting.") | ||
| 191 | |||
| 192 | exec_cmd("cd %s && find . | cpio -o -H newc -R root:root >%s/initrd.cpio " \ | ||
| 193 | % (initrd_dir, cr_workdir), as_shell=True) | ||
| 194 | exec_cmd("gzip -f -9 %s/initrd.cpio" % cr_workdir, as_shell=True) | ||
| 195 | shutil.rmtree(initrd_dir) | ||
| 196 | |||
| 197 | return initrd | ||
| 198 | |||
| 199 | @classmethod | ||
| 200 | def do_configure_partition(cls, part, source_params, creator, cr_workdir, | ||
| 201 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 202 | native_sysroot): | ||
| 203 | """ | ||
| 204 | Called before do_prepare_partition(), creates loader-specific config | ||
| 205 | """ | ||
| 206 | isodir = "%s/ISO/" % cr_workdir | ||
| 207 | |||
| 208 | if os.path.exists(isodir): | ||
| 209 | shutil.rmtree(isodir) | ||
| 210 | |||
| 211 | install_cmd = "install -d %s " % isodir | ||
| 212 | exec_cmd(install_cmd) | ||
| 213 | |||
| 214 | # Overwrite the name of the created image | ||
| 215 | logger.debug(source_params) | ||
| 216 | if 'image_name' in source_params and \ | ||
| 217 | source_params['image_name'].strip(): | ||
| 218 | creator.name = source_params['image_name'].strip() | ||
| 219 | logger.debug("The name of the image is: %s", creator.name) | ||
| 220 | |||
| 221 | @staticmethod | ||
| 222 | def _install_payload(source_params, iso_dir): | ||
| 223 | """ | ||
| 224 | Copies contents of payload directory (as specified in 'payload_dir' param) into iso_dir | ||
| 225 | """ | ||
| 226 | |||
| 227 | if source_params.get('payload_dir'): | ||
| 228 | payload_dir = source_params['payload_dir'] | ||
| 229 | |||
| 230 | logger.debug("Payload directory: %s", payload_dir) | ||
| 231 | shutil.copytree(payload_dir, iso_dir, symlinks=True, dirs_exist_ok=True) | ||
| 232 | |||
| 233 | @classmethod | ||
| 234 | def do_prepare_partition(cls, part, source_params, creator, cr_workdir, | ||
| 235 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 236 | rootfs_dir, native_sysroot): | ||
| 237 | """ | ||
| 238 | Called to do the actual content population for a partition i.e. it | ||
| 239 | 'prepares' the partition to be incorporated into the image. | ||
| 240 | In this case, prepare content for a bootable ISO image. | ||
| 241 | """ | ||
| 242 | |||
| 243 | isodir = "%s/ISO" % cr_workdir | ||
| 244 | |||
| 245 | cls._install_payload(source_params, isodir) | ||
| 246 | |||
| 247 | if part.rootfs_dir is None: | ||
| 248 | if not 'ROOTFS_DIR' in rootfs_dir: | ||
| 249 | raise WicError("Couldn't find --rootfs-dir, exiting.") | ||
| 250 | rootfs_dir = rootfs_dir['ROOTFS_DIR'] | ||
| 251 | else: | ||
| 252 | if part.rootfs_dir in rootfs_dir: | ||
| 253 | rootfs_dir = rootfs_dir[part.rootfs_dir] | ||
| 254 | elif part.rootfs_dir: | ||
| 255 | rootfs_dir = part.rootfs_dir | ||
| 256 | else: | ||
| 257 | raise WicError("Couldn't find --rootfs-dir=%s connection " | ||
| 258 | "or it is not a valid path, exiting." % | ||
| 259 | part.rootfs_dir) | ||
| 260 | |||
| 261 | if not os.path.isdir(rootfs_dir): | ||
| 262 | rootfs_dir = get_bitbake_var("IMAGE_ROOTFS") | ||
| 263 | if not os.path.isdir(rootfs_dir): | ||
| 264 | raise WicError("Couldn't find IMAGE_ROOTFS, exiting.") | ||
| 265 | |||
| 266 | part.rootfs_dir = rootfs_dir | ||
| 267 | deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 268 | img_iso_dir = get_bitbake_var("ISODIR") | ||
| 269 | |||
| 270 | # Remove the temporary file created by part.prepare_rootfs() | ||
| 271 | if os.path.isfile(part.source_file): | ||
| 272 | os.remove(part.source_file) | ||
| 273 | |||
| 274 | # Support using a different initrd other than default | ||
| 275 | if source_params.get('initrd'): | ||
| 276 | initrd = source_params['initrd'] | ||
| 277 | if not deploy_dir: | ||
| 278 | raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") | ||
| 279 | cp_cmd = "cp %s/%s %s" % (deploy_dir, initrd, cr_workdir) | ||
| 280 | exec_cmd(cp_cmd) | ||
| 281 | else: | ||
| 282 | # Prepare initial ramdisk | ||
| 283 | initrd = "%s/initrd" % deploy_dir | ||
| 284 | if not os.path.isfile(initrd): | ||
| 285 | initrd = "%s/initrd" % img_iso_dir | ||
| 286 | if not os.path.isfile(initrd): | ||
| 287 | initrd = cls._build_initramfs_path(rootfs_dir, cr_workdir) | ||
| 288 | |||
| 289 | install_cmd = "install -m 0644 %s %s/initrd" % (initrd, isodir) | ||
| 290 | exec_cmd(install_cmd) | ||
| 291 | |||
| 292 | # Remove the temporary file created by _build_initramfs_path function | ||
| 293 | if os.path.isfile("%s/initrd.cpio.gz" % cr_workdir): | ||
| 294 | os.remove("%s/initrd.cpio.gz" % cr_workdir) | ||
| 295 | |||
| 296 | kernel = get_bitbake_var("KERNEL_IMAGETYPE") | ||
| 297 | if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1": | ||
| 298 | if get_bitbake_var("INITRAMFS_IMAGE"): | ||
| 299 | kernel = "%s-%s.bin" % \ | ||
| 300 | (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) | ||
| 301 | |||
| 302 | install_cmd = "install -m 0644 %s/%s %s/%s" % \ | ||
| 303 | (kernel_dir, kernel, isodir, kernel) | ||
| 304 | exec_cmd(install_cmd) | ||
| 305 | |||
| 306 | #Create bootloader for efi boot | ||
| 307 | try: | ||
| 308 | target_dir = "%s/EFI/BOOT" % isodir | ||
| 309 | if os.path.exists(target_dir): | ||
| 310 | shutil.rmtree(target_dir) | ||
| 311 | |||
| 312 | os.makedirs(target_dir) | ||
| 313 | |||
| 314 | if source_params['loader'] == 'grub-efi': | ||
| 315 | # Builds bootx64.efi/bootia32.efi if ISODIR didn't exist or | ||
| 316 | # didn't contains it | ||
| 317 | target_arch = get_bitbake_var("TARGET_SYS") | ||
| 318 | if not target_arch: | ||
| 319 | raise WicError("Coludn't find target architecture") | ||
| 320 | |||
| 321 | if re.match("x86_64", target_arch): | ||
| 322 | grub_src_image = "grub-efi-bootx64.efi" | ||
| 323 | grub_dest_image = "bootx64.efi" | ||
| 324 | elif re.match('i.86', target_arch): | ||
| 325 | grub_src_image = "grub-efi-bootia32.efi" | ||
| 326 | grub_dest_image = "bootia32.efi" | ||
| 327 | else: | ||
| 328 | raise WicError("grub-efi is incompatible with target %s" % | ||
| 329 | target_arch) | ||
| 330 | |||
| 331 | grub_target = os.path.join(target_dir, grub_dest_image) | ||
| 332 | if not os.path.isfile(grub_target): | ||
| 333 | grub_src = os.path.join(deploy_dir, grub_src_image) | ||
| 334 | if not os.path.exists(grub_src): | ||
| 335 | raise WicError("Grub loader %s is not found in %s. " | ||
| 336 | "Please build grub-efi first" % (grub_src_image, deploy_dir)) | ||
| 337 | shutil.copy(grub_src, grub_target) | ||
| 338 | |||
| 339 | if not os.path.isfile(os.path.join(target_dir, "boot.cfg")): | ||
| 340 | cls.do_configure_grubefi(part, creator, target_dir) | ||
| 341 | |||
| 342 | else: | ||
| 343 | raise WicError("unrecognized bootimg-efi loader: %s" % | ||
| 344 | source_params['loader']) | ||
| 345 | except KeyError: | ||
| 346 | raise WicError("bootimg-efi requires a loader, none specified") | ||
| 347 | |||
| 348 | # Create efi.img that contains bootloader files for EFI booting | ||
| 349 | # if ISODIR didn't exist or didn't contains it | ||
| 350 | if os.path.isfile("%s/efi.img" % img_iso_dir): | ||
| 351 | install_cmd = "install -m 0644 %s/efi.img %s/efi.img" % \ | ||
| 352 | (img_iso_dir, isodir) | ||
| 353 | exec_cmd(install_cmd) | ||
| 354 | else: | ||
| 355 | # Default to 100 blocks of extra space for file system overhead | ||
| 356 | esp_extra_blocks = int(source_params.get('esp_extra_blocks', '100')) | ||
| 357 | |||
| 358 | du_cmd = "du -bks %s/EFI" % isodir | ||
| 359 | out = exec_cmd(du_cmd) | ||
| 360 | blocks = int(out.split()[0]) | ||
| 361 | blocks += esp_extra_blocks | ||
| 362 | logger.debug("Added 100 extra blocks to %s to get to %d " | ||
| 363 | "total blocks", part.mountpoint, blocks) | ||
| 364 | |||
| 365 | # dosfs image for EFI boot | ||
| 366 | bootimg = "%s/efi.img" % isodir | ||
| 367 | |||
| 368 | esp_label = source_params.get('esp_label', 'EFIimg') | ||
| 369 | |||
| 370 | dosfs_cmd = 'mkfs.vfat -n \'%s\' -S 512 -C %s %d' \ | ||
| 371 | % (esp_label, bootimg, blocks) | ||
| 372 | exec_native_cmd(dosfs_cmd, native_sysroot) | ||
| 373 | |||
| 374 | mmd_cmd = "mmd -i %s ::/EFI" % bootimg | ||
| 375 | exec_native_cmd(mmd_cmd, native_sysroot) | ||
| 376 | |||
| 377 | mcopy_cmd = "mcopy -i %s -s %s/EFI/* ::/EFI/" \ | ||
| 378 | % (bootimg, isodir) | ||
| 379 | exec_native_cmd(mcopy_cmd, native_sysroot) | ||
| 380 | |||
| 381 | chmod_cmd = "chmod 644 %s" % bootimg | ||
| 382 | exec_cmd(chmod_cmd) | ||
| 383 | |||
| 384 | # Prepare files for legacy boot | ||
| 385 | syslinux_dir = get_bitbake_var("STAGING_DATADIR") | ||
| 386 | if not syslinux_dir: | ||
| 387 | raise WicError("Couldn't find STAGING_DATADIR, exiting.") | ||
| 388 | |||
| 389 | if os.path.exists("%s/isolinux" % isodir): | ||
| 390 | shutil.rmtree("%s/isolinux" % isodir) | ||
| 391 | |||
| 392 | install_cmd = "install -d %s/isolinux" % isodir | ||
| 393 | exec_cmd(install_cmd) | ||
| 394 | |||
| 395 | cls.do_configure_syslinux(creator, cr_workdir) | ||
| 396 | |||
| 397 | install_cmd = "install -m 444 %s/syslinux/ldlinux.sys " % syslinux_dir | ||
| 398 | install_cmd += "%s/isolinux/ldlinux.sys" % isodir | ||
| 399 | exec_cmd(install_cmd) | ||
| 400 | |||
| 401 | install_cmd = "install -m 444 %s/syslinux/isohdpfx.bin " % syslinux_dir | ||
| 402 | install_cmd += "%s/isolinux/isohdpfx.bin" % isodir | ||
| 403 | exec_cmd(install_cmd) | ||
| 404 | |||
| 405 | install_cmd = "install -m 644 %s/syslinux/isolinux.bin " % syslinux_dir | ||
| 406 | install_cmd += "%s/isolinux/isolinux.bin" % isodir | ||
| 407 | exec_cmd(install_cmd) | ||
| 408 | |||
| 409 | install_cmd = "install -m 644 %s/syslinux/ldlinux.c32 " % syslinux_dir | ||
| 410 | install_cmd += "%s/isolinux/ldlinux.c32" % isodir | ||
| 411 | exec_cmd(install_cmd) | ||
| 412 | |||
| 413 | #create ISO image | ||
| 414 | iso_img = "%s/tempiso_img.iso" % cr_workdir | ||
| 415 | iso_bootimg = "isolinux/isolinux.bin" | ||
| 416 | iso_bootcat = "isolinux/boot.cat" | ||
| 417 | efi_img = "efi.img" | ||
| 418 | |||
| 419 | mkisofs_cmd = "mkisofs -V %s " % part.label | ||
| 420 | mkisofs_cmd += "-o %s -U " % iso_img | ||
| 421 | mkisofs_cmd += "-J -joliet-long -r -iso-level 2 -b %s " % iso_bootimg | ||
| 422 | mkisofs_cmd += "-c %s -no-emul-boot -boot-load-size 4 " % iso_bootcat | ||
| 423 | mkisofs_cmd += "-boot-info-table -eltorito-alt-boot " | ||
| 424 | mkisofs_cmd += "-eltorito-platform 0xEF -eltorito-boot %s " % efi_img | ||
| 425 | mkisofs_cmd += "-no-emul-boot %s " % isodir | ||
| 426 | |||
| 427 | logger.debug("running command: %s", mkisofs_cmd) | ||
| 428 | exec_native_cmd(mkisofs_cmd, native_sysroot) | ||
| 429 | |||
| 430 | shutil.rmtree(isodir) | ||
| 431 | |||
| 432 | du_cmd = "du -Lbks %s" % iso_img | ||
| 433 | out = exec_cmd(du_cmd) | ||
| 434 | isoimg_size = int(out.split()[0]) | ||
| 435 | |||
| 436 | part.size = isoimg_size | ||
| 437 | part.source_file = iso_img | ||
| 438 | |||
| 439 | @classmethod | ||
| 440 | def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir, | ||
| 441 | bootimg_dir, kernel_dir, native_sysroot): | ||
| 442 | """ | ||
| 443 | Called after all partitions have been prepared and assembled into a | ||
| 444 | disk image. In this case, we insert/modify the MBR using isohybrid | ||
| 445 | utility for booting via BIOS from disk storage devices. | ||
| 446 | """ | ||
| 447 | |||
| 448 | iso_img = "%s.p1" % disk.path | ||
| 449 | full_path = creator._full_path(workdir, disk_name, "direct") | ||
| 450 | full_path_iso = creator._full_path(workdir, disk_name, "iso") | ||
| 451 | |||
| 452 | isohybrid_cmd = "isohybrid -u %s" % iso_img | ||
| 453 | logger.debug("running command: %s", isohybrid_cmd) | ||
| 454 | exec_native_cmd(isohybrid_cmd, native_sysroot) | ||
| 455 | |||
| 456 | # Replace the image created by direct plugin with the one created by | ||
| 457 | # mkisofs command. This is necessary because the iso image created by | ||
| 458 | # mkisofs has a very specific MBR is system area of the ISO image, and | ||
| 459 | # direct plugin adds and configures an another MBR. | ||
| 460 | logger.debug("Replaceing the image created by direct plugin\n") | ||
| 461 | os.remove(disk.path) | ||
| 462 | shutil.copy2(iso_img, full_path_iso) | ||
| 463 | shutil.copy2(full_path_iso, full_path) | ||
diff --git a/scripts/lib/wic/plugins/source/rawcopy.py b/scripts/lib/wic/plugins/source/rawcopy.py deleted file mode 100644 index 21903c2f23..0000000000 --- a/scripts/lib/wic/plugins/source/rawcopy.py +++ /dev/null | |||
| @@ -1,115 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright OpenEmbedded Contributors | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | |||
| 7 | import logging | ||
| 8 | import os | ||
| 9 | import signal | ||
| 10 | import subprocess | ||
| 11 | |||
| 12 | from wic import WicError | ||
| 13 | from wic.pluginbase import SourcePlugin | ||
| 14 | from wic.misc import exec_cmd, get_bitbake_var | ||
| 15 | from wic.filemap import sparse_copy | ||
| 16 | |||
| 17 | logger = logging.getLogger('wic') | ||
| 18 | |||
| 19 | class RawCopyPlugin(SourcePlugin): | ||
| 20 | """ | ||
| 21 | Populate partition content from raw image file. | ||
| 22 | """ | ||
| 23 | |||
| 24 | name = 'rawcopy' | ||
| 25 | |||
| 26 | @staticmethod | ||
| 27 | def do_image_label(fstype, dst, label): | ||
| 28 | # don't create label when fstype is none | ||
| 29 | if fstype == 'none': | ||
| 30 | return | ||
| 31 | |||
| 32 | if fstype.startswith('ext'): | ||
| 33 | cmd = 'tune2fs -L %s %s' % (label, dst) | ||
| 34 | elif fstype in ('msdos', 'vfat'): | ||
| 35 | cmd = 'dosfslabel %s %s' % (dst, label) | ||
| 36 | elif fstype == 'btrfs': | ||
| 37 | cmd = 'btrfs filesystem label %s %s' % (dst, label) | ||
| 38 | elif fstype == 'swap': | ||
| 39 | cmd = 'mkswap -L %s %s' % (label, dst) | ||
| 40 | elif fstype in ('squashfs', 'erofs'): | ||
| 41 | raise WicError("It's not possible to update a %s " | ||
| 42 | "filesystem label '%s'" % (fstype, label)) | ||
| 43 | else: | ||
| 44 | raise WicError("Cannot update filesystem label: " | ||
| 45 | "Unknown fstype: '%s'" % (fstype)) | ||
| 46 | |||
| 47 | exec_cmd(cmd) | ||
| 48 | |||
| 49 | @staticmethod | ||
| 50 | def do_image_uncompression(src, dst, workdir): | ||
| 51 | def subprocess_setup(): | ||
| 52 | # Python installs a SIGPIPE handler by default. This is usually not what | ||
| 53 | # non-Python subprocesses expect. | ||
| 54 | # SIGPIPE errors are known issues with gzip/bash | ||
| 55 | signal.signal(signal.SIGPIPE, signal.SIG_DFL) | ||
| 56 | |||
| 57 | extension = os.path.splitext(src)[1] | ||
| 58 | decompressor = { | ||
| 59 | ".bz2": "bzip2", | ||
| 60 | ".gz": "gzip", | ||
| 61 | ".xz": "xz", | ||
| 62 | ".zst": "zstd -f", | ||
| 63 | }.get(extension) | ||
| 64 | if not decompressor: | ||
| 65 | raise WicError("Not supported compressor filename extension: %s" % extension) | ||
| 66 | cmd = "%s -dc %s > %s" % (decompressor, src, dst) | ||
| 67 | subprocess.call(cmd, preexec_fn=subprocess_setup, shell=True, cwd=workdir) | ||
| 68 | |||
| 69 | @classmethod | ||
| 70 | def do_prepare_partition(cls, part, source_params, cr, cr_workdir, | ||
| 71 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 72 | rootfs_dir, native_sysroot): | ||
| 73 | """ | ||
| 74 | Called to do the actual content population for a partition i.e. it | ||
| 75 | 'prepares' the partition to be incorporated into the image. | ||
| 76 | """ | ||
| 77 | if not kernel_dir: | ||
| 78 | kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE") | ||
| 79 | if not kernel_dir: | ||
| 80 | raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting") | ||
| 81 | |||
| 82 | logger.debug('Kernel dir: %s', kernel_dir) | ||
| 83 | |||
| 84 | if 'file' not in source_params: | ||
| 85 | raise WicError("No file specified") | ||
| 86 | |||
| 87 | if 'unpack' in source_params: | ||
| 88 | img = os.path.join(kernel_dir, source_params['file']) | ||
| 89 | src = os.path.join(cr_workdir, os.path.splitext(source_params['file'])[0]) | ||
| 90 | RawCopyPlugin.do_image_uncompression(img, src, cr_workdir) | ||
| 91 | else: | ||
| 92 | src = os.path.join(kernel_dir, source_params['file']) | ||
| 93 | |||
| 94 | dst = os.path.join(cr_workdir, "%s.%s" % (os.path.basename(source_params['file']), part.lineno)) | ||
| 95 | |||
| 96 | if not os.path.exists(os.path.dirname(dst)): | ||
| 97 | os.makedirs(os.path.dirname(dst)) | ||
| 98 | |||
| 99 | if 'skip' in source_params: | ||
| 100 | sparse_copy(src, dst, skip=int(source_params['skip'])) | ||
| 101 | else: | ||
| 102 | sparse_copy(src, dst) | ||
| 103 | |||
| 104 | # get the size in the right units for kickstart (kB) | ||
| 105 | du_cmd = "du -Lbks %s" % dst | ||
| 106 | out = exec_cmd(du_cmd) | ||
| 107 | filesize = int(out.split()[0]) | ||
| 108 | |||
| 109 | if filesize > part.size: | ||
| 110 | part.size = filesize | ||
| 111 | |||
| 112 | if part.label: | ||
| 113 | RawCopyPlugin.do_image_label(part.fstype, dst, part.label) | ||
| 114 | |||
| 115 | part.source_file = dst | ||
diff --git a/scripts/lib/wic/plugins/source/rootfs.py b/scripts/lib/wic/plugins/source/rootfs.py deleted file mode 100644 index c990143c0d..0000000000 --- a/scripts/lib/wic/plugins/source/rootfs.py +++ /dev/null | |||
| @@ -1,236 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (c) 2014, Intel Corporation. | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # DESCRIPTION | ||
| 7 | # This implements the 'rootfs' source plugin class for 'wic' | ||
| 8 | # | ||
| 9 | # AUTHORS | ||
| 10 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
| 11 | # Joao Henrique Ferreira de Freitas <joaohf (at] gmail.com> | ||
| 12 | # | ||
| 13 | |||
| 14 | import logging | ||
| 15 | import os | ||
| 16 | import shutil | ||
| 17 | import sys | ||
| 18 | |||
| 19 | from oe.path import copyhardlinktree | ||
| 20 | from pathlib import Path | ||
| 21 | |||
| 22 | from wic import WicError | ||
| 23 | from wic.pluginbase import SourcePlugin | ||
| 24 | from wic.misc import get_bitbake_var, exec_native_cmd | ||
| 25 | |||
| 26 | logger = logging.getLogger('wic') | ||
| 27 | |||
| 28 | class RootfsPlugin(SourcePlugin): | ||
| 29 | """ | ||
| 30 | Populate partition content from a rootfs directory. | ||
| 31 | """ | ||
| 32 | |||
| 33 | name = 'rootfs' | ||
| 34 | |||
| 35 | @staticmethod | ||
| 36 | def __validate_path(cmd, rootfs_dir, path): | ||
| 37 | if os.path.isabs(path): | ||
| 38 | logger.error("%s: Must be relative: %s" % (cmd, path)) | ||
| 39 | sys.exit(1) | ||
| 40 | |||
| 41 | # Disallow climbing outside of parent directory using '..', | ||
| 42 | # because doing so could be quite disastrous (we will delete the | ||
| 43 | # directory, or modify a directory outside OpenEmbedded). | ||
| 44 | full_path = os.path.realpath(os.path.join(rootfs_dir, path)) | ||
| 45 | if not full_path.startswith(os.path.realpath(rootfs_dir)): | ||
| 46 | logger.error("%s: Must point inside the rootfs: %s" % (cmd, path)) | ||
| 47 | sys.exit(1) | ||
| 48 | |||
| 49 | return full_path | ||
| 50 | |||
| 51 | @staticmethod | ||
| 52 | def __get_rootfs_dir(rootfs_dir): | ||
| 53 | if rootfs_dir and os.path.isdir(rootfs_dir): | ||
| 54 | return os.path.realpath(rootfs_dir) | ||
| 55 | |||
| 56 | image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir) | ||
| 57 | if not os.path.isdir(image_rootfs_dir): | ||
| 58 | raise WicError("No valid artifact IMAGE_ROOTFS from image " | ||
| 59 | "named %s has been found at %s, exiting." % | ||
| 60 | (rootfs_dir, image_rootfs_dir)) | ||
| 61 | |||
| 62 | return os.path.realpath(image_rootfs_dir) | ||
| 63 | |||
| 64 | @staticmethod | ||
| 65 | def __get_pseudo(native_sysroot, rootfs, pseudo_dir): | ||
| 66 | pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot | ||
| 67 | pseudo += "export PSEUDO_LOCALSTATEDIR=%s;" % pseudo_dir | ||
| 68 | pseudo += "export PSEUDO_PASSWD=%s;" % rootfs | ||
| 69 | pseudo += "export PSEUDO_NOSYMLINKEXP=1;" | ||
| 70 | pseudo += "%s " % get_bitbake_var("FAKEROOTCMD") | ||
| 71 | return pseudo | ||
| 72 | |||
| 73 | @classmethod | ||
| 74 | def do_prepare_partition(cls, part, source_params, cr, cr_workdir, | ||
| 75 | oe_builddir, bootimg_dir, kernel_dir, | ||
| 76 | krootfs_dir, native_sysroot): | ||
| 77 | """ | ||
| 78 | Called to do the actual content population for a partition i.e. it | ||
| 79 | 'prepares' the partition to be incorporated into the image. | ||
| 80 | In this case, prepare content for legacy bios boot partition. | ||
| 81 | """ | ||
| 82 | if part.rootfs_dir is None: | ||
| 83 | if not 'ROOTFS_DIR' in krootfs_dir: | ||
| 84 | raise WicError("Couldn't find --rootfs-dir, exiting") | ||
| 85 | |||
| 86 | rootfs_dir = krootfs_dir['ROOTFS_DIR'] | ||
| 87 | else: | ||
| 88 | if part.rootfs_dir in krootfs_dir: | ||
| 89 | rootfs_dir = krootfs_dir[part.rootfs_dir] | ||
| 90 | elif part.rootfs_dir: | ||
| 91 | rootfs_dir = part.rootfs_dir | ||
| 92 | else: | ||
| 93 | raise WicError("Couldn't find --rootfs-dir=%s connection or " | ||
| 94 | "it is not a valid path, exiting" % part.rootfs_dir) | ||
| 95 | |||
| 96 | part.rootfs_dir = cls.__get_rootfs_dir(rootfs_dir) | ||
| 97 | part.has_fstab = os.path.exists(os.path.join(part.rootfs_dir, "etc/fstab")) | ||
| 98 | pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo") | ||
| 99 | if not os.path.lexists(pseudo_dir): | ||
| 100 | pseudo_dir = os.path.join(cls.__get_rootfs_dir(None), '../pseudo') | ||
| 101 | |||
| 102 | if not os.path.lexists(pseudo_dir): | ||
| 103 | logger.warn("%s folder does not exist. " | ||
| 104 | "Usernames and permissions will be invalid " % pseudo_dir) | ||
| 105 | pseudo_dir = None | ||
| 106 | |||
| 107 | new_rootfs = None | ||
| 108 | new_pseudo = None | ||
| 109 | # Handle excluded paths. | ||
| 110 | if part.exclude_path or part.include_path or part.change_directory or part.update_fstab_in_rootfs: | ||
| 111 | # We need a new rootfs directory we can safely modify without | ||
| 112 | # interfering with other tasks. Copy to workdir. | ||
| 113 | new_rootfs = os.path.realpath(os.path.join(cr_workdir, "rootfs%d" % part.lineno)) | ||
| 114 | |||
| 115 | if os.path.lexists(new_rootfs): | ||
| 116 | shutil.rmtree(os.path.join(new_rootfs)) | ||
| 117 | |||
| 118 | if part.change_directory: | ||
| 119 | cd = part.change_directory | ||
| 120 | if cd[-1] == '/': | ||
| 121 | cd = cd[:-1] | ||
| 122 | orig_dir = cls.__validate_path("--change-directory", part.rootfs_dir, cd) | ||
| 123 | else: | ||
| 124 | orig_dir = part.rootfs_dir | ||
| 125 | copyhardlinktree(orig_dir, new_rootfs) | ||
| 126 | |||
| 127 | # Convert the pseudo directory to its new location | ||
| 128 | if (pseudo_dir): | ||
| 129 | new_pseudo = os.path.realpath( | ||
| 130 | os.path.join(cr_workdir, "pseudo%d" % part.lineno)) | ||
| 131 | if os.path.lexists(new_pseudo): | ||
| 132 | shutil.rmtree(new_pseudo) | ||
| 133 | os.mkdir(new_pseudo) | ||
| 134 | shutil.copy(os.path.join(pseudo_dir, "files.db"), | ||
| 135 | os.path.join(new_pseudo, "files.db")) | ||
| 136 | |||
| 137 | pseudo_cmd = "%s -B -m %s -M %s" % (cls.__get_pseudo(native_sysroot, | ||
| 138 | new_rootfs, | ||
| 139 | new_pseudo), | ||
| 140 | orig_dir, new_rootfs) | ||
| 141 | exec_native_cmd(pseudo_cmd, native_sysroot) | ||
| 142 | |||
| 143 | for in_path in part.include_path or []: | ||
| 144 | #parse arguments | ||
| 145 | include_path = in_path[0] | ||
| 146 | if len(in_path) > 2: | ||
| 147 | logger.error("'Invalid number of arguments for include-path") | ||
| 148 | sys.exit(1) | ||
| 149 | if len(in_path) == 2: | ||
| 150 | path = in_path[1] | ||
| 151 | else: | ||
| 152 | path = None | ||
| 153 | |||
| 154 | # Pack files to be included into a tar file. | ||
| 155 | # We need to create a tar file, because that way we can keep the | ||
| 156 | # permissions from the files even when they belong to different | ||
| 157 | # pseudo enviroments. | ||
| 158 | # If we simply copy files using copyhardlinktree/copytree... the | ||
| 159 | # copied files will belong to the user running wic. | ||
| 160 | tar_file = os.path.realpath( | ||
| 161 | os.path.join(cr_workdir, "include-path%d.tar" % part.lineno)) | ||
| 162 | if os.path.isfile(include_path): | ||
| 163 | parent = os.path.dirname(os.path.realpath(include_path)) | ||
| 164 | tar_cmd = "tar c --owner=root --group=root -f %s -C %s %s" % ( | ||
| 165 | tar_file, parent, os.path.relpath(include_path, parent)) | ||
| 166 | exec_native_cmd(tar_cmd, native_sysroot) | ||
| 167 | else: | ||
| 168 | if include_path in krootfs_dir: | ||
| 169 | include_path = krootfs_dir[include_path] | ||
| 170 | include_path = cls.__get_rootfs_dir(include_path) | ||
| 171 | include_pseudo = os.path.join(include_path, "../pseudo") | ||
| 172 | if os.path.lexists(include_pseudo): | ||
| 173 | pseudo = cls.__get_pseudo(native_sysroot, include_path, | ||
| 174 | include_pseudo) | ||
| 175 | tar_cmd = "tar cf %s -C %s ." % (tar_file, include_path) | ||
| 176 | else: | ||
| 177 | pseudo = None | ||
| 178 | tar_cmd = "tar c --owner=root --group=root -f %s -C %s ." % ( | ||
| 179 | tar_file, include_path) | ||
| 180 | exec_native_cmd(tar_cmd, native_sysroot, pseudo) | ||
| 181 | |||
| 182 | #create destination | ||
| 183 | if path: | ||
| 184 | destination = cls.__validate_path("--include-path", new_rootfs, path) | ||
| 185 | Path(destination).mkdir(parents=True, exist_ok=True) | ||
| 186 | else: | ||
| 187 | destination = new_rootfs | ||
| 188 | |||
| 189 | #extract destination | ||
| 190 | untar_cmd = "tar xf %s -C %s" % (tar_file, destination) | ||
| 191 | if new_pseudo: | ||
| 192 | pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) | ||
| 193 | else: | ||
| 194 | pseudo = None | ||
| 195 | exec_native_cmd(untar_cmd, native_sysroot, pseudo) | ||
| 196 | os.remove(tar_file) | ||
| 197 | |||
| 198 | for orig_path in part.exclude_path or []: | ||
| 199 | path = orig_path | ||
| 200 | |||
| 201 | full_path = cls.__validate_path("--exclude-path", new_rootfs, path) | ||
| 202 | |||
| 203 | if not os.path.lexists(full_path): | ||
| 204 | continue | ||
| 205 | |||
| 206 | if new_pseudo: | ||
| 207 | pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) | ||
| 208 | else: | ||
| 209 | pseudo = None | ||
| 210 | if path.endswith(os.sep): | ||
| 211 | # Delete content only. | ||
| 212 | for entry in os.listdir(full_path): | ||
| 213 | full_entry = os.path.join(full_path, entry) | ||
| 214 | rm_cmd = "rm -rf %s" % (full_entry) | ||
| 215 | exec_native_cmd(rm_cmd, native_sysroot, pseudo) | ||
| 216 | else: | ||
| 217 | # Delete whole directory. | ||
| 218 | rm_cmd = "rm -rf %s" % (full_path) | ||
| 219 | exec_native_cmd(rm_cmd, native_sysroot, pseudo) | ||
| 220 | |||
| 221 | # Update part.has_fstab here as fstab may have been added or | ||
| 222 | # removed by the above modifications. | ||
| 223 | part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab")) | ||
| 224 | if part.update_fstab_in_rootfs and part.has_fstab and not part.no_fstab_update: | ||
| 225 | fstab_path = os.path.join(new_rootfs, "etc/fstab") | ||
| 226 | # Assume that fstab should always be owned by root with fixed permissions | ||
| 227 | install_cmd = "install -m 0644 -p %s %s" % (part.updated_fstab_path, fstab_path) | ||
| 228 | if new_pseudo: | ||
| 229 | pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) | ||
| 230 | else: | ||
| 231 | pseudo = None | ||
| 232 | exec_native_cmd(install_cmd, native_sysroot, pseudo) | ||
| 233 | |||
| 234 | part.prepare_rootfs(cr_workdir, oe_builddir, | ||
| 235 | new_rootfs or part.rootfs_dir, native_sysroot, | ||
| 236 | pseudo_dir = new_pseudo or pseudo_dir) | ||
