diff options
Diffstat (limited to 'scripts/lib/wic/plugins/imager/direct.py')
-rw-r--r-- | scripts/lib/wic/plugins/imager/direct.py | 134 |
1 files changed, 104 insertions, 30 deletions
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 | ||