Add persistent backing for encrypted storage in VM
Virtualization service will allow clients to pass the file that will
back the (encrypted) storage inside VM.
This patch:
1. Exposes (optional) encryptedStorageImage in VirtualMachineAppConfig.
2. Expose it as a block device to VM (by including it as partition in a
writable disk).
Test: bin/vm run-app --storage -> inspect the block device in vm
Bug: 241543632
Change-Id: I9adbe832bda2c5f5a749d8614f056f51244ae52c
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 7327df7..68e1948 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -122,6 +122,7 @@
apk: Some(apk_fd),
idsig: Some(idsig_fd),
instanceImage: Some(instance_fd),
+ encryptedStorageImage: None,
payload: Payload::ConfigPath(config_path),
debugLevel: debug_level,
extraIdsigs: extra_idsigs,
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 9b0a658..e99757e 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -34,6 +34,13 @@
/** instance.img that has per-instance data */
ParcelFileDescriptor instanceImage;
+ /**
+ * This backs the persistent, encrypted storage in vm.
+ * It also comes with some integrity guarantees.
+ * Note: Storage is an optional feature
+ */
+ @nullable ParcelFileDescriptor encryptedStorageImage;
+
union Payload {
/**
* Path to a JSON file in an APK containing the configuration.
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 563fab0..10d70ec 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -17,7 +17,7 @@
use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
use crate::composite::make_composite_image;
use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
-use crate::payload::add_microdroid_images;
+use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
use crate::{Cid, FIRST_GUEST_CID, SYSPROP_LAST_CID};
use crate::selinux::{SeContext, getfilecon};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
@@ -603,6 +603,12 @@
let idsig_file = clone_file(config.idsig.as_ref().unwrap())?;
let instance_file = clone_file(config.instanceImage.as_ref().unwrap())?;
+ let storage_image = if let Some(file) = config.encryptedStorageImage.as_ref() {
+ Some(clone_file(file)?)
+ } else {
+ None
+ };
+
let vm_payload_config = match &config.payload {
Payload::ConfigPath(config_path) => {
load_vm_payload_config_from_file(&apk_file, config_path.as_str())
@@ -632,13 +638,15 @@
vm_config.numCpus = config.numCpus;
vm_config.taskProfiles = config.taskProfiles.clone();
- // Microdroid requires an additional init ramdisk & payload disk image
- add_microdroid_images(
+ // Microdroid takes additional init ramdisk & (optionally) storage image
+ add_microdroid_system_images(config, instance_file, storage_image, &mut vm_config)?;
+
+ // Include Microdroid payload disk (contains apks, idsigs) in vm config
+ add_microdroid_payload_images(
config,
temporary_directory,
apk_file,
idsig_file,
- instance_file,
&vm_payload_config,
&mut vm_config,
)?;
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 82aa760..b6df500 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -363,13 +363,10 @@
.collect()
}
-pub fn add_microdroid_images(
+pub fn add_microdroid_system_images(
config: &VirtualMachineAppConfig,
- temporary_directory: &Path,
- apk_file: File,
- idsig_file: File,
instance_file: File,
- vm_payload_config: &VmPayloadConfig,
+ storage_image: Option<File>,
vm_config: &mut VirtualMachineRawConfig,
) -> Result<()> {
let debug_suffix = match config.debugLevel {
@@ -381,12 +378,37 @@
let initrd = format!("/apex/com.android.virt/etc/microdroid_initrd_{}.img", debug_suffix);
vm_config.initrd = Some(open_parcel_file(Path::new(&initrd), false)?);
- let instance_img = Partition {
+ let mut writable_partitions = vec![Partition {
label: "vm-instance".to_owned(),
image: Some(ParcelFileDescriptor::new(instance_file)),
writable: true,
- };
- vm_config.disks.push(DiskImage { image: None, partitions: vec![instance_img], writable: true });
+ }];
+
+ if let Some(file) = storage_image {
+ writable_partitions.push(Partition {
+ label: "encrypted-storage".to_owned(),
+ image: Some(ParcelFileDescriptor::new(file)),
+ writable: true,
+ });
+ }
+
+ vm_config.disks.push(DiskImage {
+ image: None,
+ partitions: writable_partitions,
+ writable: true,
+ });
+
+ Ok(())
+}
+
+pub fn add_microdroid_payload_images(
+ config: &VirtualMachineAppConfig,
+ temporary_directory: &Path,
+ apk_file: File,
+ idsig_file: File,
+ vm_payload_config: &VmPayloadConfig,
+ vm_config: &mut VirtualMachineRawConfig,
+) -> Result<()> {
vm_config.disks.push(make_payload_disk(
config,
apk_file,
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 21cc74b..3b887d3 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -58,6 +58,16 @@
#[clap(short, long)]
daemonize: bool,
+ /// Path to the file backing the storage.
+ /// Created if the option is used but the path does not exist in the device.
+ #[clap(long)]
+ storage: Option<PathBuf>,
+
+ /// Size of the storage. Used only if --storage is supplied but path does not exist
+ /// Default size is 10*1024*1024
+ #[clap(long)]
+ storage_size: Option<u64>,
+
/// Path to file for VM console output.
#[clap(long)]
console: Option<PathBuf>,
@@ -188,6 +198,8 @@
apk,
idsig,
instance,
+ storage,
+ storage_size,
config_path,
daemonize,
console,
@@ -205,6 +217,8 @@
&apk,
&idsig,
&instance,
+ storage.as_deref(),
+ storage_size,
config_path.as_deref().unwrap_or(""),
daemonize,
console.as_deref(),
diff --git a/vm/src/run.rs b/vm/src/run.rs
index c524c59..967314b 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -41,6 +41,8 @@
apk: &Path,
idsig: &Path,
instance: &Path,
+ storage: Option<&Path>,
+ storage_size: Option<u64>,
config_path: &str,
daemonize: bool,
console_path: Option<&Path>,
@@ -88,6 +90,20 @@
)?;
}
+ let storage = if let Some(path) = storage {
+ if !path.exists() {
+ command_create_partition(
+ service,
+ path,
+ storage_size.unwrap_or(10 * 1024 * 1024),
+ PartitionType::RAW,
+ )?;
+ }
+ Some(open_parcel_file(path, true)?)
+ } else {
+ None
+ };
+
let extra_idsig_files: Result<Vec<File>, _> = extra_idsigs.iter().map(File::open).collect();
let extra_idsig_fds = extra_idsig_files?.into_iter().map(ParcelFileDescriptor::new).collect();
@@ -97,6 +113,7 @@
idsig: idsig_fd.into(),
extraIdsigs: extra_idsig_fds,
instanceImage: open_parcel_file(instance, true /* writable */)?.into(),
+ encryptedStorageImage: storage,
payload: Payload::ConfigPath(config_path.to_owned()),
debugLevel: debug_level,
protectedVm: protected,