Merge "vmbase: Temporarily disable logging in exception handlers"
diff --git a/libs/vsutil/Android.bp b/libs/vsutil/Android.bp
new file mode 100644
index 0000000..ccb36a0
--- /dev/null
+++ b/libs/vsutil/Android.bp
@@ -0,0 +1,26 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+    name: "libvsutil",
+    crate_name: "vsutil",
+    srcs: ["src/lib.rs"],
+    // Only build on targets which crosvm builds on.
+    enabled: false,
+    target: {
+        android64: {
+            compile_multilib: "64",
+            enabled: true,
+        },
+        linux_bionic_arm64: {
+            enabled: true,
+        },
+    },
+    rustlibs: [
+        "android.system.virtualizationservice-rust",
+        "libbinder_rs",
+        "libdisk",
+    ],
+    apex_available: ["com.android.virt"],
+}
diff --git a/libs/vsutil/src/file.rs b/libs/vsutil/src/file.rs
new file mode 100644
index 0000000..4adaae1
--- /dev/null
+++ b/libs/vsutil/src/file.rs
@@ -0,0 +1,28 @@
+// 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 files.
+
+use binder::{self, ExceptionCode, ParcelFileDescriptor, Status};
+use std::fs::File;
+
+/// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
+pub fn clone_file(fd: &ParcelFileDescriptor) -> binder::Result<File> {
+    fd.as_ref().try_clone().map_err(|e| {
+        Status::new_exception_str(
+            ExceptionCode::BAD_PARCELABLE,
+            Some(format!("Failed to clone File from ParcelFileDescriptor: {:?}", e)),
+        )
+    })
+}
diff --git a/libs/vsutil/src/lib.rs b/libs/vsutil/src/lib.rs
new file mode 100644
index 0000000..cac888a
--- /dev/null
+++ b/libs/vsutil/src/lib.rs
@@ -0,0 +1,22 @@
+// 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.
+
+//! This library provides utility functions used by both various
+//! Virtualization services.
+
+mod file;
+mod partition;
+
+pub use file::clone_file;
+pub use partition::init_writable_partition;
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()
+}
diff --git a/virtualizationmanager/Android.bp b/virtualizationmanager/Android.bp
index 59e507f..fedfd76 100644
--- a/virtualizationmanager/Android.bp
+++ b/virtualizationmanager/Android.bp
@@ -54,6 +54,7 @@
         "libtombstoned_client_rust",
         "libvm_control",
         "libvmconfig",
+        "libvsutil",
         "libzip",
         "libvsock",
         "liblibfdt",
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 468ee19..4bc25da 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -53,7 +53,6 @@
     self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor,
     Status, StatusCode, Strong,
 };
-use disk::QcowFile;
 use lazy_static::lazy_static;
 use log::{debug, error, info, warn};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
@@ -64,7 +63,7 @@
 use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fs::{read_dir, remove_file, File, OpenOptions};
-use std::io::{BufRead, BufReader, Error, ErrorKind, Write};
+use std::io::{BufRead, BufReader, Write};
 use std::num::{NonZeroU16, NonZeroU32};
 use std::os::unix::io::{FromRawFd, IntoRawFd};
 use std::os::unix::raw::pid_t;
@@ -72,6 +71,7 @@
 use std::sync::{Arc, Mutex, Weak};
 use vmconfig::VmConfig;
 use vsock::VsockStream;
+use vsutil::{clone_file, init_writable_partition};
 use zip::ZipArchive;
 
 /// The unique ID of a VM used (together with a port number) for vsock communication.
@@ -83,19 +83,8 @@
 /// Gaps in composite disk images are filled with a shared zero.img.
 const ZERO_FILLER_SIZE: u64 = 4096;
 
-/// Magic string for the instance image
-const ANDROID_VM_INSTANCE_MAGIC: &str = "Android-VM-instance";
-
-/// Version of the instance image format
-const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
-
 const MICRODROID_OS_NAME: &str = "microdroid";
 
-const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
-
-/// crosvm requires all partitions to be a multiple of 4KiB.
-const PARTITION_GRANULARITY_BYTES: u64 = 4096;
-
 lazy_static! {
     pub static ref GLOBAL_SERVICE: Strong<dyn IVirtualizationServiceInternal> =
         wait_for_interface(BINDER_SERVICE_IDENTIFIER)
@@ -189,45 +178,7 @@
         partition_type: PartitionType,
     ) -> binder::Result<()> {
         check_manage_access()?;
-        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_GRANULARITY_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)),
-            )
-        })?;
-
-        Ok(())
+        init_writable_partition(image_fd, size_bytes, partition_type)
     }
 
     /// Creates or update the idsig file by digesting the input APK file.
@@ -484,26 +435,6 @@
     Ok(())
 }
 
-fn format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()> {
-    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<()> {
-    part.write_all(UNFORMATTED_STORAGE_MAGIC.as_bytes())?;
-    part.flush()
-}
-
-fn round_up(input: u64, granularity: u64) -> u64 {
-    if granularity == 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(granularity - 1).unwrap_or(input);
-    (result / granularity) * granularity
-}
-
 /// 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.
@@ -955,16 +886,6 @@
     }
 }
 
-/// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
-pub fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
-    file.as_ref().try_clone().map_err(|e| {
-        Status::new_exception_str(
-            ExceptionCode::BAD_PARCELABLE,
-            Some(format!("Failed to clone File from ParcelFileDescriptor: {:?}", e)),
-        )
-    })
-}
-
 /// Converts an `&Option<ParcelFileDescriptor>` to an `Option<File>` by cloning the file.
 fn maybe_clone_file(file: &Option<ParcelFileDescriptor>) -> Result<Option<File>, Status> {
     file.as_ref().map(clone_file).transpose()
diff --git a/virtualizationmanager/src/atom.rs b/virtualizationmanager/src/atom.rs
index d6eb141..5118a8d 100644
--- a/virtualizationmanager/src/atom.rs
+++ b/virtualizationmanager/src/atom.rs
@@ -14,7 +14,7 @@
 
 //! Functions for creating and collecting atoms.
 
-use crate::aidl::{clone_file, GLOBAL_SERVICE};
+use crate::aidl::GLOBAL_SERVICE;
 use crate::crosvm::VmMetric;
 use crate::get_calling_uid;
 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
@@ -37,6 +37,7 @@
 use statslog_virtualization_rust::vm_creation_requested;
 use std::thread;
 use std::time::{Duration, SystemTime};
+use vsutil::clone_file;
 use zip::ZipArchive;
 
 const INVALID_NUM_CPUS: i32 = -1;
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index e0b78ba..3888df2 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -26,13 +26,13 @@
     IVirtualizationServiceInternal::IVirtualizationServiceInternal,
 };
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{anyhow, ensure, Context, Result};
 use binder::{self, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong};
 use libc::VMADDR_CID_HOST;
 use log::{error, info, warn};
 use rustutils::system_properties;
 use std::collections::HashMap;
-use std::fs::{create_dir, read_dir, remove_dir, remove_file, set_permissions, Permissions};
+use std::fs::{create_dir, remove_dir_all, set_permissions, Permissions};
 use std::io::{Read, Write};
 use std::os::unix::fs::PermissionsExt;
 use std::os::unix::raw::{pid_t, uid_t};
@@ -268,20 +268,10 @@
 /// Removes a directory owned by a different user by first changing its owner back
 /// to VirtualizationService.
 pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
-    if !path.as_path().is_dir() {
-        bail!("Path {:?} is not a directory", path);
-    }
+    ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
     chown(path, Some(Uid::current()), None)?;
     set_permissions(path, Permissions::from_mode(0o700))?;
-    remove_temporary_files(path)?;
-    remove_dir(path)?;
-    Ok(())
-}
-
-pub fn remove_temporary_files(path: &PathBuf) -> Result<()> {
-    for dir_entry in read_dir(path)? {
-        remove_file(dir_entry?.path())?;
-    }
+    remove_dir_all(path)?;
     Ok(())
 }