128 lines
4.9 KiB
Rust
128 lines
4.9 KiB
Rust
// 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.
|
|
|
|
//! Functions for creating a composite disk image.
|
|
|
|
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::Partition::Partition;
|
|
use anyhow::{anyhow, Context, Error};
|
|
use disk::{
|
|
create_composite_disk, create_disk_file, ImagePartitionType, PartitionInfo, MAX_NESTING_DEPTH,
|
|
};
|
|
use std::fs::{File, OpenOptions};
|
|
use std::os::unix::io::AsRawFd;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
/// Constructs a composite disk image for the given list of partitions, and opens it ready to use.
|
|
///
|
|
/// Returns the composite disk image file, and a list of files whose file descriptors must be passed
|
|
/// to any process which wants to use it. This is necessary because the composite image contains
|
|
/// paths of the form `/proc/self/fd/N` for the partition images.
|
|
pub fn make_composite_image(
|
|
partitions: &[Partition],
|
|
zero_filler_path: &Path,
|
|
output_path: &Path,
|
|
header_path: &Path,
|
|
footer_path: &Path,
|
|
) -> Result<(File, Vec<File>), Error> {
|
|
let (partitions, mut files) = convert_partitions(partitions)?;
|
|
|
|
let mut composite_image = OpenOptions::new()
|
|
.create_new(true)
|
|
.read(true)
|
|
.write(true)
|
|
.open(output_path)
|
|
.with_context(|| format!("Failed to create composite image {:?}", output_path))?;
|
|
let mut header_file =
|
|
OpenOptions::new().create_new(true).read(true).write(true).open(header_path).with_context(
|
|
|| format!("Failed to create composite image header {:?}", header_path),
|
|
)?;
|
|
let mut footer_file =
|
|
OpenOptions::new().create_new(true).read(true).write(true).open(footer_path).with_context(
|
|
|| format!("Failed to create composite image header {:?}", footer_path),
|
|
)?;
|
|
let zero_filler_file = File::open(&zero_filler_path).with_context(|| {
|
|
format!("Failed to open composite image zero filler {:?}", zero_filler_path)
|
|
})?;
|
|
|
|
create_composite_disk(
|
|
&partitions,
|
|
&fd_path_for_file(&zero_filler_file),
|
|
&fd_path_for_file(&header_file),
|
|
&mut header_file,
|
|
&fd_path_for_file(&footer_file),
|
|
&mut footer_file,
|
|
&mut composite_image,
|
|
)?;
|
|
|
|
// Re-open the composite image as read-only.
|
|
let composite_image = File::open(&output_path)
|
|
.with_context(|| format!("Failed to open composite image {:?}", output_path))?;
|
|
|
|
files.push(header_file);
|
|
files.push(footer_file);
|
|
files.push(zero_filler_file);
|
|
|
|
Ok((composite_image, files))
|
|
}
|
|
|
|
/// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
|
|
/// partition, returns the corresponding list of PartitionInfo and the list of files whose file
|
|
/// descriptors must be passed to any process using the composite image.
|
|
fn convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error> {
|
|
// File descriptors to pass to child process.
|
|
let mut files = vec![];
|
|
|
|
let partitions = partitions
|
|
.iter()
|
|
.map(|partition| {
|
|
// TODO(b/187187765): This shouldn't be an Option.
|
|
let file = partition
|
|
.image
|
|
.as_ref()
|
|
.context("Invalid partition image file descriptor")?
|
|
.as_ref()
|
|
.try_clone()
|
|
.context("Failed to clone partition image file descriptor")?;
|
|
let path = fd_path_for_file(&file);
|
|
let size = get_partition_size(&file, &path)?;
|
|
files.push(file);
|
|
|
|
Ok(PartitionInfo {
|
|
label: partition.label.to_owned(),
|
|
path,
|
|
partition_type: ImagePartitionType::LinuxFilesystem,
|
|
writable: partition.writable,
|
|
size,
|
|
})
|
|
})
|
|
.collect::<Result<_, Error>>()?;
|
|
|
|
Ok((partitions, files))
|
|
}
|
|
|
|
fn fd_path_for_file(file: &File) -> PathBuf {
|
|
let fd = file.as_raw_fd();
|
|
format!("/proc/self/fd/{}", fd).into()
|
|
}
|
|
|
|
/// Find the size of the partition image in the given file by parsing the header.
|
|
///
|
|
/// This will work for raw, QCOW2, composite and Android sparse images.
|
|
fn get_partition_size(partition: &File, path: &Path) -> Result<u64, Error> {
|
|
// TODO: Use `context` once disk::Error implements std::error::Error.
|
|
Ok(create_disk_file(partition.try_clone()?, MAX_NESTING_DEPTH, path)
|
|
.map_err(|e| anyhow!("Failed to open partition image: {}", e))?
|
|
.get_len()?)
|
|
}
|