blob: 9f5035ed590dbe9a9bc241b8ea1bfed443cacb0e [file] [log] [blame]
# -*- 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