blob: bc11669be5cee008c62215345c71bcb53413870c [file] [log] [blame]
# Copyright 2021 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Utility functions that process goldfish images."""
import os
import shutil
from acloud import errors
from acloud.internal.lib import ota_tools
# File names under working directory.
_UNPACK_DIR_NAME = "unpacked_boot_img"
_MIXED_RAMDISK_IMAGE_NAME = "mixed_ramdisk"
# File names in unpacked boot image.
_UNPACKED_KERNEL_IMAGE_NAME = "kernel"
_UNPACKED_RAMDISK_IMAGE_NAME = "ramdisk"
# File names in a build environment or an SDK repository.
SYSTEM_QEMU_IMAGE_NAME = "system-qemu.img"
_SDK_REPO_SYSTEM_IMAGE_NAME = "system.img"
_MISC_INFO_FILE_NAME = "misc_info.txt"
_SYSTEM_QEMU_CONFIG_FILE_NAME = "system-qemu-config.txt"
# File names in the search order of emulator.
_DISK_IMAGE_NAMES = (SYSTEM_QEMU_IMAGE_NAME, _SDK_REPO_SYSTEM_IMAGE_NAME)
_KERNEL_IMAGE_NAMES = ("kernel-ranchu", "kernel-ranchu-64", "kernel")
_RAMDISK_IMAGE_NAMES = ("ramdisk-qemu.img", "ramdisk.img")
def _FindFileByNames(parent_dir, names):
"""Find file under a directory by names.
Args:
parent_dir: The directory to find the file in.
names: A list of file names.
Returns:
The path to the first existing file in the list.
Raises:
errors.GetLocalImageError if none of the files exist.
"""
for name in names:
path = os.path.join(parent_dir, name)
if os.path.isfile(path):
return path
raise errors.GetLocalImageError("No %s in %s." %
(", ".join(names), parent_dir))
def _UnpackBootImage(output_dir, boot_image_path, ota):
"""Unpack a boot image and find kernel images.
Args:
output_dir: The directory where the boot image is unpacked.
boot_image_path: The path to the boot image.
ota: An instance of ota_tools.OtaTools.
Returns:
The kernel image path and the ramdisk image path.
Raises:
errors.GetLocalImageError if the kernel or the ramdisk is not found.
"""
ota.UnpackBootImg(output_dir, boot_image_path)
kernel_path = os.path.join(output_dir, _UNPACKED_KERNEL_IMAGE_NAME)
ramdisk_path = os.path.join(output_dir, _UNPACKED_RAMDISK_IMAGE_NAME)
if not os.path.isfile(kernel_path):
raise errors.GetLocalImageError("No kernel in %s." % boot_image_path)
if not os.path.isfile(ramdisk_path):
raise errors.GetLocalImageError("No ramdisk in %s." % boot_image_path)
return kernel_path, ramdisk_path
def _MixRamdiskImages(output_path, original_ramdisk_path,
boot_ramdisk_path):
"""Mix an emulator ramdisk with a boot ramdisk.
An emulator ramdisk consists of a boot ramdisk and a vendor ramdisk.
This method overlays a new boot ramdisk on the emulator ramdisk by
concatenating them.
Args:
output_path: The path to the output ramdisk.
original_ramdisk_path: The path to the emulator ramdisk.
boot_ramdisk_path: The path to the boot ramdisk.
"""
with open(output_path, "wb") as mixed_ramdisk:
with open(original_ramdisk_path, "rb") as ramdisk:
shutil.copyfileobj(ramdisk, mixed_ramdisk)
with open(boot_ramdisk_path, "rb") as ramdisk:
shutil.copyfileobj(ramdisk, mixed_ramdisk)
def MixWithBootImage(output_dir, image_dir, boot_image_path, ota):
"""Mix emulator kernel images with a boot image.
Args:
output_dir: The directory containing the output and intermediate files.
image_dir: The directory containing emulator kernel and ramdisk images.
boot_image_path: The path to the boot image.
ota: An instance of ota_tools.OtaTools.
Returns:
The paths to the kernel and ramdisk images in output_dir.
Raises:
errors.GetLocalImageError if any image is not found.
"""
unpack_dir = os.path.join(output_dir, _UNPACK_DIR_NAME)
if os.path.exists(unpack_dir):
shutil.rmtree(unpack_dir)
os.makedirs(unpack_dir, exist_ok=True)
kernel_path, boot_ramdisk_path = _UnpackBootImage(
unpack_dir, boot_image_path, ota)
# The ramdisk unpacked from boot_image_path does not include emulator's
# kernel modules. The ramdisk in image_dir contains the modules. This
# method mixes the two ramdisks.
mixed_ramdisk_path = os.path.join(output_dir, _MIXED_RAMDISK_IMAGE_NAME)
original_ramdisk_path = _FindFileByNames(image_dir, _RAMDISK_IMAGE_NAMES)
_MixRamdiskImages(mixed_ramdisk_path, original_ramdisk_path,
boot_ramdisk_path)
return kernel_path, mixed_ramdisk_path
def FindKernelImages(image_dir):
"""Find emulator kernel images in a directory.
Args:
image_dir: The directory to find the images in.
Returns:
The paths to the kernel image and the ramdisk image.
Raises:
errors.GetLocalImageError if any image is not found.
"""
return (_FindFileByNames(image_dir, _KERNEL_IMAGE_NAMES),
_FindFileByNames(image_dir, _RAMDISK_IMAGE_NAMES))
def FindDiskImage(image_dir):
"""Find an emulator disk image in a directory.
Args:
image_dir: The directory to find the image in.
Returns:
The path to the disk image.
Raises:
errors.GetLocalImageError if the image is not found.
"""
return _FindFileByNames(image_dir, _DISK_IMAGE_NAMES)
def MixWithSystemImage(output_dir, image_dir, system_image_path, ota):
"""Mix emulator images and a system image into a disk image.
Args:
output_dir: The path to the output directory.
image_dir: The input directory that provides images except
system.img.
system_image_path: The path to the system image.
ota: An instance of ota_tools.OtaTools.
Returns:
The path to the mixed disk image in output_dir.
Raises:
errors.GetLocalImageError if any required file is not found.
"""
os.makedirs(output_dir, exist_ok=True)
# Create the super image.
mixed_super_image_path = os.path.join(output_dir, "mixed_super.img")
ota.BuildSuperImage(
mixed_super_image_path,
_FindFileByNames(image_dir, [_MISC_INFO_FILE_NAME]),
lambda partition: ota_tools.GetImageForPartition(
partition, image_dir, system=system_image_path))
# Create the vbmeta image.
vbmeta_image_path = os.path.join(output_dir, "disabled_vbmeta.img")
ota.MakeDisabledVbmetaImage(vbmeta_image_path)
# Create the disk image.
disk_image = os.path.join(output_dir, "mixed_disk.img")
ota.MkCombinedImg(
disk_image,
_FindFileByNames(image_dir, [_SYSTEM_QEMU_CONFIG_FILE_NAME]),
lambda partition: ota_tools.GetImageForPartition(
partition, image_dir, super=mixed_super_image_path,
vbmeta=vbmeta_image_path))
return disk_image