Create instance.img

The file is a per-VM persistent storage which will contain identity
information like the certs of the payload.

As the first step, this change creates instance.img upon creation of a
VM and passes it to virtualizationservice. For now, the file is not
passed down to crosvm yet.

Bug: 193504400
Test: atest MicrodroidHostTestCases
Change-Id: I44115a93abdd23c36a4c52a38e5355bfa3bf69b3
diff --git a/vm/src/create_partition.rs b/vm/src/create_partition.rs
new file mode 100644
index 0000000..acebbf2
--- /dev/null
+++ b/vm/src/create_partition.rs
@@ -0,0 +1,40 @@
+// 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.
+
+//! Command to create an empty partition
+
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::IVirtualizationService;
+use android_system_virtualizationservice::binder::{ParcelFileDescriptor, Strong};
+use anyhow::{Context, Error};
+use std::convert::TryInto;
+use std::fs::OpenOptions;
+use std::path::Path;
+
+/// Initialise an empty partition image of the given size to be used as a writable partition.
+pub fn command_create_partition(
+    service: Strong<dyn IVirtualizationService>,
+    image_path: &Path,
+    size: u64,
+) -> Result<(), Error> {
+    let image = OpenOptions::new()
+        .create_new(true)
+        .read(true)
+        .write(true)
+        .open(image_path)
+        .with_context(|| format!("Failed to create {:?}", image_path))?;
+    service
+        .initializeWritablePartition(&ParcelFileDescriptor::new(image), size.try_into()?)
+        .context("Failed to initialize partition with size {}, size")?;
+    Ok(())
+}
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 9ca0ea6..09f11d5 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -14,16 +14,16 @@
 
 //! Android VM control tool.
 
+mod create_partition;
 mod run;
 mod sync;
 
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::IVirtualizationService;
-use android_system_virtualizationservice::binder::{wait_for_interface, ProcessState, Strong, ParcelFileDescriptor};
+use android_system_virtualizationservice::binder::{wait_for_interface, ProcessState, Strong};
 use anyhow::{Context, Error};
+use create_partition::command_create_partition;
 use run::{command_run, command_run_app};
-use std::convert::TryInto;
-use std::fs::OpenOptions;
-use std::path::{PathBuf, Path};
+use std::path::PathBuf;
 use structopt::clap::AppSettings;
 use structopt::StructOpt;
 
@@ -43,6 +43,10 @@
         #[structopt(parse(from_os_str))]
         idsig: PathBuf,
 
+        /// Path to the instance image. Created if not exists.
+        #[structopt(parse(from_os_str))]
+        instance: PathBuf,
+
         /// Path to VM config JSON within APK (e.g. assets/vm_config.json)
         config_path: String,
 
@@ -101,8 +105,17 @@
         .context("Failed to find VirtualizationService")?;
 
     match opt {
-        Opt::RunApp { apk, idsig, config_path, daemonize, log, debug } => {
-            command_run_app(service, &apk, &idsig, &config_path, daemonize, log.as_deref(), debug)
+        Opt::RunApp { apk, idsig, instance, config_path, daemonize, log, debug } => {
+            command_run_app(
+                service,
+                &apk,
+                &idsig,
+                &instance,
+                &config_path,
+                daemonize,
+                log.as_deref(),
+                debug,
+            )
         }
         Opt::Run { config, daemonize, log } => {
             command_run(service, &config, daemonize, log.as_deref())
@@ -128,21 +141,3 @@
     println!("Running VMs: {:#?}", vms);
     Ok(())
 }
-
-/// Initialise an empty partition image of the given size to be used as a writable partition.
-fn command_create_partition(
-    service: Strong<dyn IVirtualizationService>,
-    image_path: &Path,
-    size: u64,
-) -> Result<(), Error> {
-    let image = OpenOptions::new()
-        .create_new(true)
-        .read(true)
-        .write(true)
-        .open(image_path)
-        .with_context(|| format!("Failed to create {:?}", image_path))?;
-    service
-        .initializeWritablePartition(&ParcelFileDescriptor::new(image), size.try_into()?)
-        .context("Failed to initialize partition with size {}, size")?;
-    Ok(())
-}
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 184a396..41fdabb 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -14,6 +14,7 @@
 
 //! Command to run a VM.
 
+use crate::create_partition::command_create_partition;
 use crate::sync::AtomicFlag;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::IVirtualizationService;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualMachine::IVirtualMachine;
@@ -33,13 +34,15 @@
 use std::io::{self, BufRead, BufReader};
 use std::os::unix::io::{AsRawFd, FromRawFd};
 use std::path::Path;
-use vmconfig::VmConfig;
+use vmconfig::{open_parcel_file, VmConfig};
 
 /// Run a VM from the given APK, idsig, and config.
+#[allow(clippy::too_many_arguments)]
 pub fn command_run_app(
     service: Strong<dyn IVirtualizationService>,
     apk: &Path,
     idsig: &Path,
+    instance: &Path,
     config_path: &str,
     daemonize: bool,
     log_path: Option<&Path>,
@@ -47,9 +50,16 @@
 ) -> Result<(), Error> {
     let apk_file = File::open(apk).context("Failed to open APK file")?;
     let idsig_file = File::open(idsig).context("Failed to open idsig file")?;
+
+    if !instance.exists() {
+        const INSTANCE_FILE_SIZE: u64 = 10 * 1024 * 1024;
+        command_create_partition(service.clone(), instance, INSTANCE_FILE_SIZE)?;
+    }
+
     let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
         apk: ParcelFileDescriptor::new(apk_file).into(),
         idsig: ParcelFileDescriptor::new(idsig_file).into(),
+        instanceImage: open_parcel_file(instance, true /* writable */)?.into(),
         configPath: config_path.to_owned(),
         debug,
     });