summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/plugins/imager/direct.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/plugins/imager/direct.py')
-rw-r--r--scripts/lib/wic/plugins/imager/direct.py706
1 files changed, 0 insertions, 706 deletions
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
deleted file mode 100644
index 6e1f1c8cba..0000000000
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ /dev/null
@@ -1,706 +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
13import logging
14import os
15import random
16import shutil
17import tempfile
18import uuid
19
20from time import strftime
21
22from oe.path import copyhardlinktree
23
24from wic import WicError
25from wic.filemap import sparse_copy
26from wic.ksparser import KickStart, KickStartError
27from wic.pluginbase import PluginMgr, ImagerPlugin
28from wic.misc import get_bitbake_var, exec_cmd, exec_native_cmd
29
30logger = logging.getLogger('wic')
31
32class 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 # Don't support '-' in plugin names
207 source_plugin = source_plugin.replace("-", "_")
208 plugin = PluginMgr.get_plugins('source')[source_plugin]
209 plugin.do_install_disk(self._image, disk_name, self, self.workdir,
210 self.oe_builddir, self.bootimg_dir,
211 self.kernel_dir, self.native_sysroot)
212
213 full_path = self._image.path
214 # Generate .bmap
215 if self.bmap:
216 logger.debug("Generating bmap file for %s", disk_name)
217 python = os.path.join(self.native_sysroot, 'usr/bin/python3-native/python3')
218 bmaptool = os.path.join(self.native_sysroot, 'usr/bin/bmaptool')
219 exec_native_cmd("%s %s create %s -o %s.bmap" % \
220 (python, bmaptool, full_path, full_path), self.native_sysroot)
221 # Compress the image
222 if self.compressor:
223 logger.debug("Compressing disk %s with %s", disk_name, self.compressor)
224 exec_cmd("%s %s" % (self.compressor, full_path))
225
226 def print_info(self):
227 """
228 Print the image(s) and artifacts used, for the user.
229 """
230 msg = "The new image(s) can be found here:\n"
231
232 extension = "direct" + {"gzip": ".gz",
233 "bzip2": ".bz2",
234 "xz": ".xz",
235 None: ""}.get(self.compressor)
236 full_path = self._full_path(self.outdir, self.parts[0].disk, extension)
237 msg += ' %s\n\n' % full_path
238
239 msg += 'The following build artifacts were used to create the image(s):\n'
240 for part in self.parts:
241 if part.rootfs_dir is None:
242 continue
243 if part.mountpoint == '/':
244 suffix = ':'
245 else:
246 suffix = '["%s"]:' % (part.mountpoint or part.label)
247 rootdir = part.rootfs_dir
248 msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), rootdir)
249
250 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
251 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
252 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
253
254 logger.info(msg)
255
256 @property
257 def rootdev(self):
258 """
259 Get root device name to use as a 'root' parameter
260 in kernel command line.
261
262 Assume partition order same as in wks
263 """
264 for part in self.parts:
265 if part.mountpoint == "/":
266 if part.uuid:
267 return "PARTUUID=%s" % part.uuid
268 elif part.label and self.ptable_format != 'msdos':
269 return "PARTLABEL=%s" % part.label
270 else:
271 suffix = 'p' if part.disk.startswith('mmcblk') else ''
272 return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum)
273
274 def cleanup(self):
275 if self._image:
276 self._image.cleanup()
277
278 # Move results to the output dir
279 if not os.path.exists(self.outdir):
280 os.makedirs(self.outdir)
281
282 for fname in os.listdir(self.workdir):
283 path = os.path.join(self.workdir, fname)
284 if os.path.isfile(path):
285 shutil.move(path, os.path.join(self.outdir, fname))
286
287 # remove work directory when it is not in debugging mode
288 if not self.debug:
289 shutil.rmtree(self.workdir, ignore_errors=True)
290
291# Overhead of the MBR partitioning scheme (just one sector)
292MBR_OVERHEAD = 1
293
294# Overhead of the GPT partitioning scheme
295GPT_OVERHEAD = 34
296
297# Size of a sector in bytes
298SECTOR_SIZE = 512
299
300class PartitionedImage():
301 """
302 Partitioned image in a file.
303 """
304
305 def __init__(self, path, ptable_format, partitions, native_sysroot=None, extra_space=0):
306 self.path = path # Path to the image file
307 self.numpart = 0 # Number of allocated partitions
308 self.realpart = 0 # Number of partitions in the partition table
309 self.primary_part_num = 0 # Number of primary partitions (msdos)
310 self.extendedpart = 0 # Create extended partition before this logical partition (msdos)
311 self.extended_size_sec = 0 # Size of exteded partition (msdos)
312 self.logical_part_cnt = 0 # Number of total logical paritions (msdos)
313 self.offset = 0 # Offset of next partition (in sectors)
314 self.min_size = 0 # Minimum required disk size to fit
315 # all partitions (in bytes)
316 self.ptable_format = ptable_format # Partition table format
317 # Disk system identifier
318 if os.getenv('SOURCE_DATE_EPOCH'):
319 self.identifier = random.Random(int(os.getenv('SOURCE_DATE_EPOCH'))).randint(1, 0xffffffff)
320 else:
321 self.identifier = random.SystemRandom().randint(1, 0xffffffff)
322
323 self.partitions = partitions
324 self.partimages = []
325 # Size of a sector used in calculations
326 sector_size_str = get_bitbake_var('WIC_SECTOR_SIZE')
327 if sector_size_str is not None:
328 try:
329 self.sector_size = int(sector_size_str)
330 except ValueError:
331 self.sector_size = SECTOR_SIZE
332 else:
333 self.sector_size = SECTOR_SIZE
334
335 self.native_sysroot = native_sysroot
336 num_real_partitions = len([p for p in self.partitions if not p.no_table])
337 self.extra_space = extra_space
338
339 # calculate the real partition number, accounting for partitions not
340 # in the partition table and logical partitions
341 realnum = 0
342 for part in self.partitions:
343 if part.no_table:
344 part.realnum = 0
345 else:
346 realnum += 1
347 if self.ptable_format == 'msdos' and realnum > 3 and num_real_partitions > 4:
348 part.realnum = realnum + 1
349 continue
350 part.realnum = realnum
351
352 # generate parition and filesystem UUIDs
353 for part in self.partitions:
354 if not part.uuid and part.use_uuid:
355 if self.ptable_format in ('gpt', 'gpt-hybrid'):
356 part.uuid = str(uuid.uuid4())
357 else: # msdos partition table
358 part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
359 if not part.fsuuid:
360 if part.fstype == 'vfat' or part.fstype == 'msdos':
361 part.fsuuid = '0x' + str(uuid.uuid4())[:8].upper()
362 else:
363 part.fsuuid = str(uuid.uuid4())
364 else:
365 #make sure the fsuuid for vfat/msdos align with format 0xYYYYYYYY
366 if part.fstype == 'vfat' or part.fstype == 'msdos':
367 if part.fsuuid.upper().startswith("0X"):
368 part.fsuuid = '0x' + part.fsuuid.upper()[2:].rjust(8,"0")
369 else:
370 part.fsuuid = '0x' + part.fsuuid.upper().rjust(8,"0")
371
372 def prepare(self, imager):
373 """Prepare an image. Call prepare method of all image partitions."""
374 for part in self.partitions:
375 # need to create the filesystems in order to get their
376 # sizes before we can add them and do the layout.
377 part.prepare(imager, imager.workdir, imager.oe_builddir,
378 imager.rootfs_dir, imager.bootimg_dir,
379 imager.kernel_dir, imager.native_sysroot,
380 imager.updated_fstab_path)
381
382 # Converting kB to sectors for parted
383 part.size_sec = part.disk_size * 1024 // self.sector_size
384
385 def layout_partitions(self):
386 """ Layout the partitions, meaning calculate the position of every
387 partition on the disk. The 'ptable_format' parameter defines the
388 partition table format and may be "msdos". """
389
390 logger.debug("Assigning %s partitions to disks", self.ptable_format)
391
392 # The number of primary and logical partitions. Extended partition and
393 # partitions not listed in the table are not included.
394 num_real_partitions = len([p for p in self.partitions if not p.no_table])
395
396 # Go through partitions in the order they are added in .ks file
397 for num in range(len(self.partitions)):
398 part = self.partitions[num]
399
400 if self.ptable_format == 'msdos' and part.part_name:
401 raise WicError("setting custom partition name is not " \
402 "implemented for msdos partitions")
403
404 if self.ptable_format == 'msdos' and part.part_type:
405 # The --part-type can also be implemented for MBR partitions,
406 # in which case it would map to the 1-byte "partition type"
407 # filed at offset 3 of the partition entry.
408 raise WicError("setting custom partition type is not " \
409 "implemented for msdos partitions")
410
411 if part.mbr and self.ptable_format != 'gpt-hybrid':
412 raise WicError("Partition may only be included in MBR with " \
413 "a gpt-hybrid partition table")
414
415 # Get the disk where the partition is located
416 self.numpart += 1
417 if not part.no_table:
418 self.realpart += 1
419
420 if self.numpart == 1:
421 if self.ptable_format == "msdos":
422 overhead = MBR_OVERHEAD
423 elif self.ptable_format in ("gpt", "gpt-hybrid"):
424 overhead = GPT_OVERHEAD
425
426 # Skip one sector required for the partitioning scheme overhead
427 self.offset += overhead
428
429 if self.ptable_format == "msdos":
430 if self.primary_part_num > 3 or \
431 (self.extendedpart == 0 and self.primary_part_num >= 3 and num_real_partitions > 4):
432 part.type = 'logical'
433 # Reserve a sector for EBR for every logical partition
434 # before alignment is performed.
435 if part.type == 'logical':
436 self.offset += 2
437
438 align_sectors = 0
439 if part.align:
440 # If not first partition and we do have alignment set we need
441 # to align the partition.
442 # FIXME: This leaves a empty spaces to the disk. To fill the
443 # gaps we could enlargea the previous partition?
444
445 # Calc how much the alignment is off.
446 align_sectors = self.offset % (part.align * 1024 // self.sector_size)
447
448 if align_sectors:
449 # If partition is not aligned as required, we need
450 # to move forward to the next alignment point
451 align_sectors = (part.align * 1024 // self.sector_size) - align_sectors
452
453 logger.debug("Realignment for %s%s with %s sectors, original"
454 " offset %s, target alignment is %sK.",
455 part.disk, self.numpart, align_sectors,
456 self.offset, part.align)
457
458 # increase the offset so we actually start the partition on right alignment
459 self.offset += align_sectors
460
461 if part.offset is not None:
462 offset = part.offset // self.sector_size
463
464 if offset * self.sector_size != part.offset:
465 raise WicError("Could not place %s%s at offset %d with sector size %d" % (part.disk, self.numpart, part.offset, self.sector_size))
466
467 delta = offset - self.offset
468 if delta < 0:
469 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))
470
471 logger.debug("Skipping %d sectors to place %s%s at offset %dK",
472 delta, part.disk, self.numpart, part.offset)
473
474 self.offset = offset
475
476 part.start = self.offset
477 self.offset += part.size_sec
478
479 if not part.no_table:
480 part.num = self.realpart
481 else:
482 part.num = 0
483
484 if self.ptable_format == "msdos" and not part.no_table:
485 if part.type == 'logical':
486 self.logical_part_cnt += 1
487 part.num = self.logical_part_cnt + 4
488 if self.extendedpart == 0:
489 # Create extended partition as a primary partition
490 self.primary_part_num += 1
491 self.extendedpart = part.num
492 else:
493 self.extended_size_sec += align_sectors
494 self.extended_size_sec += part.size_sec + 2
495 else:
496 self.primary_part_num += 1
497 part.num = self.primary_part_num
498
499 logger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
500 "sectors (%d bytes).", part.mountpoint, part.disk,
501 part.num, part.start, self.offset - 1, part.size_sec,
502 part.size_sec * self.sector_size)
503
504 # Once all the partitions have been layed out, we can calculate the
505 # minumim disk size
506 self.min_size = self.offset
507 if self.ptable_format in ("gpt", "gpt-hybrid"):
508 self.min_size += GPT_OVERHEAD
509
510 self.min_size *= self.sector_size
511 self.min_size += self.extra_space
512
513 def _create_partition(self, device, parttype, fstype, start, size):
514 """ Create a partition on an image described by the 'device' object. """
515
516 # Start is included to the size so we need to substract one from the end.
517 end = start + size - 1
518 logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors",
519 parttype, start, end, size)
520
521 cmd = "export PARTED_SECTOR_SIZE=%d; parted -s %s unit s mkpart %s" % \
522 (self.sector_size, device, parttype)
523 if fstype:
524 cmd += " %s" % fstype
525 cmd += " %d %d" % (start, end)
526
527 return exec_native_cmd(cmd, self.native_sysroot)
528
529 def _write_identifier(self, device, identifier):
530 logger.debug("Set disk identifier %x", identifier)
531 with open(device, 'r+b') as img:
532 img.seek(0x1B8)
533 img.write(identifier.to_bytes(4, 'little'))
534
535 def _make_disk(self, device, ptable_format, min_size):
536 logger.debug("Creating sparse file %s", device)
537 with open(device, 'w') as sparse:
538 os.ftruncate(sparse.fileno(), min_size)
539
540 logger.debug("Initializing partition table for %s", device)
541 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s mklabel %s" %
542 (self.sector_size, device, ptable_format), self.native_sysroot)
543
544 def _write_disk_guid(self):
545 if self.ptable_format in ('gpt', 'gpt-hybrid'):
546 if os.getenv('SOURCE_DATE_EPOCH'):
547 self.disk_guid = uuid.UUID(int=int(os.getenv('SOURCE_DATE_EPOCH')))
548 else:
549 self.disk_guid = uuid.uuid4()
550
551 logger.debug("Set disk guid %s", self.disk_guid)
552 sfdisk_cmd = "sfdisk --sector-size %s --disk-id %s %s" % \
553 (self.sector_size, self.path, self.disk_guid)
554 exec_native_cmd(sfdisk_cmd, self.native_sysroot)
555
556 def create(self):
557 self._make_disk(self.path,
558 "gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format,
559 self.min_size)
560
561 self._write_identifier(self.path, self.identifier)
562 self._write_disk_guid()
563
564 if self.ptable_format == "gpt-hybrid":
565 mbr_path = self.path + ".mbr"
566 self._make_disk(mbr_path, "msdos", self.min_size)
567 self._write_identifier(mbr_path, self.identifier)
568
569 logger.debug("Creating partitions")
570
571 hybrid_mbr_part_num = 0
572
573 for part in self.partitions:
574 if part.num == 0:
575 continue
576
577 if self.ptable_format == "msdos" and part.num == self.extendedpart:
578 # Create an extended partition (note: extended
579 # partition is described in MBR and contains all
580 # logical partitions). The logical partitions save a
581 # sector for an EBR just before the start of a
582 # partition. The extended partition must start one
583 # sector before the start of the first logical
584 # partition. This way the first EBR is inside of the
585 # extended partition. Since the extended partitions
586 # starts a sector before the first logical partition,
587 # add a sector at the back, so that there is enough
588 # room for all logical partitions.
589 self._create_partition(self.path, "extended",
590 None, part.start - 2,
591 self.extended_size_sec)
592
593 if part.fstype == "swap":
594 parted_fs_type = "linux-swap"
595 elif part.fstype == "vfat":
596 parted_fs_type = "fat32"
597 elif part.fstype == "msdos":
598 parted_fs_type = "fat16"
599 if not part.system_id:
600 part.system_id = '0x6' # FAT16
601 else:
602 # Type for ext2/ext3/ext4/btrfs
603 parted_fs_type = "ext2"
604
605 # Boot ROM of OMAP boards require vfat boot partition to have an
606 # even number of sectors.
607 if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \
608 and part.size_sec % 2:
609 logger.debug("Subtracting one sector from '%s' partition to "
610 "get even number of sectors for the partition",
611 part.mountpoint)
612 part.size_sec -= 1
613
614 self._create_partition(self.path, part.type,
615 parted_fs_type, part.start, part.size_sec)
616
617 if self.ptable_format == "gpt-hybrid" and part.mbr:
618 hybrid_mbr_part_num += 1
619 if hybrid_mbr_part_num > 4:
620 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
621 self._create_partition(mbr_path, "primary",
622 parted_fs_type, part.start, part.size_sec)
623
624 if self.ptable_format in ("gpt", "gpt-hybrid") and (part.part_name or part.label):
625 partition_label = part.part_name if part.part_name else part.label
626 logger.debug("partition %d: set name to %s",
627 part.num, partition_label)
628 exec_native_cmd("sfdisk --sector-size %s --part-label %s %d %s" % \
629 (self.sector_size, self.path, part.num,
630 partition_label), self.native_sysroot)
631 if part.part_type:
632 logger.debug("partition %d: set type UID to %s",
633 part.num, part.part_type)
634 exec_native_cmd("sfdisk --sector-size %s --part-type %s %d %s" % \
635 (self.sector_size, self.path, part.num,
636 part.part_type), self.native_sysroot)
637
638 if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"):
639 logger.debug("partition %d: set UUID to %s",
640 part.num, part.uuid)
641 exec_native_cmd("sfdisk --sector-size %s --part-uuid %s %d %s" % \
642 (self.sector_size, self.path, part.num, part.uuid),
643 self.native_sysroot)
644
645 if part.active:
646 flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot"
647 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
648 flag_name, part.num, self.path)
649 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s set %d %s on" % \
650 (self.sector_size, self.path, part.num, flag_name),
651 self.native_sysroot)
652 if self.ptable_format == 'gpt-hybrid' and part.mbr:
653 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s set %d %s on" % \
654 (self.sector_size, mbr_path, hybrid_mbr_part_num, "boot"),
655 self.native_sysroot)
656 if part.system_id:
657 exec_native_cmd("sfdisk --sector-size %s --part-type %s %s %s" % \
658 (self.sector_size, self.path, part.num, part.system_id),
659 self.native_sysroot)
660
661 if part.hidden and self.ptable_format == "gpt":
662 logger.debug("Set hidden attribute for partition '%s' on disk '%s'",
663 part.num, self.path)
664 exec_native_cmd("sfdisk --sector-size %s --part-attrs %s %s RequiredPartition" % \
665 (self.sector_size, self.path, part.num),
666 self.native_sysroot)
667
668 if self.ptable_format == "gpt-hybrid":
669 # Write a protective GPT partition
670 hybrid_mbr_part_num += 1
671 if hybrid_mbr_part_num > 4:
672 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
673
674 # parted cannot directly create a protective GPT partition, so
675 # create with an arbitrary type, then change it to the correct type
676 # with sfdisk
677 self._create_partition(mbr_path, "primary", "fat32", 1, GPT_OVERHEAD)
678 exec_native_cmd("sfdisk --sector-size %s --part-type %s %d 0xee" % \
679 (self.sector_size, mbr_path, hybrid_mbr_part_num),
680 self.native_sysroot)
681
682 # Copy hybrid MBR
683 with open(mbr_path, "rb") as mbr_file:
684 with open(self.path, "r+b") as image_file:
685 mbr = mbr_file.read(512)
686 image_file.write(mbr)
687
688 def cleanup(self):
689 pass
690
691 def assemble(self):
692 logger.debug("Installing partitions")
693
694 for part in self.partitions:
695 source = part.source_file
696 if source:
697 # install source_file contents into a partition
698 sparse_copy(source, self.path, seek=part.start * self.sector_size)
699
700 logger.debug("Installed %s in partition %d, sectors %d-%d, "
701 "size %d sectors", source, part.num, part.start,
702 part.start + part.size_sec - 1, part.size_sec)
703
704 partimage = self.path + '.p%d' % part.num
705 os.rename(source, partimage)
706 self.partimages.append(partimage)