Merge "Update example build rules in vmbase README."
diff --git a/authfs/fd_server/Android.bp b/authfs/fd_server/Android.bp
index 5097408..db1fd44 100644
--- a/authfs/fd_server/Android.bp
+++ b/authfs/fd_server/Android.bp
@@ -12,6 +12,7 @@
         "libauthfs_fsverity_metadata",
         "libbinder_rs",
         "libclap",
+        "libfsverity_rs",
         "liblibc",
         "liblog_rust",
         "libnix",
@@ -31,6 +32,7 @@
         "libauthfs_fsverity_metadata",
         "libbinder_rs",
         "libclap",
+        "libfsverity_rs",
         "liblibc",
         "liblog_rust",
         "libnix",
diff --git a/authfs/fd_server/src/aidl.rs b/authfs/fd_server/src/aidl.rs
index 01b8209..ada3ffb 100644
--- a/authfs/fd_server/src/aidl.rs
+++ b/authfs/fd_server/src/aidl.rs
@@ -31,7 +31,6 @@
 use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
 use std::sync::{Arc, RwLock};
 
-use crate::fsverity;
 use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
     BnVirtFdService, FsStat::FsStat, IVirtFdService, MAX_REQUESTING_DATA,
 };
diff --git a/authfs/fd_server/src/fsverity.rs b/authfs/fd_server/src/fsverity.rs
deleted file mode 100644
index 576f9dd..0000000
--- a/authfs/fd_server/src/fsverity.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-use nix::ioctl_readwrite;
-use std::io;
-
-// Constants/values from uapi/linux/fsverity.h
-const FS_VERITY_METADATA_TYPE_MERKLE_TREE: u64 = 1;
-const FS_VERITY_METADATA_TYPE_SIGNATURE: u64 = 3;
-const FS_IOCTL_MAGIC: u8 = b'f';
-const FS_IOCTL_READ_VERITY_METADATA: u8 = 135;
-
-#[repr(C)]
-pub struct fsverity_read_metadata_arg {
-    metadata_type: u64,
-    offset: u64,
-    length: u64,
-    buf_ptr: u64,
-    __reserved: u64,
-}
-
-ioctl_readwrite!(
-    read_verity_metadata,
-    FS_IOCTL_MAGIC,
-    FS_IOCTL_READ_VERITY_METADATA,
-    fsverity_read_metadata_arg
-);
-
-fn read_metadata(fd: i32, metadata_type: u64, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
-    let mut arg = fsverity_read_metadata_arg {
-        metadata_type,
-        offset,
-        length: buf.len() as u64,
-        buf_ptr: buf.as_mut_ptr() as u64,
-        __reserved: 0,
-    };
-    Ok(unsafe { read_verity_metadata(fd, &mut arg) }? as usize)
-}
-
-/// Read the raw Merkle tree from the fd, if it exists. The API semantics is similar to a regular
-/// pread(2), and may not return full requested buffer.
-pub fn read_merkle_tree(fd: i32, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
-    read_metadata(fd, FS_VERITY_METADATA_TYPE_MERKLE_TREE, offset, buf)
-}
-
-/// Read the fs-verity signature from the fd (if exists). The returned signature should be complete.
-pub fn read_signature(fd: i32, buf: &mut [u8]) -> io::Result<usize> {
-    read_metadata(fd, FS_VERITY_METADATA_TYPE_SIGNATURE, 0 /* offset */, buf)
-}
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index f91ebec..47983cb 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -23,7 +23,6 @@
 //! client can then request the content of file 9 by offset and size.
 
 mod aidl;
-mod fsverity;
 
 use anyhow::{bail, Result};
 use clap::Parser;
diff --git a/compos/composd/Android.bp b/compos/composd/Android.bp
index cee4b01..b0294dd 100644
--- a/compos/composd/Android.bp
+++ b/compos/composd/Android.bp
@@ -16,10 +16,13 @@
         "libbinder_rs",
         "libcompos_common",
         "libcomposd_native_rust",
+        "libfsverity_rs",
         "libminijail_rust",
         "libnix",
         "liblibc",
         "liblog_rust",
+        "libodsign_proto_rust",
+        "libprotobuf",
         "librustutils",
         "libshared_child",
         "libvmclient",
diff --git a/compos/composd/aidl/android/system/composd/ICompilationTaskCallback.aidl b/compos/composd/aidl/android/system/composd/ICompilationTaskCallback.aidl
index 569bba5..a3ce553 100644
--- a/compos/composd/aidl/android/system/composd/ICompilationTaskCallback.aidl
+++ b/compos/composd/aidl/android/system/composd/ICompilationTaskCallback.aidl
@@ -25,6 +25,8 @@
         CompilationFailed,
         /** We ran compilation in the VM, but it reported a problem. */
         UnexpectedCompilationResult,
+        /** We failed to enable fs-verity completely to the output artifacts. */
+        FailedToEnableFsverity,
     }
 
     /**
diff --git a/compos/composd/src/odrefresh_task.rs b/compos/composd/src/odrefresh_task.rs
index 3a699ab..a98f50d 100644
--- a/compos/composd/src/odrefresh_task.rs
+++ b/compos/composd/src/odrefresh_task.rs
@@ -28,11 +28,16 @@
     CompilationMode::CompilationMode, ICompOsService, OdrefreshArgs::OdrefreshArgs,
 };
 use compos_common::odrefresh::{
-    is_system_property_interesting, ExitCode, ODREFRESH_OUTPUT_ROOT_DIR,
+    is_system_property_interesting, ExitCode, CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR,
+    PENDING_ARTIFACTS_SUBDIR,
 };
+use compos_common::BUILD_MANIFEST_SYSTEM_EXT_APK_PATH;
 use log::{error, info, warn};
+use odsign_proto::odsign_info::OdsignInfo;
+use protobuf::Message;
 use rustutils::system_properties;
-use std::fs::{remove_dir_all, OpenOptions};
+use std::fs::{remove_dir_all, File, OpenOptions};
+use std::os::fd::AsFd;
 use std::os::unix::fs::OpenOptionsExt;
 use std::os::unix::io::{AsRawFd, OwnedFd};
 use std::path::Path;
@@ -103,8 +108,21 @@
 
                 let result = match exit_code {
                     Ok(ExitCode::CompilationSuccess) => {
-                        info!("CompilationSuccess");
-                        callback.onSuccess()
+                        if compilation_mode == CompilationMode::TEST_COMPILE {
+                            info!("Compilation success");
+                            callback.onSuccess()
+                        } else {
+                            // compos.info is generated only during NORMAL_COMPILE
+                            if let Err(e) = enable_fsverity_to_all() {
+                                let message =
+                                    format!("Unexpected failure when enabling fs-verity: {:?}", e);
+                                error!("{}", message);
+                                callback.onFailure(FailureReason::FailedToEnableFsverity, &message)
+                            } else {
+                                info!("Compilation success, fs-verity enabled");
+                                callback.onSuccess()
+                            }
+                        }
                     }
                     Ok(exit_code) => {
                         let message = format!("Unexpected odrefresh result: {:?}", exit_code);
@@ -161,13 +179,20 @@
     let output_dir_raw_fd = output_dir_fd.as_raw_fd();
     let staging_dir_raw_fd = staging_dir_fd.as_raw_fd();
 
-    // Get the /system_ext FD differently because it may not exist.
-    let (system_ext_dir_raw_fd, ro_dir_fds) =
-        if let Ok(system_ext_dir_fd) = open_dir(Path::new("/system_ext")) {
-            (system_ext_dir_fd.as_raw_fd(), vec![system_dir_fd, system_ext_dir_fd])
-        } else {
-            (-1, vec![system_dir_fd])
-        };
+    // When the VM starts, it starts with or without mouting the extra build manifest APK from
+    // /system_ext. Later on request (here), we need to pass the directory FD of /system_ext, but
+    // only if the VM is configured to need it.
+    //
+    // It is possible to plumb the information from ComposClient to here, but it's extra complexity
+    // and feel slightly weird to encode the VM's state to the task itself, as it is a request to
+    // the VM.
+    let need_system_ext = Path::new(BUILD_MANIFEST_SYSTEM_EXT_APK_PATH).exists();
+    let (system_ext_dir_raw_fd, ro_dir_fds) = if need_system_ext {
+        let system_ext_dir_fd = open_dir(Path::new("/system_ext"))?;
+        (system_ext_dir_fd.as_raw_fd(), vec![system_dir_fd, system_ext_dir_fd])
+    } else {
+        (-1, vec![system_dir_fd])
+    };
 
     // Spawn a fd_server to serve the FDs.
     let fd_server_config = FdServerConfig {
@@ -197,6 +222,31 @@
     ExitCode::from_i32(exit_code.into())
 }
 
+/// Enable fs-verity to output artifacts according to compos.info in the pending directory. Any
+/// error before the completion will just abort, leaving the previous files enabled.
+fn enable_fsverity_to_all() -> Result<()> {
+    let odrefresh_current_dir = Path::new(ODREFRESH_OUTPUT_ROOT_DIR).join(CURRENT_ARTIFACTS_SUBDIR);
+    let pending_dir = Path::new(ODREFRESH_OUTPUT_ROOT_DIR).join(PENDING_ARTIFACTS_SUBDIR);
+    let mut reader =
+        File::open(&pending_dir.join("compos.info")).context("Failed to open compos.info")?;
+    let compos_info = OdsignInfo::parse_from_reader(&mut reader).context("Failed to parse")?;
+
+    for path_str in compos_info.file_hashes.keys() {
+        // Need to rebase the directory on to compos-pending first
+        if let Ok(relpath) = Path::new(path_str).strip_prefix(&odrefresh_current_dir) {
+            let path = pending_dir.join(relpath);
+            let file = File::open(&path).with_context(|| format!("Failed to open {:?}", path))?;
+            // We don't expect error. But when it happens, don't bother handle it here. For
+            // simplicity, just let odsign do the regular check.
+            fsverity::enable(file.as_fd())
+                .with_context(|| format!("Failed to enable fs-verity to {:?}", path))?;
+        } else {
+            warn!("Skip due to unexpected path: {}", path_str);
+        }
+    }
+    Ok(())
+}
+
 /// Returns an `OwnedFD` of the directory.
 fn open_dir(path: &Path) -> Result<OwnedFd> {
     Ok(OwnedFd::from(
diff --git a/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java b/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
index 479ae7f..933ac7a 100644
--- a/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
+++ b/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
@@ -234,6 +234,10 @@
                     result = IsolatedCompilationMetrics.RESULT_UNEXPECTED_COMPILATION_RESULT;
                     break;
 
+                case ICompilationTaskCallback.FailureReason.FailedToEnableFsverity:
+                    result = IsolatedCompilationMetrics.RESULT_FAILED_TO_ENABLE_FSVERITY;
+                    break;
+
                 default:
                     result = IsolatedCompilationMetrics.RESULT_UNKNOWN_FAILURE;
                     break;
diff --git a/compos/service/java/com/android/server/compos/IsolatedCompilationMetrics.java b/compos/service/java/com/android/server/compos/IsolatedCompilationMetrics.java
index e333198..f7799a4 100644
--- a/compos/service/java/com/android/server/compos/IsolatedCompilationMetrics.java
+++ b/compos/service/java/com/android/server/compos/IsolatedCompilationMetrics.java
@@ -36,9 +36,17 @@
 
     // TODO(b/218525257): Move the definition of these enums to atoms.proto
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RESULT_UNKNOWN, RESULT_SUCCESS, RESULT_UNKNOWN_FAILURE, RESULT_FAILED_TO_START,
-            RESULT_JOB_CANCELED, RESULT_COMPILATION_FAILED, RESULT_UNEXPECTED_COMPILATION_RESULT,
-            RESULT_COMPOSD_DIED})
+    @IntDef({
+        RESULT_UNKNOWN,
+        RESULT_SUCCESS,
+        RESULT_UNKNOWN_FAILURE,
+        RESULT_FAILED_TO_START,
+        RESULT_JOB_CANCELED,
+        RESULT_COMPILATION_FAILED,
+        RESULT_UNEXPECTED_COMPILATION_RESULT,
+        RESULT_COMPOSD_DIED,
+        RESULT_FAILED_TO_ENABLE_FSVERITY
+    })
     public @interface CompilationResult {}
 
     // Keep this in sync with Result enum in IsolatedCompilationEnded in
@@ -59,6 +67,9 @@
             .ISOLATED_COMPILATION_ENDED__COMPILATION_RESULT__RESULT_UNEXPECTED_COMPILATION_RESULT;
     public static final int RESULT_COMPOSD_DIED =
             ArtStatsLog.ISOLATED_COMPILATION_ENDED__COMPILATION_RESULT__RESULT_COMPOSD_DIED;
+    public static final int RESULT_FAILED_TO_ENABLE_FSVERITY =
+            ArtStatsLog
+                    .ISOLATED_COMPILATION_ENDED__COMPILATION_RESULT__RESULT_FAILED_TO_ENABLE_FSVERITY;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SCHEDULING_RESULT_UNKNOWN, SCHEDULING_SUCCESS, SCHEDULING_FAILURE})
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 0abaf79..9c9be6c 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -72,8 +72,6 @@
         "apexd.microdroid",
         "debuggerd",
         "linker",
-        "tombstoned.microdroid",
-        "tombstone_transmit.microdroid",
         "cgroups.json",
         "task_profiles.json",
         "public.libraries.android.txt",
diff --git a/microdroid/init.rc b/microdroid/init.rc
index c997bfd..29f8970 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -126,14 +126,6 @@
     mkdir /data/vendor_de 0771 root root
     mkdir /data/vendor/hardware 0771 root root
 
-    # Start tombstoned early to be able to store tombstones.
-    # microdroid doesn't have anr, but tombstoned requires it
-    mkdir /data/anr 0775 system system
-    mkdir /data/tombstones 0771 system system
-    mkdir /data/vendor/tombstones 0771 root root
-
-    start tombstoned
-
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local 0751 root root
@@ -146,15 +138,6 @@
     # Mark boot completed. This will notify microdroid_manager to run payload.
     setprop dev.bootcomplete 1
 
-on property:tombstone_transmit.start=1
-    mkdir /data/tombstones 0771 system system
-    start tombstone_transmit
-
-service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000 -remove_tombstones_after_transmitting
-    user system
-    group system
-    shutdown critical
-
 service apexd-vm /system/bin/apexd --vm
     user root
     group system
diff --git a/microdroid/kdump/kexec.c b/microdroid/kdump/kexec.c
index 8d88951..d3e8e02 100644
--- a/microdroid/kdump/kexec.c
+++ b/microdroid/kdump/kexec.c
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -53,6 +54,20 @@
     if (syscall(SYS_kexec_file_load, open_checked(KERNEL), open_checked(INITRD), cmdline_len,
                 CMDLINE, KEXEC_FILE_ON_CRASH) == -1) {
         fprintf(stderr, "Failed to load panic kernel: %s\n", strerror(errno));
+        if (errno == EADDRNOTAVAIL) {
+            struct stat st;
+            off_t kernel_size = 0;
+            off_t initrd_size = 0;
+
+            if (stat(KERNEL, &st) == 0) {
+                kernel_size = st.st_size;
+            }
+            if (stat(INITRD, &st) == 0) {
+                initrd_size = st.st_size;
+            }
+            fprintf(stderr, "Image size too big? %s:%ld bytes, %s:%ld bytes", KERNEL, kernel_size,
+                    INITRD, initrd_size);
+        }
         return 1;
     }
     return 0;
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index fa96bf4..ffa2e45 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -81,7 +81,6 @@
 const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
 
 const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
-const TOMBSTONE_TRANSMIT_DONE_PROP: &str = "tombstone_transmit.init_done";
 const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
 
 // SYNC WITH virtualizationservice/src/crosvm.rs
@@ -423,12 +422,11 @@
 
     setup_config_sysprops(&config)?;
 
-    // Start tombstone_transmit if enabled
+    // Set export_tombstones if enabled
     if should_export_tombstones(&config) {
-        system_properties::write("tombstone_transmit.start", "1")
-            .context("set tombstone_transmit.start")?;
-    } else {
-        control_service("stop", "tombstoned")?;
+        // This property is read by tombstone_handler.
+        system_properties::write("microdroid_manager.export_tombstones.enabled", "1")
+            .context("set microdroid_manager.export_tombstones.enabled")?;
     }
 
     // Wait until zipfuse has mounted the APKs so we can access the payload
@@ -448,20 +446,10 @@
     system_properties::write("microdroid_manager.init_done", "1")
         .context("set microdroid_manager.init_done")?;
 
-    // Wait for tombstone_transmit to init
-    if should_export_tombstones(&config) {
-        wait_for_tombstone_transmit_done()?;
-    }
-
     info!("boot completed, time to run payload");
     exec_task(task, service).context("Failed to run payload")
 }
 
-fn control_service(action: &str, service: &str) -> Result<()> {
-    system_properties::write(&format!("ctl.{}", action), service)
-        .with_context(|| format!("Failed to {} {}", action, service))
-}
-
 struct ApkDmverityArgument<'a> {
     apk: &'a str,
     idsig: &'a str,
@@ -733,11 +721,6 @@
     wait_for_property_true(APEX_CONFIG_DONE_PROP).context("Failed waiting for apex config done")
 }
 
-fn wait_for_tombstone_transmit_done() -> Result<()> {
-    wait_for_property_true(TOMBSTONE_TRANSMIT_DONE_PROP)
-        .context("Failed waiting for tombstone transmit done")
-}
-
 fn wait_for_property_true(property_name: &str) -> Result<()> {
     let mut prop = PropertyWatcher::new(property_name)?;
     loop {
diff --git a/pvmfw/src/crypto.rs b/pvmfw/src/crypto.rs
index 85dc6c9..275de7a 100644
--- a/pvmfw/src/crypto.rs
+++ b/pvmfw/src/crypto.rs
@@ -14,6 +14,8 @@
 
 //! Wrapper around BoringSSL/OpenSSL symbols.
 
+use crate::cstr;
+
 use core::convert::AsRef;
 use core::ffi::{c_char, c_int, CStr};
 use core::fmt;
@@ -81,14 +83,10 @@
 
 impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let unknown_library = CStr::from_bytes_with_nul(b"{unknown library}\0").unwrap();
-        let unknown_reason = CStr::from_bytes_with_nul(b"{unknown reason}\0").unwrap();
-        let unknown_file = CStr::from_bytes_with_nul(b"??\0").unwrap();
-
         let packed = self.packed_value();
-        let library = self.library_name().unwrap_or(unknown_library).to_str().unwrap();
-        let reason = self.reason().unwrap_or(unknown_reason).to_str().unwrap();
-        let file = self.file.unwrap_or(unknown_file).to_str().unwrap();
+        let library = self.library_name().unwrap_or(cstr!("{unknown library}")).to_str().unwrap();
+        let reason = self.reason().unwrap_or(cstr!("{unknown reason}")).to_str().unwrap();
+        let file = self.file.unwrap_or(cstr!("??")).to_str().unwrap();
         let line = self.line;
 
         write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
diff --git a/pvmfw/src/debug_policy.rs b/pvmfw/src/debug_policy.rs
index 23d3e1d..15efa1c 100644
--- a/pvmfw/src/debug_policy.rs
+++ b/pvmfw/src/debug_policy.rs
@@ -14,6 +14,7 @@
 
 //! Support for the debug policy overlay in pvmfw
 
+use crate::cstr;
 use alloc::{vec, vec::Vec};
 use core::ffi::CStr;
 use core::fmt;
@@ -65,11 +66,8 @@
 
 /// Disables ramdump by removing crashkernel from bootargs in /chosen.
 fn disable_ramdump(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> {
-    let chosen_path = CStr::from_bytes_with_nul(b"/chosen\0").unwrap();
-    let bootargs_name = CStr::from_bytes_with_nul(b"bootargs\0").unwrap();
-
     let chosen = match fdt
-        .node(chosen_path)
+        .node(cstr!("/chosen"))
         .map_err(|e| DebugPolicyError::Fdt("Failed to find /chosen", e))?
     {
         Some(node) => node,
@@ -77,7 +75,7 @@
     };
 
     let bootargs = match chosen
-        .getprop_str(bootargs_name)
+        .getprop_str(cstr!("bootargs"))
         .map_err(|e| DebugPolicyError::Fdt("Failed to find bootargs prop", e))?
     {
         Some(value) if !value.to_bytes().is_empty() => value,
@@ -100,8 +98,8 @@
     new_bootargs.push(b'\0');
 
     // We've checked existence of /chosen node at the beginning.
-    let mut chosen_mut = fdt.node_mut(chosen_path).unwrap().unwrap();
-    chosen_mut.setprop(bootargs_name, new_bootargs.as_slice()).map_err(|e| {
+    let mut chosen_mut = fdt.node_mut(cstr!("/chosen")).unwrap().unwrap();
+    chosen_mut.setprop(cstr!("bootargs"), new_bootargs.as_slice()).map_err(|e| {
         DebugPolicyError::OverlaidFdt("Failed to remove crashkernel. FDT might be corrupted", e)
     })
 }
@@ -109,7 +107,7 @@
 /// Returns true only if fdt has ramdump prop in the /avf/guest/common node with value <1>
 fn is_ramdump_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> {
     let common = match fdt
-        .node(CStr::from_bytes_with_nul(b"/avf/guest/common\0").unwrap())
+        .node(cstr!("/avf/guest/common"))
         .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find /avf/guest/common node", e))?
     {
         Some(node) => node,
@@ -117,7 +115,7 @@
     };
 
     match common
-        .getprop_u32(CStr::from_bytes_with_nul(b"ramdump\0").unwrap())
+        .getprop_u32(cstr!("ramdump"))
         .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find ramdump prop", e))?
     {
         Some(1) => Ok(true),
@@ -128,11 +126,8 @@
 /// Enables console output by adding kernel.printk.devkmsg and kernel.console to bootargs.
 /// This uses hardcoded console name 'hvc0' and it should be match with microdroid's bootconfig.debuggable.
 fn enable_console_output(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> {
-    let chosen_path = CStr::from_bytes_with_nul(b"/chosen\0").unwrap();
-    let bootargs_name = CStr::from_bytes_with_nul(b"bootargs\0").unwrap();
-
     let chosen = match fdt
-        .node(chosen_path)
+        .node(cstr!("/chosen"))
         .map_err(|e| DebugPolicyError::Fdt("Failed to find /chosen", e))?
     {
         Some(node) => node,
@@ -140,7 +135,7 @@
     };
 
     let bootargs = match chosen
-        .getprop_str(bootargs_name)
+        .getprop_str(cstr!("bootargs"))
         .map_err(|e| DebugPolicyError::Fdt("Failed to find bootargs prop", e))?
     {
         Some(value) if !value.to_bytes().is_empty() => value,
@@ -154,8 +149,8 @@
     fdt.unpack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to unpack", e))?;
 
     // We've checked existence of /chosen node at the beginning.
-    let mut chosen_mut = fdt.node_mut(chosen_path).unwrap().unwrap();
-    chosen_mut.setprop(bootargs_name, new_bootargs.as_slice()).map_err(|e| {
+    let mut chosen_mut = fdt.node_mut(cstr!("/chosen")).unwrap().unwrap();
+    chosen_mut.setprop(cstr!("bootargs"), new_bootargs.as_slice()).map_err(|e| {
         DebugPolicyError::OverlaidFdt("Failed to enabled console output. FDT might be corrupted", e)
     })?;
 
@@ -166,7 +161,7 @@
 /// Returns true only if fdt has log prop in the /avf/guest/common node with value <1>
 fn is_console_output_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> {
     let common = match fdt
-        .node(CStr::from_bytes_with_nul(b"/avf/guest/common\0").unwrap())
+        .node(cstr!("/avf/guest/common"))
         .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find /avf/guest/common node", e))?
     {
         Some(node) => node,
@@ -174,7 +169,7 @@
     };
 
     match common
-        .getprop_u32(CStr::from_bytes_with_nul(b"log\0").unwrap())
+        .getprop_u32(cstr!("log"))
         .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find log prop", e))?
     {
         Some(1) => Ok(true),
diff --git a/pvmfw/src/dice.rs b/pvmfw/src/dice.rs
index 3ceb8ef..bad3453 100644
--- a/pvmfw/src/dice.rs
+++ b/pvmfw/src/dice.rs
@@ -14,6 +14,7 @@
 
 //! Support for DICE derivation and BCC generation.
 
+use crate::cstr;
 use crate::helpers::flushed_zeroize;
 use core::ffi::c_void;
 use core::ffi::CStr;
@@ -60,10 +61,9 @@
         self,
         salt: &[u8; HIDDEN_SIZE],
     ) -> diced_open_dice::Result<InputValues> {
-        let component_name = CStr::from_bytes_with_nul(b"vm_entry\0").unwrap();
         let mut config_descriptor_buffer = [0; 128];
         let config_descriptor_size = bcc_format_config_descriptor(
-            Some(component_name),
+            Some(cstr!("vm_entry")),
             None,  // component_version
             false, // resettable
             &mut config_descriptor_buffer,
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index f4b0244..f56d6e0 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -14,6 +14,7 @@
 
 //! High-level FDT functions.
 
+use crate::cstr;
 use crate::helpers::GUEST_PAGE_SIZE;
 use crate::RebootReason;
 use core::ffi::CStr;
@@ -30,11 +31,10 @@
 
 /// Extract from /config the address range containing the pre-loaded kernel.
 pub fn kernel_range(fdt: &libfdt::Fdt) -> libfdt::Result<Option<Range<usize>>> {
-    let config = CStr::from_bytes_with_nul(b"/config\0").unwrap();
-    let addr = CStr::from_bytes_with_nul(b"kernel-address\0").unwrap();
-    let size = CStr::from_bytes_with_nul(b"kernel-size\0").unwrap();
+    let addr = cstr!("kernel-address");
+    let size = cstr!("kernel-size");
 
-    if let Some(config) = fdt.node(config)? {
+    if let Some(config) = fdt.node(cstr!("/config"))? {
         if let (Some(addr), Some(size)) = (config.getprop_u32(addr)?, config.getprop_u32(size)?) {
             let addr = addr as usize;
             let size = size as usize;
@@ -48,8 +48,8 @@
 
 /// Extract from /chosen the address range containing the pre-loaded ramdisk.
 pub fn initrd_range(fdt: &libfdt::Fdt) -> libfdt::Result<Option<Range<usize>>> {
-    let start = CStr::from_bytes_with_nul(b"linux,initrd-start\0").unwrap();
-    let end = CStr::from_bytes_with_nul(b"linux,initrd-end\0").unwrap();
+    let start = cstr!("linux,initrd-start");
+    let end = cstr!("linux,initrd-end");
 
     if let Some(chosen) = fdt.chosen()? {
         if let (Some(start), Some(end)) = (chosen.getprop_u32(start)?, chosen.getprop_u32(end)?) {
@@ -106,7 +106,7 @@
 /// Read the number of CPUs
 fn parse_cpu_nodes(fdt: &libfdt::Fdt) -> Result<NonZeroUsize, RebootReason> {
     let num = fdt
-        .compatible_nodes(CStr::from_bytes_with_nul(b"arm,arm-v8\0").unwrap())
+        .compatible_nodes(cstr!("arm,arm-v8"))
         .map_err(|e| {
             error!("Failed to read compatible nodes \"arm,arm-v8\" from DT: {e}");
             RebootReason::InvalidFdt
@@ -128,7 +128,7 @@
 /// Read and validate PCI node
 fn parse_pci_nodes(fdt: &libfdt::Fdt) -> Result<PciInfo, RebootReason> {
     let node = fdt
-        .compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
+        .compatible_nodes(cstr!("pci-host-cam-generic"))
         .map_err(|e| {
             error!("Failed to read compatible node \"pci-host-cam-generic\" from DT: {e}");
             RebootReason::InvalidFdt
@@ -230,11 +230,11 @@
     const IRQ_MASK_ANY_IRQ: u32 = 0x7;
     const EXPECTED: [u32; IRQ_MASK_CELLS] =
         [IRQ_MASK_ADDR_HI, IRQ_MASK_ADDR_ME, IRQ_MASK_ADDR_LO, IRQ_MASK_ANY_IRQ];
-    let name = CStr::from_bytes_with_nul(b"interrupt-map-mask\0").unwrap();
+
     let mut irq_count: usize = 0;
     for irq_mask in CellChunkIterator::<IRQ_MASK_CELLS>::new(
         pci_node
-            .getprop_cells(name)
+            .getprop_cells(cstr!("interrupt-map-mask"))
             .map_err(|e| {
                 error!("Failed to read interrupt-map-mask property: {e}");
                 RebootReason::InvalidFdt
@@ -266,10 +266,9 @@
     let mut phys_hi: u32 = 0;
     let mut irq_nr = AARCH64_IRQ_BASE;
 
-    let name = CStr::from_bytes_with_nul(b"interrupt-map\0").unwrap();
     for irq_map in CellChunkIterator::<IRQ_MAP_CELLS>::new(
         pci_node
-            .getprop_cells(name)
+            .getprop_cells(cstr!("interrupt-map"))
             .map_err(|e| {
                 error!("Failed to read interrupt-map property: {e}");
                 RebootReason::InvalidFdt
@@ -350,7 +349,7 @@
 fn parse_serial_nodes(fdt: &libfdt::Fdt) -> Result<SerialInfo, RebootReason> {
     let mut ret: SerialInfo = Default::default();
     for (i, node) in fdt
-        .compatible_nodes(CStr::from_bytes_with_nul(b"ns16550a\0").unwrap())
+        .compatible_nodes(cstr!("ns16550a"))
         .map_err(|e| {
             error!("Failed to read compatible nodes \"ns16550a\" from DT: {e}");
             RebootReason::InvalidFdt
@@ -390,7 +389,7 @@
 
 fn parse_swiotlb_nodes(fdt: &libfdt::Fdt) -> Result<SwiotlbInfo, RebootReason> {
     let node = fdt
-        .compatible_nodes(CStr::from_bytes_with_nul(b"restricted-dma-pool\0").unwrap())
+        .compatible_nodes(cstr!("restricted-dma-pool"))
         .map_err(|e| {
             error!("Failed to read compatible nodes \"restricted-dma-pool\" from DT: {e}");
             RebootReason::InvalidFdt
@@ -401,7 +400,7 @@
             RebootReason::InvalidFdt
         })?;
     let size = node
-        .getprop_u64(CStr::from_bytes_with_nul(b"size\0").unwrap())
+        .getprop_u64(cstr!("size"))
         .map_err(|e| {
             error!("Failed to read \"size\" property of \"restricted-dma-pool\": {e}");
             RebootReason::InvalidFdt
@@ -412,7 +411,7 @@
         })?;
 
     let align = node
-        .getprop_u64(CStr::from_bytes_with_nul(b"alignment\0").unwrap())
+        .getprop_u64(cstr!("alignment"))
         .map_err(|e| {
             error!("Failed to read \"alignment\" property of \"restricted-dma-pool\": {e}");
             RebootReason::InvalidFdt
@@ -470,16 +469,8 @@
 
     add_dice_node(fdt, bcc.as_ptr() as usize, bcc.len())?;
 
-    set_or_clear_chosen_flag(
-        fdt,
-        CStr::from_bytes_with_nul(b"avf,strict-boot\0").unwrap(),
-        strict_boot,
-    )?;
-    set_or_clear_chosen_flag(
-        fdt,
-        CStr::from_bytes_with_nul(b"avf,new-instance\0").unwrap(),
-        new_instance,
-    )?;
+    set_or_clear_chosen_flag(fdt, cstr!("avf,strict-boot"), strict_boot)?;
+    set_or_clear_chosen_flag(fdt, cstr!("avf,new-instance"), new_instance)?;
 
     fdt.pack()?;
 
@@ -488,24 +479,20 @@
 
 /// Add a "google,open-dice"-compatible reserved-memory node to the tree.
 fn add_dice_node(fdt: &mut Fdt, addr: usize, size: usize) -> libfdt::Result<()> {
-    let reserved_memory = CStr::from_bytes_with_nul(b"/reserved-memory\0").unwrap();
     // We reject DTs with missing reserved-memory node as validation should have checked that the
     // "swiotlb" subnode (compatible = "restricted-dma-pool") was present.
-    let mut reserved_memory = fdt.node_mut(reserved_memory)?.ok_or(libfdt::FdtError::NotFound)?;
+    let mut reserved_memory =
+        fdt.node_mut(cstr!("/reserved-memory"))?.ok_or(libfdt::FdtError::NotFound)?;
 
-    let dice = CStr::from_bytes_with_nul(b"dice\0").unwrap();
-    let mut dice = reserved_memory.add_subnode(dice)?;
+    let mut dice = reserved_memory.add_subnode(cstr!("dice"))?;
 
-    let compatible = CStr::from_bytes_with_nul(b"compatible\0").unwrap();
-    dice.appendprop(compatible, b"google,open-dice\0")?;
+    dice.appendprop(cstr!("compatible"), b"google,open-dice\0")?;
 
-    let no_map = CStr::from_bytes_with_nul(b"no-map\0").unwrap();
-    dice.appendprop(no_map, &[])?;
+    dice.appendprop(cstr!("no-map"), &[])?;
 
     let addr = addr.try_into().unwrap();
     let size = size.try_into().unwrap();
-    let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
-    dice.appendprop_addrrange(reg, addr, size)?;
+    dice.appendprop_addrrange(cstr!("reg"), addr, size)?;
 
     Ok(())
 }
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index e6e3406..fddd8c3 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -113,3 +113,11 @@
     reg.zeroize();
     flush(reg)
 }
+
+/// Create &CStr out of &str literal
+#[macro_export]
+macro_rules! cstr {
+    ($str:literal) => {{
+        CStr::from_bytes_with_nul(concat!($str, "\0").as_bytes()).unwrap()
+    }};
+}
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 20a6045..a7f7906 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -67,6 +67,8 @@
 
         // remove any leftover files under test root
         android.tryRun("rm", "-rf", TEST_ROOT + "*");
+
+        android.tryRun("mkdir " + TEST_ROOT);
     }
 
     public static void cleanUpVirtualizationTestSetup(ITestDevice androidDevice)
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index e015d9d..749d75f 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -725,10 +725,11 @@
 /// user devices (W^X).
 fn check_label_is_allowed(context: &SeContext) -> Result<()> {
     match context.selinux_type()? {
-        | "system_file" // immutable dm-verity protected partition
         | "apk_data_file" // APKs of an installed app
-        | "staging_data_file" // updated/staged APEX images
         | "shell_data_file" // test files created via adb shell
+        | "staging_data_file" // updated/staged APEX images
+        | "system_file" // immutable dm-verity protected partition
+        | "virtualizationservice_data_file" // files created by VS / VirtMgr
          => Ok(()),
         _ => bail!("Label {} is not allowed", context),
     }