blob: a4e72dee4e2f48d882bdaea5272d2613813f7122 [file] [log] [blame]
// Copyright 2023, 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 to process partitions.
use crate::file::clone_file;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
PartitionType::PartitionType,
};
use binder::{self, ExceptionCode, ParcelFileDescriptor, Status};
use disk::QcowFile;
use std::io::{Error, ErrorKind, Write};
/// crosvm requires all partitions to be a multiple of 4KiB.
const PARTITION_GRANULE_BYTES: u64 = 4096;
/// Initialize an empty partition image of the given size to be used as a writable partition.
pub fn init_writable_partition(
image_fd: &ParcelFileDescriptor,
size_bytes: i64,
partition_type: PartitionType,
) -> binder::Result<()> {
let size_bytes = size_bytes.try_into().map_err(|e| {
Status::new_exception_str(
ExceptionCode::ILLEGAL_ARGUMENT,
Some(format!("Invalid size {}: {:?}", size_bytes, e)),
)
})?;
let size_bytes = round_up(size_bytes, PARTITION_GRANULE_BYTES);
let image = clone_file(image_fd)?;
// initialize the file. Any data in the file will be erased.
image.set_len(0).map_err(|e| {
Status::new_service_specific_error_str(-1, Some(format!("Failed to reset a file: {:?}", e)))
})?;
let mut part = QcowFile::new(image, size_bytes).map_err(|e| {
Status::new_service_specific_error_str(
-1,
Some(format!("Failed to create QCOW2 image: {:?}", e)),
)
})?;
match partition_type {
PartitionType::RAW => Ok(()),
PartitionType::ANDROID_VM_INSTANCE => format_as_android_vm_instance(&mut part),
PartitionType::ENCRYPTEDSTORE => format_as_encryptedstore(&mut part),
_ => Err(Error::new(
ErrorKind::Unsupported,
format!("Unsupported partition type {:?}", partition_type),
)),
}
.map_err(|e| {
Status::new_service_specific_error_str(
-1,
Some(format!("Failed to initialize partition as {:?}: {:?}", partition_type, e)),
)
})
}
fn round_up(input: u64, granule: u64) -> u64 {
if granule == 0 {
return input;
}
// If the input is absurdly large we round down instead of up; it's going to fail anyway.
let result = input.checked_add(granule - 1).unwrap_or(input);
(result / granule) * granule
}
fn format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()> {
const ANDROID_VM_INSTANCE_MAGIC: &str = "Android-VM-instance";
const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
part.write_all(ANDROID_VM_INSTANCE_MAGIC.as_bytes())?;
part.write_all(&ANDROID_VM_INSTANCE_VERSION.to_le_bytes())?;
part.flush()
}
fn format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()> {
const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
part.write_all(UNFORMATTED_STORAGE_MAGIC.as_bytes())?;
part.flush()
}