Add support for autoclear flag for loopdevice

Returning a struct with the File reference instead of just a device path
to make sure autoclear loop device is not automatically cleared once the
attach function returns.

Also add host supported attribute for libdm since the library should
work on the host.

Bug: 375432644
Test: atest libdm_rust.test; atest apkdmverity.test

Change-Id: I223fe2eff056fb6ca86df64e871825f9b04881d7
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());
+    }
 }