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.py710
1 files changed, 0 insertions, 710 deletions
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
deleted file mode 100644
index ad922cfbf1..0000000000
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ /dev/null
@@ -1,710 +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, self.ks.bootloader.diskid,
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, disk_id, 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 disk_id and ptable_format in ('gpt', 'gpt-hybrid'):
319 self.disk_guid = disk_id
320 elif os.getenv('SOURCE_DATE_EPOCH'):
321 self.disk_guid = uuid.UUID(int=int(os.getenv('SOURCE_DATE_EPOCH')))
322 else:
323 self.disk_guid = uuid.uuid4()
324
325 if disk_id and ptable_format == 'msdos':
326 self.identifier = disk_id
327 elif os.getenv('SOURCE_DATE_EPOCH'):
328 self.identifier = random.Random(int(os.getenv('SOURCE_DATE_EPOCH'))).randint(1, 0xffffffff)
329 else:
330 self.identifier = random.SystemRandom().randint(1, 0xffffffff)
331
332 self.partitions = partitions
333 self.partimages = []
334 # Size of a sector used in calculations
335 sector_size_str = get_bitbake_var('WIC_SECTOR_SIZE')
336 if sector_size_str is not None:
337 try:
338 self.sector_size = int(sector_size_str)
339 except ValueError:
340 self.sector_size = SECTOR_SIZE
341 else:
342 self.sector_size = SECTOR_SIZE
343
344 self.native_sysroot = native_sysroot
345 num_real_partitions = len([p for p in self.partitions if not p.no_table])
346 self.extra_space = extra_space
347
348 # calculate the real partition number, accounting for partitions not
349 # in the partition table and logical partitions
350 realnum = 0
351 for part in self.partitions:
352 if part.no_table:
353 part.realnum = 0
354 else:
355 realnum += 1
356 if self.ptable_format == 'msdos' and realnum > 3 and num_real_partitions > 4:
357 part.realnum = realnum + 1
358 continue
359 part.realnum = realnum
360
361 # generate parition and filesystem UUIDs
362 for part in self.partitions:
363 if not part.uuid and part.use_uuid:
364 if self.ptable_format in ('gpt', 'gpt-hybrid'):
365 part.uuid = str(uuid.uuid4())
366 else: # msdos partition table
367 part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
368 if not part.fsuuid:
369 if part.fstype == 'vfat' or part.fstype == 'msdos':
370 part.fsuuid = '0x' + str(uuid.uuid4())[:8].upper()
371 else:
372 part.fsuuid = str(uuid.uuid4())
373 else:
374 #make sure the fsuuid for vfat/msdos align with format 0xYYYYYYYY
375 if part.fstype == 'vfat' or part.fstype == 'msdos':
376 if part.fsuuid.upper().startswith("0X"):
377 part.fsuuid = '0x' + part.fsuuid.upper()[2:].rjust(8,"0")
378 else:
379 part.fsuuid = '0x' + part.fsuuid.upper().rjust(8,"0")
380
381 def prepare(self, imager):
382 """Prepare an image. Call prepare method of all image partitions."""
383 for part in self.partitions:
384 # need to create the filesystems in order to get their
385 # sizes before we can add them and do the layout.
386 part.prepare(imager, imager.workdir, imager.oe_builddir,
387 imager.rootfs_dir, imager.bootimg_dir,
388 imager.kernel_dir, imager.native_sysroot,
389 imager.updated_fstab_path)
390
391 # Converting kB to sectors for parted
392 part.size_sec = part.disk_size * 1024 // self.sector_size
393
394 def layout_partitions(self):
395 """ Layout the partitions, meaning calculate the position of every
396 partition on the disk. The 'ptable_format' parameter defines the
397 partition table format and may be "msdos". """
398
399 logger.debug("Assigning %s partitions to disks", self.ptable_format)
400
401 # The number of primary and logical partitions. Extended partition and
402 # partitions not listed in the table are not included.
403 num_real_partitions = len([p for p in self.partitions if not p.no_table])
404
405 # Go through partitions in the order they are added in .ks file
406 for num in range(len(self.partitions)):
407 part = self.partitions[num]
408
409 if self.ptable_format == 'msdos' and part.part_name:
410 raise WicError("setting custom partition name is not " \
411 "implemented for msdos partitions")
412
413 if self.ptable_format == 'msdos' and part.part_type:
414 # The --part-type can also be implemented for MBR partitions,
415 # in which case it would map to the 1-byte "partition type"
416 # filed at offset 3 of the partition entry.
417 raise WicError("setting custom partition type is not " \
418 "implemented for msdos partitions")
419
420 if part.mbr and self.ptable_format != 'gpt-hybrid':
421 raise WicError("Partition may only be included in MBR with " \
422 "a gpt-hybrid partition table")
423
424 # Get the disk where the partition is located
425 self.numpart += 1
426 if not part.no_table:
427 self.realpart += 1
428
429 if self.numpart == 1:
430 if self.ptable_format == "msdos":
431 overhead = MBR_OVERHEAD
432 elif self.ptable_format in ("gpt", "gpt-hybrid"):
433 overhead = GPT_OVERHEAD
434
435 # Skip one sector required for the partitioning scheme overhead
436 self.offset += overhead
437
438 if self.ptable_format == "msdos":
439 if self.primary_part_num > 3 or \
440 (self.extendedpart == 0 and self.primary_part_num >= 3 and num_real_partitions > 4):
441 part.type = 'logical'
442 # Reserve a sector for EBR for every logical partition
443 # before alignment is performed.
444 if part.type == 'logical':
445 self.offset += 2
446
447 align_sectors = 0
448 if part.align:
449 # If not first partition and we do have alignment set we need
450 # to align the partition.
451 # FIXME: This leaves a empty spaces to the disk. To fill the
452 # gaps we could enlargea the previous partition?
453
454 # Calc how much the alignment is off.
455 align_sectors = self.offset % (part.align * 1024 // self.sector_size)
456
457 if align_sectors:
458 # If partition is not aligned as required, we need
459 # to move forward to the next alignment point
460 align_sectors = (part.align * 1024 // self.sector_size) - align_sectors
461
462 logger.debug("Realignment for %s%s with %s sectors, original"
463 " offset %s, target alignment is %sK.",
464 part.disk, self.numpart, align_sectors,
465 self.offset, part.align)
466
467 # increase the offset so we actually start the partition on right alignment
468 self.offset += align_sectors
469
470 if part.offset is not None:
471 offset = part.offset // self.sector_size
472
473 if offset * self.sector_size != part.offset:
474 raise WicError("Could not place %s%s at offset %d with sector size %d" % (part.disk, self.numpart, part.offset, self.sector_size))
475
476 delta = offset - self.offset
477 if delta < 0:
478 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))
479
480 logger.debug("Skipping %d sectors to place %s%s at offset %dK",
481 delta, part.disk, self.numpart, part.offset)
482
483 self.offset = offset
484
485 part.start = self.offset
486 self.offset += part.size_sec
487
488 if not part.no_table:
489 part.num = self.realpart
490 else:
491 part.num = 0
492
493 if self.ptable_format == "msdos" and not part.no_table:
494 if part.type == 'logical':
495 self.logical_part_cnt += 1
496 part.num = self.logical_part_cnt + 4
497 if self.extendedpart == 0:
498 # Create extended partition as a primary partition
499 self.primary_part_num += 1
500 self.extendedpart = part.num
501 else:
502 self.extended_size_sec += align_sectors
503 self.extended_size_sec += part.size_sec + 2
504 else:
505 self.primary_part_num += 1
506 part.num = self.primary_part_num
507
508 logger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
509 "sectors (%d bytes).", part.mountpoint, part.disk,
510 part.num, part.start, self.offset - 1, part.size_sec,
511 part.size_sec * self.sector_size)
512
513 # Once all the partitions have been layed out, we can calculate the
514 # minumim disk size
515 self.min_size = self.offset
516 if self.ptable_format in ("gpt", "gpt-hybrid"):
517 self.min_size += GPT_OVERHEAD
518
519 self.min_size *= self.sector_size
520 self.min_size += self.extra_space
521
522 def _create_partition(self, device, parttype, fstype, start, size):
523 """ Create a partition on an image described by the 'device' object. """
524
525 # Start is included to the size so we need to substract one from the end.
526 end = start + size - 1
527 logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors",
528 parttype, start, end, size)
529
530 cmd = "export PARTED_SECTOR_SIZE=%d; parted -s %s unit s mkpart %s" % \
531 (self.sector_size, device, parttype)
532 if fstype:
533 cmd += " %s" % fstype
534 cmd += " %d %d" % (start, end)
535
536 return exec_native_cmd(cmd, self.native_sysroot)
537
538 def _write_identifier(self, device, identifier):
539 logger.debug("Set disk identifier %x", identifier)
540 with open(device, 'r+b') as img:
541 img.seek(0x1B8)
542 img.write(identifier.to_bytes(4, 'little'))
543
544 def _make_disk(self, device, ptable_format, min_size):
545 logger.debug("Creating sparse file %s", device)
546 with open(device, 'w') as sparse:
547 os.ftruncate(sparse.fileno(), min_size)
548
549 logger.debug("Initializing partition table for %s", device)
550 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s mklabel %s" %
551 (self.sector_size, device, ptable_format), self.native_sysroot)
552
553 def _write_disk_guid(self):
554 if self.ptable_format in ('gpt', 'gpt-hybrid'):
555 logger.debug("Set disk guid %s", self.disk_guid)
556 sfdisk_cmd = "sfdisk --sector-size %s --disk-id %s %s" % \
557 (self.sector_size, self.path, self.disk_guid)
558 exec_native_cmd(sfdisk_cmd, self.native_sysroot)
559
560 def create(self):
561 self._make_disk(self.path,
562 "gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format,
563 self.min_size)
564
565 self._write_identifier(self.path, self.identifier)
566 self._write_disk_guid()
567
568 if self.ptable_format == "gpt-hybrid":
569 mbr_path = self.path + ".mbr"
570 self._make_disk(mbr_path, "msdos", self.min_size)
571 self._write_identifier(mbr_path, self.identifier)
572
573 logger.debug("Creating partitions")
574
575 hybrid_mbr_part_num = 0
576
577 for part in self.partitions:
578 if part.num == 0:
579 continue
580
581 if self.ptable_format == "msdos" and part.num == self.extendedpart:
582 # Create an extended partition (note: extended
583 # partition is described in MBR and contains all
584 # logical partitions). The logical partitions save a
585 # sector for an EBR just before the start of a
586 # partition. The extended partition must start one
587 # sector before the start of the first logical
588 # partition. This way the first EBR is inside of the
589 # extended partition. Since the extended partitions
590 # starts a sector before the first logical partition,
591 # add a sector at the back, so that there is enough
592 # room for all logical partitions.
593 self._create_partition(self.path, "extended",
594 None, part.start - 2,
595 self.extended_size_sec)
596
597 if part.fstype == "swap":
598 parted_fs_type = "linux-swap"
599 elif part.fstype == "vfat":
600 parted_fs_type = "fat32"
601 elif part.fstype == "msdos":
602 parted_fs_type = "fat16"
603 if not part.system_id:
604 part.system_id = '0x6' # FAT16
605 else:
606 # Type for ext2/ext3/ext4/btrfs
607 parted_fs_type = "ext2"
608
609 # Boot ROM of OMAP boards require vfat boot partition to have an
610 # even number of sectors.
611 if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \
612 and part.size_sec % 2:
613 logger.debug("Subtracting one sector from '%s' partition to "
614 "get even number of sectors for the partition",
615 part.mountpoint)
616 part.size_sec -= 1
617
618 self._create_partition(self.path, part.type,
619 parted_fs_type, part.start, part.size_sec)
620
621 if self.ptable_format == "gpt-hybrid" and part.mbr:
622 hybrid_mbr_part_num += 1
623 if hybrid_mbr_part_num > 4:
624 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
625 self._create_partition(mbr_path, "primary",
626 parted_fs_type, part.start, part.size_sec)
627
628 if self.ptable_format in ("gpt", "gpt-hybrid") and (part.part_name or part.label):
629 partition_label = part.part_name if part.part_name else part.label
630 logger.debug("partition %d: set name to %s",
631 part.num, partition_label)
632 exec_native_cmd("sfdisk --sector-size %s --part-label %s %d %s" % \
633 (self.sector_size, self.path, part.num,
634 partition_label), self.native_sysroot)
635 if part.part_type:
636 logger.debug("partition %d: set type UID to %s",
637 part.num, part.part_type)
638 exec_native_cmd("sfdisk --sector-size %s --part-type %s %d %s" % \
639 (self.sector_size, self.path, part.num,
640 part.part_type), self.native_sysroot)
641
642 if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"):
643 logger.debug("partition %d: set UUID to %s",
644 part.num, part.uuid)
645 exec_native_cmd("sfdisk --sector-size %s --part-uuid %s %d %s" % \
646 (self.sector_size, self.path, part.num, part.uuid),
647 self.native_sysroot)
648
649 if part.active:
650 flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot"
651 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
652 flag_name, part.num, self.path)
653 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s set %d %s on" % \
654 (self.sector_size, self.path, part.num, flag_name),
655 self.native_sysroot)
656 if self.ptable_format == 'gpt-hybrid' and part.mbr:
657 exec_native_cmd("export PARTED_SECTOR_SIZE=%d; parted -s %s set %d %s on" % \
658 (self.sector_size, mbr_path, hybrid_mbr_part_num, "boot"),
659 self.native_sysroot)
660 if part.system_id:
661 exec_native_cmd("sfdisk --sector-size %s --part-type %s %s %s" % \
662 (self.sector_size, self.path, part.num, part.system_id),
663 self.native_sysroot)
664
665 if part.hidden and self.ptable_format == "gpt":
666 logger.debug("Set hidden attribute for partition '%s' on disk '%s'",
667 part.num, self.path)
668 exec_native_cmd("sfdisk --sector-size %s --part-attrs %s %s RequiredPartition" % \
669 (self.sector_size, self.path, part.num),
670 self.native_sysroot)
671
672 if self.ptable_format == "gpt-hybrid":
673 # Write a protective GPT partition
674 hybrid_mbr_part_num += 1
675 if hybrid_mbr_part_num > 4:
676 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
677
678 # parted cannot directly create a protective GPT partition, so
679 # create with an arbitrary type, then change it to the correct type
680 # with sfdisk
681 self._create_partition(mbr_path, "primary", "fat32", 1, GPT_OVERHEAD)
682 exec_native_cmd("sfdisk --sector-size %s --part-type %s %d 0xee" % \
683 (self.sector_size, mbr_path, hybrid_mbr_part_num),
684 self.native_sysroot)
685
686 # Copy hybrid MBR
687 with open(mbr_path, "rb") as mbr_file:
688 with open(self.path, "r+b") as image_file:
689 mbr = mbr_file.read(512)
690 image_file.write(mbr)
691
692 def cleanup(self):
693 pass
694
695 def assemble(self):
696 logger.debug("Installing partitions")
697
698 for part in self.partitions:
699 source = part.source_file
700 if source:
701 # install source_file contents into a partition
702 sparse_copy(source, self.path, seek=part.start * self.sector_size)
703
704 logger.debug("Installed %s in partition %d, sectors %d-%d, "
705 "size %d sectors", source, part.num, part.start,
706 part.start + part.size_sec - 1, part.size_sec)
707
708 partimage = self.path + '.p%d' % part.num
709 os.rename(source, partimage)
710 self.partimages.append(partimage)