From 2550622371f5c50857e5d58eabab01a1823c6fc3 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Thu, 9 Feb 2017 16:28:54 +0200 Subject: wic: move PartitionedImage class to direct.py As PartitionedImage is only used in direct.py it makes sense to move it there. It's easier to maintain (and refactor) it this way. Signed-off-by: Ed Bartosh --- scripts/lib/wic/plugins/imager/direct.py | 278 ++++++++++++++++++++++++++++++- 1 file changed, 276 insertions(+), 2 deletions(-) (limited to 'scripts/lib/wic/plugins/imager/direct.py') 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 from time import strftime from wic import msger +from wic.filemap import sparse_copy from wic.ksparser import KickStart, KickStartError from wic.plugin import pluginmgr from wic.pluginbase import ImagerPlugin from wic.utils.errors import CreatorError, ImageError from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd -from wic.utils.partitionedfs import PartitionedImage - class DirectPlugin(ImagerPlugin): """ @@ -316,3 +315,278 @@ class DirectPlugin(ImagerPlugin): # remove work directory shutil.rmtree(self.workdir, ignore_errors=True) + +# Overhead of the MBR partitioning scheme (just one sector) +MBR_OVERHEAD = 1 + +# Overhead of the GPT partitioning scheme +GPT_OVERHEAD = 34 + +# Size of a sector in bytes +SECTOR_SIZE = 512 + +class PartitionedImage(): + """ + Partitioned image in a file. + """ + + def __init__(self, path, ptable_format, native_sysroot=None): + self.path = path # Path to the image file + self.numpart = 0 # Number of allocated partitions + self.realpart = 0 # Number of partitions in the partition table + self.offset = 0 # Offset of next partition (in sectors) + self.min_size = 0 # Minimum required disk size to fit + # all partitions (in bytes) + self.ptable_format = ptable_format # Partition table format + # Disk system identifier + self.identifier = int.from_bytes(os.urandom(4), 'little') + + self.partitions = [] + self.partimages = [] + # Size of a sector used in calculations + self.sector_size = SECTOR_SIZE + self.native_sysroot = native_sysroot + + def add_partition(self, part): + """ + Add the next partition. Partitions have to be added in the + first-to-last order. + """ + part.ks_pnum = len(self.partitions) + + # Converting kB to sectors for parted + part.size_sec = part.disk_size * 1024 // self.sector_size + + self.partitions.append(part) + + def layout_partitions(self): + """ Layout the partitions, meaning calculate the position of every + partition on the disk. The 'ptable_format' parameter defines the + partition table format and may be "msdos". """ + + msger.debug("Assigning %s partitions to disks" % self.ptable_format) + + # Go through partitions in the order they are added in .ks file + for num in range(len(self.partitions)): + part = self.partitions[num] + + if self.ptable_format == 'msdos' and part.part_type: + # The --part-type can also be implemented for MBR partitions, + # in which case it would map to the 1-byte "partition type" + # filed at offset 3 of the partition entry. + raise ImageError("setting custom partition type is not " \ + "implemented for msdos partitions") + + # Get the disk where the partition is located + self.numpart += 1 + if not part.no_table: + self.realpart += 1 + + if self.numpart == 1: + if self.ptable_format == "msdos": + overhead = MBR_OVERHEAD + elif self.ptable_format == "gpt": + overhead = GPT_OVERHEAD + + # Skip one sector required for the partitioning scheme overhead + self.offset += overhead + + if self.realpart > 3: + # Reserve a sector for EBR for every logical partition + # before alignment is performed. + if self.ptable_format == "msdos": + self.offset += 1 + + if part.align: + # If not first partition and we do have alignment set we need + # to align the partition. + # FIXME: This leaves a empty spaces to the disk. To fill the + # gaps we could enlargea the previous partition? + + # Calc how much the alignment is off. + align_sectors = self.offset % (part.align * 1024 // self.sector_size) + + if align_sectors: + # If partition is not aligned as required, we need + # to move forward to the next alignment point + align_sectors = (part.align * 1024 // self.sector_size) - align_sectors + + msger.debug("Realignment for %s%s with %s sectors, original" + " offset %s, target alignment is %sK." % + (part.disk, self.numpart, align_sectors, + self.offset, part.align)) + + # increase the offset so we actually start the partition on right alignment + self.offset += align_sectors + + part.start = self.offset + self.offset += part.size_sec + + part.type = 'primary' + if not part.no_table: + part.num = self.realpart + else: + part.num = 0 + + if self.ptable_format == "msdos": + # only count the partitions that are in partition table + if len([p for p in self.partitions if not p.no_table]) > 4: + if self.realpart > 3: + part.type = 'logical' + part.num = self.realpart + 1 + + msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " + "sectors (%d bytes)." \ + % (part.mountpoint, part.disk, part.num, + part.start, self.offset - 1, + part.size_sec, part.size_sec * self.sector_size)) + + # Once all the partitions have been layed out, we can calculate the + # minumim disk size + self.min_size = self.offset + if self.ptable_format == "gpt": + self.min_size += GPT_OVERHEAD + + self.min_size *= self.sector_size + + def _create_partition(self, device, parttype, fstype, start, size): + """ Create a partition on an image described by the 'device' object. """ + + # Start is included to the size so we need to substract one from the end. + end = start + size - 1 + msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" % + (parttype, start, end, size)) + + cmd = "parted -s %s unit s mkpart %s" % (device, parttype) + if fstype: + cmd += " %s" % fstype + cmd += " %d %d" % (start, end) + + return exec_native_cmd(cmd, self.native_sysroot) + + def create(self): + msger.debug("Creating sparse file %s" % self.path) + with open(self.path, 'w') as sparse: + os.ftruncate(sparse.fileno(), self.min_size) + + msger.debug("Initializing partition table for %s" % self.path) + exec_native_cmd("parted -s %s mklabel %s" % + (self.path, self.ptable_format), self.native_sysroot) + + msger.debug("Set disk identifier %x" % self.identifier) + with open(self.path, 'r+b') as img: + img.seek(0x1B8) + img.write(self.identifier.to_bytes(4, 'little')) + + msger.debug("Creating partitions") + + for part in self.partitions: + if part.num == 0: + continue + + if self.ptable_format == "msdos" and part.num == 5: + # Create an extended partition (note: extended + # partition is described in MBR and contains all + # logical partitions). The logical partitions save a + # sector for an EBR just before the start of a + # partition. The extended partition must start one + # sector before the start of the first logical + # partition. This way the first EBR is inside of the + # extended partition. Since the extended partitions + # starts a sector before the first logical partition, + # add a sector at the back, so that there is enough + # room for all logical partitions. + self._create_partition(self.path, "extended", + None, part.start - 1, + self.offset - part.start + 1) + + if part.fstype == "swap": + parted_fs_type = "linux-swap" + elif part.fstype == "vfat": + parted_fs_type = "fat32" + elif part.fstype == "msdos": + parted_fs_type = "fat16" + elif part.fstype == "ontrackdm6aux3": + parted_fs_type = "ontrackdm6aux3" + else: + # Type for ext2/ext3/ext4/btrfs + parted_fs_type = "ext2" + + # Boot ROM of OMAP boards require vfat boot partition to have an + # even number of sectors. + if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \ + and part.size_sec % 2: + msger.debug("Subtracting one sector from '%s' partition to " \ + "get even number of sectors for the partition" % \ + part.mountpoint) + part.size_sec -= 1 + + self._create_partition(self.path, part.type, + parted_fs_type, part.start, part.size_sec) + + if part.part_type: + msger.debug("partition %d: set type UID to %s" % \ + (part.num, part.part_type)) + exec_native_cmd("sgdisk --typecode=%d:%s %s" % \ + (part.num, part.part_type, + self.path), self.native_sysroot) + + if part.uuid and self.ptable_format == "gpt": + msger.debug("partition %d: set UUID to %s" % \ + (part.num, part.uuid)) + exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ + (part.num, part.uuid, self.path), + self.native_sysroot) + + if part.label and self.ptable_format == "gpt": + msger.debug("partition %d: set name to %s" % \ + (part.num, part.label)) + exec_native_cmd("parted -s %s name %d %s" % \ + (self.path, part.num, part.label), + self.native_sysroot) + + if part.active: + flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot" + msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ + (flag_name, part.num, self.path)) + exec_native_cmd("parted -s %s set %d %s on" % \ + (self.path, part.num, flag_name), + self.native_sysroot) + if part.system_id: + exec_native_cmd("sfdisk --part-type %s %s %s" % \ + (self.path, part.num, part.system_id), + self.native_sysroot) + + # Parted defaults to enabling the lba flag for fat16 partitions, + # which causes compatibility issues with some firmware (and really + # isn't necessary). + if parted_fs_type == "fat16": + if self.ptable_format == 'msdos': + msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ + (part.num, self.path)) + exec_native_cmd("parted -s %s set %d lba off" % \ + (self.path, part.num), + self.native_sysroot) + + def cleanup(self): + # remove partition images + for image in self.partimages: + os.remove(image) + + def assemble(self): + msger.debug("Installing partitions") + + for part in self.partitions: + source = part.source_file + if source: + # install source_file contents into a partition + sparse_copy(source, self.path, part.start * self.sector_size) + + msger.debug("Installed %s in partition %d, sectors %d-%d, " + "size %d sectors" % \ + (source, part.num, part.start, + part.start + part.size_sec - 1, part.size_sec)) + + partimage = self.path + '.p%d' % part.num + os.rename(source, partimage) + self.partimages.append(partimage) -- cgit 1.2.3-korg