Merge "virtiofs: Add AVF API to share directory paths" into main
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 52bfd87..5dac07f 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -17,7 +17,7 @@
use crate::{get_calling_pid, get_calling_uid, get_this_pid};
use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
use crate::composite::make_composite_image;
-use crate::crosvm::{AudioConfig, CrosvmConfig, DiskFile, DisplayConfig, GpuConfig, InputDeviceOption, PayloadState, UsbConfig, VmContext, VmInstance, VmState};
+use crate::crosvm::{AudioConfig, CrosvmConfig, DiskFile, SharedPathConfig, DisplayConfig, GpuConfig, InputDeviceOption, PayloadState, UsbConfig, VmContext, VmInstance, VmState};
use crate::debug_config::DebugConfig;
use crate::dt_overlay::{create_device_tree_overlay, VM_DT_OVERLAY_MAX_SIZE, VM_DT_OVERLAY_PATH};
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
@@ -32,6 +32,7 @@
AssignableDevice::AssignableDevice,
CpuTopology::CpuTopology,
DiskImage::DiskImage,
+ SharedPath::SharedPath,
InputDevice::InputDevice,
IVirtualMachine::{self, BnVirtualMachine},
IVirtualMachineCallback::IVirtualMachineCallback,
@@ -613,6 +614,8 @@
})
.collect::<Result<Vec<DiskFile>, _>>()?;
+ let shared_paths = assemble_shared_paths(&config.sharedPaths, &temporary_directory)?;
+
let (cpus, host_cpu_topology) = match config.cpuTopology {
CpuTopology::MATCH_HOST => (None, true),
CpuTopology::ONE_CPU => (NonZeroU32::new(1), false),
@@ -719,6 +722,7 @@
kernel,
initrd,
disks,
+ shared_paths,
params: config.params.to_owned(),
protected: *is_protected,
debug_config,
@@ -956,6 +960,32 @@
},
})
}
+
+fn assemble_shared_paths(
+ shared_paths: &[SharedPath],
+ temporary_directory: &Path,
+) -> Result<Vec<SharedPathConfig>, Status> {
+ if shared_paths.is_empty() {
+ return Ok(Vec::new()); // Return an empty vector if shared_paths is empty
+ }
+
+ shared_paths
+ .iter()
+ .map(|path| {
+ Ok(SharedPathConfig {
+ path: path.sharedPath.clone(),
+ host_uid: path.hostUid,
+ host_gid: path.hostGid,
+ guest_uid: path.guestUid,
+ guest_gid: path.guestGid,
+ mask: path.mask,
+ tag: path.tag.clone(),
+ socket_path: temporary_directory.join(&path.socket).to_string_lossy().to_string(),
+ })
+ })
+ .collect()
+}
+
/// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
///
/// This may involve assembling a composite disk from a set of partition images.
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index ed45a51..8e3dde1 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -108,6 +108,7 @@
pub kernel: Option<File>,
pub initrd: Option<File>,
pub disks: Vec<DiskFile>,
+ pub shared_paths: Vec<SharedPathConfig>,
pub params: Option<String>,
pub protected: bool,
pub debug_config: DebugConfig,
@@ -224,6 +225,19 @@
pub writable: bool,
}
+/// Shared path between host and guest VM.
+#[derive(Debug)]
+pub struct SharedPathConfig {
+ pub path: String,
+ pub host_uid: i32,
+ pub host_gid: i32,
+ pub guest_uid: i32,
+ pub guest_gid: i32,
+ pub mask: i32,
+ pub tag: String,
+ pub socket_path: String,
+}
+
/// virtio-input device configuration from `external/crosvm/src/crosvm/config.rs`
#[derive(Debug)]
#[allow(dead_code)]
@@ -306,6 +320,8 @@
let tap =
if let Some(tap_file) = &config.tap { Some(tap_file.try_clone()?) } else { None };
+ run_virtiofs(&config)?;
+
// If this fails and returns an error, `self` will be left in the `Failed` state.
let child =
Arc::new(run_vm(config, &instance.crosvm_control_socket_path, failure_pipe_write)?);
@@ -884,6 +900,39 @@
}
}
+fn run_virtiofs(config: &CrosvmConfig) -> io::Result<()> {
+ for shared_path in &config.shared_paths {
+ let ugid_map_value = format!(
+ "{} {} {} {} {} /",
+ shared_path.guest_uid,
+ shared_path.guest_gid,
+ shared_path.host_uid,
+ shared_path.host_gid,
+ shared_path.mask,
+ );
+
+ let cfg_arg = format!("writeback=true,cache_policy=always,ugid_map='{}'", ugid_map_value);
+
+ let mut command = Command::new(CROSVM_PATH);
+ command
+ .arg("device")
+ .arg("fs")
+ .arg(format!("--socket={}", &shared_path.socket_path))
+ .arg(format!("--tag={}", &shared_path.tag))
+ .arg(format!("--shared-dir={}", &shared_path.path))
+ .arg("--cfg")
+ .arg(cfg_arg.as_str())
+ .arg("--disable-sandbox");
+
+ print_crosvm_args(&command);
+
+ let result = SharedChild::spawn(&mut command)?;
+ info!("Spawned virtiofs crosvm({})", result.id());
+ }
+
+ Ok(())
+}
+
/// Starts an instance of `crosvm` to manage a new VM.
fn run_vm(
config: CrosvmConfig,
@@ -1190,6 +1239,12 @@
command.arg(vfio_argument_for_platform_device(&device)?);
}
+ for shared_path in &config.shared_paths {
+ command
+ .arg("--vhost-user-fs")
+ .arg(format!("{},tag={}", &shared_path.socket_path, &shared_path.tag));
+ }
+
debug!("Preserving FDs {:?}", preserved_fds);
command.preserved_fds(preserved_fds);
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/SharedPath.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/SharedPath.aidl
new file mode 100644
index 0000000..7be7a5f
--- /dev/null
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/SharedPath.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 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.
+ */
+package android.system.virtualizationservice;
+
+/** Shared directory path between host and guest */
+parcelable SharedPath {
+ /** Shared path between host and guest */
+ String sharedPath;
+
+ /** UID of the path on the host */
+ int hostUid;
+
+ /** GID of the path on the host */
+ int hostGid;
+
+ /** UID of the path on the guest */
+ int guestUid;
+
+ /** GID of the path on the guest */
+ int guestGid;
+
+ /** umask settings for the path */
+ int mask;
+
+ /** virtiofs unique tag per path */
+ String tag;
+
+ /** socket name for vhost-user-fs */
+ String socket;
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index f559a71..9f2a23e 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -21,6 +21,7 @@
import android.system.virtualizationservice.DisplayConfig;
import android.system.virtualizationservice.GpuConfig;
import android.system.virtualizationservice.InputDevice;
+import android.system.virtualizationservice.SharedPath;
import android.system.virtualizationservice.UsbConfig;
/** Raw configuration for running a VM. */
@@ -52,6 +53,9 @@
/** Disk images to be made available to the VM. */
DiskImage[] disks;
+ /** Shared paths between host and guest */
+ SharedPath[] sharedPaths;
+
/** Whether the VM should be a protected VM. */
boolean protectedVm;
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index de1b081..2bcb40b 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -40,6 +40,7 @@
import android.sysprop.HypervisorProperties;
import android.system.virtualizationservice.DiskImage;
import android.system.virtualizationservice.Partition;
+import android.system.virtualizationservice.SharedPath;
import android.system.virtualizationservice.UsbConfig;
import android.system.virtualizationservice.VirtualMachineAppConfig;
import android.system.virtualizationservice.VirtualMachinePayloadConfig;
@@ -712,6 +713,15 @@
config.disks[i].partitions = partitions.toArray(new Partition[0]);
}
+ config.sharedPaths =
+ new SharedPath
+ [Optional.ofNullable(customImageConfig.getSharedPaths())
+ .map(arr -> arr.length)
+ .orElse(0)];
+ for (int i = 0; i < config.sharedPaths.length; i++) {
+ config.sharedPaths[i] = customImageConfig.getSharedPaths()[i].toParcelable();
+ }
+
config.displayConfig =
Optional.ofNullable(customImageConfig.getDisplayConfig())
.map(dc -> dc.toParcelable())
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
index 9774585..9b0709d 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -54,6 +54,7 @@
@Nullable private final String bootloaderPath;
@Nullable private final String[] params;
@Nullable private final Disk[] disks;
+ @Nullable private final SharedPath[] sharedPaths;
@Nullable private final DisplayConfig displayConfig;
@Nullable private final AudioConfig audioConfig;
private final boolean touch;
@@ -96,6 +97,11 @@
return params;
}
+ @Nullable
+ public SharedPath[] getSharedPaths() {
+ return sharedPaths;
+ }
+
public boolean useTouch() {
return touch;
}
@@ -132,6 +138,7 @@
String bootloaderPath,
String[] params,
Disk[] disks,
+ SharedPath[] sharedPaths,
DisplayConfig displayConfig,
boolean touch,
boolean keyboard,
@@ -149,6 +156,7 @@
this.bootloaderPath = bootloaderPath;
this.params = params;
this.disks = disks;
+ this.sharedPaths = sharedPaths;
this.displayConfig = displayConfig;
this.touch = touch;
this.keyboard = keyboard;
@@ -300,6 +308,91 @@
}
/** @hide */
+ public static final class SharedPath {
+ private final String path;
+ private final int hostUid;
+ private final int hostGid;
+ private final int guestUid;
+ private final int guestGid;
+ private final int mask;
+ private final String tag;
+ private final String socket;
+
+ public SharedPath(
+ String path,
+ int hostUid,
+ int hostGid,
+ int guestUid,
+ int guestGid,
+ int mask,
+ String tag,
+ String socket) {
+ this.path = path;
+ this.hostUid = hostUid;
+ this.hostGid = hostGid;
+ this.guestUid = guestUid;
+ this.guestGid = guestGid;
+ this.mask = mask;
+ this.tag = tag;
+ this.socket = socket;
+ }
+
+ android.system.virtualizationservice.SharedPath toParcelable() {
+ android.system.virtualizationservice.SharedPath parcelable =
+ new android.system.virtualizationservice.SharedPath();
+ parcelable.sharedPath = this.path;
+ parcelable.hostUid = this.hostUid;
+ parcelable.hostGid = this.hostGid;
+ parcelable.guestUid = this.guestUid;
+ parcelable.guestGid = this.guestGid;
+ parcelable.mask = this.mask;
+ parcelable.tag = this.tag;
+ parcelable.socket = this.socket;
+ return parcelable;
+ }
+
+ /** @hide */
+ public String getSharedPath() {
+ return path;
+ }
+
+ /** @hide */
+ public int getHostUid() {
+ return hostUid;
+ }
+
+ /** @hide */
+ public int getHostGid() {
+ return hostGid;
+ }
+
+ /** @hide */
+ public int getGuestUid() {
+ return guestUid;
+ }
+
+ /** @hide */
+ public int getGuestGid() {
+ return guestGid;
+ }
+
+ /** @hide */
+ public int getMask() {
+ return mask;
+ }
+
+ /** @hide */
+ public String getTag() {
+ return tag;
+ }
+
+ /** @hide */
+ public String getSocket() {
+ return socket;
+ }
+ }
+
+ /** @hide */
public static final class Disk {
private final boolean writable;
private final String imagePath;
@@ -366,6 +459,7 @@
private String bootloaderPath;
private List<String> params = new ArrayList<>();
private List<Disk> disks = new ArrayList<>();
+ private List<SharedPath> sharedPaths = new ArrayList<>();
private AudioConfig audioConfig;
private DisplayConfig displayConfig;
private boolean touch;
@@ -413,6 +507,12 @@
}
/** @hide */
+ public Builder addSharedPath(SharedPath path) {
+ this.sharedPaths.add(path);
+ return this;
+ }
+
+ /** @hide */
public Builder addParam(String param) {
this.params.add(param);
return this;
@@ -493,6 +593,7 @@
this.bootloaderPath,
this.params.toArray(new String[0]),
this.disks.toArray(new Disk[0]),
+ this.sharedPaths.toArray(new SharedPath[0]),
displayConfig,
touch,
keyboard,
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
index 6d39b46..5d6b13f 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
@@ -17,6 +17,7 @@
package com.android.virtualization.vmlauncher;
import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.system.virtualmachine.VirtualMachineConfig;
import android.system.virtualmachine.VirtualMachineCustomImageConfig;
@@ -25,6 +26,7 @@
import android.system.virtualmachine.VirtualMachineCustomImageConfig.DisplayConfig;
import android.system.virtualmachine.VirtualMachineCustomImageConfig.GpuConfig;
import android.system.virtualmachine.VirtualMachineCustomImageConfig.Partition;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.SharedPath;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -34,6 +36,7 @@
import java.io.FileReader;
import java.util.Arrays;
+import java.util.Objects;
/** This class and its inner classes model vm_config.json. */
class ConfigJson {
@@ -60,6 +63,7 @@
private InputJson input;
private AudioJson audio;
private DiskJson[] disks;
+ private SharedPathJson[] sharedPath;
private DisplayJson display;
private GpuJson gpu;
@@ -141,9 +145,36 @@
Arrays.stream(disks).map(d -> d.toConfig()).forEach(builder::addDisk);
}
+ if (sharedPath != null) {
+ Arrays.stream(sharedPath)
+ .map(d -> d.toConfig(context))
+ .filter(Objects::nonNull)
+ .forEach(builder::addSharedPath);
+ }
return builder.build();
}
+ private static class SharedPathJson {
+ private SharedPathJson() {}
+
+ // Package ID of Terminal app.
+ private static final String TERMINAL_PACKAGE_ID =
+ "com.google.android.virtualization.terminal";
+ private String sharedPath;
+
+ private SharedPath toConfig(Context context) {
+ try {
+ int uid =
+ context.getPackageManager()
+ .getPackageUidAsUser(TERMINAL_PACKAGE_ID, context.getUserId());
+
+ return new SharedPath(sharedPath, uid, uid, 0, 0, 0007, "android", "android");
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+ }
+
private static class InputJson {
private InputJson() {}