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 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) 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 @@ -#!/usr/bin/env python -tt -# -# Copyright (c) 2009, 2010, 2011 Intel, Inc. -# Copyright (c) 2007, 2008 Red Hat, Inc. -# Copyright (c) 2008 Daniel P. Berrange -# Copyright (c) 2008 David P. Huff -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation; version 2 of the License -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., 59 -# Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -import os - -from wic import msger -from wic.utils.errors import ImageError -from wic.utils.misc import exec_native_cmd -from wic.filemap import sparse_copy - -# 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) |