diff options
Diffstat (limited to 'scripts/lib')
| -rw-r--r-- | scripts/lib/wic/plugins/imager/direct.py | 278 | ||||
| -rw-r--r-- | scripts/lib/wic/utils/partitionedfs.py | 301 |
2 files changed, 276 insertions, 303 deletions
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py index fefe88e0df..12044318e8 100644 --- a/scripts/lib/wic/plugins/imager/direct.py +++ b/scripts/lib/wic/plugins/imager/direct.py | |||
| @@ -31,13 +31,12 @@ import tempfile | |||
| 31 | from time import strftime | 31 | from time import strftime |
| 32 | 32 | ||
| 33 | from wic import msger | 33 | from wic import msger |
| 34 | from wic.filemap import sparse_copy | ||
| 34 | from wic.ksparser import KickStart, KickStartError | 35 | from wic.ksparser import KickStart, KickStartError |
| 35 | from wic.plugin import pluginmgr | 36 | from wic.plugin import pluginmgr |
| 36 | from wic.pluginbase import ImagerPlugin | 37 | from wic.pluginbase import ImagerPlugin |
| 37 | from wic.utils.errors import CreatorError, ImageError | 38 | from wic.utils.errors import CreatorError, ImageError |
| 38 | from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd | 39 | from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd |
| 39 | from wic.utils.partitionedfs import PartitionedImage | ||
| 40 | |||
| 41 | 40 | ||
| 42 | class DirectPlugin(ImagerPlugin): | 41 | class DirectPlugin(ImagerPlugin): |
| 43 | """ | 42 | """ |
| @@ -316,3 +315,278 @@ class DirectPlugin(ImagerPlugin): | |||
| 316 | 315 | ||
| 317 | # remove work directory | 316 | # remove work directory |
| 318 | shutil.rmtree(self.workdir, ignore_errors=True) | 317 | shutil.rmtree(self.workdir, ignore_errors=True) |
| 318 | |||
| 319 | # Overhead of the MBR partitioning scheme (just one sector) | ||
| 320 | MBR_OVERHEAD = 1 | ||
| 321 | |||
| 322 | # Overhead of the GPT partitioning scheme | ||
| 323 | GPT_OVERHEAD = 34 | ||
| 324 | |||
| 325 | # Size of a sector in bytes | ||
| 326 | SECTOR_SIZE = 512 | ||
| 327 | |||
| 328 | class PartitionedImage(): | ||
| 329 | """ | ||
| 330 | Partitioned image in a file. | ||
| 331 | """ | ||
| 332 | |||
| 333 | def __init__(self, path, ptable_format, native_sysroot=None): | ||
| 334 | self.path = path # Path to the image file | ||
| 335 | self.numpart = 0 # Number of allocated partitions | ||
| 336 | self.realpart = 0 # Number of partitions in the partition table | ||
| 337 | self.offset = 0 # Offset of next partition (in sectors) | ||
| 338 | self.min_size = 0 # Minimum required disk size to fit | ||
| 339 | # all partitions (in bytes) | ||
| 340 | self.ptable_format = ptable_format # Partition table format | ||
| 341 | # Disk system identifier | ||
| 342 | self.identifier = int.from_bytes(os.urandom(4), 'little') | ||
| 343 | |||
| 344 | self.partitions = [] | ||
| 345 | self.partimages = [] | ||
| 346 | # Size of a sector used in calculations | ||
| 347 | self.sector_size = SECTOR_SIZE | ||
| 348 | self.native_sysroot = native_sysroot | ||
| 349 | |||
| 350 | def add_partition(self, part): | ||
| 351 | """ | ||
| 352 | Add the next partition. Partitions have to be added in the | ||
| 353 | first-to-last order. | ||
| 354 | """ | ||
| 355 | part.ks_pnum = len(self.partitions) | ||
| 356 | |||
| 357 | # Converting kB to sectors for parted | ||
| 358 | part.size_sec = part.disk_size * 1024 // self.sector_size | ||
| 359 | |||
| 360 | self.partitions.append(part) | ||
| 361 | |||
| 362 | def layout_partitions(self): | ||
| 363 | """ Layout the partitions, meaning calculate the position of every | ||
| 364 | partition on the disk. The 'ptable_format' parameter defines the | ||
| 365 | partition table format and may be "msdos". """ | ||
| 366 | |||
| 367 | msger.debug("Assigning %s partitions to disks" % self.ptable_format) | ||
| 368 | |||
| 369 | # Go through partitions in the order they are added in .ks file | ||
| 370 | for num in range(len(self.partitions)): | ||
| 371 | part = self.partitions[num] | ||
| 372 | |||
| 373 | if self.ptable_format == 'msdos' and part.part_type: | ||
| 374 | # The --part-type can also be implemented for MBR partitions, | ||
| 375 | # in which case it would map to the 1-byte "partition type" | ||
| 376 | # filed at offset 3 of the partition entry. | ||
| 377 | raise ImageError("setting custom partition type is not " \ | ||
| 378 | "implemented for msdos partitions") | ||
| 379 | |||
| 380 | # Get the disk where the partition is located | ||
| 381 | self.numpart += 1 | ||
| 382 | if not part.no_table: | ||
| 383 | self.realpart += 1 | ||
| 384 | |||
| 385 | if self.numpart == 1: | ||
| 386 | if self.ptable_format == "msdos": | ||
| 387 | overhead = MBR_OVERHEAD | ||
| 388 | elif self.ptable_format == "gpt": | ||
| 389 | overhead = GPT_OVERHEAD | ||
| 390 | |||
| 391 | # Skip one sector required for the partitioning scheme overhead | ||
| 392 | self.offset += overhead | ||
| 393 | |||
| 394 | if self.realpart > 3: | ||
| 395 | # Reserve a sector for EBR for every logical partition | ||
| 396 | # before alignment is performed. | ||
| 397 | if self.ptable_format == "msdos": | ||
| 398 | self.offset += 1 | ||
| 399 | |||
| 400 | if part.align: | ||
| 401 | # If not first partition and we do have alignment set we need | ||
| 402 | # to align the partition. | ||
| 403 | # FIXME: This leaves a empty spaces to the disk. To fill the | ||
| 404 | # gaps we could enlargea the previous partition? | ||
| 405 | |||
| 406 | # Calc how much the alignment is off. | ||
| 407 | align_sectors = self.offset % (part.align * 1024 // self.sector_size) | ||
| 408 | |||
| 409 | if align_sectors: | ||
| 410 | # If partition is not aligned as required, we need | ||
| 411 | # to move forward to the next alignment point | ||
| 412 | align_sectors = (part.align * 1024 // self.sector_size) - align_sectors | ||
| 413 | |||
| 414 | msger.debug("Realignment for %s%s with %s sectors, original" | ||
| 415 | " offset %s, target alignment is %sK." % | ||
| 416 | (part.disk, self.numpart, align_sectors, | ||
| 417 | self.offset, part.align)) | ||
| 418 | |||
| 419 | # increase the offset so we actually start the partition on right alignment | ||
| 420 | self.offset += align_sectors | ||
| 421 | |||
| 422 | part.start = self.offset | ||
| 423 | self.offset += part.size_sec | ||
| 424 | |||
| 425 | part.type = 'primary' | ||
| 426 | if not part.no_table: | ||
| 427 | part.num = self.realpart | ||
| 428 | else: | ||
| 429 | part.num = 0 | ||
| 430 | |||
| 431 | if self.ptable_format == "msdos": | ||
| 432 | # only count the partitions that are in partition table | ||
| 433 | if len([p for p in self.partitions if not p.no_table]) > 4: | ||
| 434 | if self.realpart > 3: | ||
| 435 | part.type = 'logical' | ||
| 436 | part.num = self.realpart + 1 | ||
| 437 | |||
| 438 | msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " | ||
| 439 | "sectors (%d bytes)." \ | ||
| 440 | % (part.mountpoint, part.disk, part.num, | ||
| 441 | part.start, self.offset - 1, | ||
| 442 | part.size_sec, part.size_sec * self.sector_size)) | ||
| 443 | |||
| 444 | # Once all the partitions have been layed out, we can calculate the | ||
| 445 | # minumim disk size | ||
| 446 | self.min_size = self.offset | ||
| 447 | if self.ptable_format == "gpt": | ||
| 448 | self.min_size += GPT_OVERHEAD | ||
| 449 | |||
| 450 | self.min_size *= self.sector_size | ||
| 451 | |||
| 452 | def _create_partition(self, device, parttype, fstype, start, size): | ||
| 453 | """ Create a partition on an image described by the 'device' object. """ | ||
| 454 | |||
| 455 | # Start is included to the size so we need to substract one from the end. | ||
| 456 | end = start + size - 1 | ||
| 457 | msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" % | ||
| 458 | (parttype, start, end, size)) | ||
| 459 | |||
| 460 | cmd = "parted -s %s unit s mkpart %s" % (device, parttype) | ||
| 461 | if fstype: | ||
| 462 | cmd += " %s" % fstype | ||
| 463 | cmd += " %d %d" % (start, end) | ||
| 464 | |||
| 465 | return exec_native_cmd(cmd, self.native_sysroot) | ||
| 466 | |||
| 467 | def create(self): | ||
| 468 | msger.debug("Creating sparse file %s" % self.path) | ||
| 469 | with open(self.path, 'w') as sparse: | ||
| 470 | os.ftruncate(sparse.fileno(), self.min_size) | ||
| 471 | |||
| 472 | msger.debug("Initializing partition table for %s" % self.path) | ||
| 473 | exec_native_cmd("parted -s %s mklabel %s" % | ||
| 474 | (self.path, self.ptable_format), self.native_sysroot) | ||
| 475 | |||
| 476 | msger.debug("Set disk identifier %x" % self.identifier) | ||
| 477 | with open(self.path, 'r+b') as img: | ||
| 478 | img.seek(0x1B8) | ||
| 479 | img.write(self.identifier.to_bytes(4, 'little')) | ||
| 480 | |||
| 481 | msger.debug("Creating partitions") | ||
| 482 | |||
| 483 | for part in self.partitions: | ||
| 484 | if part.num == 0: | ||
| 485 | continue | ||
| 486 | |||
| 487 | if self.ptable_format == "msdos" and part.num == 5: | ||
| 488 | # Create an extended partition (note: extended | ||
| 489 | # partition is described in MBR and contains all | ||
| 490 | # logical partitions). The logical partitions save a | ||
| 491 | # sector for an EBR just before the start of a | ||
| 492 | # partition. The extended partition must start one | ||
| 493 | # sector before the start of the first logical | ||
| 494 | # partition. This way the first EBR is inside of the | ||
| 495 | # extended partition. Since the extended partitions | ||
| 496 | # starts a sector before the first logical partition, | ||
| 497 | # add a sector at the back, so that there is enough | ||
| 498 | # room for all logical partitions. | ||
| 499 | self._create_partition(self.path, "extended", | ||
| 500 | None, part.start - 1, | ||
| 501 | self.offset - part.start + 1) | ||
| 502 | |||
| 503 | if part.fstype == "swap": | ||
| 504 | parted_fs_type = "linux-swap" | ||
| 505 | elif part.fstype == "vfat": | ||
| 506 | parted_fs_type = "fat32" | ||
| 507 | elif part.fstype == "msdos": | ||
| 508 | parted_fs_type = "fat16" | ||
| 509 | elif part.fstype == "ontrackdm6aux3": | ||
| 510 | parted_fs_type = "ontrackdm6aux3" | ||
| 511 | else: | ||
| 512 | # Type for ext2/ext3/ext4/btrfs | ||
| 513 | parted_fs_type = "ext2" | ||
| 514 | |||
| 515 | # Boot ROM of OMAP boards require vfat boot partition to have an | ||
| 516 | # even number of sectors. | ||
| 517 | if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \ | ||
| 518 | and part.size_sec % 2: | ||
| 519 | msger.debug("Subtracting one sector from '%s' partition to " \ | ||
| 520 | "get even number of sectors for the partition" % \ | ||
| 521 | part.mountpoint) | ||
| 522 | part.size_sec -= 1 | ||
| 523 | |||
| 524 | self._create_partition(self.path, part.type, | ||
| 525 | parted_fs_type, part.start, part.size_sec) | ||
| 526 | |||
| 527 | if part.part_type: | ||
| 528 | msger.debug("partition %d: set type UID to %s" % \ | ||
| 529 | (part.num, part.part_type)) | ||
| 530 | exec_native_cmd("sgdisk --typecode=%d:%s %s" % \ | ||
| 531 | (part.num, part.part_type, | ||
| 532 | self.path), self.native_sysroot) | ||
| 533 | |||
| 534 | if part.uuid and self.ptable_format == "gpt": | ||
| 535 | msger.debug("partition %d: set UUID to %s" % \ | ||
| 536 | (part.num, part.uuid)) | ||
| 537 | exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ | ||
| 538 | (part.num, part.uuid, self.path), | ||
| 539 | self.native_sysroot) | ||
| 540 | |||
| 541 | if part.label and self.ptable_format == "gpt": | ||
| 542 | msger.debug("partition %d: set name to %s" % \ | ||
| 543 | (part.num, part.label)) | ||
| 544 | exec_native_cmd("parted -s %s name %d %s" % \ | ||
| 545 | (self.path, part.num, part.label), | ||
| 546 | self.native_sysroot) | ||
| 547 | |||
| 548 | if part.active: | ||
| 549 | flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot" | ||
| 550 | msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ | ||
| 551 | (flag_name, part.num, self.path)) | ||
| 552 | exec_native_cmd("parted -s %s set %d %s on" % \ | ||
| 553 | (self.path, part.num, flag_name), | ||
| 554 | self.native_sysroot) | ||
| 555 | if part.system_id: | ||
| 556 | exec_native_cmd("sfdisk --part-type %s %s %s" % \ | ||
| 557 | (self.path, part.num, part.system_id), | ||
| 558 | self.native_sysroot) | ||
| 559 | |||
| 560 | # Parted defaults to enabling the lba flag for fat16 partitions, | ||
| 561 | # which causes compatibility issues with some firmware (and really | ||
| 562 | # isn't necessary). | ||
| 563 | if parted_fs_type == "fat16": | ||
| 564 | if self.ptable_format == 'msdos': | ||
| 565 | msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ | ||
| 566 | (part.num, self.path)) | ||
| 567 | exec_native_cmd("parted -s %s set %d lba off" % \ | ||
| 568 | (self.path, part.num), | ||
| 569 | self.native_sysroot) | ||
| 570 | |||
| 571 | def cleanup(self): | ||
| 572 | # remove partition images | ||
| 573 | for image in self.partimages: | ||
| 574 | os.remove(image) | ||
| 575 | |||
| 576 | def assemble(self): | ||
| 577 | msger.debug("Installing partitions") | ||
| 578 | |||
| 579 | for part in self.partitions: | ||
| 580 | source = part.source_file | ||
| 581 | if source: | ||
| 582 | # install source_file contents into a partition | ||
| 583 | sparse_copy(source, self.path, part.start * self.sector_size) | ||
| 584 | |||
| 585 | msger.debug("Installed %s in partition %d, sectors %d-%d, " | ||
| 586 | "size %d sectors" % \ | ||
| 587 | (source, part.num, part.start, | ||
| 588 | part.start + part.size_sec - 1, part.size_sec)) | ||
| 589 | |||
| 590 | partimage = self.path + '.p%d' % part.num | ||
| 591 | os.rename(source, partimage) | ||
| 592 | self.partimages.append(partimage) | ||
diff --git a/scripts/lib/wic/utils/partitionedfs.py b/scripts/lib/wic/utils/partitionedfs.py deleted file mode 100644 index cdf8f08015..0000000000 --- a/scripts/lib/wic/utils/partitionedfs.py +++ /dev/null | |||
| @@ -1,301 +0,0 @@ | |||
| 1 | #!/usr/bin/env python -tt | ||
| 2 | # | ||
| 3 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
| 4 | # Copyright (c) 2007, 2008 Red Hat, Inc. | ||
| 5 | # Copyright (c) 2008 Daniel P. Berrange | ||
| 6 | # Copyright (c) 2008 David P. Huff | ||
| 7 | # | ||
| 8 | # This program is free software; you can redistribute it and/or modify it | ||
| 9 | # under the terms of the GNU General Public License as published by the Free | ||
| 10 | # Software Foundation; version 2 of the License | ||
| 11 | # | ||
| 12 | # This program is distributed in the hope that it will be useful, but | ||
| 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 15 | # for more details. | ||
| 16 | # | ||
| 17 | # You should have received a copy of the GNU General Public License along | ||
| 18 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
| 19 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 20 | |||
| 21 | import os | ||
| 22 | |||
| 23 | from wic import msger | ||
| 24 | from wic.utils.errors import ImageError | ||
| 25 | from wic.utils.misc import exec_native_cmd | ||
| 26 | from wic.filemap import sparse_copy | ||
| 27 | |||
| 28 | # Overhead of the MBR partitioning scheme (just one sector) | ||
| 29 | MBR_OVERHEAD = 1 | ||
| 30 | |||
| 31 | # Overhead of the GPT partitioning scheme | ||
| 32 | GPT_OVERHEAD = 34 | ||
| 33 | |||
| 34 | # Size of a sector in bytes | ||
| 35 | SECTOR_SIZE = 512 | ||
| 36 | |||
| 37 | class PartitionedImage(): | ||
| 38 | """ | ||
| 39 | Partitioned image in a file. | ||
| 40 | """ | ||
| 41 | |||
| 42 | def __init__(self, path, ptable_format, native_sysroot=None): | ||
| 43 | self.path = path # Path to the image file | ||
| 44 | self.numpart = 0 # Number of allocated partitions | ||
| 45 | self.realpart = 0 # Number of partitions in the partition table | ||
| 46 | self.offset = 0 # Offset of next partition (in sectors) | ||
| 47 | self.min_size = 0 # Minimum required disk size to fit | ||
| 48 | # all partitions (in bytes) | ||
| 49 | self.ptable_format = ptable_format # Partition table format | ||
| 50 | # Disk system identifier | ||
| 51 | self.identifier = int.from_bytes(os.urandom(4), 'little') | ||
| 52 | |||
| 53 | self.partitions = [] | ||
| 54 | self.partimages = [] | ||
| 55 | # Size of a sector used in calculations | ||
| 56 | self.sector_size = SECTOR_SIZE | ||
| 57 | self.native_sysroot = native_sysroot | ||
| 58 | |||
| 59 | def add_partition(self, part): | ||
| 60 | """ | ||
| 61 | Add the next partition. Partitions have to be added in the | ||
| 62 | first-to-last order. | ||
| 63 | """ | ||
| 64 | part.ks_pnum = len(self.partitions) | ||
| 65 | |||
| 66 | # Converting kB to sectors for parted | ||
| 67 | part.size_sec = part.disk_size * 1024 // self.sector_size | ||
| 68 | |||
| 69 | self.partitions.append(part) | ||
| 70 | |||
| 71 | def layout_partitions(self): | ||
| 72 | """ Layout the partitions, meaning calculate the position of every | ||
| 73 | partition on the disk. The 'ptable_format' parameter defines the | ||
| 74 | partition table format and may be "msdos". """ | ||
| 75 | |||
| 76 | msger.debug("Assigning %s partitions to disks" % self.ptable_format) | ||
| 77 | |||
| 78 | # Go through partitions in the order they are added in .ks file | ||
| 79 | for num in range(len(self.partitions)): | ||
| 80 | part = self.partitions[num] | ||
| 81 | |||
| 82 | if self.ptable_format == 'msdos' and part.part_type: | ||
| 83 | # The --part-type can also be implemented for MBR partitions, | ||
| 84 | # in which case it would map to the 1-byte "partition type" | ||
| 85 | # filed at offset 3 of the partition entry. | ||
| 86 | raise ImageError("setting custom partition type is not " \ | ||
| 87 | "implemented for msdos partitions") | ||
| 88 | |||
| 89 | # Get the disk where the partition is located | ||
| 90 | self.numpart += 1 | ||
| 91 | if not part.no_table: | ||
| 92 | self.realpart += 1 | ||
| 93 | |||
| 94 | if self.numpart == 1: | ||
| 95 | if self.ptable_format == "msdos": | ||
| 96 | overhead = MBR_OVERHEAD | ||
| 97 | elif self.ptable_format == "gpt": | ||
| 98 | overhead = GPT_OVERHEAD | ||
| 99 | |||
| 100 | # Skip one sector required for the partitioning scheme overhead | ||
| 101 | self.offset += overhead | ||
| 102 | |||
| 103 | if self.realpart > 3: | ||
| 104 | # Reserve a sector for EBR for every logical partition | ||
| 105 | # before alignment is performed. | ||
| 106 | if self.ptable_format == "msdos": | ||
| 107 | self.offset += 1 | ||
| 108 | |||
| 109 | if part.align: | ||
| 110 | # If not first partition and we do have alignment set we need | ||
| 111 | # to align the partition. | ||
| 112 | # FIXME: This leaves a empty spaces to the disk. To fill the | ||
| 113 | # gaps we could enlargea the previous partition? | ||
| 114 | |||
| 115 | # Calc how much the alignment is off. | ||
| 116 | align_sectors = self.offset % (part.align * 1024 // self.sector_size) | ||
| 117 | |||
| 118 | if align_sectors: | ||
| 119 | # If partition is not aligned as required, we need | ||
| 120 | # to move forward to the next alignment point | ||
| 121 | align_sectors = (part.align * 1024 // self.sector_size) - align_sectors | ||
| 122 | |||
| 123 | msger.debug("Realignment for %s%s with %s sectors, original" | ||
| 124 | " offset %s, target alignment is %sK." % | ||
| 125 | (part.disk, self.numpart, align_sectors, | ||
| 126 | self.offset, part.align)) | ||
| 127 | |||
| 128 | # increase the offset so we actually start the partition on right alignment | ||
| 129 | self.offset += align_sectors | ||
| 130 | |||
| 131 | part.start = self.offset | ||
| 132 | self.offset += part.size_sec | ||
| 133 | |||
| 134 | part.type = 'primary' | ||
| 135 | if not part.no_table: | ||
| 136 | part.num = self.realpart | ||
| 137 | else: | ||
| 138 | part.num = 0 | ||
| 139 | |||
| 140 | if self.ptable_format == "msdos": | ||
| 141 | # only count the partitions that are in partition table | ||
| 142 | if len([p for p in self.partitions if not p.no_table]) > 4: | ||
| 143 | if self.realpart > 3: | ||
| 144 | part.type = 'logical' | ||
| 145 | part.num = self.realpart + 1 | ||
| 146 | |||
| 147 | msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " | ||
| 148 | "sectors (%d bytes)." \ | ||
| 149 | % (part.mountpoint, part.disk, part.num, | ||
| 150 | part.start, self.offset - 1, | ||
| 151 | part.size_sec, part.size_sec * self.sector_size)) | ||
| 152 | |||
| 153 | # Once all the partitions have been layed out, we can calculate the | ||
| 154 | # minumim disk size | ||
| 155 | self.min_size = self.offset | ||
| 156 | if self.ptable_format == "gpt": | ||
| 157 | self.min_size += GPT_OVERHEAD | ||
| 158 | |||
| 159 | self.min_size *= self.sector_size | ||
| 160 | |||
| 161 | def _create_partition(self, device, parttype, fstype, start, size): | ||
| 162 | """ Create a partition on an image described by the 'device' object. """ | ||
| 163 | |||
| 164 | # Start is included to the size so we need to substract one from the end. | ||
| 165 | end = start + size - 1 | ||
| 166 | msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" % | ||
| 167 | (parttype, start, end, size)) | ||
| 168 | |||
| 169 | cmd = "parted -s %s unit s mkpart %s" % (device, parttype) | ||
| 170 | if fstype: | ||
| 171 | cmd += " %s" % fstype | ||
| 172 | cmd += " %d %d" % (start, end) | ||
| 173 | |||
| 174 | return exec_native_cmd(cmd, self.native_sysroot) | ||
| 175 | |||
| 176 | def create(self): | ||
| 177 | msger.debug("Creating sparse file %s" % self.path) | ||
| 178 | with open(self.path, 'w') as sparse: | ||
| 179 | os.ftruncate(sparse.fileno(), self.min_size) | ||
| 180 | |||
| 181 | msger.debug("Initializing partition table for %s" % self.path) | ||
| 182 | exec_native_cmd("parted -s %s mklabel %s" % | ||
| 183 | (self.path, self.ptable_format), self.native_sysroot) | ||
| 184 | |||
| 185 | msger.debug("Set disk identifier %x" % self.identifier) | ||
| 186 | with open(self.path, 'r+b') as img: | ||
| 187 | img.seek(0x1B8) | ||
| 188 | img.write(self.identifier.to_bytes(4, 'little')) | ||
| 189 | |||
| 190 | msger.debug("Creating partitions") | ||
| 191 | |||
| 192 | for part in self.partitions: | ||
| 193 | if part.num == 0: | ||
| 194 | continue | ||
| 195 | |||
| 196 | if self.ptable_format == "msdos" and part.num == 5: | ||
| 197 | # Create an extended partition (note: extended | ||
| 198 | # partition is described in MBR and contains all | ||
| 199 | # logical partitions). The logical partitions save a | ||
| 200 | # sector for an EBR just before the start of a | ||
| 201 | # partition. The extended partition must start one | ||
| 202 | # sector before the start of the first logical | ||
| 203 | # partition. This way the first EBR is inside of the | ||
| 204 | # extended partition. Since the extended partitions | ||
| 205 | # starts a sector before the first logical partition, | ||
| 206 | # add a sector at the back, so that there is enough | ||
| 207 | # room for all logical partitions. | ||
| 208 | self._create_partition(self.path, "extended", | ||
| 209 | None, part.start - 1, | ||
| 210 | self.offset - part.start + 1) | ||
| 211 | |||
| 212 | if part.fstype == "swap": | ||
| 213 | parted_fs_type = "linux-swap" | ||
| 214 | elif part.fstype == "vfat": | ||
| 215 | parted_fs_type = "fat32" | ||
| 216 | elif part.fstype == "msdos": | ||
| 217 | parted_fs_type = "fat16" | ||
| 218 | elif part.fstype == "ontrackdm6aux3": | ||
| 219 | parted_fs_type = "ontrackdm6aux3" | ||
| 220 | else: | ||
| 221 | # Type for ext2/ext3/ext4/btrfs | ||
| 222 | parted_fs_type = "ext2" | ||
| 223 | |||
| 224 | # Boot ROM of OMAP boards require vfat boot partition to have an | ||
| 225 | # even number of sectors. | ||
| 226 | if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \ | ||
| 227 | and part.size_sec % 2: | ||
| 228 | msger.debug("Subtracting one sector from '%s' partition to " \ | ||
| 229 | "get even number of sectors for the partition" % \ | ||
| 230 | part.mountpoint) | ||
| 231 | part.size_sec -= 1 | ||
| 232 | |||
| 233 | self._create_partition(self.path, part.type, | ||
| 234 | parted_fs_type, part.start, part.size_sec) | ||
| 235 | |||
| 236 | if part.part_type: | ||
| 237 | msger.debug("partition %d: set type UID to %s" % \ | ||
| 238 | (part.num, part.part_type)) | ||
| 239 | exec_native_cmd("sgdisk --typecode=%d:%s %s" % \ | ||
| 240 | (part.num, part.part_type, | ||
| 241 | self.path), self.native_sysroot) | ||
| 242 | |||
| 243 | if part.uuid and self.ptable_format == "gpt": | ||
| 244 | msger.debug("partition %d: set UUID to %s" % \ | ||
| 245 | (part.num, part.uuid)) | ||
| 246 | exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ | ||
| 247 | (part.num, part.uuid, self.path), | ||
| 248 | self.native_sysroot) | ||
| 249 | |||
| 250 | if part.label and self.ptable_format == "gpt": | ||
| 251 | msger.debug("partition %d: set name to %s" % \ | ||
| 252 | (part.num, part.label)) | ||
| 253 | exec_native_cmd("parted -s %s name %d %s" % \ | ||
| 254 | (self.path, part.num, part.label), | ||
| 255 | self.native_sysroot) | ||
| 256 | |||
| 257 | if part.active: | ||
| 258 | flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot" | ||
| 259 | msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ | ||
| 260 | (flag_name, part.num, self.path)) | ||
| 261 | exec_native_cmd("parted -s %s set %d %s on" % \ | ||
| 262 | (self.path, part.num, flag_name), | ||
| 263 | self.native_sysroot) | ||
| 264 | if part.system_id: | ||
| 265 | exec_native_cmd("sfdisk --part-type %s %s %s" % \ | ||
| 266 | (self.path, part.num, part.system_id), | ||
| 267 | self.native_sysroot) | ||
| 268 | |||
| 269 | # Parted defaults to enabling the lba flag for fat16 partitions, | ||
| 270 | # which causes compatibility issues with some firmware (and really | ||
| 271 | # isn't necessary). | ||
| 272 | if parted_fs_type == "fat16": | ||
| 273 | if self.ptable_format == 'msdos': | ||
| 274 | msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ | ||
| 275 | (part.num, self.path)) | ||
| 276 | exec_native_cmd("parted -s %s set %d lba off" % \ | ||
| 277 | (self.path, part.num), | ||
| 278 | self.native_sysroot) | ||
| 279 | |||
| 280 | def cleanup(self): | ||
| 281 | # remove partition images | ||
| 282 | for image in self.partimages: | ||
| 283 | os.remove(image) | ||
| 284 | |||
| 285 | def assemble(self): | ||
| 286 | msger.debug("Installing partitions") | ||
| 287 | |||
| 288 | for part in self.partitions: | ||
| 289 | source = part.source_file | ||
| 290 | if source: | ||
| 291 | # install source_file contents into a partition | ||
| 292 | sparse_copy(source, self.path, part.start * self.sector_size) | ||
| 293 | |||
| 294 | msger.debug("Installed %s in partition %d, sectors %d-%d, " | ||
| 295 | "size %d sectors" % \ | ||
| 296 | (source, part.num, part.start, | ||
| 297 | part.start + part.size_sec - 1, part.size_sec)) | ||
| 298 | |||
| 299 | partimage = self.path + '.p%d' % part.num | ||
| 300 | os.rename(source, partimage) | ||
| 301 | self.partimages.append(partimage) | ||
