[vs] Extract writable partition initialization into a new library
So that we can reuse this function across various virtualization
services.
Bug: 272226230
Test: atest virtualizationmanager_device_test
Change-Id: I54c142ce8a4163835b312ba70424b63ffc056ae8
diff --git a/libs/vsutil/src/partition.rs b/libs/vsutil/src/partition.rs
new file mode 100644
index 0000000..a4e72de
--- /dev/null
+++ b/libs/vsutil/src/partition.rs
@@ -0,0 +1,93 @@
+// 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()
+}