apkdmverity: use data_model crate for better handling of C structs
The data_model:DataInit trait provides methods for converting between a C struct and an array of the same size, allowing us to drop the hand crafted as_u8_slice methods. Bug: N/A Test: cargo test Test: atest apkdmverity.test Change-Id: Iaff910f0a638e91a428777b94dc6fb0b5fe53831
This commit is contained in:
parent
e8c6368776
commit
3c327d2996
|
@ -12,6 +12,7 @@ rust_defaults {
|
||||||
"libanyhow",
|
"libanyhow",
|
||||||
"libbitflags",
|
"libbitflags",
|
||||||
"libclap",
|
"libclap",
|
||||||
|
"libdata_model",
|
||||||
"liblibc",
|
"liblibc",
|
||||||
"libnix",
|
"libnix",
|
||||||
"libnum_traits",
|
"libnum_traits",
|
||||||
|
|
|
@ -14,6 +14,7 @@ nix = "0.21"
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
uuid = { version = "0.8", features = ["v1"] }
|
uuid = { version = "0.8", features = ["v1"] }
|
||||||
|
data_model = "0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.2"
|
tempfile = "3.2"
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use data_model::DataInit;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
@ -74,6 +75,7 @@ fn dm_dev_remove(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
|
||||||
// `DmTargetSpec` is the header of the data structure for a device-mapper target. When doing the
|
// `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.
|
// ioctl, one of more `DmTargetSpec` (and its body) are appened to the `DmIoctl` struct.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
struct DmTargetSpec {
|
struct DmTargetSpec {
|
||||||
sector_start: u64,
|
sector_start: u64,
|
||||||
length: u64, // number of 512 sectors
|
length: u64, // number of 512 sectors
|
||||||
|
@ -82,24 +84,22 @@ struct DmTargetSpec {
|
||||||
target_type: [u8; DM_MAX_TYPE_NAME],
|
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 {
|
impl DmTargetSpec {
|
||||||
fn new(target_type: &str) -> Result<Self> {
|
fn new(target_type: &str) -> Result<Self> {
|
||||||
// SAFETY: zero initialized C struct is safe
|
// safe because the size of the array is the same as the size of the struct
|
||||||
let mut spec = unsafe { std::mem::MaybeUninit::<Self>::zeroed().assume_init() };
|
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())?;
|
spec.target_type.as_mut().write_all(target_type.as_bytes())?;
|
||||||
Ok(spec)
|
Ok(spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_u8_slice(&self) -> &[u8; size_of::<Self>()] {
|
|
||||||
// SAFETY: lifetime of the output reference isn't changed.
|
|
||||||
unsafe { &*(&self as *const &Self as *const [u8; size_of::<Self>()]) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DmIoctl {
|
impl DmIoctl {
|
||||||
fn new(name: &str) -> Result<DmIoctl> {
|
fn new(name: &str) -> Result<DmIoctl> {
|
||||||
// SAFETY: zero initialized C struct is safe
|
// safe because the size of the array is the same as the size of the struct
|
||||||
let mut data = unsafe { std::mem::MaybeUninit::<Self>::zeroed().assume_init() };
|
let mut data: Self = *DataInit::from_mut_slice(&mut [0; size_of::<Self>()]).unwrap();
|
||||||
data.version[0] = DM_VERSION_MAJOR;
|
data.version[0] = DM_VERSION_MAJOR;
|
||||||
data.version[1] = DM_VERSION_MINOR;
|
data.version[1] = DM_VERSION_MINOR;
|
||||||
data.version[2] = DM_VERSION_PATCHLEVEL;
|
data.version[2] = DM_VERSION_PATCHLEVEL;
|
||||||
|
@ -115,11 +115,6 @@ impl DmIoctl {
|
||||||
dst.write_all(uuid.as_bytes())?;
|
dst.write_all(uuid.as_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_u8_slice(&self) -> &[u8; size_of::<Self>()] {
|
|
||||||
// SAFETY: lifetime of the output reference isn't changed.
|
|
||||||
unsafe { &*(&self as *const &Self as *const [u8; size_of::<Self>()]) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `DeviceMapper` is the entry point for the device mapper framework. It essentially is a file
|
/// `DeviceMapper` is the entry point for the device mapper framework. It essentially is a file
|
||||||
|
@ -153,7 +148,7 @@ impl DeviceMapper {
|
||||||
dm_dev_create(&self, &mut data)?;
|
dm_dev_create(&self, &mut data)?;
|
||||||
|
|
||||||
// Step 2: load table onto the device
|
// Step 2: load table onto the device
|
||||||
let payload_size = size_of::<DmIoctl>() + target.as_u8_slice().len();
|
let payload_size = size_of::<DmIoctl>() + target.as_slice().len();
|
||||||
|
|
||||||
let mut data = DmIoctl::new(&name)?;
|
let mut data = DmIoctl::new(&name)?;
|
||||||
data.data_size = payload_size as u32;
|
data.data_size = payload_size as u32;
|
||||||
|
@ -162,8 +157,8 @@ impl DeviceMapper {
|
||||||
data.flags |= Flag::DM_READONLY_FLAG;
|
data.flags |= Flag::DM_READONLY_FLAG;
|
||||||
|
|
||||||
let mut payload = Vec::with_capacity(payload_size);
|
let mut payload = Vec::with_capacity(payload_size);
|
||||||
payload.extend_from_slice(data.as_u8_slice());
|
payload.extend_from_slice(data.as_slice());
|
||||||
payload.extend_from_slice(target.as_u8_slice());
|
payload.extend_from_slice(target.as_slice());
|
||||||
dm_table_load(&self, payload.as_mut_ptr() as *mut DmIoctl)?;
|
dm_table_load(&self, payload.as_mut_ptr() as *mut DmIoctl)?;
|
||||||
|
|
||||||
// Step 3: activate the device (note: the term 'suspend' might be misleading, but it
|
// Step 3: activate the device (note: the term 'suspend' might be misleading, but it
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use data_model::DataInit;
|
||||||
|
|
||||||
// UAPI for device mapper can be found at include/uapi/linux/dm-ioctl.h
|
// UAPI for device mapper can be found at include/uapi/linux/dm-ioctl.h
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ pub enum Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct DmIoctl {
|
pub struct DmIoctl {
|
||||||
pub version: [u32; 3],
|
pub version: [u32; 3],
|
||||||
pub data_size: u32,
|
pub data_size: u32,
|
||||||
|
@ -58,6 +60,9 @@ pub struct DmIoctl {
|
||||||
pub data: [u8; 7],
|
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_MAJOR: u32 = 4;
|
||||||
pub const DM_VERSION_MINOR: u32 = 0;
|
pub const DM_VERSION_MINOR: u32 = 0;
|
||||||
pub const DM_VERSION_PATCHLEVEL: u32 = 0;
|
pub const DM_VERSION_PATCHLEVEL: u32 = 0;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
// which is then given to `DeviceMapper` to create a mapper device.
|
// which is then given to `DeviceMapper` to create a mapper device.
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
use data_model::DataInit;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -55,7 +56,7 @@ pub struct DmVerityTargetBuilder<'a> {
|
||||||
pub struct DmVerityTarget(Box<[u8]>);
|
pub struct DmVerityTarget(Box<[u8]>);
|
||||||
|
|
||||||
impl DmVerityTarget {
|
impl DmVerityTarget {
|
||||||
pub fn as_u8_slice(&self) -> &[u8] {
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
self.0.as_ref()
|
self.0.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +189,7 @@ impl<'a> DmVerityTargetBuilder<'a> {
|
||||||
header.next = aligned_size as u32;
|
header.next = aligned_size as u32;
|
||||||
|
|
||||||
let mut buf = Vec::with_capacity(aligned_size);
|
let mut buf = Vec::with_capacity(aligned_size);
|
||||||
buf.write_all(header.as_u8_slice())?;
|
buf.write_all(header.as_slice())?;
|
||||||
buf.write_all(body.as_bytes())?;
|
buf.write_all(body.as_bytes())?;
|
||||||
buf.write_all(vec![0; padding].as_slice())?;
|
buf.write_all(vec![0; padding].as_slice())?;
|
||||||
Ok(DmVerityTarget(buf.into_boxed_slice()))
|
Ok(DmVerityTarget(buf.into_boxed_slice()))
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use data_model::DataInit;
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::mem::size_of;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -106,8 +108,9 @@ fn try_attach<P: AsRef<Path>>(path: P, offset: u64, size_limit: u64) -> Result<P
|
||||||
.read(true)
|
.read(true)
|
||||||
.open(&path)
|
.open(&path)
|
||||||
.context(format!("failed to open {:?}", path.as_ref()))?;
|
.context(format!("failed to open {:?}", path.as_ref()))?;
|
||||||
// SAFETY: zero initialized C structs is safe
|
// safe because the size of the array is the same as the size of the struct
|
||||||
let mut config = unsafe { std::mem::MaybeUninit::<loop_config>::zeroed().assume_init() };
|
let mut config: loop_config =
|
||||||
|
*DataInit::from_mut_slice(&mut [0; size_of::<loop_config>()]).unwrap();
|
||||||
config.fd = backing_file.as_raw_fd() as u32;
|
config.fd = backing_file.as_raw_fd() as u32;
|
||||||
config.block_size = 4096;
|
config.block_size = 4096;
|
||||||
config.info.lo_offset = offset;
|
config.info.lo_offset = offset;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use data_model::DataInit;
|
||||||
|
|
||||||
// This UAPI is copied and converted from include/uapi/linux/loop.h Note that this module doesn't
|
// This UAPI is copied and converted from include/uapi/linux/loop.h Note that this module doesn't
|
||||||
// implement all the features introduced in loop(4). Only the features that are required to support
|
// implement all the features introduced in loop(4). Only the features that are required to support
|
||||||
|
@ -28,6 +29,7 @@ pub const LOOP_CONFIGURE: libc::c_ulong = 0x4C0A;
|
||||||
pub const LOOP_CLR_FD: libc::c_ulong = 0x4C01;
|
pub const LOOP_CLR_FD: libc::c_ulong = 0x4C01;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct loop_config {
|
pub struct loop_config {
|
||||||
pub fd: u32,
|
pub fd: u32,
|
||||||
pub block_size: u32,
|
pub block_size: u32,
|
||||||
|
@ -35,7 +37,11 @@ pub struct loop_config {
|
||||||
pub reserved: [u64; 8],
|
pub reserved: [u64; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: C struct is safe to be initialized from raw data
|
||||||
|
unsafe impl DataInit for loop_config {}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct loop_info64 {
|
pub struct loop_info64 {
|
||||||
pub lo_device: u64,
|
pub lo_device: u64,
|
||||||
pub lo_inode: u64,
|
pub lo_inode: u64,
|
||||||
|
|
Loading…
Reference in New Issue