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