Merge "Increase the timeout of the logwrapper"
diff --git a/compositediskconfig/src/lib.rs b/compositediskconfig/src/lib.rs
deleted file mode 100644
index dc199e4..0000000
--- a/compositediskconfig/src/lib.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-//! JSON configuration for composite disks, as used for running `mk_cdisk` and by the `vm` tool.
-
-use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
-
-/// Configuration for running `mk_cdisk`.
-#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
-pub struct Config {
-    /// The set of partitions to be assembled into a composite image.
-    pub partitions: Vec<Partition>,
-}
-
-/// A partition to be assembled into a composite image.
-#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub struct Partition {
-    /// A label for the partition.
-    pub label: String,
-    /// The filename of the partition image.
-    pub path: PathBuf,
-    /// Whether the partition should be writable.
-    #[serde(default)]
-    pub writable: bool,
-}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
index 825c3da..9b8658b 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
@@ -20,8 +20,8 @@
     /** A label for the partition. */
     @utf8InCpp String label;
 
-    /** The backing file descriptor of the partition image. */
-    ParcelFileDescriptor image;
+    /** The backing file descriptors of the partition images. */
+    ParcelFileDescriptor[] images;
 
     /** Whether the partition should be writable by the VM. */
     boolean writable;
diff --git a/virtualizationservice/src/composite.rs b/virtualizationservice/src/composite.rs
index 5f792fd..7b5a258 100644
--- a/virtualizationservice/src/composite.rs
+++ b/virtualizationservice/src/composite.rs
@@ -336,9 +336,9 @@
     Ok((composite_image, files))
 }
 
-/// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
-/// partition, return the list of file descriptors which must be passed to the mk_cdisk child
-/// process and the composite disk image partition configuration for it.
+/// Given the AIDL config containing a list of partitions, with [`ParcelFileDescriptor`]s for each
+/// partition, return the list of file descriptors which must be passed to the composite disk image
+/// partition configuration for it.
 fn convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error> {
     // File descriptors to pass to child process.
     let mut files = vec![];
@@ -346,24 +346,27 @@
     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 size = get_partition_size(&file)?;
-            let fd = file.as_raw_fd();
-            files.push(file);
+            let image_files = partition
+                .images
+                .iter()
+                .map(|image| {
+                    let file = image
+                        .as_ref()
+                        .try_clone()
+                        .context("Failed to clone partition image file descriptor")?;
+
+                    let size = get_partition_size(&file)?;
+                    let fd = file.as_raw_fd();
+                    let partition_info_file =
+                        PartitionFileInfo { path: format!("/proc/self/fd/{}", fd).into(), size };
+                    files.push(file);
+                    Ok(partition_info_file)
+                })
+                .collect::<Result<Vec<_>, Error>>()?;
 
             Ok(PartitionInfo {
                 label: partition.label.to_owned(),
-                files: vec![PartitionFileInfo {
-                    path: format!("/proc/self/fd/{}", fd).into(),
-                    size,
-                }],
+                files: image_files,
                 partition_type: ImagePartitionType::LinuxFilesystem,
                 writable: partition.writable,
             })
diff --git a/vm/Android.bp b/vm/Android.bp
index c07beb2..734f2d3 100644
--- a/vm/Android.bp
+++ b/vm/Android.bp
@@ -10,13 +10,13 @@
     rustlibs: [
         "android.system.virtualizationservice-rust",
         "libanyhow",
-        "libcompositediskconfig",
         "libenv_logger",
         "liblibc",
         "liblog_rust",
         "libserde_json",
         "libserde",
         "libstructopt",
+        "libvmconfig",
     ],
     apex_available: [
         "com.android.virt",
diff --git a/vm/src/main.rs b/vm/src/main.rs
index b79f42a..8f16eb5 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -14,7 +14,6 @@
 
 //! Android VM control tool.
 
-mod config;
 mod run;
 mod sync;
 
diff --git a/vm/src/run.rs b/vm/src/run.rs
index ec95646..fbf849b 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -14,7 +14,6 @@
 
 //! Command to run a VM.
 
-use crate::config::VmConfig;
 use crate::sync::AtomicFlag;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::IVirtualizationService;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualMachine::IVirtualMachine;
@@ -30,6 +29,7 @@
 use std::io;
 use std::os::unix::io::{AsRawFd, FromRawFd};
 use std::path::Path;
+use vmconfig::VmConfig;
 
 /// Run a VM from the given configuration file.
 pub fn command_run(
diff --git a/compositediskconfig/Android.bp b/vmconfig/Android.bp
similarity index 66%
rename from compositediskconfig/Android.bp
rename to vmconfig/Android.bp
index 4608323..321eba0 100644
--- a/compositediskconfig/Android.bp
+++ b/vmconfig/Android.bp
@@ -3,12 +3,13 @@
 }
 
 rust_library {
-    name: "libcompositediskconfig",
-    host_supported: true,
-    crate_name: "compositediskconfig",
+    name: "libvmconfig",
+    crate_name: "vmconfig",
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
+        "android.system.virtualizationservice-rust",
+        "libanyhow",
         "libserde_json",
         "libserde",
     ],
diff --git a/vm/src/config.rs b/vmconfig/src/lib.rs
similarity index 74%
rename from vm/src/config.rs
rename to vmconfig/src/lib.rs
index 8ea0d8f..c9385f3 100644
--- a/vm/src/config.rs
+++ b/vmconfig/src/lib.rs
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Struct for VM configuration.
+//! Struct for VM configuration with JSON (de)serialization and AIDL parcelables
 
 use android_system_virtualizationservice::{
     aidl::android::system::virtualizationservice::DiskImage::DiskImage as AidlDiskImage,
@@ -20,8 +20,8 @@
     aidl::android::system::virtualizationservice::VirtualMachineConfig::VirtualMachineConfig,
     binder::ParcelFileDescriptor,
 };
-use anyhow::{bail, Context, Error};
-use compositediskconfig::Partition;
+
+use anyhow::{bail, Context, Error, Result};
 use serde::{Deserialize, Serialize};
 use std::fs::{File, OpenOptions};
 use std::io::BufReader;
@@ -105,7 +105,7 @@
 impl DiskImage {
     fn to_parcelable(&self) -> Result<AidlDiskImage, Error> {
         let partitions =
-            self.partitions.iter().map(partition_to_parcelable).collect::<Result<_, Error>>()?;
+            self.partitions.iter().map(Partition::to_parcelable).collect::<Result<_>>()?;
         Ok(AidlDiskImage {
             image: maybe_open_parcel_file(&self.image, self.writable)?,
             writable: self.writable,
@@ -114,16 +114,49 @@
     }
 }
 
-fn partition_to_parcelable(partition: &Partition) -> Result<AidlPartition, Error> {
-    Ok(AidlPartition {
-        image: Some(open_parcel_file(&partition.path, partition.writable)?),
-        writable: partition.writable,
-        label: partition.label.to_owned(),
-    })
+/// A partition to be assembled into a composite image.
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub struct Partition {
+    /// A label for the partition.
+    pub label: String,
+    /// The filename of the partition image.
+    #[serde(default)]
+    pub path: Option<PathBuf>,
+    /// The filename of the partition image.
+    #[serde(default)]
+    pub paths: Vec<PathBuf>,
+    /// Whether the partition should be writable.
+    #[serde(default)]
+    pub writable: bool,
+}
+
+impl Partition {
+    fn to_parcelable(&self) -> Result<AidlPartition> {
+        if !self.paths.is_empty() {
+            if self.path.is_some() {
+                bail!("Partition {} contains both path/paths", &self.label);
+            }
+            let images = self
+                .paths
+                .iter()
+                .map(|path| open_parcel_file(&path, self.writable))
+                .collect::<Result<Vec<_>, _>>()?;
+            Ok(AidlPartition { images, writable: self.writable, label: self.label.to_owned() })
+        } else {
+            let path = self.path.as_ref().ok_or_else(|| {
+                Error::msg(format!("Partition {} doesn't set path/paths", &self.label))
+            })?;
+            Ok(AidlPartition {
+                images: vec![open_parcel_file(&path, self.writable)?],
+                writable: self.writable,
+                label: self.label.to_owned(),
+            })
+        }
+    }
 }
 
 /// Try to open the given file and wrap it in a [`ParcelFileDescriptor`].
-fn open_parcel_file(filename: &Path, writable: bool) -> Result<ParcelFileDescriptor, Error> {
+fn open_parcel_file(filename: &Path, writable: bool) -> Result<ParcelFileDescriptor> {
     Ok(ParcelFileDescriptor::new(
         OpenOptions::new()
             .read(true)
@@ -137,6 +170,6 @@
 fn maybe_open_parcel_file(
     filename: &Option<PathBuf>,
     writable: bool,
-) -> Result<Option<ParcelFileDescriptor>, Error> {
+) -> Result<Option<ParcelFileDescriptor>> {
     filename.as_deref().map(|filename| open_parcel_file(filename, writable)).transpose()
 }