summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic')
-rw-r--r--scripts/lib/wic/canned-wks/common.wks.inc2
-rw-r--r--scripts/lib/wic/canned-wks/directdisk-gpt.wks2
-rw-r--r--scripts/lib/wic/canned-wks/efi-bootdisk.wks.in2
-rw-r--r--scripts/lib/wic/canned-wks/mkefidisk.wks2
-rw-r--r--scripts/lib/wic/canned-wks/qemuloongarch.wks3
-rw-r--r--scripts/lib/wic/canned-wks/qemux86-directdisk.wks2
-rw-r--r--scripts/lib/wic/engine.py6
-rw-r--r--scripts/lib/wic/filemap.py7
-rw-r--r--scripts/lib/wic/help.py18
-rw-r--r--scripts/lib/wic/ksparser.py13
-rw-r--r--scripts/lib/wic/misc.py14
-rw-r--r--scripts/lib/wic/partition.py73
-rw-r--r--scripts/lib/wic/pluginbase.py8
-rw-r--r--scripts/lib/wic/plugins/imager/direct.py134
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-efi.py211
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-partition.py9
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-pcbios.py12
-rw-r--r--scripts/lib/wic/plugins/source/empty.py59
-rw-r--r--scripts/lib/wic/plugins/source/isoimage-isohybrid.py2
-rw-r--r--scripts/lib/wic/plugins/source/rawcopy.py42
-rw-r--r--scripts/lib/wic/plugins/source/rootfs.py13
21 files changed, 524 insertions, 110 deletions
diff --git a/scripts/lib/wic/canned-wks/common.wks.inc b/scripts/lib/wic/canned-wks/common.wks.inc
index 4fd29fa8c1..89880b417b 100644
--- a/scripts/lib/wic/canned-wks/common.wks.inc
+++ b/scripts/lib/wic/canned-wks/common.wks.inc
@@ -1,3 +1,3 @@
1# This file is included into 3 canned wks files from this directory 1# This file is included into 3 canned wks files from this directory
2part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 2part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024
3part / --source rootfs --use-uuid --fstype=ext4 --mkfs-extraopts "-T default" --label platform --align 1024 3part / --source rootfs --use-uuid --fstype=ext4 --label platform --align 1024
diff --git a/scripts/lib/wic/canned-wks/directdisk-gpt.wks b/scripts/lib/wic/canned-wks/directdisk-gpt.wks
index cf16c0c30b..8d7d8de6ea 100644
--- a/scripts/lib/wic/canned-wks/directdisk-gpt.wks
+++ b/scripts/lib/wic/canned-wks/directdisk-gpt.wks
@@ -4,7 +4,7 @@
4 4
5 5
6part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024 6part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024
7part / --source rootfs --ondisk sda --fstype=ext4 --mkfs-extraopts "-T default" --label platform --align 1024 --use-uuid 7part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid
8 8
9bootloader --ptable gpt --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8" 9bootloader --ptable gpt --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8"
10 10
diff --git a/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in b/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in
index 7300e65e32..2fd286ff98 100644
--- a/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in
+++ b/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in
@@ -1,3 +1,3 @@
1bootloader --ptable gpt 1bootloader --ptable gpt
2part /boot --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --label boot --active --align 1024 --use-uuid --overhead-factor 1.0 2part /boot --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --label boot --active --align 1024 --use-uuid --overhead-factor 1.1
3part / --source rootfs --fstype=ext4 --label root --align 1024 --exclude-path boot/ 3part / --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
index d1878e23e5..9f534fe184 100644
--- a/scripts/lib/wic/canned-wks/mkefidisk.wks
+++ b/scripts/lib/wic/canned-wks/mkefidisk.wks
@@ -4,7 +4,7 @@
4 4
5part /boot --source bootimg-efi --sourceparams="loader=grub-efi" --ondisk sda --label msdos --active --align 1024 5part /boot --source bootimg-efi --sourceparams="loader=grub-efi" --ondisk sda --label msdos --active --align 1024
6 6
7part / --source rootfs --ondisk sda --fstype=ext4 --mkfs-extraopts "-T default" --label platform --align 1024 --use-uuid 7part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid
8 8
9part swap --ondisk sda --size 44 --label swap1 --fstype=swap 9part swap --ondisk sda --size 44 --label swap1 --fstype=swap
10 10
diff --git a/scripts/lib/wic/canned-wks/qemuloongarch.wks b/scripts/lib/wic/canned-wks/qemuloongarch.wks
new file mode 100644
index 0000000000..8465c7a8c0
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/qemuloongarch.wks
@@ -0,0 +1,3 @@
1# short-description: Create qcow2 image for LoongArch QEMU machines
2
3part / --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
index 22b45217f1..808997611a 100644
--- a/scripts/lib/wic/canned-wks/qemux86-directdisk.wks
+++ b/scripts/lib/wic/canned-wks/qemux86-directdisk.wks
@@ -4,5 +4,5 @@
4 4
5include common.wks.inc 5include common.wks.inc
6 6
7bootloader --timeout=0 --append="rw oprofile.timer=1 rootfstype=ext4 " 7bootloader --timeout=0 --append="rw oprofile.timer=1 rootfstype=ext4 console=tty console=ttyS0 "
8 8
diff --git a/scripts/lib/wic/engine.py b/scripts/lib/wic/engine.py
index 018815b966..674ccfc244 100644
--- a/scripts/lib/wic/engine.py
+++ b/scripts/lib/wic/engine.py
@@ -19,10 +19,10 @@ import os
19import tempfile 19import tempfile
20import json 20import json
21import subprocess 21import subprocess
22import shutil
22import re 23import re
23 24
24from collections import namedtuple, OrderedDict 25from collections import namedtuple, OrderedDict
25from distutils.spawn import find_executable
26 26
27from wic import WicError 27from wic import WicError
28from wic.filemap import sparse_copy 28from wic.filemap import sparse_copy
@@ -245,7 +245,7 @@ class Disk:
245 for path in pathlist.split(':'): 245 for path in pathlist.split(':'):
246 self.paths = "%s%s:%s" % (native_sysroot, path, self.paths) 246 self.paths = "%s%s:%s" % (native_sysroot, path, self.paths)
247 247
248 self.parted = find_executable("parted", self.paths) 248 self.parted = shutil.which("parted", path=self.paths)
249 if not self.parted: 249 if not self.parted:
250 raise WicError("Can't find executable parted") 250 raise WicError("Can't find executable parted")
251 251
@@ -283,7 +283,7 @@ class Disk:
283 "resize2fs", "mkswap", "mkdosfs", "debugfs","blkid"): 283 "resize2fs", "mkswap", "mkdosfs", "debugfs","blkid"):
284 aname = "_%s" % name 284 aname = "_%s" % name
285 if aname not in self.__dict__: 285 if aname not in self.__dict__:
286 setattr(self, aname, find_executable(name, self.paths)) 286 setattr(self, aname, shutil.which(name, path=self.paths))
287 if aname not in self.__dict__ or self.__dict__[aname] is None: 287 if aname not in self.__dict__ or self.__dict__[aname] is None:
288 raise WicError("Can't find executable '{}'".format(name)) 288 raise WicError("Can't find executable '{}'".format(name))
289 return self.__dict__[aname] 289 return self.__dict__[aname]
diff --git a/scripts/lib/wic/filemap.py b/scripts/lib/wic/filemap.py
index 4d9da28172..85b39d5d74 100644
--- a/scripts/lib/wic/filemap.py
+++ b/scripts/lib/wic/filemap.py
@@ -46,6 +46,13 @@ def get_block_size(file_obj):
46 bsize = stat.st_blksize 46 bsize = stat.st_blksize
47 else: 47 else:
48 raise IOError("Unable to determine block size") 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
49 return bsize 56 return bsize
50 57
51class ErrorNotSupp(Exception): 58class ErrorNotSupp(Exception):
diff --git a/scripts/lib/wic/help.py b/scripts/lib/wic/help.py
index bd3a2b97df..163535e431 100644
--- a/scripts/lib/wic/help.py
+++ b/scripts/lib/wic/help.py
@@ -637,7 +637,7 @@ DESCRIPTION
637 oe-core: directdisk.bbclass and mkefidisk.sh. The difference 637 oe-core: directdisk.bbclass and mkefidisk.sh. The difference
638 between wic and those examples is that with wic the functionality 638 between wic and those examples is that with wic the functionality
639 of those scripts is implemented by a general-purpose partitioning 639 of those scripts is implemented by a general-purpose partitioning
640 'language' based on Redhat kickstart syntax). 640 'language' based on Red Hat kickstart syntax).
641 641
642 The initial motivation and design considerations that lead to the 642 The initial motivation and design considerations that lead to the
643 current tool are described exhaustively in Yocto Bug #3847 643 current tool are described exhaustively in Yocto Bug #3847
@@ -840,8 +840,8 @@ DESCRIPTION
840 meanings. The commands are based on the Fedora kickstart 840 meanings. The commands are based on the Fedora kickstart
841 documentation but with modifications to reflect wic capabilities. 841 documentation but with modifications to reflect wic capabilities.
842 842
843 http://fedoraproject.org/wiki/Anaconda/Kickstart#part_or_partition 843 https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#part-or-partition
844 http://fedoraproject.org/wiki/Anaconda/Kickstart#bootloader 844 https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#bootloader
845 845
846 Commands 846 Commands
847 847
@@ -930,6 +930,7 @@ DESCRIPTION
930 ext4 930 ext4
931 btrfs 931 btrfs
932 squashfs 932 squashfs
933 erofs
933 swap 934 swap
934 935
935 --fsoptions: Specifies a free-form string of options to be 936 --fsoptions: Specifies a free-form string of options to be
@@ -939,6 +940,12 @@ DESCRIPTION
939 quotes. If not specified, the default string is 940 quotes. If not specified, the default string is
940 "defaults". 941 "defaults".
941 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
942 --label label: Specifies the label to give to the filesystem 949 --label label: Specifies the label to give to the filesystem
943 to be made on the partition. If the given 950 to be made on the partition. If the given
944 label is already in use by another filesystem, 951 label is already in use by another filesystem,
@@ -990,6 +997,9 @@ DESCRIPTION
990 multiple partitions and we want to keep the right 997 multiple partitions and we want to keep the right
991 permissions and usernames in all the partitions. 998 permissions and usernames in all the partitions.
992 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
993 --extra-space: This option is specific to wic. It adds extra 1003 --extra-space: This option is specific to wic. It adds extra
994 space after the space filled by the content 1004 space after the space filled by the content
995 of the partition. The final size can go 1005 of the partition. The final size can go
@@ -1108,7 +1118,7 @@ COMMAND:
1108TOPIC: 1118TOPIC:
1109 overview - Presents an overall overview of Wic 1119 overview - Presents an overall overview of Wic
1110 plugins - Presents an overview and API for Wic plugins 1120 plugins - Presents an overview and API for Wic plugins
1111 kickstart - Presents a Wic kicstart file reference 1121 kickstart - Presents a Wic kickstart file reference
1112 1122
1113 1123
1114Examples: 1124Examples:
diff --git a/scripts/lib/wic/ksparser.py b/scripts/lib/wic/ksparser.py
index 3eb669da39..7ef3dc83dd 100644
--- a/scripts/lib/wic/ksparser.py
+++ b/scripts/lib/wic/ksparser.py
@@ -155,9 +155,11 @@ class KickStart():
155 part.add_argument('--change-directory') 155 part.add_argument('--change-directory')
156 part.add_argument("--extra-space", type=sizetype("M")) 156 part.add_argument("--extra-space", type=sizetype("M"))
157 part.add_argument('--fsoptions', dest='fsopts') 157 part.add_argument('--fsoptions', dest='fsopts')
158 part.add_argument('--fspassno', dest='fspassno')
158 part.add_argument('--fstype', default='vfat', 159 part.add_argument('--fstype', default='vfat',
159 choices=('ext2', 'ext3', 'ext4', 'btrfs', 160 choices=('ext2', 'ext3', 'ext4', 'btrfs',
160 'squashfs', 'vfat', 'msdos', 'swap')) 161 'squashfs', 'vfat', 'msdos', 'erofs',
162 'swap', 'none'))
161 part.add_argument('--mkfs-extraopts', default='') 163 part.add_argument('--mkfs-extraopts', default='')
162 part.add_argument('--label') 164 part.add_argument('--label')
163 part.add_argument('--use-label', action='store_true') 165 part.add_argument('--use-label', action='store_true')
@@ -169,6 +171,7 @@ class KickStart():
169 part.add_argument('--rootfs-dir') 171 part.add_argument('--rootfs-dir')
170 part.add_argument('--type', default='primary', 172 part.add_argument('--type', default='primary',
171 choices = ('primary', 'logical')) 173 choices = ('primary', 'logical'))
174 part.add_argument('--hidden', action='store_true')
172 175
173 # --size and --fixed-size cannot be specified together; options 176 # --size and --fixed-size cannot be specified together; options
174 # ----extra-space and --overhead-factor should also raise a parser 177 # ----extra-space and --overhead-factor should also raise a parser
@@ -184,11 +187,13 @@ class KickStart():
184 part.add_argument('--use-uuid', action='store_true') 187 part.add_argument('--use-uuid', action='store_true')
185 part.add_argument('--uuid') 188 part.add_argument('--uuid')
186 part.add_argument('--fsuuid') 189 part.add_argument('--fsuuid')
190 part.add_argument('--no-fstab-update', action='store_true')
191 part.add_argument('--mbr', action='store_true')
187 192
188 bootloader = subparsers.add_parser('bootloader') 193 bootloader = subparsers.add_parser('bootloader')
189 bootloader.add_argument('--append') 194 bootloader.add_argument('--append')
190 bootloader.add_argument('--configfile') 195 bootloader.add_argument('--configfile')
191 bootloader.add_argument('--ptable', choices=('msdos', 'gpt'), 196 bootloader.add_argument('--ptable', choices=('msdos', 'gpt', 'gpt-hybrid'),
192 default='msdos') 197 default='msdos')
193 bootloader.add_argument('--timeout', type=int) 198 bootloader.add_argument('--timeout', type=int)
194 bootloader.add_argument('--source') 199 bootloader.add_argument('--source')
@@ -229,6 +234,10 @@ class KickStart():
229 err = "%s:%d: SquashFS does not support LABEL" \ 234 err = "%s:%d: SquashFS does not support LABEL" \
230 % (confpath, lineno) 235 % (confpath, lineno)
231 raise KickStartError(err) 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)
232 if parsed.fstype == 'msdos' or parsed.fstype == 'vfat': 241 if parsed.fstype == 'msdos' or parsed.fstype == 'vfat':
233 if parsed.fsuuid: 242 if parsed.fsuuid:
234 if parsed.fsuuid.upper().startswith('0X'): 243 if parsed.fsuuid.upper().startswith('0X'):
diff --git a/scripts/lib/wic/misc.py b/scripts/lib/wic/misc.py
index 57c042c503..1a7c140fa6 100644
--- a/scripts/lib/wic/misc.py
+++ b/scripts/lib/wic/misc.py
@@ -16,16 +16,16 @@ import logging
16import os 16import os
17import re 17import re
18import subprocess 18import subprocess
19import shutil
19 20
20from collections import defaultdict 21from collections import defaultdict
21from distutils import spawn
22 22
23from wic import WicError 23from wic import WicError
24 24
25logger = logging.getLogger('wic') 25logger = logging.getLogger('wic')
26 26
27# executable -> recipe pairs for exec_native_cmd 27# executable -> recipe pairs for exec_native_cmd
28NATIVE_RECIPES = {"bmaptool": "bmap-tools", 28NATIVE_RECIPES = {"bmaptool": "bmaptool",
29 "dumpe2fs": "e2fsprogs", 29 "dumpe2fs": "e2fsprogs",
30 "grub-mkimage": "grub-efi", 30 "grub-mkimage": "grub-efi",
31 "isohybrid": "syslinux", 31 "isohybrid": "syslinux",
@@ -36,6 +36,7 @@ NATIVE_RECIPES = {"bmaptool": "bmap-tools",
36 "mkdosfs": "dosfstools", 36 "mkdosfs": "dosfstools",
37 "mkisofs": "cdrtools", 37 "mkisofs": "cdrtools",
38 "mkfs.btrfs": "btrfs-tools", 38 "mkfs.btrfs": "btrfs-tools",
39 "mkfs.erofs": "erofs-utils",
39 "mkfs.ext2": "e2fsprogs", 40 "mkfs.ext2": "e2fsprogs",
40 "mkfs.ext3": "e2fsprogs", 41 "mkfs.ext3": "e2fsprogs",
41 "mkfs.ext4": "e2fsprogs", 42 "mkfs.ext4": "e2fsprogs",
@@ -122,7 +123,7 @@ def find_executable(cmd, paths):
122 if provided and "%s-native" % recipe in provided: 123 if provided and "%s-native" % recipe in provided:
123 return True 124 return True
124 125
125 return spawn.find_executable(cmd, paths) 126 return shutil.which(cmd, path=paths)
126 127
127def exec_native_cmd(cmd_and_args, native_sysroot, pseudo=""): 128def exec_native_cmd(cmd_and_args, native_sysroot, pseudo=""):
128 """ 129 """
@@ -140,11 +141,12 @@ def exec_native_cmd(cmd_and_args, native_sysroot, pseudo=""):
140 cmd_and_args = pseudo + cmd_and_args 141 cmd_and_args = pseudo + cmd_and_args
141 142
142 hosttools_dir = get_bitbake_var("HOSTTOOLS_DIR") 143 hosttools_dir = get_bitbake_var("HOSTTOOLS_DIR")
144 target_sys = get_bitbake_var("TARGET_SYS")
143 145
144 native_paths = "%s/sbin:%s/usr/sbin:%s/usr/bin:%s/bin:%s" % \ 146 native_paths = "%s/sbin:%s/usr/sbin:%s/usr/bin:%s/usr/bin/%s:%s/bin:%s" % \
145 (native_sysroot, native_sysroot, 147 (native_sysroot, native_sysroot,
146 native_sysroot, native_sysroot, 148 native_sysroot, native_sysroot, target_sys,
147 hosttools_dir) 149 native_sysroot, hosttools_dir)
148 150
149 native_cmd_and_args = "export PATH=%s:$PATH;%s" % \ 151 native_cmd_and_args = "export PATH=%s:$PATH;%s" % \
150 (native_paths, cmd_and_args) 152 (native_paths, cmd_and_args)
diff --git a/scripts/lib/wic/partition.py b/scripts/lib/wic/partition.py
index 76d144d12d..795707ec5d 100644
--- a/scripts/lib/wic/partition.py
+++ b/scripts/lib/wic/partition.py
@@ -33,6 +33,7 @@ class Partition():
33 self.include_path = args.include_path 33 self.include_path = args.include_path
34 self.change_directory = args.change_directory 34 self.change_directory = args.change_directory
35 self.fsopts = args.fsopts 35 self.fsopts = args.fsopts
36 self.fspassno = args.fspassno
36 self.fstype = args.fstype 37 self.fstype = args.fstype
37 self.label = args.label 38 self.label = args.label
38 self.use_label = args.use_label 39 self.use_label = args.use_label
@@ -54,9 +55,12 @@ class Partition():
54 self.uuid = args.uuid 55 self.uuid = args.uuid
55 self.fsuuid = args.fsuuid 56 self.fsuuid = args.fsuuid
56 self.type = args.type 57 self.type = args.type
58 self.no_fstab_update = args.no_fstab_update
57 self.updated_fstab_path = None 59 self.updated_fstab_path = None
58 self.has_fstab = False 60 self.has_fstab = False
59 self.update_fstab_in_rootfs = False 61 self.update_fstab_in_rootfs = False
62 self.hidden = args.hidden
63 self.mbr = args.mbr
60 64
61 self.lineno = lineno 65 self.lineno = lineno
62 self.source_file = "" 66 self.source_file = ""
@@ -104,7 +108,7 @@ class Partition():
104 extra_blocks = self.extra_space 108 extra_blocks = self.extra_space
105 109
106 rootfs_size = actual_rootfs_size + extra_blocks 110 rootfs_size = actual_rootfs_size + extra_blocks
107 rootfs_size *= self.overhead_factor 111 rootfs_size = int(rootfs_size * self.overhead_factor)
108 112
109 logger.debug("Added %d extra blocks to %s to get to %d total blocks", 113 logger.debug("Added %d extra blocks to %s to get to %d total blocks",
110 extra_blocks, self.mountpoint, rootfs_size) 114 extra_blocks, self.mountpoint, rootfs_size)
@@ -131,6 +135,8 @@ class Partition():
131 self.update_fstab_in_rootfs = True 135 self.update_fstab_in_rootfs = True
132 136
133 if not self.source: 137 if not self.source:
138 if self.fstype == "none" or self.no_table:
139 return
134 if not self.size and not self.fixed_size: 140 if not self.size and not self.fixed_size:
135 raise WicError("The %s partition has a size of zero. Please " 141 raise WicError("The %s partition has a size of zero. Please "
136 "specify a non-zero --size/--fixed-size for that " 142 "specify a non-zero --size/--fixed-size for that "
@@ -141,9 +147,9 @@ class Partition():
141 native_sysroot) 147 native_sysroot)
142 self.source_file = "%s/fs.%s" % (cr_workdir, self.fstype) 148 self.source_file = "%s/fs.%s" % (cr_workdir, self.fstype)
143 else: 149 else:
144 if self.fstype == 'squashfs': 150 if self.fstype in ('squashfs', 'erofs'):
145 raise WicError("It's not possible to create empty squashfs " 151 raise WicError("It's not possible to create empty %s "
146 "partition '%s'" % (self.mountpoint)) 152 "partition '%s'" % (self.fstype, self.mountpoint))
147 153
148 rootfs = "%s/fs_%s.%s.%s" % (cr_workdir, self.label, 154 rootfs = "%s/fs_%s.%s.%s" % (cr_workdir, self.label,
149 self.lineno, self.fstype) 155 self.lineno, self.fstype)
@@ -170,7 +176,7 @@ class Partition():
170 # Split sourceparams string of the form key1=val1[,key2=val2,...] 176 # Split sourceparams string of the form key1=val1[,key2=val2,...]
171 # into a dict. Also accepts valueless keys i.e. without = 177 # into a dict. Also accepts valueless keys i.e. without =
172 splitted = self.sourceparams.split(',') 178 splitted = self.sourceparams.split(',')
173 srcparams_dict = dict(par.split('=', 1) for par in splitted if par) 179 srcparams_dict = dict((par.split('=', 1) + [None])[:2] for par in splitted if par)
174 180
175 plugin = PluginMgr.get_plugins('source')[self.source] 181 plugin = PluginMgr.get_plugins('source')[self.source]
176 plugin.do_configure_partition(self, srcparams_dict, creator, 182 plugin.do_configure_partition(self, srcparams_dict, creator,
@@ -278,6 +284,20 @@ class Partition():
278 284
279 extraopts = self.mkfs_extraopts or "-F -i 8192" 285 extraopts = self.mkfs_extraopts or "-F -i 8192"
280 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
281 label_str = "" 301 label_str = ""
282 if self.label: 302 if self.label:
283 label_str = "-L %s" % self.label 303 label_str = "-L %s" % self.label
@@ -286,7 +306,7 @@ class Partition():
286 (self.fstype, extraopts, rootfs, label_str, self.fsuuid, rootfs_dir) 306 (self.fstype, extraopts, rootfs, label_str, self.fsuuid, rootfs_dir)
287 exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo) 307 exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo)
288 308
289 if self.updated_fstab_path and self.has_fstab: 309 if self.updated_fstab_path and self.has_fstab and not self.no_fstab_update:
290 debugfs_script_path = os.path.join(cr_workdir, "debugfs_script") 310 debugfs_script_path = os.path.join(cr_workdir, "debugfs_script")
291 with open(debugfs_script_path, "w") as f: 311 with open(debugfs_script_path, "w") as f:
292 f.write("cd etc\n") 312 f.write("cd etc\n")
@@ -298,6 +318,30 @@ class Partition():
298 mkfs_cmd = "fsck.%s -pvfD %s" % (self.fstype, rootfs) 318 mkfs_cmd = "fsck.%s -pvfD %s" % (self.fstype, rootfs)
299 exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo) 319 exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo)
300 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
301 self.check_for_Y2038_problem(rootfs, native_sysroot) 345 self.check_for_Y2038_problem(rootfs, native_sysroot)
302 346
303 def prepare_rootfs_btrfs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir, 347 def prepare_rootfs_btrfs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir,
@@ -350,8 +394,8 @@ class Partition():
350 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (rootfs, rootfs_dir) 394 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (rootfs, rootfs_dir)
351 exec_native_cmd(mcopy_cmd, native_sysroot) 395 exec_native_cmd(mcopy_cmd, native_sysroot)
352 396
353 if self.updated_fstab_path and self.has_fstab: 397 if self.updated_fstab_path and self.has_fstab and not self.no_fstab_update:
354 mcopy_cmd = "mcopy -i %s %s ::/etc/fstab" % (rootfs, self.updated_fstab_path) 398 mcopy_cmd = "mcopy -m -i %s %s ::/etc/fstab" % (rootfs, self.updated_fstab_path)
355 exec_native_cmd(mcopy_cmd, native_sysroot) 399 exec_native_cmd(mcopy_cmd, native_sysroot)
356 400
357 chmod_cmd = "chmod 644 %s" % rootfs 401 chmod_cmd = "chmod 644 %s" % rootfs
@@ -369,6 +413,19 @@ class Partition():
369 (rootfs_dir, rootfs, extraopts) 413 (rootfs_dir, rootfs, extraopts)
370 exec_native_cmd(squashfs_cmd, native_sysroot, pseudo=pseudo) 414 exec_native_cmd(squashfs_cmd, native_sysroot, pseudo=pseudo)
371 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
372 def prepare_empty_partition_ext(self, rootfs, oe_builddir, 429 def prepare_empty_partition_ext(self, rootfs, oe_builddir,
373 native_sysroot): 430 native_sysroot):
374 """ 431 """
diff --git a/scripts/lib/wic/pluginbase.py b/scripts/lib/wic/pluginbase.py
index d9b4e57747..b64568339b 100644
--- a/scripts/lib/wic/pluginbase.py
+++ b/scripts/lib/wic/pluginbase.py
@@ -9,9 +9,11 @@ __all__ = ['ImagerPlugin', 'SourcePlugin']
9 9
10import os 10import os
11import logging 11import logging
12import types
12 13
13from collections import defaultdict 14from collections import defaultdict
14from importlib.machinery import SourceFileLoader 15import importlib
16import importlib.util
15 17
16from wic import WicError 18from wic import WicError
17from wic.misc import get_bitbake_var 19from wic.misc import get_bitbake_var
@@ -54,7 +56,9 @@ class PluginMgr:
54 mname = fname[:-3] 56 mname = fname[:-3]
55 mpath = os.path.join(ppath, fname) 57 mpath = os.path.join(ppath, fname)
56 logger.debug("loading plugin module %s", mpath) 58 logger.debug("loading plugin module %s", mpath)
57 SourceFileLoader(mname, mpath).load_module() 59 spec = importlib.util.spec_from_file_location(mname, mpath)
60 module = importlib.util.module_from_spec(spec)
61 spec.loader.exec_module(module)
58 62
59 return PLUGINS.get(ptype) 63 return PLUGINS.get(ptype)
60 64
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
index ea709e8c54..a1d152659b 100644
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -77,7 +77,8 @@ class DirectPlugin(ImagerPlugin):
77 77
78 image_path = self._full_path(self.workdir, self.parts[0].disk, "direct") 78 image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
79 self._image = PartitionedImage(image_path, self.ptable_format, 79 self._image = PartitionedImage(image_path, self.ptable_format,
80 self.parts, self.native_sysroot) 80 self.parts, self.native_sysroot,
81 options.extra_space)
81 82
82 def setup_workdir(self, workdir): 83 def setup_workdir(self, workdir):
83 if workdir: 84 if workdir:
@@ -116,7 +117,7 @@ class DirectPlugin(ImagerPlugin):
116 updated = False 117 updated = False
117 for part in self.parts: 118 for part in self.parts:
118 if not part.realnum or not part.mountpoint \ 119 if not part.realnum or not part.mountpoint \
119 or part.mountpoint == "/": 120 or part.mountpoint == "/" or not (part.mountpoint.startswith('/') or part.mountpoint == "swap"):
120 continue 121 continue
121 122
122 if part.use_uuid: 123 if part.use_uuid:
@@ -137,8 +138,9 @@ class DirectPlugin(ImagerPlugin):
137 device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum) 138 device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum)
138 139
139 opts = part.fsopts if part.fsopts else "defaults" 140 opts = part.fsopts if part.fsopts else "defaults"
141 passno = part.fspassno if part.fspassno else "0"
140 line = "\t".join([device_name, part.mountpoint, part.fstype, 142 line = "\t".join([device_name, part.mountpoint, part.fstype,
141 opts, "0", "0"]) + "\n" 143 opts, "0", passno]) + "\n"
142 144
143 fstab_lines.append(line) 145 fstab_lines.append(line)
144 updated = True 146 updated = True
@@ -147,6 +149,9 @@ class DirectPlugin(ImagerPlugin):
147 self.updated_fstab_path = os.path.join(self.workdir, "fstab") 149 self.updated_fstab_path = os.path.join(self.workdir, "fstab")
148 with open(self.updated_fstab_path, "w") as f: 150 with open(self.updated_fstab_path, "w") as f:
149 f.writelines(fstab_lines) 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))
150 155
151 def _full_path(self, path, name, extention): 156 def _full_path(self, path, name, extention):
152 """ Construct full file path to a file we generate. """ 157 """ Construct full file path to a file we generate. """
@@ -258,6 +263,8 @@ class DirectPlugin(ImagerPlugin):
258 if part.mountpoint == "/": 263 if part.mountpoint == "/":
259 if part.uuid: 264 if part.uuid:
260 return "PARTUUID=%s" % part.uuid 265 return "PARTUUID=%s" % part.uuid
266 elif part.label and self.ptable_format != 'msdos':
267 return "PARTLABEL=%s" % part.label
261 else: 268 else:
262 suffix = 'p' if part.disk.startswith('mmcblk') else '' 269 suffix = 'p' if part.disk.startswith('mmcblk') else ''
263 return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum) 270 return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum)
@@ -293,7 +300,7 @@ class PartitionedImage():
293 Partitioned image in a file. 300 Partitioned image in a file.
294 """ 301 """
295 302
296 def __init__(self, path, ptable_format, partitions, native_sysroot=None): 303 def __init__(self, path, ptable_format, partitions, native_sysroot=None, extra_space=0):
297 self.path = path # Path to the image file 304 self.path = path # Path to the image file
298 self.numpart = 0 # Number of allocated partitions 305 self.numpart = 0 # Number of allocated partitions
299 self.realpart = 0 # Number of partitions in the partition table 306 self.realpart = 0 # Number of partitions in the partition table
@@ -306,7 +313,10 @@ class PartitionedImage():
306 # all partitions (in bytes) 313 # all partitions (in bytes)
307 self.ptable_format = ptable_format # Partition table format 314 self.ptable_format = ptable_format # Partition table format
308 # Disk system identifier 315 # Disk system identifier
309 self.identifier = random.SystemRandom().randint(1, 0xffffffff) 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)
310 320
311 self.partitions = partitions 321 self.partitions = partitions
312 self.partimages = [] 322 self.partimages = []
@@ -314,6 +324,7 @@ class PartitionedImage():
314 self.sector_size = SECTOR_SIZE 324 self.sector_size = SECTOR_SIZE
315 self.native_sysroot = native_sysroot 325 self.native_sysroot = native_sysroot
316 num_real_partitions = len([p for p in self.partitions if not p.no_table]) 326 num_real_partitions = len([p for p in self.partitions if not p.no_table])
327 self.extra_space = extra_space
317 328
318 # calculate the real partition number, accounting for partitions not 329 # calculate the real partition number, accounting for partitions not
319 # in the partition table and logical partitions 330 # in the partition table and logical partitions
@@ -331,7 +342,7 @@ class PartitionedImage():
331 # generate parition and filesystem UUIDs 342 # generate parition and filesystem UUIDs
332 for part in self.partitions: 343 for part in self.partitions:
333 if not part.uuid and part.use_uuid: 344 if not part.uuid and part.use_uuid:
334 if self.ptable_format == 'gpt': 345 if self.ptable_format in ('gpt', 'gpt-hybrid'):
335 part.uuid = str(uuid.uuid4()) 346 part.uuid = str(uuid.uuid4())
336 else: # msdos partition table 347 else: # msdos partition table
337 part.uuid = '%08x-%02d' % (self.identifier, part.realnum) 348 part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
@@ -387,6 +398,10 @@ class PartitionedImage():
387 raise WicError("setting custom partition type is not " \ 398 raise WicError("setting custom partition type is not " \
388 "implemented for msdos partitions") 399 "implemented for msdos partitions")
389 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
390 # Get the disk where the partition is located 405 # Get the disk where the partition is located
391 self.numpart += 1 406 self.numpart += 1
392 if not part.no_table: 407 if not part.no_table:
@@ -395,7 +410,7 @@ class PartitionedImage():
395 if self.numpart == 1: 410 if self.numpart == 1:
396 if self.ptable_format == "msdos": 411 if self.ptable_format == "msdos":
397 overhead = MBR_OVERHEAD 412 overhead = MBR_OVERHEAD
398 elif self.ptable_format == "gpt": 413 elif self.ptable_format in ("gpt", "gpt-hybrid"):
399 overhead = GPT_OVERHEAD 414 overhead = GPT_OVERHEAD
400 415
401 # Skip one sector required for the partitioning scheme overhead 416 # Skip one sector required for the partitioning scheme overhead
@@ -479,10 +494,11 @@ class PartitionedImage():
479 # Once all the partitions have been layed out, we can calculate the 494 # Once all the partitions have been layed out, we can calculate the
480 # minumim disk size 495 # minumim disk size
481 self.min_size = self.offset 496 self.min_size = self.offset
482 if self.ptable_format == "gpt": 497 if self.ptable_format in ("gpt", "gpt-hybrid"):
483 self.min_size += GPT_OVERHEAD 498 self.min_size += GPT_OVERHEAD
484 499
485 self.min_size *= self.sector_size 500 self.min_size *= self.sector_size
501 self.min_size += self.extra_space
486 502
487 def _create_partition(self, device, parttype, fstype, start, size): 503 def _create_partition(self, device, parttype, fstype, start, size):
488 """ Create a partition on an image described by the 'device' object. """ 504 """ Create a partition on an image described by the 'device' object. """
@@ -499,22 +515,49 @@ class PartitionedImage():
499 515
500 return exec_native_cmd(cmd, self.native_sysroot) 516 return exec_native_cmd(cmd, self.native_sysroot)
501 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
502 def create(self): 544 def create(self):
503 logger.debug("Creating sparse file %s", self.path) 545 self._make_disk(self.path,
504 with open(self.path, 'w') as sparse: 546 "gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format,
505 os.ftruncate(sparse.fileno(), self.min_size) 547 self.min_size)
506 548
507 logger.debug("Initializing partition table for %s", self.path) 549 self._write_identifier(self.path, self.identifier)
508 exec_native_cmd("parted -s %s mklabel %s" % 550 self._write_disk_guid()
509 (self.path, self.ptable_format), self.native_sysroot)
510 551
511 logger.debug("Set disk identifier %x", self.identifier) 552 if self.ptable_format == "gpt-hybrid":
512 with open(self.path, 'r+b') as img: 553 mbr_path = self.path + ".mbr"
513 img.seek(0x1B8) 554 self._make_disk(mbr_path, "msdos", self.min_size)
514 img.write(self.identifier.to_bytes(4, 'little')) 555 self._write_identifier(mbr_path, self.identifier)
515 556
516 logger.debug("Creating partitions") 557 logger.debug("Creating partitions")
517 558
559 hybrid_mbr_part_num = 0
560
518 for part in self.partitions: 561 for part in self.partitions:
519 if part.num == 0: 562 if part.num == 0:
520 continue 563 continue
@@ -559,11 +602,19 @@ class PartitionedImage():
559 self._create_partition(self.path, part.type, 602 self._create_partition(self.path, part.type,
560 parted_fs_type, part.start, part.size_sec) 603 parted_fs_type, part.start, part.size_sec)
561 604
562 if part.part_name: 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
563 logger.debug("partition %d: set name to %s", 614 logger.debug("partition %d: set name to %s",
564 part.num, part.part_name) 615 part.num, partition_label)
565 exec_native_cmd("sgdisk --change-name=%d:%s %s" % \ 616 exec_native_cmd("sgdisk --change-name=%d:%s %s" % \
566 (part.num, part.part_name, 617 (part.num, partition_label,
567 self.path), self.native_sysroot) 618 self.path), self.native_sysroot)
568 619
569 if part.part_type: 620 if part.part_type:
@@ -573,32 +624,55 @@ class PartitionedImage():
573 (part.num, part.part_type, 624 (part.num, part.part_type,
574 self.path), self.native_sysroot) 625 self.path), self.native_sysroot)
575 626
576 if part.uuid and self.ptable_format == "gpt": 627 if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"):
577 logger.debug("partition %d: set UUID to %s", 628 logger.debug("partition %d: set UUID to %s",
578 part.num, part.uuid) 629 part.num, part.uuid)
579 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ 630 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
580 (part.num, part.uuid, self.path), 631 (part.num, part.uuid, self.path),
581 self.native_sysroot) 632 self.native_sysroot)
582 633
583 if part.label and self.ptable_format == "gpt":
584 logger.debug("partition %d: set name to %s",
585 part.num, part.label)
586 exec_native_cmd("parted -s %s name %d %s" % \
587 (self.path, part.num, part.label),
588 self.native_sysroot)
589
590 if part.active: 634 if part.active:
591 flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot" 635 flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot"
592 logger.debug("Set '%s' flag for partition '%s' on disk '%s'", 636 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
593 flag_name, part.num, self.path) 637 flag_name, part.num, self.path)
594 exec_native_cmd("parted -s %s set %d %s on" % \ 638 exec_native_cmd("parted -s %s set %d %s on" % \
595 (self.path, part.num, flag_name), 639 (self.path, part.num, flag_name),
596 self.native_sysroot) 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)
597 if part.system_id: 645 if part.system_id:
598 exec_native_cmd("sfdisk --part-type %s %s %s" % \ 646 exec_native_cmd("sfdisk --part-type %s %s %s" % \
599 (self.path, part.num, part.system_id), 647 (self.path, part.num, part.system_id),
600 self.native_sysroot) 648 self.native_sysroot)
601 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
602 def cleanup(self): 676 def cleanup(self):
603 pass 677 pass
604 678
diff --git a/scripts/lib/wic/plugins/source/bootimg-efi.py b/scripts/lib/wic/plugins/source/bootimg-efi.py
index cdc72543c2..13a9cddf4e 100644
--- a/scripts/lib/wic/plugins/source/bootimg-efi.py
+++ b/scripts/lib/wic/plugins/source/bootimg-efi.py
@@ -12,6 +12,7 @@
12 12
13import logging 13import logging
14import os 14import os
15import tempfile
15import shutil 16import shutil
16import re 17import re
17 18
@@ -34,6 +35,26 @@ class BootimgEFIPlugin(SourcePlugin):
34 name = 'bootimg-efi' 35 name = 'bootimg-efi'
35 36
36 @classmethod 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
37 def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params): 58 def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params):
38 """ 59 """
39 Create loader-specific (grub-efi) config 60 Create loader-specific (grub-efi) config
@@ -52,18 +73,9 @@ class BootimgEFIPlugin(SourcePlugin):
52 "get it from %s." % configfile) 73 "get it from %s." % configfile)
53 74
54 initrd = source_params.get('initrd') 75 initrd = source_params.get('initrd')
76 dtb = source_params.get('dtb')
55 77
56 if initrd: 78 cls._copy_additional_files(hdddir, initrd, dtb)
57 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
58 if not bootimg_dir:
59 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
60
61 initrds = initrd.split(';')
62 for rd in initrds:
63 cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir)
64 exec_cmd(cp_cmd, True)
65 else:
66 logger.debug("Ignoring missing initrd")
67 79
68 if not custom_cfg: 80 if not custom_cfg:
69 # Create grub configuration using parameters from wks file 81 # Create grub configuration using parameters from wks file
@@ -97,6 +109,9 @@ class BootimgEFIPlugin(SourcePlugin):
97 grubefi_conf += " /%s" % rd 109 grubefi_conf += " /%s" % rd
98 grubefi_conf += "\n" 110 grubefi_conf += "\n"
99 111
112 if dtb:
113 grubefi_conf += "devicetree /%s\n" % dtb
114
100 grubefi_conf += "}\n" 115 grubefi_conf += "}\n"
101 116
102 logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg", 117 logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg",
@@ -118,24 +133,18 @@ class BootimgEFIPlugin(SourcePlugin):
118 133
119 bootloader = creator.ks.bootloader 134 bootloader = creator.ks.bootloader
120 135
136 unified_image = source_params.get('create-unified-kernel-image') == "true"
137
121 loader_conf = "" 138 loader_conf = ""
122 loader_conf += "default boot\n" 139 if not unified_image:
140 loader_conf += "default boot\n"
123 loader_conf += "timeout %d\n" % bootloader.timeout 141 loader_conf += "timeout %d\n" % bootloader.timeout
124 142
125 initrd = source_params.get('initrd') 143 initrd = source_params.get('initrd')
144 dtb = source_params.get('dtb')
126 145
127 if initrd: 146 if not unified_image:
128 # obviously we need to have a common common deploy var 147 cls._copy_additional_files(hdddir, initrd, dtb)
129 bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
130 if not bootimg_dir:
131 raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
132
133 initrds = initrd.split(';')
134 for rd in initrds:
135 cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir)
136 exec_cmd(cp_cmd, True)
137 else:
138 logger.debug("Ignoring missing initrd")
139 148
140 logger.debug("Writing systemd-boot config " 149 logger.debug("Writing systemd-boot config "
141 "%s/hdd/boot/loader/loader.conf", cr_workdir) 150 "%s/hdd/boot/loader/loader.conf", cr_workdir)
@@ -183,11 +192,15 @@ class BootimgEFIPlugin(SourcePlugin):
183 for rd in initrds: 192 for rd in initrds:
184 boot_conf += "initrd /%s\n" % rd 193 boot_conf += "initrd /%s\n" % rd
185 194
186 logger.debug("Writing systemd-boot config " 195 if dtb:
187 "%s/hdd/boot/loader/entries/boot.conf", cr_workdir) 196 boot_conf += "devicetree /%s\n" % dtb
188 cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w") 197
189 cfg.write(boot_conf) 198 if not unified_image:
190 cfg.close() 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()
191 204
192 205
193 @classmethod 206 @classmethod
@@ -207,6 +220,8 @@ class BootimgEFIPlugin(SourcePlugin):
207 cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params) 220 cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params)
208 elif source_params['loader'] == 'systemd-boot': 221 elif source_params['loader'] == 'systemd-boot':
209 cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params) 222 cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params)
223 elif source_params['loader'] == 'uefi-kernel':
224 pass
210 else: 225 else:
211 raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader']) 226 raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader'])
212 except KeyError: 227 except KeyError:
@@ -288,9 +303,107 @@ class BootimgEFIPlugin(SourcePlugin):
288 kernel = "%s-%s.bin" % \ 303 kernel = "%s-%s.bin" % \
289 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) 304 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
290 305
291 install_cmd = "install -m 0644 %s/%s %s/%s" % \ 306 if source_params.get('create-unified-kernel-image') == "true":
292 (staging_kernel_dir, kernel, hdddir, kernel) 307 initrd = source_params.get('initrd')
293 exec_cmd(install_cmd) 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)
294 407
295 if get_bitbake_var("IMAGE_EFI_BOOT_FILES"): 408 if get_bitbake_var("IMAGE_EFI_BOOT_FILES"):
296 for src_path, dst_path in cls.install_task: 409 for src_path, dst_path in cls.install_task:
@@ -312,6 +425,28 @@ class BootimgEFIPlugin(SourcePlugin):
312 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]: 425 for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]:
313 cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:]) 426 cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:])
314 exec_cmd(cp_cmd, True) 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)
315 else: 450 else:
316 raise WicError("unrecognized bootimg-efi loader: %s" % 451 raise WicError("unrecognized bootimg-efi loader: %s" %
317 source_params['loader']) 452 source_params['loader'])
@@ -323,6 +458,11 @@ class BootimgEFIPlugin(SourcePlugin):
323 cp_cmd = "cp %s %s/" % (startup, hdddir) 458 cp_cmd = "cp %s %s/" % (startup, hdddir)
324 exec_cmd(cp_cmd, True) 459 exec_cmd(cp_cmd, True)
325 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
326 du_cmd = "du -bks %s" % hdddir 466 du_cmd = "du -bks %s" % hdddir
327 out = exec_cmd(du_cmd) 467 out = exec_cmd(du_cmd)
328 blocks = int(out.split()[0]) 468 blocks = int(out.split()[0])
@@ -337,6 +477,13 @@ class BootimgEFIPlugin(SourcePlugin):
337 logger.debug("Added %d extra blocks to %s to get to %d total blocks", 477 logger.debug("Added %d extra blocks to %s to get to %d total blocks",
338 extra_blocks, part.mountpoint, blocks) 478 extra_blocks, part.mountpoint, blocks)
339 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
340 # dosfs image, created by mkdosfs 487 # dosfs image, created by mkdosfs
341 bootimg = "%s/boot.img" % cr_workdir 488 bootimg = "%s/boot.img" % cr_workdir
342 489
diff --git a/scripts/lib/wic/plugins/source/bootimg-partition.py b/scripts/lib/wic/plugins/source/bootimg-partition.py
index 5dbe2558d2..1071d1af3f 100644
--- a/scripts/lib/wic/plugins/source/bootimg-partition.py
+++ b/scripts/lib/wic/plugins/source/bootimg-partition.py
@@ -1,4 +1,6 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
3# 5#
4# DESCRIPTION 6# DESCRIPTION
@@ -30,6 +32,7 @@ class BootimgPartitionPlugin(SourcePlugin):
30 """ 32 """
31 33
32 name = 'bootimg-partition' 34 name = 'bootimg-partition'
35 image_boot_files_var_name = 'IMAGE_BOOT_FILES'
33 36
34 @classmethod 37 @classmethod
35 def do_configure_partition(cls, part, source_params, cr, cr_workdir, 38 def do_configure_partition(cls, part, source_params, cr, cr_workdir,
@@ -54,12 +57,12 @@ class BootimgPartitionPlugin(SourcePlugin):
54 else: 57 else:
55 var = "" 58 var = ""
56 59
57 boot_files = get_bitbake_var("IMAGE_BOOT_FILES" + var) 60 boot_files = get_bitbake_var(cls.image_boot_files_var_name + var)
58 if boot_files is not None: 61 if boot_files is not None:
59 break 62 break
60 63
61 if boot_files is None: 64 if boot_files is None:
62 raise WicError('No boot files defined, IMAGE_BOOT_FILES unset for entry #%d' % part.lineno) 65 raise WicError('No boot files defined, %s unset for entry #%d' % (cls.image_boot_files_var_name, part.lineno))
63 66
64 logger.debug('Boot files: %s', boot_files) 67 logger.debug('Boot files: %s', boot_files)
65 68
@@ -110,7 +113,7 @@ class BootimgPartitionPlugin(SourcePlugin):
110 # Use a custom configuration for extlinux.conf 113 # Use a custom configuration for extlinux.conf
111 extlinux_conf = custom_cfg 114 extlinux_conf = custom_cfg
112 logger.debug("Using custom configuration file " 115 logger.debug("Using custom configuration file "
113 "%s for extlinux.cfg", configfile) 116 "%s for extlinux.conf", configfile)
114 else: 117 else:
115 raise WicError("configfile is specified but failed to " 118 raise WicError("configfile is specified but failed to "
116 "get it from %s." % configfile) 119 "get it from %s." % configfile)
diff --git a/scripts/lib/wic/plugins/source/bootimg-pcbios.py b/scripts/lib/wic/plugins/source/bootimg-pcbios.py
index f2639e7004..a207a83530 100644
--- a/scripts/lib/wic/plugins/source/bootimg-pcbios.py
+++ b/scripts/lib/wic/plugins/source/bootimg-pcbios.py
@@ -122,7 +122,7 @@ class BootimgPcbiosPlugin(SourcePlugin):
122 syslinux_conf += "DEFAULT boot\n" 122 syslinux_conf += "DEFAULT boot\n"
123 syslinux_conf += "LABEL boot\n" 123 syslinux_conf += "LABEL boot\n"
124 124
125 kernel = "/vmlinuz" 125 kernel = "/" + get_bitbake_var("KERNEL_IMAGETYPE")
126 syslinux_conf += "KERNEL " + kernel + "\n" 126 syslinux_conf += "KERNEL " + kernel + "\n"
127 127
128 syslinux_conf += "APPEND label=boot root=%s %s\n" % \ 128 syslinux_conf += "APPEND label=boot root=%s %s\n" % \
@@ -155,8 +155,8 @@ class BootimgPcbiosPlugin(SourcePlugin):
155 kernel = "%s-%s.bin" % \ 155 kernel = "%s-%s.bin" % \
156 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME")) 156 (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
157 157
158 cmds = ("install -m 0644 %s/%s %s/vmlinuz" % 158 cmds = ("install -m 0644 %s/%s %s/%s" %
159 (staging_kernel_dir, kernel, hdddir), 159 (staging_kernel_dir, kernel, hdddir, get_bitbake_var("KERNEL_IMAGETYPE")),
160 "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" % 160 "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" %
161 (bootimg_dir, hdddir), 161 (bootimg_dir, hdddir),
162 "install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" % 162 "install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" %
@@ -186,8 +186,10 @@ class BootimgPcbiosPlugin(SourcePlugin):
186 # dosfs image, created by mkdosfs 186 # dosfs image, created by mkdosfs
187 bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno) 187 bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno)
188 188
189 dosfs_cmd = "mkdosfs -n boot -i %s -S 512 -C %s %d" % \ 189 label = part.label if part.label else "boot"
190 (part.fsuuid, bootimg, blocks) 190
191 dosfs_cmd = "mkdosfs -n %s -i %s -S 512 -C %s %d" % \
192 (label, part.fsuuid, bootimg, blocks)
191 exec_native_cmd(dosfs_cmd, native_sysroot) 193 exec_native_cmd(dosfs_cmd, native_sysroot)
192 194
193 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) 195 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
diff --git a/scripts/lib/wic/plugins/source/empty.py b/scripts/lib/wic/plugins/source/empty.py
index 041617d648..4178912377 100644
--- a/scripts/lib/wic/plugins/source/empty.py
+++ b/scripts/lib/wic/plugins/source/empty.py
@@ -1,4 +1,6 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: MIT 4# SPDX-License-Identifier: MIT
3# 5#
4 6
@@ -7,9 +9,19 @@
7# To use it you must pass "empty" as argument for the "--source" parameter in 9# To use it you must pass "empty" as argument for the "--source" parameter in
8# the wks file. For example: 10# the wks file. For example:
9# part foo --source empty --ondisk sda --size="1024" --align 1024 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.
10 19
11import logging 20import logging
21import os
12 22
23from wic import WicError
24from wic.ksparser import sizetype
13from wic.pluginbase import SourcePlugin 25from wic.pluginbase import SourcePlugin
14 26
15logger = logging.getLogger('wic') 27logger = logging.getLogger('wic')
@@ -17,6 +29,16 @@ logger = logging.getLogger('wic')
17class EmptyPartitionPlugin(SourcePlugin): 29class EmptyPartitionPlugin(SourcePlugin):
18 """ 30 """
19 Populate unformatted empty partition. 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'.
20 """ 42 """
21 43
22 name = 'empty' 44 name = 'empty'
@@ -29,4 +51,39 @@ class EmptyPartitionPlugin(SourcePlugin):
29 Called to do the actual content population for a partition i.e. it 51 Called to do the actual content population for a partition i.e. it
30 'prepares' the partition to be incorporated into the image. 52 'prepares' the partition to be incorporated into the image.
31 """ 53 """
32 return 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
index afc9ea0f8f..607356ad13 100644
--- a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
+++ b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
@@ -1,4 +1,6 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
3# 5#
4# DESCRIPTION 6# DESCRIPTION
diff --git a/scripts/lib/wic/plugins/source/rawcopy.py b/scripts/lib/wic/plugins/source/rawcopy.py
index 3c4997d8ba..21903c2f23 100644
--- a/scripts/lib/wic/plugins/source/rawcopy.py
+++ b/scripts/lib/wic/plugins/source/rawcopy.py
@@ -1,9 +1,13 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
3# 5#
4 6
5import logging 7import logging
6import os 8import os
9import signal
10import subprocess
7 11
8from wic import WicError 12from wic import WicError
9from wic.pluginbase import SourcePlugin 13from wic.pluginbase import SourcePlugin
@@ -21,6 +25,10 @@ class RawCopyPlugin(SourcePlugin):
21 25
22 @staticmethod 26 @staticmethod
23 def do_image_label(fstype, dst, label): 27 def do_image_label(fstype, dst, label):
28 # don't create label when fstype is none
29 if fstype == 'none':
30 return
31
24 if fstype.startswith('ext'): 32 if fstype.startswith('ext'):
25 cmd = 'tune2fs -L %s %s' % (label, dst) 33 cmd = 'tune2fs -L %s %s' % (label, dst)
26 elif fstype in ('msdos', 'vfat'): 34 elif fstype in ('msdos', 'vfat'):
@@ -29,15 +37,35 @@ class RawCopyPlugin(SourcePlugin):
29 cmd = 'btrfs filesystem label %s %s' % (dst, label) 37 cmd = 'btrfs filesystem label %s %s' % (dst, label)
30 elif fstype == 'swap': 38 elif fstype == 'swap':
31 cmd = 'mkswap -L %s %s' % (label, dst) 39 cmd = 'mkswap -L %s %s' % (label, dst)
32 elif fstype == 'squashfs': 40 elif fstype in ('squashfs', 'erofs'):
33 raise WicError("It's not possible to update a squashfs " 41 raise WicError("It's not possible to update a %s "
34 "filesystem label '%s'" % (label)) 42 "filesystem label '%s'" % (fstype, label))
35 else: 43 else:
36 raise WicError("Cannot update filesystem label: " 44 raise WicError("Cannot update filesystem label: "
37 "Unknown fstype: '%s'" % (fstype)) 45 "Unknown fstype: '%s'" % (fstype))
38 46
39 exec_cmd(cmd) 47 exec_cmd(cmd)
40 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
41 @classmethod 69 @classmethod
42 def do_prepare_partition(cls, part, source_params, cr, cr_workdir, 70 def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
43 oe_builddir, bootimg_dir, kernel_dir, 71 oe_builddir, bootimg_dir, kernel_dir,
@@ -56,7 +84,13 @@ class RawCopyPlugin(SourcePlugin):
56 if 'file' not in source_params: 84 if 'file' not in source_params:
57 raise WicError("No file specified") 85 raise WicError("No file specified")
58 86
59 src = os.path.join(kernel_dir, source_params['file']) 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
60 dst = os.path.join(cr_workdir, "%s.%s" % (os.path.basename(source_params['file']), part.lineno)) 94 dst = os.path.join(cr_workdir, "%s.%s" % (os.path.basename(source_params['file']), part.lineno))
61 95
62 if not os.path.exists(os.path.dirname(dst)): 96 if not os.path.exists(os.path.dirname(dst)):
diff --git a/scripts/lib/wic/plugins/source/rootfs.py b/scripts/lib/wic/plugins/source/rootfs.py
index 96d940a91d..c990143c0d 100644
--- a/scripts/lib/wic/plugins/source/rootfs.py
+++ b/scripts/lib/wic/plugins/source/rootfs.py
@@ -35,7 +35,7 @@ class RootfsPlugin(SourcePlugin):
35 @staticmethod 35 @staticmethod
36 def __validate_path(cmd, rootfs_dir, path): 36 def __validate_path(cmd, rootfs_dir, path):
37 if os.path.isabs(path): 37 if os.path.isabs(path):
38 logger.error("%s: Must be relative: %s" % (cmd, orig_path)) 38 logger.error("%s: Must be relative: %s" % (cmd, path))
39 sys.exit(1) 39 sys.exit(1)
40 40
41 # Disallow climbing outside of parent directory using '..', 41 # Disallow climbing outside of parent directory using '..',
@@ -43,14 +43,14 @@ class RootfsPlugin(SourcePlugin):
43 # directory, or modify a directory outside OpenEmbedded). 43 # directory, or modify a directory outside OpenEmbedded).
44 full_path = os.path.realpath(os.path.join(rootfs_dir, path)) 44 full_path = os.path.realpath(os.path.join(rootfs_dir, path))
45 if not full_path.startswith(os.path.realpath(rootfs_dir)): 45 if not full_path.startswith(os.path.realpath(rootfs_dir)):
46 logger.error("%s: Must point inside the rootfs:" % (cmd, path)) 46 logger.error("%s: Must point inside the rootfs: %s" % (cmd, path))
47 sys.exit(1) 47 sys.exit(1)
48 48
49 return full_path 49 return full_path
50 50
51 @staticmethod 51 @staticmethod
52 def __get_rootfs_dir(rootfs_dir): 52 def __get_rootfs_dir(rootfs_dir):
53 if os.path.isdir(rootfs_dir): 53 if rootfs_dir and os.path.isdir(rootfs_dir):
54 return os.path.realpath(rootfs_dir) 54 return os.path.realpath(rootfs_dir)
55 55
56 image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir) 56 image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir)
@@ -97,6 +97,9 @@ class RootfsPlugin(SourcePlugin):
97 part.has_fstab = os.path.exists(os.path.join(part.rootfs_dir, "etc/fstab")) 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") 98 pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo")
99 if not os.path.lexists(pseudo_dir): 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):
100 logger.warn("%s folder does not exist. " 103 logger.warn("%s folder does not exist. "
101 "Usernames and permissions will be invalid " % pseudo_dir) 104 "Usernames and permissions will be invalid " % pseudo_dir)
102 pseudo_dir = None 105 pseudo_dir = None
@@ -218,10 +221,10 @@ class RootfsPlugin(SourcePlugin):
218 # Update part.has_fstab here as fstab may have been added or 221 # Update part.has_fstab here as fstab may have been added or
219 # removed by the above modifications. 222 # removed by the above modifications.
220 part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab")) 223 part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab"))
221 if part.update_fstab_in_rootfs and part.has_fstab: 224 if part.update_fstab_in_rootfs and part.has_fstab and not part.no_fstab_update:
222 fstab_path = os.path.join(new_rootfs, "etc/fstab") 225 fstab_path = os.path.join(new_rootfs, "etc/fstab")
223 # Assume that fstab should always be owned by root with fixed permissions 226 # Assume that fstab should always be owned by root with fixed permissions
224 install_cmd = "install -m 0644 %s %s" % (part.updated_fstab_path, fstab_path) 227 install_cmd = "install -m 0644 -p %s %s" % (part.updated_fstab_path, fstab_path)
225 if new_pseudo: 228 if new_pseudo:
226 pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo) 229 pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
227 else: 230 else: