blob: 1bcaf1aac33139e6c64589021e6b2fb555ea1d8c [file] [log] [blame]
Jiyong Park86c9b082021-06-04 19:03:48 +09001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// `dm` module implements part of the `device-mapper` ioctl interfaces. It currently supports
18// creation and deletion of the mapper device. It doesn't support other operations like querying
19// the status of the mapper device. And there's no plan to extend the support unless it is
20// required.
21//
22// Why in-house development? [`devicemapper`](https://crates.io/crates/devicemapper) is a public
23// Rust implementation of the device mapper APIs. However, it doesn't provide any abstraction for
24// the target-specific tables. User has to manually craft the table. Ironically, the library
25// provides a lot of APIs for the features that are not required for `apkdmverity` such as listing
26// the device mapper block devices that are currently listed in the kernel. Size is an important
27// criteria for Microdroid.
28
Shikha Panwar414ea892022-10-12 13:45:52 +000029//! A library to create device mapper spec & issue ioctls.
30
31#![allow(missing_docs)]
Jiyong Park86c9b082021-06-04 19:03:48 +090032
Jiyong Park0553ff22021-07-15 12:25:36 +090033use anyhow::{Context, Result};
Jiyong Park3c327d22021-06-08 20:51:54 +090034use data_model::DataInit;
Jiyong Park86c9b082021-06-04 19:03:48 +090035use std::fs::{File, OpenOptions};
36use std::io::Write;
37use std::mem::size_of;
38use std::os::unix::io::AsRawFd;
39use std::path::{Path, PathBuf};
Jiyong Park86c9b082021-06-04 19:03:48 +090040
Shikha Panwar414ea892022-10-12 13:45:52 +000041/// Expose util functions
42pub mod util;
43/// Exposes the DmVerityTarget & related builder
44pub mod verity;
45
Jiyong Park86c9b082021-06-04 19:03:48 +090046mod sys;
Jiyong Park86c9b082021-06-04 19:03:48 +090047use sys::*;
Shikha Panwar414ea892022-10-12 13:45:52 +000048use util::*;
49use verity::*;
Jiyong Park86c9b082021-06-04 19:03:48 +090050
51nix::ioctl_readwrite!(_dm_dev_create, DM_IOCTL, Cmd::DM_DEV_CREATE, DmIoctl);
Jiyong Park86c9b082021-06-04 19:03:48 +090052nix::ioctl_readwrite!(_dm_dev_suspend, DM_IOCTL, Cmd::DM_DEV_SUSPEND, DmIoctl);
53nix::ioctl_readwrite!(_dm_table_load, DM_IOCTL, Cmd::DM_TABLE_LOAD, DmIoctl);
Jiyong Park99a35b82021-06-07 10:13:44 +090054nix::ioctl_readwrite!(_dm_dev_remove, DM_IOCTL, Cmd::DM_DEV_REMOVE, DmIoctl);
Jiyong Park86c9b082021-06-04 19:03:48 +090055
Shikha Panwar414ea892022-10-12 13:45:52 +000056/// Create a new (mapper) device
Jiyong Park86c9b082021-06-04 19:03:48 +090057fn dm_dev_create(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
58 // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
59 // state of this process in any way.
60 Ok(unsafe { _dm_dev_create(dm.0.as_raw_fd(), ioctl) }?)
61}
62
Jiyong Park86c9b082021-06-04 19:03:48 +090063fn dm_dev_suspend(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
64 // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
65 // state of this process in any way.
66 Ok(unsafe { _dm_dev_suspend(dm.0.as_raw_fd(), ioctl) }?)
67}
68
69fn dm_table_load(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
70 // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
71 // state of this process in any way.
72 Ok(unsafe { _dm_table_load(dm.0.as_raw_fd(), ioctl) }?)
73}
74
Jiyong Park99a35b82021-06-07 10:13:44 +090075fn dm_dev_remove(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
76 // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
77 // state of this process in any way.
78 Ok(unsafe { _dm_dev_remove(dm.0.as_raw_fd(), ioctl) }?)
79}
80
Jiyong Park86c9b082021-06-04 19:03:48 +090081// `DmTargetSpec` is the header of the data structure for a device-mapper target. When doing the
82// ioctl, one of more `DmTargetSpec` (and its body) are appened to the `DmIoctl` struct.
83#[repr(C)]
Jiyong Park3c327d22021-06-08 20:51:54 +090084#[derive(Copy, Clone)]
Jiyong Park86c9b082021-06-04 19:03:48 +090085struct DmTargetSpec {
86 sector_start: u64,
87 length: u64, // number of 512 sectors
88 status: i32,
89 next: u32,
90 target_type: [u8; DM_MAX_TYPE_NAME],
91}
92
Jiyong Park3c327d22021-06-08 20:51:54 +090093// SAFETY: C struct is safe to be initialized from raw data
94unsafe impl DataInit for DmTargetSpec {}
95
Jiyong Park86c9b082021-06-04 19:03:48 +090096impl DmTargetSpec {
97 fn new(target_type: &str) -> Result<Self> {
Jiyong Park3c327d22021-06-08 20:51:54 +090098 // safe because the size of the array is the same as the size of the struct
99 let mut spec: Self = *DataInit::from_mut_slice(&mut [0; size_of::<Self>()]).unwrap();
Jiyong Park86c9b082021-06-04 19:03:48 +0900100 spec.target_type.as_mut().write_all(target_type.as_bytes())?;
101 Ok(spec)
102 }
Jiyong Park86c9b082021-06-04 19:03:48 +0900103}
104
105impl DmIoctl {
106 fn new(name: &str) -> Result<DmIoctl> {
Jiyong Park3c327d22021-06-08 20:51:54 +0900107 // safe because the size of the array is the same as the size of the struct
108 let mut data: Self = *DataInit::from_mut_slice(&mut [0; size_of::<Self>()]).unwrap();
Jiyong Park86c9b082021-06-04 19:03:48 +0900109 data.version[0] = DM_VERSION_MAJOR;
110 data.version[1] = DM_VERSION_MINOR;
111 data.version[2] = DM_VERSION_PATCHLEVEL;
112 data.data_size = size_of::<Self>() as u32;
113 data.data_start = 0;
114 data.name.as_mut().write_all(name.as_bytes())?;
115 Ok(data)
116 }
117
118 fn set_uuid(&mut self, uuid: &str) -> Result<()> {
119 let mut dst = self.uuid.as_mut();
120 dst.fill(0);
121 dst.write_all(uuid.as_bytes())?;
122 Ok(())
123 }
Jiyong Park86c9b082021-06-04 19:03:48 +0900124}
125
126/// `DeviceMapper` is the entry point for the device mapper framework. It essentially is a file
127/// handle to "/dev/mapper/control".
128pub struct DeviceMapper(File);
129
Jiyong Park5f0ebea2021-06-07 12:53:35 +0900130#[cfg(not(target_os = "android"))]
131const MAPPER_CONTROL: &str = "/dev/mapper/control";
132#[cfg(not(target_os = "android"))]
133const MAPPER_DEV_ROOT: &str = "/dev/mapper";
134
135#[cfg(target_os = "android")]
136const MAPPER_CONTROL: &str = "/dev/device-mapper";
137#[cfg(target_os = "android")]
138const MAPPER_DEV_ROOT: &str = "/dev/block/mapper";
139
Jiyong Park86c9b082021-06-04 19:03:48 +0900140impl DeviceMapper {
141 /// Constructs a new `DeviceMapper` entrypoint. This is essentially the same as opening
142 /// "/dev/mapper/control".
143 pub fn new() -> Result<DeviceMapper> {
Jiyong Park0553ff22021-07-15 12:25:36 +0900144 let f = OpenOptions::new()
145 .read(true)
146 .write(true)
147 .open(MAPPER_CONTROL)
148 .context(format!("failed to open {}", MAPPER_CONTROL))?;
Jiyong Park86c9b082021-06-04 19:03:48 +0900149 Ok(DeviceMapper(f))
150 }
151
152 /// Creates a device mapper device and configure it according to the `target` specification.
153 /// The path to the generated device is "/dev/mapper/<name>".
Shikha Panwar414ea892022-10-12 13:45:52 +0000154 pub fn create_verity_device(&self, name: &str, target: &DmVerityTarget) -> Result<PathBuf> {
Jiyong Park86c9b082021-06-04 19:03:48 +0900155 // Step 1: create an empty device
Chris Wailes68c39f82021-07-27 16:03:44 -0700156 let mut data = DmIoctl::new(name)?;
Shikha Panwar414ea892022-10-12 13:45:52 +0000157 data.set_uuid(&uuid("apkver".as_bytes())?)?;
Chris Wailes68c39f82021-07-27 16:03:44 -0700158 dm_dev_create(self, &mut data)
Jiyong Park0553ff22021-07-15 12:25:36 +0900159 .context(format!("failed to create an empty device with name {}", &name))?;
Jiyong Park86c9b082021-06-04 19:03:48 +0900160
161 // Step 2: load table onto the device
Jiyong Park3c327d22021-06-08 20:51:54 +0900162 let payload_size = size_of::<DmIoctl>() + target.as_slice().len();
Jiyong Park86c9b082021-06-04 19:03:48 +0900163
Chris Wailes68c39f82021-07-27 16:03:44 -0700164 let mut data = DmIoctl::new(name)?;
Jiyong Park86c9b082021-06-04 19:03:48 +0900165 data.data_size = payload_size as u32;
166 data.data_start = size_of::<DmIoctl>() as u32;
167 data.target_count = 1;
168 data.flags |= Flag::DM_READONLY_FLAG;
169
170 let mut payload = Vec::with_capacity(payload_size);
Jiyong Park3c327d22021-06-08 20:51:54 +0900171 payload.extend_from_slice(data.as_slice());
172 payload.extend_from_slice(target.as_slice());
Chris Wailes68c39f82021-07-27 16:03:44 -0700173 dm_table_load(self, payload.as_mut_ptr() as *mut DmIoctl)
Jiyong Park0553ff22021-07-15 12:25:36 +0900174 .context("failed to load table")?;
Jiyong Park86c9b082021-06-04 19:03:48 +0900175
176 // Step 3: activate the device (note: the term 'suspend' might be misleading, but it
177 // actually activates the table. See include/uapi/linux/dm-ioctl.h
Chris Wailes68c39f82021-07-27 16:03:44 -0700178 let mut data = DmIoctl::new(name)?;
179 dm_dev_suspend(self, &mut data).context("failed to activate")?;
Jiyong Park86c9b082021-06-04 19:03:48 +0900180
181 // Step 4: wait unti the device is created and return the device path
Jiyong Park5f0ebea2021-06-07 12:53:35 +0900182 let path = Path::new(MAPPER_DEV_ROOT).join(&name);
Jiyong Park86c9b082021-06-04 19:03:48 +0900183 wait_for_path(&path)?;
184 Ok(path)
185 }
186
187 /// Removes a mapper device
188 pub fn delete_device_deferred(&self, name: &str) -> Result<()> {
Chris Wailes68c39f82021-07-27 16:03:44 -0700189 let mut data = DmIoctl::new(name)?;
Jiyong Park86c9b082021-06-04 19:03:48 +0900190 data.flags |= Flag::DM_DEFERRED_REMOVE;
Chris Wailes68c39f82021-07-27 16:03:44 -0700191 dm_dev_remove(self, &mut data)
Jiyong Park0553ff22021-07-15 12:25:36 +0900192 .context(format!("failed to remove device with name {}", &name))?;
Jiyong Park86c9b082021-06-04 19:03:48 +0900193 Ok(())
194 }
195}
196
197/// Used to derive a UUID that uniquely identifies a device mapper device when creating it.
Shikha Panwar414ea892022-10-12 13:45:52 +0000198fn uuid(node_id: &[u8]) -> Result<String> {
Jiyong Parkf02061f2021-06-07 09:44:44 +0900199 use std::time::{SystemTime, UNIX_EPOCH};
200 use uuid::v1::{Context, Timestamp};
201 use uuid::Uuid;
202
203 let context = Context::new(0);
204 let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
205 let ts = Timestamp::from_unix(&context, now.as_secs(), now.subsec_nanos());
Shikha Panwar414ea892022-10-12 13:45:52 +0000206 let uuid = Uuid::new_v1(ts, node_id)?;
Jiyong Parkf02061f2021-06-07 09:44:44 +0900207 Ok(String::from(uuid.to_hyphenated().encode_lower(&mut Uuid::encode_buffer())))
Jiyong Park86c9b082021-06-04 19:03:48 +0900208}