Decouple apkdmverity from device mapper modules

We need to extend & reuse device mapper modules to construct other
targets such as crypt. This patch moves dm related modules to a separate
lib & change apkdmverity binary to use that library.

Other changes include making delete_device_deferred removing test
annotation from delete_device_deferred & changing visibility of
functions.

Test: atest apkdmverity.test
Bug: 250880499
Change-Id: I5894c362e2b85c6260931b4c9d90f7e3d602291c
diff --git a/libs/devicemapper/src/lib.rs b/libs/devicemapper/src/lib.rs
new file mode 100644
index 0000000..1bcaf1a
--- /dev/null
+++ b/libs/devicemapper/src/lib.rs
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+// `dm` module implements part of the `device-mapper` ioctl interfaces. It currently supports
+// creation and deletion of the mapper device. It doesn't support other operations like querying
+// the status of the mapper device. And there's no plan to extend the support unless it is
+// required.
+//
+// Why in-house development? [`devicemapper`](https://crates.io/crates/devicemapper) is a public
+// Rust implementation of the device mapper APIs. However, it doesn't provide any abstraction for
+// the target-specific tables. User has to manually craft the table. Ironically, the library
+// provides a lot of APIs for the features that are not required for `apkdmverity` such as listing
+// the device mapper block devices that are currently listed in the kernel. Size is an important
+// criteria for Microdroid.
+
+//! A library to create device mapper spec & issue ioctls.
+
+#![allow(missing_docs)]
+
+use anyhow::{Context, Result};
+use data_model::DataInit;
+use std::fs::{File, OpenOptions};
+use std::io::Write;
+use std::mem::size_of;
+use std::os::unix::io::AsRawFd;
+use std::path::{Path, PathBuf};
+
+/// Expose util functions
+pub mod util;
+/// Exposes the DmVerityTarget & related builder
+pub mod verity;
+
+mod sys;
+use sys::*;
+use util::*;
+use verity::*;
+
+nix::ioctl_readwrite!(_dm_dev_create, DM_IOCTL, Cmd::DM_DEV_CREATE, DmIoctl);
+nix::ioctl_readwrite!(_dm_dev_suspend, DM_IOCTL, Cmd::DM_DEV_SUSPEND, DmIoctl);
+nix::ioctl_readwrite!(_dm_table_load, DM_IOCTL, Cmd::DM_TABLE_LOAD, DmIoctl);
+nix::ioctl_readwrite!(_dm_dev_remove, DM_IOCTL, Cmd::DM_DEV_REMOVE, DmIoctl);
+
+/// Create a new (mapper) device
+fn dm_dev_create(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    Ok(unsafe { _dm_dev_create(dm.0.as_raw_fd(), ioctl) }?)
+}
+
+fn dm_dev_suspend(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    Ok(unsafe { _dm_dev_suspend(dm.0.as_raw_fd(), ioctl) }?)
+}
+
+fn dm_table_load(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    Ok(unsafe { _dm_table_load(dm.0.as_raw_fd(), ioctl) }?)
+}
+
+fn dm_dev_remove(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    Ok(unsafe { _dm_dev_remove(dm.0.as_raw_fd(), ioctl) }?)
+}
+
+// `DmTargetSpec` is the header of the data structure for a device-mapper target. When doing the
+// ioctl, one of more `DmTargetSpec` (and its body) are appened to the `DmIoctl` struct.
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct DmTargetSpec {
+    sector_start: u64,
+    length: u64, // number of 512 sectors
+    status: i32,
+    next: u32,
+    target_type: [u8; DM_MAX_TYPE_NAME],
+}
+
+// SAFETY: C struct is safe to be initialized from raw data
+unsafe impl DataInit for DmTargetSpec {}
+
+impl DmTargetSpec {
+    fn new(target_type: &str) -> Result<Self> {
+        // safe because the size of the array is the same as the size of the struct
+        let mut spec: Self = *DataInit::from_mut_slice(&mut [0; size_of::<Self>()]).unwrap();
+        spec.target_type.as_mut().write_all(target_type.as_bytes())?;
+        Ok(spec)
+    }
+}
+
+impl DmIoctl {
+    fn new(name: &str) -> Result<DmIoctl> {
+        // safe because the size of the array is the same as the size of the struct
+        let mut data: Self = *DataInit::from_mut_slice(&mut [0; size_of::<Self>()]).unwrap();
+        data.version[0] = DM_VERSION_MAJOR;
+        data.version[1] = DM_VERSION_MINOR;
+        data.version[2] = DM_VERSION_PATCHLEVEL;
+        data.data_size = size_of::<Self>() as u32;
+        data.data_start = 0;
+        data.name.as_mut().write_all(name.as_bytes())?;
+        Ok(data)
+    }
+
+    fn set_uuid(&mut self, uuid: &str) -> Result<()> {
+        let mut dst = self.uuid.as_mut();
+        dst.fill(0);
+        dst.write_all(uuid.as_bytes())?;
+        Ok(())
+    }
+}
+
+/// `DeviceMapper` is the entry point for the device mapper framework. It essentially is a file
+/// handle to "/dev/mapper/control".
+pub struct DeviceMapper(File);
+
+#[cfg(not(target_os = "android"))]
+const MAPPER_CONTROL: &str = "/dev/mapper/control";
+#[cfg(not(target_os = "android"))]
+const MAPPER_DEV_ROOT: &str = "/dev/mapper";
+
+#[cfg(target_os = "android")]
+const MAPPER_CONTROL: &str = "/dev/device-mapper";
+#[cfg(target_os = "android")]
+const MAPPER_DEV_ROOT: &str = "/dev/block/mapper";
+
+impl DeviceMapper {
+    /// Constructs a new `DeviceMapper` entrypoint. This is essentially the same as opening
+    /// "/dev/mapper/control".
+    pub fn new() -> Result<DeviceMapper> {
+        let f = OpenOptions::new()
+            .read(true)
+            .write(true)
+            .open(MAPPER_CONTROL)
+            .context(format!("failed to open {}", MAPPER_CONTROL))?;
+        Ok(DeviceMapper(f))
+    }
+
+    /// Creates a device mapper device and configure it according to the `target` specification.
+    /// The path to the generated device is "/dev/mapper/<name>".
+    pub fn create_verity_device(&self, name: &str, target: &DmVerityTarget) -> Result<PathBuf> {
+        // Step 1: create an empty device
+        let mut data = DmIoctl::new(name)?;
+        data.set_uuid(&uuid("apkver".as_bytes())?)?;
+        dm_dev_create(self, &mut data)
+            .context(format!("failed to create an empty device with name {}", &name))?;
+
+        // Step 2: load table onto the device
+        let payload_size = size_of::<DmIoctl>() + target.as_slice().len();
+
+        let mut data = DmIoctl::new(name)?;
+        data.data_size = payload_size as u32;
+        data.data_start = size_of::<DmIoctl>() as u32;
+        data.target_count = 1;
+        data.flags |= Flag::DM_READONLY_FLAG;
+
+        let mut payload = Vec::with_capacity(payload_size);
+        payload.extend_from_slice(data.as_slice());
+        payload.extend_from_slice(target.as_slice());
+        dm_table_load(self, payload.as_mut_ptr() as *mut DmIoctl)
+            .context("failed to load table")?;
+
+        // Step 3: activate the device (note: the term 'suspend' might be misleading, but it
+        // actually activates the table. See include/uapi/linux/dm-ioctl.h
+        let mut data = DmIoctl::new(name)?;
+        dm_dev_suspend(self, &mut data).context("failed to activate")?;
+
+        // Step 4: wait unti the device is created and return the device path
+        let path = Path::new(MAPPER_DEV_ROOT).join(&name);
+        wait_for_path(&path)?;
+        Ok(path)
+    }
+
+    /// Removes a mapper device
+    pub fn delete_device_deferred(&self, name: &str) -> Result<()> {
+        let mut data = DmIoctl::new(name)?;
+        data.flags |= Flag::DM_DEFERRED_REMOVE;
+        dm_dev_remove(self, &mut data)
+            .context(format!("failed to remove device with name {}", &name))?;
+        Ok(())
+    }
+}
+
+/// Used to derive a UUID that uniquely identifies a device mapper device when creating it.
+fn uuid(node_id: &[u8]) -> Result<String> {
+    use std::time::{SystemTime, UNIX_EPOCH};
+    use uuid::v1::{Context, Timestamp};
+    use uuid::Uuid;
+
+    let context = Context::new(0);
+    let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
+    let ts = Timestamp::from_unix(&context, now.as_secs(), now.subsec_nanos());
+    let uuid = Uuid::new_v1(ts, node_id)?;
+    Ok(String::from(uuid.to_hyphenated().encode_lower(&mut Uuid::encode_buffer())))
+}