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/apkdmverity/src/dm.rs b/apkdmverity/src/dm.rs
deleted file mode 100644
index 4cb24fc..0000000
--- a/apkdmverity/src/dm.rs
+++ /dev/null
@@ -1,203 +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.
- */
-
-// `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.
-
-use crate::util::*;
-
-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};
-
-mod sys;
-mod verity;
-use sys::*;
-pub 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);
-#[cfg(test)]
-nix::ioctl_readwrite!(_dm_dev_remove, DM_IOCTL, Cmd::DM_DEV_REMOVE, DmIoctl);
-
-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) }?)
-}
-
-#[cfg(test)]
-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_device(&self, name: &str, target: &DmVerityTarget) -> Result<PathBuf> {
-        // Step 1: create an empty device
-        let mut data = DmIoctl::new(name)?;
-        data.set_uuid(&uuid()?)?;
-        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
-    #[cfg(test)]
-    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() -> 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, "apkver".as_bytes())?;
-    Ok(String::from(uuid.to_hyphenated().encode_lower(&mut Uuid::encode_buffer())))
-}
diff --git a/apkdmverity/src/dm/sys.rs b/apkdmverity/src/dm/sys.rs
deleted file mode 100644
index e709bf0..0000000
--- a/apkdmverity/src/dm/sys.rs
+++ /dev/null
@@ -1,94 +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 bitflags::bitflags;
-use data_model::DataInit;
-
-// UAPI for device mapper can be found at include/uapi/linux/dm-ioctl.h
-
-pub const DM_IOCTL: u8 = 0xfd;
-
-#[repr(u16)]
-#[allow(non_camel_case_types)]
-#[allow(dead_code)]
-pub enum Cmd {
-    DM_VERSION = 0,
-    DM_REMOVE_ALL,
-    DM_LIST_DEVICES,
-    DM_DEV_CREATE,
-    DM_DEV_REMOVE,
-    DM_DEV_RENAME,
-    DM_DEV_SUSPEND,
-    DM_DEV_STATUS,
-    DM_DEV_WAIT,
-    DM_TABLE_LOAD,
-    DM_TABLE_CLEAR,
-    DM_TABLE_DEPS,
-    DM_TABLE_STATUS,
-    DM_LIST_VERSIONS,
-    DM_TARGET_MSG,
-    DM_DEV_SET_GEOMETRY,
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct DmIoctl {
-    pub version: [u32; 3],
-    pub data_size: u32,
-    pub data_start: u32,
-    pub target_count: u32,
-    pub open_count: i32,
-    pub flags: Flag,
-    pub event_nr: u32,
-    pub padding: u32,
-    pub dev: u64,
-    pub name: [u8; DM_NAME_LEN],
-    pub uuid: [u8; DM_UUID_LEN],
-    pub data: [u8; 7],
-}
-
-// SAFETY: C struct is safe to be initialized from raw data
-unsafe impl DataInit for DmIoctl {}
-
-pub const DM_VERSION_MAJOR: u32 = 4;
-pub const DM_VERSION_MINOR: u32 = 0;
-pub const DM_VERSION_PATCHLEVEL: u32 = 0;
-
-pub const DM_NAME_LEN: usize = 128;
-pub const DM_UUID_LEN: usize = 129;
-pub const DM_MAX_TYPE_NAME: usize = 16;
-
-bitflags! {
-    pub struct Flag: u32 {
-        const DM_READONLY_FLAG = 1 << 0;
-        const DM_SUSPEND_FLAG = 1 << 1;
-        const DM_PERSISTENT_DEV_FLAG = 1 << 3;
-        const DM_STATUS_TABLE_FLAG = 1 << 4;
-        const DM_ACTIVE_PRESENT_FLAG = 1 << 5;
-        const DM_INACTIVE_PRESENT_FLAG = 1 << 6;
-        const DM_BUFFER_FULL_FLAG = 1 << 8;
-        const DM_SKIP_BDGET_FLAG = 1 << 9;
-        const DM_SKIP_LOCKFS_FLAG = 1 << 10;
-        const DM_NOFLUSH_FLAG = 1 << 11;
-        const DM_QUERY_INACTIVE_TABLE_FLAG = 1 << 12;
-        const DM_UEVENT_GENERATED_FLAG = 1 << 13;
-        const DM_UUID_FLAG = 1 << 14;
-        const DM_SECURE_DATA_FLAG = 1 << 15;
-        const DM_DATA_OUT_FLAG = 1 << 16;
-        const DM_DEFERRED_REMOVE = 1 << 17;
-        const DM_INTERNAL_SUSPEND_FLAG = 1 << 18;
-    }
-}
diff --git a/apkdmverity/src/dm/verity.rs b/apkdmverity/src/dm/verity.rs
deleted file mode 100644
index 3a49ee2..0000000
--- a/apkdmverity/src/dm/verity.rs
+++ /dev/null
@@ -1,197 +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.
- */
-
-// `dm::verity` module implements the "verity" target in the device mapper framework. Specifically,
-// it provides `DmVerityTargetBuilder` struct which is used to construct a `DmVerityTarget` struct
-// which is then given to `DeviceMapper` to create a mapper device.
-
-use anyhow::{bail, Context, Result};
-use data_model::DataInit;
-use std::io::Write;
-use std::mem::size_of;
-use std::path::Path;
-
-use super::DmTargetSpec;
-use crate::util::*;
-
-// The UAPI for the verity target is here.
-// https://www.kernel.org/doc/Documentation/device-mapper/verity.txt
-
-/// Version of the verity target spec. Only `V1` is supported.
-pub enum DmVerityVersion {
-    V1,
-}
-
-/// The hash algorithm to use. SHA256 and SHA512 are supported.
-#[allow(dead_code)]
-pub enum DmVerityHashAlgorithm {
-    SHA256,
-    SHA512,
-}
-
-/// A builder that constructs `DmVerityTarget` struct.
-pub struct DmVerityTargetBuilder<'a> {
-    version: DmVerityVersion,
-    data_device: Option<&'a Path>,
-    data_size: u64,
-    hash_device: Option<&'a Path>,
-    hash_algorithm: DmVerityHashAlgorithm,
-    root_digest: Option<&'a [u8]>,
-    salt: Option<&'a [u8]>,
-}
-
-pub struct DmVerityTarget(Box<[u8]>);
-
-impl DmVerityTarget {
-    pub fn as_slice(&self) -> &[u8] {
-        self.0.as_ref()
-    }
-}
-
-impl<'a> Default for DmVerityTargetBuilder<'a> {
-    fn default() -> Self {
-        DmVerityTargetBuilder {
-            version: DmVerityVersion::V1,
-            data_device: None,
-            data_size: 0,
-            hash_device: None,
-            hash_algorithm: DmVerityHashAlgorithm::SHA256,
-            root_digest: None,
-            salt: None,
-        }
-    }
-}
-
-impl<'a> DmVerityTargetBuilder<'a> {
-    /// Sets the device that will be used as the data device (i.e. providing actual data).
-    pub fn data_device(&mut self, p: &'a Path, size: u64) -> &mut Self {
-        self.data_device = Some(p);
-        self.data_size = size;
-        self
-    }
-
-    /// Sets the device that provides the merkle tree.
-    pub fn hash_device(&mut self, p: &'a Path) -> &mut Self {
-        self.hash_device = Some(p);
-        self
-    }
-
-    /// Sets the hash algorithm that the merkel tree is using.
-    pub fn hash_algorithm(&mut self, algo: DmVerityHashAlgorithm) -> &mut Self {
-        self.hash_algorithm = algo;
-        self
-    }
-
-    /// Sets the root digest of the merkle tree. The format is hexadecimal string.
-    pub fn root_digest(&mut self, digest: &'a [u8]) -> &mut Self {
-        self.root_digest = Some(digest);
-        self
-    }
-
-    /// Sets the salt used when creating the merkle tree. Note that this is empty for merkle trees
-    /// created following the APK signature scheme V4.
-    pub fn salt(&mut self, salt: &'a [u8]) -> &mut Self {
-        self.salt = Some(salt);
-        self
-    }
-
-    /// Constructs a `DmVerityTarget`.
-    pub fn build(&self) -> Result<DmVerityTarget> {
-        // The `DmVerityTarget` struct actually is a flattened data consisting of a header and
-        // body. The format of the header is `dm_target_spec` as defined in
-        // include/uapi/linux/dm-ioctl.h. The format of the body, in case of `verity` target is
-        // https://www.kernel.org/doc/Documentation/device-mapper/verity.txt
-        //
-        // Step 1: check the validity of the inputs and extra additional data (e.g. block size)
-        // from them.
-        let version = match self.version {
-            DmVerityVersion::V1 => 1,
-        };
-
-        let data_device_path = self
-            .data_device
-            .context("data device is not set")?
-            .to_str()
-            .context("data device path is not encoded in utf8")?;
-        let stat = fstat(self.data_device.unwrap())?; // safe; checked just above
-        let data_block_size = stat.st_blksize as u64;
-        let data_size = self.data_size;
-        let num_data_blocks = data_size / data_block_size;
-
-        let hash_device_path = self
-            .hash_device
-            .context("hash device is not set")?
-            .to_str()
-            .context("hash device path is not encoded in utf8")?;
-        let stat = fstat(self.data_device.unwrap())?; // safe; checked just above
-        let hash_block_size = stat.st_blksize;
-
-        let hash_algorithm = match self.hash_algorithm {
-            DmVerityHashAlgorithm::SHA256 => "sha256",
-            DmVerityHashAlgorithm::SHA512 => "sha512",
-        };
-
-        let root_digest = if let Some(root_digest) = self.root_digest {
-            hexstring_from(root_digest)
-        } else {
-            bail!("root digest is not set")
-        };
-
-        let salt = if self.salt.is_none() || self.salt.unwrap().is_empty() {
-            "-".to_string() // Note. It's not an empty string!
-        } else {
-            hexstring_from(self.salt.unwrap())
-        };
-
-        // Step2: serialize the information according to the spec, which is ...
-        // DmTargetSpec{...}
-        // <version> <dev> <hash_dev>
-        // <data_block_size> <hash_block_size>
-        // <num_data_blocks> <hash_start_block>
-        // <algorithm> <digest> <salt>
-        // [<#opt_params> <opt_params>]
-        // null terminator
-
-        // TODO(jiyong): support the optional parameters... if needed.
-        let mut body = String::new();
-        use std::fmt::Write;
-        write!(&mut body, "{} ", version)?;
-        write!(&mut body, "{} ", data_device_path)?;
-        write!(&mut body, "{} ", hash_device_path)?;
-        write!(&mut body, "{} ", data_block_size)?;
-        write!(&mut body, "{} ", hash_block_size)?;
-        write!(&mut body, "{} ", num_data_blocks)?;
-        write!(&mut body, "{} ", 0)?; // hash_start_block
-        write!(&mut body, "{} ", hash_algorithm)?;
-        write!(&mut body, "{} ", root_digest)?;
-        write!(&mut body, "{}", salt)?;
-        write!(&mut body, "\0")?; // null terminator
-
-        let size = size_of::<DmTargetSpec>() + body.len();
-        let aligned_size = (size + 7) & !7; // align to 8 byte boundaries
-        let padding = aligned_size - size;
-        let mut header = DmTargetSpec::new("verity")?;
-        header.sector_start = 0;
-        header.length = data_size / 512; // number of 512-byte sectors
-        header.next = aligned_size as u32;
-
-        let mut buf = Vec::with_capacity(aligned_size);
-        buf.write_all(header.as_slice())?;
-        buf.write_all(body.as_bytes())?;
-        buf.write_all(vec![0; padding].as_slice())?;
-        Ok(DmVerityTarget(buf.into_boxed_slice()))
-    }
-}
diff --git a/apkdmverity/src/loopdevice.rs b/apkdmverity/src/loopdevice.rs
index 35ae154..fa49cea 100644
--- a/apkdmverity/src/loopdevice.rs
+++ b/apkdmverity/src/loopdevice.rs
@@ -25,6 +25,7 @@
 
 use anyhow::{Context, Result};
 use data_model::DataInit;
+use dm::util::*;
 use libc::O_DIRECT;
 use std::fs::{File, OpenOptions};
 use std::mem::size_of;
@@ -35,7 +36,6 @@
 use std::time::{Duration, Instant};
 
 use crate::loopdevice::sys::*;
-use crate::util::*;
 
 // These are old-style ioctls, thus *_bad.
 nix::ioctl_none_bad!(_loop_ctl_get_free, LOOP_CTL_GET_FREE);
@@ -71,7 +71,7 @@
 ) -> Result<PathBuf> {
     // 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
-    // subsequet LOOP_CONFIGURE ioctl returns with EBUSY. Try until it succeeds.
+    // subsequent LOOP_CONFIGURE ioctl returns with EBUSY. Try until it succeeds.
     //
     // Note that the timing parameters below are chosen rather arbitrarily. In practice (i.e.
     // inside Microdroid) we can't experience the race condition because `apkverity` is the only
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index bce25e8..1a4a929 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -21,13 +21,13 @@
 //! system managed by the host Android which is assumed to be compromisable, it is important to
 //! keep the integrity of the file "inside" Microdroid.
 
-mod dm;
 mod loopdevice;
-mod util;
 
 use anyhow::{bail, Context, Result};
 use apkverify::{HashAlgorithm, V4Signature};
 use clap::{App, Arg};
+use dm::util;
+use dm::verity::{DmVerityHashAlgorithm, DmVerityTargetBuilder};
 use itertools::Itertools;
 use std::fmt::Debug;
 use std::fs;
@@ -114,7 +114,7 @@
 
     // 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.
-    let target = dm::DmVerityTargetBuilder::default()
+    let target = DmVerityTargetBuilder::default()
         .data_device(&data_device, apk_size)
         .hash_device(&hash_device)
         .root_digest(if let Some(roothash) = roothash {
@@ -123,7 +123,7 @@
             &sig.hashing_info.raw_root_hash
         })
         .hash_algorithm(match sig.hashing_info.hash_algorithm {
-            HashAlgorithm::SHA256 => dm::DmVerityHashAlgorithm::SHA256,
+            HashAlgorithm::SHA256 => DmVerityHashAlgorithm::SHA256,
         })
         .salt(&sig.hashing_info.salt)
         .build()
@@ -132,7 +132,7 @@
     // Actually create a dm-verity block device using the spec.
     let dm = dm::DeviceMapper::new()?;
     let mapper_device =
-        dm.create_device(name, &target).context("Failed to create dm-verity device")?;
+        dm.create_verity_device(name, &target).context("Failed to create dm-verity device")?;
 
     Ok(VerityResult { data_device, hash_device, mapper_device })
 }
diff --git a/apkdmverity/src/util.rs b/apkdmverity/src/util.rs
deleted file mode 100644
index 913f827..0000000
--- a/apkdmverity/src/util.rs
+++ /dev/null
@@ -1,80 +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 anyhow::{anyhow, bail, Result};
-use nix::sys::stat::FileStat;
-use std::fs::File;
-use std::os::unix::fs::FileTypeExt;
-use std::os::unix::io::AsRawFd;
-use std::path::Path;
-use std::thread;
-use std::time::{Duration, Instant};
-
-/// Returns when the file exists on the given `path` or timeout (1s) occurs.
-pub fn wait_for_path<P: AsRef<Path>>(path: P) -> Result<()> {
-    const TIMEOUT: Duration = Duration::from_secs(1);
-    const INTERVAL: Duration = Duration::from_millis(10);
-    let begin = Instant::now();
-    while !path.as_ref().exists() {
-        if begin.elapsed() > TIMEOUT {
-            bail!("{:?} not found. TIMEOUT.", path.as_ref());
-        }
-        thread::sleep(INTERVAL);
-    }
-    Ok(())
-}
-
-/// Returns hexadecimal reprentation of a given byte array.
-pub fn hexstring_from(s: &[u8]) -> String {
-    s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
-}
-
-/// Parses a hexadecimal string into a byte array
-pub fn parse_hexstring(s: &str) -> Result<Vec<u8>> {
-    let len = s.len();
-    if len % 2 != 0 {
-        bail!("length {} is not even", len)
-    } else {
-        (0..len)
-            .step_by(2)
-            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| anyhow!(e)))
-            .collect()
-    }
-}
-
-/// fstat that accepts a path rather than FD
-pub fn fstat(p: &Path) -> Result<FileStat> {
-    let f = File::open(p)?;
-    Ok(nix::sys::stat::fstat(f.as_raw_fd())?)
-}
-
-// From include/uapi/linux/fs.h
-const BLK: u8 = 0x12;
-const BLKGETSIZE64: u8 = 114;
-nix::ioctl_read!(_blkgetsize64, BLK, BLKGETSIZE64, libc::size_t);
-
-/// Gets the size of a block device
-pub fn blkgetsize64(p: &Path) -> Result<u64> {
-    let f = File::open(p)?;
-    if !f.metadata()?.file_type().is_block_device() {
-        bail!("{:?} is not a block device", p);
-    }
-    let mut size: usize = 0;
-    // SAFETY: kernel copies the return value out to `size`. The file is kept open until the end of
-    // this function.
-    unsafe { _blkgetsize64(f.as_raw_fd(), &mut size) }?;
-    Ok(size as u64)
-}