Merge "Add support for autoclear flag for loopdevice" into main
diff --git a/guest/apkdmverity/src/main.rs b/guest/apkdmverity/src/main.rs
index d2f88ae..2fc964b 100644
--- a/guest/apkdmverity/src/main.rs
+++ b/guest/apkdmverity/src/main.rs
@@ -27,6 +27,7 @@
 use apkverify::{HashAlgorithm, V4Signature};
 use clap::{arg, Arg, ArgAction, Command};
 use dm::loopdevice;
+use dm::loopdevice::LoopConfigOptions;
 use dm::util;
 use dm::verity::{DmVerityHashAlgorithm, DmVerityTargetBuilder};
 use itertools::Itertools;
@@ -109,9 +110,13 @@
         }
         (
             loopdevice::attach(
-                &apk, 0, apk_size, /* direct_io */ true, /* writable */ false,
+                &apk,
+                0,
+                apk_size,
+                &LoopConfigOptions { direct_io: true, ..Default::default() },
             )
-            .context("Failed to attach APK to a loop device")?,
+            .context("Failed to attach APK to a loop device")?
+            .path,
             apk_size,
         )
     };
@@ -125,10 +130,9 @@
     // Due to unknown reason(b/191344832), we can't enable "direct IO" for the IDSIG file (backing
     // the hash). For now we don't use "direct IO" but it seems OK since the IDSIG file is very
     // small and the benefit of direct-IO would be negliable.
-    let hash_device = loopdevice::attach(
-        &idsig, offset, size, /* direct_io */ false, /* writable */ false,
-    )
-    .context("Failed to attach idsig to a loop device")?;
+    let hash_device = loopdevice::attach(&idsig, offset, size, &LoopConfigOptions::default())
+        .context("Failed to attach idsig to a loop device")?
+        .path;
 
     // Build a dm-verity target spec from the information from the idsig file. The apk and the
     // idsig files are used as the data device and the hash device, respectively.
@@ -347,18 +351,17 @@
         // of the data device is done in the scopeguard for the return value of `enable_verity`
         // below. Only the idsig_loop_device needs detatching.
         let apk_loop_device = loopdevice::attach(
-            &apk_path, 0, apk_size, /* direct_io */ true, /* writable */ false,
+            &apk_path,
+            0,
+            apk_size,
+            &LoopConfigOptions { direct_io: true, ..Default::default() },
         )
-        .unwrap();
+        .unwrap()
+        .path;
         let idsig_loop_device = scopeguard::guard(
-            loopdevice::attach(
-                &idsig_path,
-                0,
-                idsig_size,
-                /* direct_io */ false,
-                /* writable */ false,
-            )
-            .unwrap(),
+            loopdevice::attach(&idsig_path, 0, idsig_size, &LoopConfigOptions::default())
+                .unwrap()
+                .path,
             |dev| loopdevice::detach(dev).unwrap(),
         );
 
diff --git a/libs/devicemapper/Android.bp b/libs/devicemapper/Android.bp
index 5332469..6b7f680 100644
--- a/libs/devicemapper/Android.bp
+++ b/libs/devicemapper/Android.bp
@@ -8,7 +8,6 @@
     defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     edition: "2021",
-    prefer_rlib: true,
     rustlibs: [
         "libanyhow",
         "libbitflags",
@@ -18,16 +17,12 @@
         "libuuid",
         "libzerocopy",
     ],
-    multilib: {
-        lib32: {
-            enabled: false,
-        },
-    },
 }
 
 rust_library {
     name: "libdm_rust",
     defaults: ["libdm_rust.defaults"],
+    host_supported: true,
 }
 
 rust_test {
diff --git a/libs/devicemapper/src/lib.rs b/libs/devicemapper/src/lib.rs
index a8f3049..a8c2833 100644
--- a/libs/devicemapper/src/lib.rs
+++ b/libs/devicemapper/src/lib.rs
@@ -235,6 +235,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::loopdevice::LoopConfigOptions;
     use crypt::{CipherType, DmCryptTargetBuilder};
     use rdroidtest::{ignore_if, rdroidtest};
     use rustutils::system_properties;
@@ -328,10 +329,10 @@
             backing_file,
             0,
             sz,
-            /* direct_io */ true,
-            /* writable */ true,
+            &LoopConfigOptions { direct_io: true, writable: true, ..Default::default() },
         )
-        .unwrap();
+        .unwrap()
+        .path;
         let device_diff = device.to_owned() + "_diff";
 
         scopeguard::defer! {
@@ -372,10 +373,10 @@
             backing_file,
             0,
             sz,
-            /* direct_io */ true,
-            /* writable */ true,
+            &LoopConfigOptions { direct_io: true, writable: true, ..Default::default() },
         )
-        .unwrap();
+        .unwrap()
+        .path;
         let device_diff = device.to_owned() + "_diff";
         scopeguard::defer! {
             loopdevice::detach(&data_device).unwrap();
diff --git a/libs/devicemapper/src/loopdevice.rs b/libs/devicemapper/src/loopdevice.rs
index 130c1c4..b830eda 100644
--- a/libs/devicemapper/src/loopdevice.rs
+++ b/libs/devicemapper/src/loopdevice.rs
@@ -59,14 +59,31 @@
     Ok(unsafe { _loop_clr_fd(device_file.as_raw_fd()) }?)
 }
 
+/// LOOP_CONFIGURE ioctl operation flags.
+#[derive(Default)]
+pub struct LoopConfigOptions {
+    /// Whether to use direct I/O
+    pub direct_io: bool,
+    /// Whether the device is writable
+    pub writable: bool,
+    /// Whether to autodestruct the device on last close
+    pub autoclear: bool,
+}
+
+pub struct LoopDevice {
+    /// The loop device file
+    pub file: File,
+    /// Path to the loop device
+    pub path: PathBuf,
+}
+
 /// Creates a loop device and attach the given file at `path` as the backing store.
 pub fn attach<P: AsRef<Path>>(
     path: P,
     offset: u64,
     size_limit: u64,
-    direct_io: bool,
-    writable: bool,
-) -> Result<PathBuf> {
+    options: &LoopConfigOptions,
+) -> Result<LoopDevice> {
     // Attaching a file to a loop device can make a race condition; a loop device number obtained
     // from LOOP_CTL_GET_FREE might have been used by another thread or process. In that case the
     // subsequent LOOP_CONFIGURE ioctl returns with EBUSY. Try until it succeeds.
@@ -80,8 +97,8 @@
 
     let begin = Instant::now();
     loop {
-        match try_attach(&path, offset, size_limit, direct_io, writable) {
-            Ok(loop_dev) => return Ok(loop_dev),
+        match try_attach(&path, offset, size_limit, options) {
+            Ok(loop_device) => return Ok(loop_device),
             Err(e) => {
                 if begin.elapsed() > TIMEOUT {
                     return Err(e);
@@ -102,9 +119,8 @@
     path: P,
     offset: u64,
     size_limit: u64,
-    direct_io: bool,
-    writable: bool,
-) -> Result<PathBuf> {
+    options: &LoopConfigOptions,
+) -> Result<LoopDevice> {
     // Get a free loop device
     wait_for_path(LOOP_CONTROL)?;
     let ctrl_file = OpenOptions::new()
@@ -117,8 +133,8 @@
     // Construct the loop_info64 struct
     let backing_file = OpenOptions::new()
         .read(true)
-        .write(writable)
-        .custom_flags(if direct_io { O_DIRECT } else { 0 })
+        .write(options.writable)
+        .custom_flags(if options.direct_io { O_DIRECT } else { 0 })
         .open(&path)
         .context(format!("failed to open {:?}", path.as_ref()))?;
     let mut config = loop_config::new_zeroed();
@@ -127,14 +143,18 @@
     config.info.lo_offset = offset;
     config.info.lo_sizelimit = size_limit;
 
-    if !writable {
+    if !options.writable {
         config.info.lo_flags = Flag::LO_FLAGS_READ_ONLY;
     }
 
-    if direct_io {
+    if options.direct_io {
         config.info.lo_flags.insert(Flag::LO_FLAGS_DIRECT_IO);
     }
 
+    if options.autoclear {
+        config.info.lo_flags.insert(Flag::LO_FLAGS_AUTOCLEAR);
+    }
+
     // Configure the loop device to attach the backing file
     let device_path = format!("{}{}", LOOP_DEV_PREFIX, num);
     wait_for_path(&device_path)?;
@@ -146,7 +166,7 @@
     loop_configure(&device_file, &config)
         .context(format!("Failed to configure {:?}", &device_path))?;
 
-    Ok(PathBuf::from(device_path))
+    Ok(LoopDevice { file: device_file, path: PathBuf::from(device_path) })
 }
 
 /// Detaches backing file from the loop device `path`.
@@ -185,7 +205,10 @@
         let a_file = a_dir.path().join("test");
         let a_size = 4096u64;
         create_empty_file(&a_file, a_size);
-        let dev = attach(a_file, 0, a_size, /* direct_io */ true, /* writable */ false).unwrap();
+        let dev =
+            attach(a_file, 0, a_size, &LoopConfigOptions { direct_io: true, ..Default::default() })
+                .unwrap()
+                .path;
         scopeguard::defer! {
             detach(&dev).unwrap();
         }
@@ -198,7 +221,7 @@
         let a_file = a_dir.path().join("test");
         let a_size = 4096u64;
         create_empty_file(&a_file, a_size);
-        let dev = attach(a_file, 0, a_size, /* direct_io */ false, /* writable */ false).unwrap();
+        let dev = attach(a_file, 0, a_size, &LoopConfigOptions::default()).unwrap().path;
         scopeguard::defer! {
             detach(&dev).unwrap();
         }
@@ -211,11 +234,34 @@
         let a_file = a_dir.path().join("test");
         let a_size = 4096u64;
         create_empty_file(&a_file, a_size);
-        let dev = attach(a_file, 0, a_size, /* direct_io */ true, /* writable */ true).unwrap();
+        let dev = attach(
+            a_file,
+            0,
+            a_size,
+            &LoopConfigOptions { direct_io: true, writable: true, ..Default::default() },
+        )
+        .unwrap()
+        .path;
         scopeguard::defer! {
             detach(&dev).unwrap();
         }
         assert!(is_direct_io(&dev));
         assert!(is_direct_io_writable(&dev));
     }
+
+    #[rdroidtest]
+    fn attach_loop_device_autoclear() {
+        let a_dir = tempfile::TempDir::new().unwrap();
+        let a_file = a_dir.path().join("test");
+        let a_size = 4096u64;
+        create_empty_file(&a_file, a_size);
+        let dev =
+            attach(a_file, 0, a_size, &LoopConfigOptions { autoclear: true, ..Default::default() })
+                .unwrap();
+        drop(dev.file);
+
+        let dev_size_path =
+            Path::new("/sys/block").join(dev.path.file_name().unwrap()).join("size");
+        assert_eq!("0", fs::read_to_string(dev_size_path).unwrap().trim());
+    }
 }