| # -*- coding: utf-8 -*- |
| # Copyright 2018 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Library for handling Chrome OS partition.""" |
| |
| from __future__ import print_function |
| |
| import os |
| import tempfile |
| |
| from chromite.lib import constants |
| from chromite.lib import cros_build_lib |
| from chromite.lib import cros_logging as logging |
| from chromite.lib import image_lib |
| from chromite.lib import path_util |
| |
| from chromite.lib.paygen import filelib |
| |
| |
| DLC_IMAGE = 'dlc' |
| CROS_IMAGE = 'cros' |
| |
| |
| def ExtractPartition(filename, partition, out_part): |
| """Extracts partition from an image file. |
| |
| Args: |
| filename: The image file. |
| partition: The partition name. e.g. ROOT or KERNEL. |
| out_part: The output partition file. |
| """ |
| parts = image_lib.GetImageDiskPartitionInfo(filename) |
| part_info = [p for p in parts if p.name == partition][0] |
| |
| offset = int(part_info.start) |
| length = int(part_info.size) |
| |
| filelib.CopyFileSegment(filename, 'rb', length, out_part, 'wb', |
| in_seek=offset) |
| |
| |
| def Ext2FileSystemSize(ext2_file): |
| """Return the size of an ext2 filesystem in bytes. |
| |
| Args: |
| ext2_file: The path to the ext2 file. |
| """ |
| # dumpe2fs is normally installed in /sbin but doesn't require root. |
| dump = cros_build_lib.run(['/sbin/dumpe2fs', '-h', ext2_file], |
| print_cmd=False, capture_output=True, |
| encoding='utf-8').stdout |
| fs_blocks = 0 |
| fs_blocksize = 0 |
| for line in dump.split('\n'): |
| if not line: |
| continue |
| label, data = line.split(':')[:2] |
| if label == 'Block count': |
| fs_blocks = int(data) |
| elif label == 'Block size': |
| fs_blocksize = int(data) |
| |
| return fs_blocks * fs_blocksize |
| |
| |
| def PatchKernel(image, kern_file): |
| """Patches a kernel with vblock from a stateful partition. |
| |
| Args: |
| image: The stateful partition image. |
| kern_file: The kernel file. |
| """ |
| |
| with tempfile.NamedTemporaryFile(prefix='stateful') as state_out, \ |
| tempfile.NamedTemporaryFile(prefix='vmlinuz_hd.vblock') as vblock: |
| ExtractPartition(image, constants.PART_STATE, state_out) |
| cros_build_lib.run( |
| ['e2cp', '%s:/vmlinuz_hd.vblock' % state_out, vblock]) |
| filelib.CopyFileSegment( |
| vblock, 'rb', os.path.getsize(vblock), kern_file, 'r+b') |
| |
| |
| def ExtractKernel(image, kern_out): |
| """Extracts the kernel from the given image. |
| |
| Args: |
| image: The image containing the kernel partition. |
| kern_out: The output kernel file. |
| """ |
| ExtractPartition(image, constants.PART_KERN_B, kern_out) |
| with open(kern_out, 'rb') as kern: |
| if not any(kern.read(65536)): |
| logging.warning('%s: Kernel B is empty, patching kernel A.', image) |
| ExtractPartition(image, constants.PART_KERN_A, kern_out) |
| PatchKernel(image, kern_out) |
| |
| |
| def ExtractRoot(image, root_out, truncate=True): |
| """Extract the rootfs partition from a gpt image. |
| |
| Args: |
| image: The input image file. |
| root_out: The output root partition file. |
| truncate: If true, truncate the partition to the file system size. |
| """ |
| ExtractPartition(image, constants.PART_ROOT_A, root_out) |
| |
| if not truncate: |
| return |
| |
| # We only update the filesystem part of the partition, which is stored in the |
| # gpt script. So we need to truncated it to the file system size if asked for. |
| root_out_size = Ext2FileSystemSize(root_out) |
| if root_out_size: |
| with open(root_out, 'ab') as root: |
| root.truncate(root_out_size) |
| logging.info('Truncated root to %d bytes.', root_out_size) |
| else: |
| raise IOError('Error truncating the rootfs to filesystem size.') |
| |
| |
| def IsSquashfsImage(image): |
| """Returns true if the image is detected to be Squashfs.""" |
| try: |
| # -s: Display file system superblock. |
| cros_build_lib.run( |
| ['unsquashfs', '-s', path_util.ToChrootPath(image)], |
| stdout=True, |
| enter_chroot=True) |
| return True |
| except cros_build_lib.RunCommandError: |
| return False |
| |
| |
| def IsExt4Image(image): |
| """Returns true if the image is detected to be ext2/ext3/ext4.""" |
| try: |
| # -l: Listing the content of the superblock structure. |
| cros_build_lib.sudo_run( |
| ['tune2fs', '-l', path_util.ToChrootPath(image)], stdout=True, |
| enter_chroot=True) |
| return True |
| except cros_build_lib.RunCommandError: |
| return False |
| |
| |
| def IsGptImage(image): |
| """Returns true if the image is a GPT image.""" |
| try: |
| return bool(image_lib.GetImageDiskPartitionInfo(image)) |
| except cros_build_lib.RunCommandError: |
| return False |
| |
| |
| def LookupImageType(image): |
| """Returns the image type given the path to an image. |
| |
| Args: |
| image: The path to a GPT or Squashfs Image. |
| |
| Returns: |
| The type of the image. None if it cannot detect the image type. |
| """ |
| if IsGptImage(image): |
| return CROS_IMAGE |
| elif IsSquashfsImage(image) or IsExt4Image(image): |
| return DLC_IMAGE |
| |
| return None |