diff options
-rw-r--r-- | scripts/lib/mic/plugins/source/bootimg-efi.py | 4 | ||||
-rw-r--r-- | scripts/lib/mic/plugins/source/bootimg-pcbios.py | 8 | ||||
-rw-r--r-- | scripts/lib/mic/utils/gpt_parser.py | 331 | ||||
-rw-r--r-- | scripts/lib/mic/utils/partitionedfs.py | 58 |
4 files changed, 11 insertions, 390 deletions
diff --git a/scripts/lib/mic/plugins/source/bootimg-efi.py b/scripts/lib/mic/plugins/source/bootimg-efi.py index 95e1c059b9..d4a777198f 100644 --- a/scripts/lib/mic/plugins/source/bootimg-efi.py +++ b/scripts/lib/mic/plugins/source/bootimg-efi.py @@ -78,9 +78,7 @@ class BootimgEFIPlugin(SourcePlugin): if cr._ptable_format == 'msdos': rootstr = rootdev else: - if not root_part_uuid: - raise MountError("Cannot find the root GPT partition UUID") - rootstr = "PARTUUID=%s" % root_part_uuid + raise MountError("Unsupported partition table format found") grubefi_conf += "linux %s root=%s rootwait %s\n" \ % (kernel, rootstr, options) diff --git a/scripts/lib/mic/plugins/source/bootimg-pcbios.py b/scripts/lib/mic/plugins/source/bootimg-pcbios.py index 9959645606..34343205ac 100644 --- a/scripts/lib/mic/plugins/source/bootimg-pcbios.py +++ b/scripts/lib/mic/plugins/source/bootimg-pcbios.py @@ -50,9 +50,7 @@ class BootimgPcbiosPlugin(SourcePlugin): disk image. In this case, we install the MBR. """ mbrfile = "%s/syslinux/" % bootimg_dir - if cr._ptable_format == 'gpt': - mbrfile += "gptmbr.bin" - else: + if cr._ptable_format == 'msdos': mbrfile += "mbr.bin" if not os.path.exists(mbrfile): @@ -110,9 +108,7 @@ class BootimgPcbiosPlugin(SourcePlugin): if cr._ptable_format == 'msdos': rootstr = rootdev else: - if not root_part_uuid: - raise MountError("Cannot find the root GPT partition UUID") - rootstr = "PARTUUID=%s" % root_part_uuid + raise MountError("Unsupported partition table format found") syslinux_conf += "APPEND label=boot root=%s %s\n" % (rootstr, options) diff --git a/scripts/lib/mic/utils/gpt_parser.py b/scripts/lib/mic/utils/gpt_parser.py deleted file mode 100644 index 5d43b70778..0000000000 --- a/scripts/lib/mic/utils/gpt_parser.py +++ /dev/null @@ -1,331 +0,0 @@ -#!/usr/bin/python -tt -# -# Copyright (c) 2013 Intel, Inc. -# -# 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. - -""" This module implements a simple GPT partitions parser which can read the -GPT header and the GPT partition table. """ - -import struct -import uuid -import binascii -from mic.utils.errors import MountError - -_GPT_HEADER_FORMAT = "<8s4sIIIQQQQ16sQIII" -_GPT_HEADER_SIZE = struct.calcsize(_GPT_HEADER_FORMAT) -_GPT_ENTRY_FORMAT = "<16s16sQQQ72s" -_GPT_ENTRY_SIZE = struct.calcsize(_GPT_ENTRY_FORMAT) -_SUPPORTED_GPT_REVISION = '\x00\x00\x01\x00' - -def _stringify_uuid(binary_uuid): - """ A small helper function to transform a binary UUID into a string - format. """ - - uuid_str = str(uuid.UUID(bytes_le = binary_uuid)) - - return uuid_str.upper() - -def _calc_header_crc(raw_hdr): - """ Calculate GPT header CRC32 checksum. The 'raw_hdr' parameter has to - be a list or a tuple containing all the elements of the GPT header in a - "raw" form, meaning that it should simply contain "unpacked" disk data. - """ - - raw_hdr = list(raw_hdr) - raw_hdr[3] = 0 - raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr) - - return binascii.crc32(raw_hdr) & 0xFFFFFFFF - -def _validate_header(raw_hdr): - """ Validate the GPT header. The 'raw_hdr' parameter has to be a list or a - tuple containing all the elements of the GPT header in a "raw" form, - meaning that it should simply contain "unpacked" disk data. """ - - # Validate the signature - if raw_hdr[0] != 'EFI PART': - raise MountError("GPT partition table not found") - - # Validate the revision - if raw_hdr[1] != _SUPPORTED_GPT_REVISION: - raise MountError("Unsupported GPT revision '%s', supported revision " \ - "is '%s'" % \ - (binascii.hexlify(raw_hdr[1]), - binascii.hexlify(_SUPPORTED_GPT_REVISION))) - - # Validate header size - if raw_hdr[2] != _GPT_HEADER_SIZE: - raise MountError("Bad GPT header size: %d bytes, expected %d" % \ - (raw_hdr[2], _GPT_HEADER_SIZE)) - - crc = _calc_header_crc(raw_hdr) - if raw_hdr[3] != crc: - raise MountError("GPT header crc mismatch: %#x, should be %#x" % \ - (crc, raw_hdr[3])) - -class GptParser: - """ GPT partition table parser. Allows reading the GPT header and the - partition table, as well as modifying the partition table records. """ - - def __init__(self, disk_path, sector_size = 512): - """ The class constructor which accepts the following parameters: - * disk_path - full path to the disk image or device node - * sector_size - size of a disk sector in bytes """ - - self.sector_size = sector_size - self.disk_path = disk_path - - try: - self._disk_obj = open(disk_path, 'r+b') - except IOError as err: - raise MountError("Cannot open file '%s' for reading GPT " \ - "partitions: %s" % (disk_path, err)) - - def __del__(self): - """ The class destructor. """ - - self._disk_obj.close() - - def _read_disk(self, offset, size): - """ A helper function which reads 'size' bytes from offset 'offset' of - the disk and checks all the error conditions. """ - - self._disk_obj.seek(offset) - try: - data = self._disk_obj.read(size) - except IOError as err: - raise MountError("cannot read from '%s': %s" % \ - (self.disk_path, err)) - - if len(data) != size: - raise MountError("cannot read %d bytes from offset '%d' of '%s', " \ - "read only %d bytes" % \ - (size, offset, self.disk_path, len(data))) - - return data - - def _write_disk(self, offset, buf): - """ A helper function which writes buffer 'buf' to offset 'offset' of - the disk. This function takes care of unaligned writes and checks all - the error conditions. """ - - # Since we may be dealing with a block device, we only can write in - # 'self.sector_size' chunks. Find the aligned starting and ending - # disk offsets to read. - start = (offset / self.sector_size) * self.sector_size - end = ((start + len(buf)) / self.sector_size + 1) * self.sector_size - - data = self._read_disk(start, end - start) - off = offset - start - data = data[:off] + buf + data[off + len(buf):] - - self._disk_obj.seek(start) - try: - self._disk_obj.write(data) - except IOError as err: - raise MountError("cannot write to '%s': %s" % (self.disk_path, err)) - - def read_header(self, primary = True): - """ Read and verify the GPT header and return a dictionary containing - the following elements: - - 'signature' : header signature - 'revision' : header revision - 'hdr_size' : header size in bytes - 'hdr_crc' : header CRC32 - 'hdr_lba' : LBA of this header - 'hdr_offs' : byte disk offset of this header - 'backup_lba' : backup header LBA - 'backup_offs' : byte disk offset of backup header - 'first_lba' : first usable LBA for partitions - 'first_offs' : first usable byte disk offset for partitions - 'last_lba' : last usable LBA for partitions - 'last_offs' : last usable byte disk offset for partitions - 'disk_uuid' : UUID of the disk - 'ptable_lba' : starting LBA of array of partition entries - 'ptable_offs' : disk byte offset of the start of the partition table - 'ptable_size' : partition table size in bytes - 'entries_cnt' : number of available partition table entries - 'entry_size' : size of a single partition entry - 'ptable_crc' : CRC32 of the partition table - 'primary' : a boolean, if 'True', this is the primary GPT header, - if 'False' - the secondary - 'primary_str' : contains string "primary" if this is the primary GPT - header, and "backup" otherwise - - This dictionary corresponds to the GPT header format. Please, see the - UEFI standard for the description of these fields. - - If the 'primary' parameter is 'True', the primary GPT header is read, - otherwise the backup GPT header is read instead. """ - - # Read and validate the primary GPT header - raw_hdr = self._read_disk(self.sector_size, _GPT_HEADER_SIZE) - raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr) - _validate_header(raw_hdr) - primary_str = "primary" - - if not primary: - # Read and validate the backup GPT header - raw_hdr = self._read_disk(raw_hdr[6] * self.sector_size, _GPT_HEADER_SIZE) - raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr) - _validate_header(raw_hdr) - primary_str = "backup" - - return { 'signature' : raw_hdr[0], - 'revision' : raw_hdr[1], - 'hdr_size' : raw_hdr[2], - 'hdr_crc' : raw_hdr[3], - 'hdr_lba' : raw_hdr[5], - 'hdr_offs' : raw_hdr[5] * self.sector_size, - 'backup_lba' : raw_hdr[6], - 'backup_offs' : raw_hdr[6] * self.sector_size, - 'first_lba' : raw_hdr[7], - 'first_offs' : raw_hdr[7] * self.sector_size, - 'last_lba' : raw_hdr[8], - 'last_offs' : raw_hdr[8] * self.sector_size, - 'disk_uuid' :_stringify_uuid(raw_hdr[9]), - 'ptable_lba' : raw_hdr[10], - 'ptable_offs' : raw_hdr[10] * self.sector_size, - 'ptable_size' : raw_hdr[11] * raw_hdr[12], - 'entries_cnt' : raw_hdr[11], - 'entry_size' : raw_hdr[12], - 'ptable_crc' : raw_hdr[13], - 'primary' : primary, - 'primary_str' : primary_str } - - def _read_raw_ptable(self, header): - """ Read and validate primary or backup partition table. The 'header' - argument is the GPT header. If it is the primary GPT header, then the - primary partition table is read and validated, otherwise - the backup - one. The 'header' argument is a dictionary which is returned by the - 'read_header()' method. """ - - raw_ptable = self._read_disk(header['ptable_offs'], - header['ptable_size']) - - crc = binascii.crc32(raw_ptable) & 0xFFFFFFFF - if crc != header['ptable_crc']: - raise MountError("Partition table at LBA %d (%s) is corrupted" % \ - (header['ptable_lba'], header['primary_str'])) - - return raw_ptable - - def get_partitions(self, primary = True): - """ This is a generator which parses the GPT partition table and - generates the following dictionary for each partition: - - 'index' : the index of the partition table endry - 'offs' : byte disk offset of the partition table entry - 'type_uuid' : partition type UUID - 'part_uuid' : partition UUID - 'first_lba' : the first LBA - 'last_lba' : the last LBA - 'flags' : attribute flags - 'name' : partition name - 'primary' : a boolean, if 'True', this is the primary partition - table, if 'False' - the secondary - 'primary_str' : contains string "primary" if this is the primary GPT - header, and "backup" otherwise - - This dictionary corresponds to the GPT header format. Please, see the - UEFI standard for the description of these fields. - - If the 'primary' parameter is 'True', partitions from the primary GPT - partition table are generated, otherwise partitions from the backup GPT - partition table are generated. """ - - if primary: - primary_str = "primary" - else: - primary_str = "backup" - - header = self.read_header(primary) - raw_ptable = self._read_raw_ptable(header) - - for index in xrange(0, header['entries_cnt']): - start = header['entry_size'] * index - end = start + header['entry_size'] - raw_entry = struct.unpack(_GPT_ENTRY_FORMAT, raw_ptable[start:end]) - - if raw_entry[2] == 0 or raw_entry[3] == 0: - continue - - part_name = str(raw_entry[5].decode('UTF-16').split('\0', 1)[0]) - - yield { 'index' : index, - 'offs' : header['ptable_offs'] + start, - 'type_uuid' : _stringify_uuid(raw_entry[0]), - 'part_uuid' : _stringify_uuid(raw_entry[1]), - 'first_lba' : raw_entry[2], - 'last_lba' : raw_entry[3], - 'flags' : raw_entry[4], - 'name' : part_name, - 'primary' : primary, - 'primary_str' : primary_str } - - def _change_partition(self, header, entry): - """ A helper function for 'change_partitions()' which changes a - a paricular instance of the partition table (primary or backup). """ - - if entry['index'] >= header['entries_cnt']: - raise MountError("Partition table at LBA %d has only %d " \ - "records cannot change record number %d" % \ - (header['entries_cnt'], entry['index'])) - # Read raw GPT header - raw_hdr = self._read_disk(header['hdr_offs'], _GPT_HEADER_SIZE) - raw_hdr = list(struct.unpack(_GPT_HEADER_FORMAT, raw_hdr)) - _validate_header(raw_hdr) - - # Prepare the new partition table entry - raw_entry = struct.pack(_GPT_ENTRY_FORMAT, - uuid.UUID(entry['type_uuid']).bytes_le, - uuid.UUID(entry['part_uuid']).bytes_le, - entry['first_lba'], - entry['last_lba'], - entry['flags'], - entry['name'].encode('UTF-16')) - - # Write the updated entry to the disk - entry_offs = header['ptable_offs'] + \ - header['entry_size'] * entry['index'] - self._write_disk(entry_offs, raw_entry) - - # Calculate and update partition table CRC32 - raw_ptable = self._read_disk(header['ptable_offs'], - header['ptable_size']) - raw_hdr[13] = binascii.crc32(raw_ptable) & 0xFFFFFFFF - - # Calculate and update the GPT header CRC - raw_hdr[3] = _calc_header_crc(raw_hdr) - - # Write the updated header to the disk - raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr) - self._write_disk(header['hdr_offs'], raw_hdr) - - def change_partition(self, entry): - """ Change a GPT partition. The 'entry' argument has the same format as - 'get_partitions()' returns. This function simply changes the partition - table record corresponding to 'entry' in both, the primary and the - backup GPT partition tables. The parition table CRC is re-calculated - and the GPT headers are modified accordingly. """ - - # Change the primary partition table - header = self.read_header(True) - self._change_partition(header, entry) - - # Change the backup partition table - header = self.read_header(False) - self._change_partition(header, entry) diff --git a/scripts/lib/mic/utils/partitionedfs.py b/scripts/lib/mic/utils/partitionedfs.py index 83ce869860..0c4c9ecfa0 100644 --- a/scripts/lib/mic/utils/partitionedfs.py +++ b/scripts/lib/mic/utils/partitionedfs.py @@ -24,13 +24,10 @@ from mic import msger from mic.utils import runner from mic.utils.errors import MountError from mic.utils.fs_related import * -from mic.utils.gpt_parser import GptParser from mic.utils.oe.misc import * # 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 @@ -148,21 +145,20 @@ class PartitionedMount(Mount): 'num': None, # Partition number 'boot': boot, # Bootable flag 'align': align, # Partition alignment - 'part_type' : part_type, # Partition type - 'partuuid': None } # Partition UUID (GPT-only) + 'part_type' : part_type } # Partition type self.__add_partition(part) def layout_partitions(self, ptable_format = "msdos"): """ 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 either "msdos" or "gpt". """ + partition table format and may be "msdos". """ msger.debug("Assigning %s partitions to disks" % ptable_format) - if ptable_format not in ('msdos', 'gpt'): + if ptable_format not in ('msdos'): raise MountError("Unknown partition table format '%s', supported " \ - "formats are: 'msdos' and 'gpt'" % ptable_format) + "formats are: 'msdos'" % ptable_format) if self._partitions_layed_out: return @@ -177,12 +173,12 @@ class PartitionedMount(Mount): raise MountError("No disk %s for partition %s" \ % (p['disk_name'], p['mountpoint'])) - if p['part_type'] and ptable_format != 'gpt': + if p['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 MountError("setting custom partition type is only " \ - "imlemented for GPT partitions") + raise MountError("setting custom partition type is not " \ + "implemented for msdos partitions") # Get the disk where the partition is located d = self.disks[p['disk_name']] @@ -192,8 +188,6 @@ class PartitionedMount(Mount): if d['numpart'] == 1: if ptable_format == "msdos": overhead = MBR_OVERHEAD - else: - overhead = GPT_OVERHEAD # Skip one sector required for the partitioning scheme overhead d['offset'] += overhead @@ -250,9 +244,6 @@ class PartitionedMount(Mount): # minumim disk sizes. for disk_name, d in self.disks.items(): d['min_size'] = d['offset'] - if d['ptable_format'] == 'gpt': - # Account for the backup partition table at the end of the disk - d['min_size'] += GPT_OVERHEAD d['min_size'] *= self.sector_size @@ -339,10 +330,7 @@ class PartitionedMount(Mount): parted_fs_type, p['start'], p['size']) if p['boot']: - if d['ptable_format'] == 'gpt': - flag_name = "legacy_boot" - else: - flag_name = "boot" + flag_name = "boot" msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ (flag_name, p['num'], d['disk'].device)) self.__run_parted(["-s", d['disk'].device, "set", @@ -358,36 +346,6 @@ class PartitionedMount(Mount): self.__run_parted(["-s", d['disk'].device, "set", "%d" % p['num'], "lba", "off"]) - # If the partition table format is "gpt", find out PARTUUIDs for all - # the partitions. And if users specified custom parition type UUIDs, - # set them. - for disk_name, disk in self.disks.items(): - if disk['ptable_format'] != 'gpt': - continue - - pnum = 0 - gpt_parser = GptParser(d['disk'].device, SECTOR_SIZE) - # Iterate over all GPT partitions on this disk - for entry in gpt_parser.get_partitions(): - pnum += 1 - # Find the matching partition in the 'self.partitions' list - for n in d['partitions']: - p = self.partitions[n] - if p['num'] == pnum: - # Found, fetch PARTUUID (partition's unique ID) - p['partuuid'] = entry['part_uuid'] - msger.debug("PARTUUID for partition %d on disk '%s' " \ - "(mount point '%s') is '%s'" % (pnum, \ - disk_name, p['mountpoint'], p['partuuid'])) - if p['part_type']: - entry['type_uuid'] = p['part_type'] - msger.debug("Change type of partition %d on disk " \ - "'%s' (mount point '%s') to '%s'" % \ - (pnum, disk_name, p['mountpoint'], - p['part_type'])) - gpt_parser.change_partition(entry) - - del gpt_parser def __map_partitions(self): """Load it if dm_snapshot isn't loaded. """ |