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)
-}