1#![recursion_limit = "512"]
6
7use bitflags::bitflags;
8use dm_verity::{
9 DmVerityError, DmVerityTargetOptionalParams, DmVerityTargetParams, HashAlgorithm,
10 MerkleVerifier,
11};
12use linux_uapi::DM_UUID_LEN;
13use mundane::hash::{Digest, Hasher, Sha256, Sha512};
14use starnix_core::device::DeviceMode;
15use starnix_core::device::kobject::{Device, DeviceMetadata};
16use starnix_core::fs::sysfs::{BlockDeviceInfo, build_block_device_directory, get_sysfs};
17use starnix_core::mm::memory::MemoryObject;
18use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt, ProtectionFlags};
19use starnix_core::security;
20use starnix_core::task::{CurrentTask, Kernel};
21use starnix_core::vfs::buffers::{InputBuffer, VecOutputBuffer};
22use starnix_core::vfs::pseudo::stub_empty_file::StubEmptyFile;
23use starnix_core::vfs::{
24 FileHandle, FileObject, FileObjectState, FileOps, FileToken, FsString, NamespaceNode,
25 OutputBuffer, fileops_impl_dataless, fileops_impl_noop_sync, fileops_impl_seekable,
26 fileops_impl_seekless,
27};
28use starnix_ext::map_ext::EntryExt;
29use starnix_logging::{bug_ref, log_trace, track_stub};
30use starnix_sync::{DeviceMapperRegistryDevicesLock, DmDeviceStateLock, LockDepMutex};
31use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
32use starnix_uapi::auth::CAP_SYS_ADMIN;
33use starnix_uapi::device_id::{DEVICE_MAPPER_MAJOR, DeviceId, LOOP_MAJOR};
34use starnix_uapi::errors::Errno;
35use starnix_uapi::file_mode::mode;
36use starnix_uapi::open_flags::OpenFlags;
37use starnix_uapi::user_address::{UserCString, UserRef};
38use starnix_uapi::{
39 DM_ACTIVE_PRESENT_FLAG, DM_BUFFER_FULL_FLAG, DM_DEV_ARM_POLL, DM_DEV_CREATE, DM_DEV_REMOVE,
40 DM_DEV_RENAME, DM_DEV_SET_GEOMETRY, DM_DEV_STATUS, DM_DEV_SUSPEND, DM_DEV_WAIT,
41 DM_GET_TARGET_VERSION, DM_IMA_MEASUREMENT_FLAG, DM_INACTIVE_PRESENT_FLAG, DM_LIST_DEVICES,
42 DM_LIST_VERSIONS, DM_MAX_TYPE_NAME, DM_NAME_LEN, DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID,
43 DM_NAME_LIST_FLAG_HAS_UUID, DM_READONLY_FLAG, DM_REMOVE_ALL, DM_STATUS_TABLE_FLAG,
44 DM_SUSPEND_FLAG, DM_TABLE_CLEAR, DM_TABLE_DEPS, DM_TABLE_LOAD, DM_TABLE_STATUS, DM_TARGET_MSG,
45 DM_UEVENT_GENERATED_FLAG, DM_VERSION, DM_VERSION_MAJOR, DM_VERSION_MINOR,
46 DM_VERSION_PATCHLEVEL, errno, error, uapi,
47};
48use std::collections::btree_map::{BTreeMap, Entry};
49use std::ops::Sub;
50use std::sync::Arc;
51
52const SECTOR_SIZE: u64 = 512;
53const DATA_SIZE: u32 = 305;
56const DM_VERITY_VERSION_MAJOR: u32 = 1;
58const DM_VERITY_VERSION_MINOR: u32 = 9;
59const DM_VERITY_VERSION_PATCHLEVEL: u32 = 0;
60
61bitflags! {
62 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
63 struct DeviceMapperFlags: u32 {
64 const ACTIVE_PRESENT = DM_ACTIVE_PRESENT_FLAG;
65 const INACTIVE_PRESENT = DM_INACTIVE_PRESENT_FLAG;
66 const READONLY = DM_READONLY_FLAG;
67 const SUSPEND = DM_SUSPEND_FLAG;
68 const UEVENT_GENERATED = DM_UEVENT_GENERATED_FLAG;
69 const BUFFER_FULL = DM_BUFFER_FULL_FLAG;
70 const STATUS_TABLE = DM_STATUS_TABLE_FLAG;
71 const IMA_MEASUREMENT = DM_IMA_MEASUREMENT_FLAG;
72 const DM_NAME_LIST_HAS_UUID = DM_NAME_LIST_FLAG_HAS_UUID;
73 const DM_NAME_LIST_NO_UUID = DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID;
74 }
75}
76
77pub fn device_mapper_init(kernel: &Kernel) -> Result<(), Errno> {
78 kernel.device_registry.register_major(
79 "device-mapper".into(),
80 DeviceMode::Block,
81 DEVICE_MAPPER_MAJOR,
82 get_or_create_dm_device,
83 )?;
84
85 let dir_mode = 0o755;
86 kernel.device_registry.objects.root.edit(&get_sysfs(kernel), |root| {
87 root.subdir("module", dir_mode, |dir| {
88 dir.subdir("dm_bufio", dir_mode, |dir| {
89 dir.subdir("parameters", dir_mode, |dir| {
90 dir.entry(
91 "max_age_seconds",
92 StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
93 mode!(IFREG, 0o644),
94 );
95 });
96 });
97 dir.subdir("dm_verity", dir_mode, |dir| {
98 dir.subdir("parameters", dir_mode, |dir| {
99 dir.entry(
100 "prefetch_cluster",
101 StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/322893670")),
102 mode!(IFREG, 0o644),
103 );
104 });
105 });
106 });
107 });
108
109 Ok(())
110}
111
112#[derive(Debug, Default)]
113pub struct DeviceMapperRegistry {
114 devices: LockDepMutex<BTreeMap<u32, Arc<DmDevice>>, DeviceMapperRegistryDevicesLock>,
115}
116
117impl DeviceMapperRegistry {
118 fn get(&self, io: &uapi::dm_ioctl) -> Result<Arc<DmDevice>, Errno> {
120 if io.name != [0; DM_NAME_LEN as usize] {
121 if io.uuid != [0; DM_UUID_LEN as usize] || io.dev > 0 {
122 return error!(EINVAL);
123 } else {
124 return self.get_by_name(io.name);
125 }
126 }
127 if io.uuid != [0; DM_UUID_LEN as usize] {
128 if io.dev > 0 {
129 return error!(EINVAL);
130 } else {
131 return self.get_by_uuid(io.uuid);
132 }
133 }
134 let dev_minor = ((io.dev >> 12 & 0xffffff00) | (io.dev & 0xff)) as u32;
135 self.devices.lock().get(&dev_minor).ok_or_else(|| errno!(ENODEV)).cloned()
136 }
137
138 fn get_by_name(
139 &self,
140 name: [std::ffi::c_char; DM_NAME_LEN as usize],
141 ) -> Result<Arc<DmDevice>, Errno> {
142 let devices = self.devices.lock();
143 let entry = devices.iter().find(|(_, device)| {
144 let state = device.state.lock();
145 state.name == name
146 });
147 if let Some((_, device)) = entry { Ok(device.clone()) } else { error!(ENODEV) }
148 }
149
150 fn get_by_uuid(
151 &self,
152 uuid: [std::ffi::c_char; DM_UUID_LEN as usize],
153 ) -> Result<Arc<DmDevice>, Errno> {
154 let devices = self.devices.lock();
155 let entry = devices.iter().find(|(_, device)| {
156 let state = device.state.lock();
157 state.uuid == uuid
158 });
159 if let Some((_, device)) = entry { Ok(device.clone()) } else { error!(ENODEV) }
160 }
161
162 fn get_or_create_by_minor(
163 &self,
164 current_task: &CurrentTask,
165 minor: u32,
166 ) -> Result<Arc<DmDevice>, Errno> {
167 self.devices
168 .lock()
169 .entry(minor)
170 .or_insert_with_fallible(|| DmDevice::new(current_task, minor))
171 .cloned()
172 }
173
174 fn find(&self, current_task: &CurrentTask) -> Result<Arc<DmDevice>, Errno> {
177 let mut devices = self.devices.lock();
178 for minor in 0..u32::MAX {
179 match devices.entry(minor) {
180 Entry::Vacant(e) => {
181 let device = DmDevice::new(current_task, minor)?;
182 e.insert(device.clone());
183 return Ok(device);
184 }
185 Entry::Occupied(_) => {}
186 }
187 }
188 error!(ENODEV)
189 }
190
191 fn remove(
193 &self,
194 current_task: &CurrentTask,
195 devices: &mut BTreeMap<u32, Arc<DmDevice>>,
196 minor: u32,
197 k_device: &Option<Device>,
198 ) -> Result<(), Errno> {
199 devices.remove(&minor).ok_or_else(|| errno!(ENODEV))?;
200 let kernel = current_task.kernel();
201 let registry = &kernel.device_registry;
202 if let Some(dev) = &k_device {
203 registry.remove_device(current_task, dev.clone());
204 } else {
205 return error!(EINVAL);
206 }
207 Ok(())
208 }
209}
210#[derive(Debug, Default)]
211pub struct DmDevice {
212 number: DeviceId,
213 state: LockDepMutex<DmDeviceState, DmDeviceStateLock>,
214}
215
216impl DmDevice {
217 fn new(current_task: &CurrentTask, minor: u32) -> Result<Arc<Self>, Errno> {
218 let kernel = current_task.kernel();
219 let registry = &kernel.device_registry;
220 let dm_device_name = FsString::from(format!("dm-{minor}"));
221 let block_class = registry.objects.block_class();
222 let device = Arc::new(Self {
223 number: DeviceId::new(DEVICE_MAPPER_MAJOR, minor),
224 ..Default::default()
225 });
226 let device_weak = Arc::<DmDevice>::downgrade(&device);
227 let k_device = registry.add_major_device(
228 current_task.kernel(),
229 dm_device_name.as_ref(),
230 DeviceMetadata::new(
231 dm_device_name.clone(),
232 DeviceId::new(DEVICE_MAPPER_MAJOR, minor),
233 DeviceMode::Block,
234 )
235 .with_devtype("disk"),
236 None,
237 block_class,
238 |device, dir| build_block_device_directory(device, device_weak, dir),
239 );
240 {
241 let mut state = device.state.lock();
242 state.set_k_device(k_device);
243 }
244 Ok(device)
245 }
246
247 fn create_file_ops(self: &Arc<Self>) -> Box<dyn FileOps> {
248 let mut state = self.state.lock();
249 state.open_count += 1;
250 Box::new(DmDeviceFile { device: self.clone() })
251 }
252}
253
254impl BlockDeviceInfo for DmDevice {
255 fn size(&self) -> Result<usize, Errno> {
256 let state = self.state.lock();
257 if !state.suspended {
258 if let Some(active_table) = &state.active_table {
259 Ok(active_table.size())
260 } else {
261 Ok(0)
262 }
263 } else {
264 Ok(0)
265 }
266 }
267}
268struct DmDeviceFile {
269 device: Arc<DmDevice>,
270}
271
272impl FileOps for DmDeviceFile {
273 fileops_impl_seekable!();
274
275 fileops_impl_noop_sync!();
277
278 fn write(
279 &self,
280 _file: &FileObject,
281 _current_task: &CurrentTask,
282 _offset: usize,
283 _data: &mut dyn InputBuffer,
284 ) -> Result<usize, Errno> {
285 error!(ENOTSUP)
286 }
287
288 fn read(
289 &self,
290 _file: &FileObject,
291 current_task: &CurrentTask,
292 offset: usize,
293 data: &mut dyn OutputBuffer,
294 ) -> Result<usize, Errno> {
295 let device = &self.device;
296 let mut state = device.state.lock();
297 if state.suspended {
298 track_stub!(TODO("https://fxbug.dev/338241090"), "Defer io for suspended devices.");
299 return Ok(0);
300 }
301 if let Some(active_table) = &mut state.active_table {
302 let mut bytes_read = 0;
303 let to_read = std::cmp::min(
304 data.available(),
305 active_table.size().checked_sub(offset).ok_or_else(|| errno!(EINVAL))?,
306 );
307 let mut buffer = VecOutputBuffer::new(to_read);
308 if active_table.targets.is_empty() {
309 return error!(EINVAL);
310 }
311 if active_table.targets.len() > 1 {
312 track_stub!(
313 TODO("https://fxbug.dev/339701082"),
314 "Support reads for multiple targets."
315 );
316 return error!(ENOTSUP);
317 }
318 let target = &mut active_table.targets[0];
319 let start = (target.sector_start * SECTOR_SIZE) as usize;
320 debug_assert!(start == 0);
321 let size = (target.length * SECTOR_SIZE) as usize;
322 if offset >= start && offset < start + size {
323 match &mut target.target_type {
324 TargetType::Verity(verity_target) => {
325 if to_read % verity_target.params.hash_block_size as usize != 0 {
326 return error!(EINVAL);
327 }
328 let read = verity_target.block_device.ops().read(
329 &verity_target.block_device,
330 current_task,
331 offset - start,
332 &mut buffer,
333 )?;
334 bytes_read += read;
335 if let Err(_) = verity_target.verifier.verify(offset - start, buffer.data())
336 {
337 verity_target.corrupted = true;
338 return error!(EINVAL);
339 }
340 }
341 TargetType::Error => {
342 return error!(EIO);
343 }
344 }
345 }
346 let read = data.write_all(buffer.data())?;
347 debug_assert!(read == bytes_read);
348 Ok(bytes_read)
349 } else {
350 Ok(0)
351 }
352 }
353
354 fn get_memory(
355 &self,
356 _file: &FileObject,
357 current_task: &CurrentTask,
358 length: Option<usize>,
359 prot: ProtectionFlags,
360 ) -> Result<Arc<MemoryObject>, Errno> {
361 let device = &self.device;
362 let state = device.state.lock();
363 if state.suspended {
364 track_stub!(TODO("https://fxbug.dev/338241090"), "Defer io for suspended devices.");
365 return error!(EINVAL);
366 }
367 if let Some(active_table) = &state.active_table {
368 if active_table.targets.is_empty() {
369 return error!(EINVAL);
370 }
371 if active_table.targets.len() > 1 {
372 track_stub!(
373 TODO("https://fxbug.dev/339701082"),
374 "Support pager-backed vmos for multiple targets."
375 );
376 return error!(ENOTSUP);
377 }
378 match &active_table.targets[0].target_type {
379 TargetType::Verity(verity_target) => verity_target.block_device.ops().get_memory(
380 &verity_target.block_device,
381 current_task,
382 length,
383 prot,
384 ),
385 TargetType::Error => {
386 error!(EIO)
387 }
388 }
389 } else {
390 error!(EINVAL)
391 }
392 }
393
394 fn get_file_token(
395 &self,
396 _file: &FileObject,
397 current_task: &CurrentTask,
398 ) -> Result<FileToken, Errno> {
399 let state = self.device.state.lock();
400 if state.suspended {
401 return error!(EINVAL);
402 }
403 if let Some(active_table) = &state.active_table {
404 if active_table.targets.is_empty() {
405 return error!(EINVAL);
406 }
407 if active_table.targets.len() > 1 {
408 return error!(ENOTSUP);
409 }
410 match &active_table.targets[0].target_type {
411 TargetType::Verity(verity_target) => {
412 verity_target.block_device.get_file_token(current_task)
419 }
420 TargetType::Error => error!(EIO),
421 }
422 } else {
423 error!(EINVAL)
424 }
425 }
426
427 fn ioctl(
428 &self,
429 _file: &FileObject,
430 current_task: &CurrentTask,
431 request: u32,
432 arg: SyscallArg,
433 ) -> Result<SyscallResult, Errno> {
434 let device = &self.device;
435 let mut state = device.state.lock();
436 if !state.suspended {
437 if let Some(active_table) = &mut state.active_table {
438 if active_table.targets.len() == 1 {
439 let target = &mut active_table.targets[0];
440 if let TargetType::Verity(args) = &mut target.target_type {
441 return args.block_device.ops().ioctl(
442 &*args.block_device,
443 current_task,
444 request,
445 arg,
446 );
447 }
448 }
449 }
450 }
451
452 error!(ENOTTY)
453 }
454
455 fn close(self: Box<Self>, _file: &FileObjectState, _current_task: &CurrentTask) {
456 let mut state = self.device.state.lock();
457 state.open_count -= 1;
458 }
459}
460#[derive(Debug)]
461struct DmDeviceState {
462 version: [u32; 3],
463 target_count: u32,
464 open_count: u64,
465 name: [std::ffi::c_char; 128],
466 uuid: [std::ffi::c_char; 129],
467 active_table: Option<DmDeviceTable>,
468 inactive_table: Option<DmDeviceTable>,
469 flags: DeviceMapperFlags,
470 suspended: bool,
471 k_device: Option<Device>,
472}
473
474impl Default for DmDeviceState {
475 fn default() -> Self {
476 DmDeviceState {
477 version: [0; 3],
478 name: [0 as std::ffi::c_char; 128],
479 uuid: [0 as std::ffi::c_char; 129],
480 target_count: 0,
481 open_count: 0,
482 active_table: None,
483 inactive_table: None,
484 flags: DeviceMapperFlags::empty(),
485 suspended: false,
486 k_device: None,
487 }
488 }
489}
490
491impl DmDeviceState {
492 fn set_version(&mut self) {
493 self.version = [DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL];
494 }
495
496 fn set_name(&mut self, name: [std::ffi::c_char; 128]) {
497 self.name = name;
498 }
499
500 fn set_uuid(&mut self, uuid: [std::ffi::c_char; 129]) {
501 self.uuid = uuid;
502 }
503
504 fn set_inactive_table(&mut self, inactive_table: DmDeviceTable) {
505 self.inactive_table = Some(inactive_table);
506 }
507
508 fn resume(&mut self) {
509 if let Some(inactive_table) = self.inactive_table.take() {
510 self.active_table = Some(inactive_table);
511 }
512 self.suspended = false;
513 }
514
515 fn remove(&mut self) {
516 self.active_table.take();
517 self.suspended = false;
518 }
519
520 fn set_target_count(&mut self, target_count: u32) {
521 self.target_count = target_count;
522 }
523
524 fn get_target_count(&self) -> u32 {
525 if let Some(_) = self.active_table { self.target_count } else { 0 }
526 }
527
528 fn add_flags(&mut self, flags: DeviceMapperFlags) {
529 self.flags |= flags;
530 }
531
532 fn get_flags(&self) -> DeviceMapperFlags {
533 let mut flags = DeviceMapperFlags::empty();
534 if let Some(active_table) = &self.active_table {
535 flags |= DeviceMapperFlags::ACTIVE_PRESENT;
536 if active_table.readonly {
537 flags |= DeviceMapperFlags::READONLY;
538 }
539 }
540 if let Some(_) = &self.inactive_table {
541 flags |= DeviceMapperFlags::INACTIVE_PRESENT;
542 }
543 if self.suspended {
544 flags |= DeviceMapperFlags::SUSPEND;
545 }
546 flags
547 }
548
549 fn suspend(&mut self) {
550 self.suspended = true;
551 }
552
553 fn set_k_device(&mut self, k_device: Device) {
554 self.k_device = Some(k_device);
555 }
556}
557#[derive(Debug, Clone)]
558struct DmDeviceTarget {
559 sector_start: u64,
560 length: u64,
561 status: i32,
562 name: [std::ffi::c_char; DM_MAX_TYPE_NAME as usize],
563 target_type: TargetType,
564}
565
566#[derive(Debug, Default, Clone)]
567pub struct DmDeviceTable {
568 targets: Vec<DmDeviceTarget>,
569 readonly: bool,
570}
571
572impl DmDeviceTable {
573 fn size(&self) -> usize {
574 let mut size = 0;
575 for target in &self.targets {
576 size += (SECTOR_SIZE * target.length) as usize;
577 }
578 size
579 }
580}
581
582struct DeviceMapper {
583 registry: Arc<DeviceMapperRegistry>,
584}
585
586impl DeviceMapper {
587 pub fn new(registry: Arc<DeviceMapperRegistry>) -> Self {
588 Self { registry: registry }
589 }
590}
591
592#[derive(Debug, Clone)]
593enum TargetType {
594 Verity(Box<VerityTarget>),
595 Error,
596}
597
598#[derive(Debug, Clone)]
599struct VerityTarget {
600 verifier: MerkleVerifier,
601 params: DmVerityTargetParams,
602 block_device: FileHandle,
603 corrupted: bool,
604}
605
606impl VerityTarget {
607 fn create_and_verify(
608 params: DmVerityTargetParams,
609 block_device: FileHandle,
610 leaf_hashes: Box<[u8]>,
611 ) -> Result<Self, Errno> {
612 let verifier = dm_verity::create_verifier(¶ms, leaf_hashes).map_err(|e| match &e {
613 DmVerityError::RootHashMismatch => {
614 starnix_logging::log_warn!(
615 "Merkle tree root hash mismatch. Expected: {}",
616 params.root_digest
617 );
618 errno!(EINVAL)
619 }
620 DmVerityError::UnsupportedAlgorithm(algorithm) => {
621 starnix_logging::log_warn!(
622 "dm-verity verifier creation failed for unsupported algorithm: {}",
623 algorithm
624 );
625 errno!(ENOTSUP)
626 }
627 _ => {
628 starnix_logging::log_warn!("dm-verity verifier creation failed: {:?}", e);
629 errno!(EINVAL)
630 }
631 })?;
632 Ok(Self { verifier, params, block_device, corrupted: false })
633 }
634
635 fn parameter_string(&self) -> String {
636 let params = &self.params;
637 let base_string = format!(
638 "{} {} {} {} {} {} {} {} {} {}",
639 params.version,
640 params.block_device_path,
641 params.hash_device_path,
642 params.data_block_size,
643 params.hash_block_size,
644 params.num_data_blocks,
645 params.hash_start_block,
646 params.hash_algorithm.as_str(),
647 params.root_digest,
648 params.salt
649 );
650 let mut optional_arg_count = 0;
651 let mut optional_string = String::new();
652 if params.optional_params.ignore_zero_blocks {
653 optional_arg_count += 1;
654 optional_string.push_str(" ignore_zero_blocks");
655 }
656 if params.optional_params.restart_on_corruption {
657 optional_arg_count += 1;
658 optional_string.push_str(" restart on corruption");
659 }
660 if optional_arg_count > 0 {
661 format!("{base_string} {optional_arg_count}{optional_string}")
662 } else {
663 base_string
664 }
665 }
666}
667
668fn open_device(
672 current_task: &CurrentTask,
673 device_path: String,
674) -> Result<(u64, FileHandle), Errno> {
675 let device_path_vec: Vec<&str> = device_path.split(":").collect();
676 if device_path_vec.len() == 1 {
677 let dev = current_task.open_file(device_path.as_str().into(), OpenFlags::RDONLY)?;
678 let loop_device_vec: Vec<&str> = device_path.split("loop").collect();
679 let minor = loop_device_vec[1].parse::<u64>().unwrap();
680 Ok((minor, dev))
681 } else {
682 let minor = device_path_vec[1].parse::<u64>().unwrap();
683 let dev = current_task
684 .open_file(format!("/dev/loop{minor}").as_str().into(), OpenFlags::RDONLY)?;
685 Ok((minor, dev))
686 }
687}
688
689fn size_of_merkle_tree_preceding_leaf_nodes(
690 leaf_nodes_size: u64,
691 hash_size: u64,
692 hash_block_size: u64,
693) -> u64 {
694 let mut total_size = 0;
695 let mut data_size = leaf_nodes_size;
696 while data_size > hash_block_size {
697 let num_hashes = data_size.div_ceil(hash_block_size);
698 let hashes_per_block = hash_block_size.div_ceil(hash_size);
699 let hash_blocks = num_hashes.div_ceil(hashes_per_block);
700 data_size = hash_blocks * hash_block_size;
701 total_size += data_size;
702 }
703 total_size
704}
705
706fn parse_parameter_string(
709 current_task: &CurrentTask,
710 target_type: &str,
711 parameter_str: String,
712) -> Result<TargetType, Errno> {
713 match target_type {
714 "verity" => {
715 let v: Vec<&str> = parameter_str.split(" ").collect();
716 let hash_algorithm = v[7].parse::<HashAlgorithm>().map_err(|_| errno!(ENOTSUP))?;
717 let mut params = DmVerityTargetParams {
718 version: String::from(v[0]),
719 block_device_path: String::from(v[1]),
720 hash_device_path: String::from(v[2]),
721 data_block_size: v[3].parse::<u64>().unwrap(),
722 hash_block_size: v[4].parse::<u64>().unwrap(),
723 num_data_blocks: v[5].parse::<u64>().unwrap(),
724 hash_start_block: v[6].parse::<u64>().unwrap(),
725 hash_algorithm,
726 root_digest: String::from(v[8]),
727 salt: String::from(v[9]),
728 optional_params: DmVerityTargetOptionalParams::default(),
729 };
730
731 if v.len() > 10 {
732 let num_optional_args = v[10].parse::<u64>().unwrap();
733 if num_optional_args > 2 {
734 return error!(ENOTSUP);
735 }
736 for i in 0..num_optional_args {
737 if v[11 + i as usize] == "ignore_zero_blocks" {
738 params.optional_params.ignore_zero_blocks = true;
739 } else if v[11 + i as usize] == "restart_on_corruption" {
740 track_stub!(
741 TODO("https://fxbug.dev/338243823"),
742 "Support restart on corruption."
743 );
744 params.optional_params.restart_on_corruption = true;
745 } else {
746 return error!(ENOTSUP);
747 }
748 }
749 }
750
751 let (minor, block_device) = open_device(current_task, params.block_device_path)?;
752 params.block_device_path = format!("{LOOP_MAJOR}:{minor}");
753
754 let (minor, hash_device) = if params.hash_device_path == params.block_device_path {
755 (minor, block_device.clone())
756 } else {
757 open_device(current_task, params.hash_device_path)?
758 };
759 params.hash_device_path = format!("{LOOP_MAJOR}:{minor}");
760
761 let hash_size: u64 = match params.hash_algorithm {
762 HashAlgorithm::Sha256 => <Sha256 as Hasher>::Digest::DIGEST_LEN as u64,
763 HashAlgorithm::Sha512 => <Sha512 as Hasher>::Digest::DIGEST_LEN as u64,
764 };
765
766 debug_assert!(params.hash_block_size > 0);
767 let data_size = params.num_data_blocks * params.data_block_size;
768 let num_hashes = data_size.div_ceil(params.hash_block_size);
769 let hashes_per_block = params.hash_block_size.div_ceil(hash_size);
770 let hash_blocks = num_hashes.div_ceil(hashes_per_block);
771 let leaf_nodes_size = hash_blocks * params.hash_block_size;
772 let mut buffer = VecOutputBuffer::new(leaf_nodes_size as usize);
773 let offset = params.hash_start_block * params.hash_block_size
774 + size_of_merkle_tree_preceding_leaf_nodes(
775 leaf_nodes_size,
776 hash_size,
777 params.hash_block_size,
778 );
779 let bytes_read =
780 hash_device.ops().read(&hash_device, current_task, offset as usize, &mut buffer)?;
781 debug_assert!(bytes_read == leaf_nodes_size as usize);
782
783 let leaf_hashes: Vec<u8> = buffer.into();
784 let verity_target = VerityTarget::create_and_verify(
785 params,
786 block_device,
787 leaf_hashes.into_boxed_slice(),
788 )?;
789 Ok(TargetType::Verity(Box::new(verity_target)))
790 }
791 "error" => Ok(TargetType::Error),
792 _ => error!(ENOTSUP),
793 }
794}
795
796fn check_version_compatibility(major: u32, minor: u32) -> Result<(), Errno> {
797 if major != DM_VERSION_MAJOR || minor > DM_VERSION_MINOR {
801 return error!(EINVAL);
802 }
803 return Ok(());
804}
805
806impl FileOps for DeviceMapper {
807 fileops_impl_seekless!();
808 fileops_impl_dataless!();
809 fileops_impl_noop_sync!();
810
811 fn ioctl(
812 &self,
813 _file: &FileObject,
814 current_task: &CurrentTask,
815 request: u32,
816 arg: SyscallArg,
817 ) -> Result<SyscallResult, Errno> {
818 security::check_task_capable(current_task, CAP_SYS_ADMIN).map_err(|_| errno!(EACCES))?;
819 let user_info = UserRef::<uapi::dm_ioctl>::from(arg);
820 let info_addr: starnix_uapi::user_address::UserAddress = user_info.addr();
821 let info = current_task.read_object(user_info)?;
822 let flags = DeviceMapperFlags::from_bits_truncate(info.flags);
823 match request {
824 DM_DEV_CREATE => {
825 if info.name == [0; DM_NAME_LEN as usize] || info.version == [0; 3] {
827 return error!(EINVAL);
828 }
829 let dm_device = self.registry.find(current_task)?;
830 let mut state = dm_device.state.lock();
831 check_version_compatibility(info.version[0], info.version[1])?;
832 state.set_version();
833 state.set_name(info.name);
834 state.set_uuid(info.uuid);
835 let i = uapi::dm_ioctl {
836 name: state.name,
837 version: state.version,
838 uuid: state.uuid,
839 dev: dm_device.number.bits(),
840 data_size: DATA_SIZE,
841 data_start: 0,
842 ..Default::default()
843 };
844 log_trace!("DM_DEV_CREATE returned dm_ioctl: {:?}", i);
845 current_task.write_object(user_info, &i)?;
846 Ok(SUCCESS)
847 }
848 DM_TABLE_LOAD => {
849 let mut start_addr = (info_addr + info.data_start)?;
850 let mut num_targets = 0;
851 let dm_device = self.registry.get(&info)?;
852 let mut state = dm_device.state.lock();
853 let mut table = DmDeviceTable { ..Default::default() };
854 if flags.contains(DeviceMapperFlags::READONLY) {
855 table.readonly = true;
856 state.add_flags(DeviceMapperFlags::READONLY);
857 }
858 if info.target_count > 1 {
859 track_stub!(TODO("https://fxbug.dev/339701082"), "Support multiple targets.");
860 return error!(ENOTSUP);
861 }
862 track_stub!(
863 TODO("https://fxbug.dev/338245544"),
864 "Make sure targets are contiguous and non-overlapping"
865 );
866 while num_targets < info.target_count {
867 let target_ref = UserRef::<uapi::dm_target_spec>::new(start_addr);
868 let target = current_task.read_object(target_ref)?;
869 let parameter_cstring =
870 UserCString::new(current_task, target_ref.next()?.addr());
871 let parameters = current_task.read_c_string_to_vec(
872 parameter_cstring,
873 target.next as usize - std::mem::size_of::<uapi::dm_target_spec>(),
874 )?;
875 let target_type_addr = start_addr
876 .checked_add(
877 2 * std::mem::size_of::<u64>()
878 + std::mem::size_of::<u32>()
879 + std::mem::size_of::<i32>(),
880 )
881 .ok_or_else(|| errno!(EINVAL))?;
882 let target_type_cstring = UserCString::new(current_task, target_type_addr);
883 let target_type = current_task
884 .read_c_string_to_vec(target_type_cstring, DM_MAX_TYPE_NAME as usize)?;
885
886 let device_target = DmDeviceTarget {
887 sector_start: target.sector_start,
888 length: target.length,
889 status: target.status,
890 name: target.target_type,
891 target_type: parse_parameter_string(
892 current_task,
893 &target_type.to_string(),
894 parameters.to_string(),
895 )?,
896 };
897 table.targets.push(device_target);
898 num_targets += 1;
899 debug_assert!(target.next % 8 == 0);
900 start_addr = start_addr
901 .checked_add(target.next as usize)
902 .ok_or_else(|| errno!(EINVAL))?;
903 }
904
905 state.set_inactive_table(table);
907 state.set_target_count(num_targets);
908 let i = uapi::dm_ioctl {
909 name: state.name,
910 version: state.version,
911 uuid: state.uuid,
912 dev: dm_device.number.bits(),
913 data_size: DATA_SIZE,
914 data_start: 0,
915 flags: state.get_flags().bits(),
916 target_count: state.get_target_count(),
917 ..Default::default()
918 };
919 log_trace!("DM_TABLE_LOAD returned dm_ioctl: {:?}", i);
920 current_task.write_object(user_info, &i)?;
921 Ok(SUCCESS)
922 }
923 DM_DEV_SUSPEND => {
924 let dm_device = self.registry.get(&info)?;
925 let mut state = dm_device.state.lock();
926 if flags.contains(DeviceMapperFlags::SUSPEND) {
927 state.suspend();
928 } else {
929 state.resume();
930 }
931 let mut out_flags = state.get_flags();
932 if !flags.contains(DeviceMapperFlags::SUSPEND) {
933 out_flags |= DeviceMapperFlags::UEVENT_GENERATED;
934 }
935 let i = uapi::dm_ioctl {
936 name: state.name,
937 version: state.version,
938 uuid: state.uuid,
939 dev: dm_device.number.bits(),
940 data_size: DATA_SIZE,
941 data_start: 0,
942 flags: out_flags.bits(),
943 target_count: state.get_target_count(),
944 ..Default::default()
945 };
946 log_trace!("DM_DEV_SUSPEND returned dm_ioctl: {:?}", i);
947 current_task.write_object(user_info, &i)?;
948 Ok(SUCCESS)
949 }
950 DM_DEV_STATUS => {
951 let dm_device = self.registry.get(&info)?;
952 let state = dm_device.state.lock();
953 let i = uapi::dm_ioctl {
954 name: state.name,
955 version: state.version,
956 uuid: state.uuid,
957 dev: dm_device.number.bits(),
958 data_size: DATA_SIZE,
959 data_start: 0,
960 flags: state.get_flags().bits(),
961 target_count: state.get_target_count(),
962 ..Default::default()
963 };
964 log_trace!("DM_DEV_STATUS returned dm_ioctl: {:?}", i);
965 current_task.write_object(user_info, &i)?;
966 Ok(SUCCESS)
967 }
968 DM_DEV_REMOVE => {
969 let dm_device = self.registry.get(&info)?;
970 let mut devices = self.registry.devices.lock();
971 let mut state = dm_device.state.lock();
972 if state.open_count > 0 {
973 return error!(ENOTSUP);
974 }
975 self.registry.remove(
976 current_task,
977 &mut devices,
978 dm_device.number.minor(),
979 &state.k_device,
980 )?;
981 state.remove();
982 let i = uapi::dm_ioctl {
983 name: state.name,
984 version: state.version,
985 uuid: state.uuid,
986 dev: dm_device.number.bits(),
987 data_size: DATA_SIZE,
988 data_start: 0,
989 flags: (state.get_flags() | DeviceMapperFlags::UEVENT_GENERATED).bits(),
990 target_count: state.get_target_count(),
991 ..Default::default()
992 };
993 log_trace!("DM_DEV_REMOVE returned dm_ioctl: {:?}", i);
994 current_task.write_object(user_info, &i)?;
995 Ok(SUCCESS)
996 }
997 DM_LIST_DEVICES => {
998 if flags.contains(DeviceMapperFlags::DM_NAME_LIST_HAS_UUID)
999 || flags.contains(DeviceMapperFlags::DM_NAME_LIST_NO_UUID)
1000 {
1001 return error!(ENOTSUP);
1002 }
1003 let mut name_list_addr = user_info.next()?.addr();
1004 let mut total_size = std::mem::size_of::<uapi::dm_ioctl>() as u32;
1005 for (_, device) in self.registry.devices.lock().iter() {
1006 let state = device.state.lock();
1007 let dm_name_list = UserRef::<uapi::dm_name_list>::new(name_list_addr);
1008 let name = state.name.iter().map(|v| *v as u8).collect::<Vec<u8>>();
1009 let name_c_str = std::ffi::CStr::from_bytes_until_nul(name.as_slice())
1010 .map_err(|_| errno!(EINVAL))?;
1011 let mut name_vec_with_nul = name_c_str.to_bytes_with_nul().to_vec();
1012 let mut size = (std::mem::size_of::<uapi::dm_name_list>() - 4
1013 + name_vec_with_nul.len()) as u32;
1014 let mut padding = 0;
1015 if size % 8 != 0 {
1016 padding = 8 - (size % 8);
1017 size += padding;
1018 };
1019 size += 8;
1021 let name_list = uapi::dm_name_list {
1022 dev: device.number.bits(),
1023 next: size,
1024 ..Default::default()
1025 };
1026 if total_size + size > info.data_size {
1027 let i = uapi::dm_ioctl {
1028 data_size: DATA_SIZE,
1029 data_start: std::mem::size_of::<uapi::dm_ioctl>() as u32,
1030 flags: DeviceMapperFlags::BUFFER_FULL.bits(),
1031 ..Default::default()
1032 };
1033 log_trace!("DM_LIST_DEVICES returned dm_ioctl: {:?}", i);
1034 current_task.write_object(user_info, &i)?;
1035 return Ok(SUCCESS);
1036 }
1037 total_size += size;
1038 let mut name_addr = dm_name_list.next()?.addr();
1039 name_addr = name_addr.sub(4 as usize)?;
1040 current_task.write_object(dm_name_list, &name_list)?;
1041 name_vec_with_nul.extend(vec![0; padding as usize + 8]);
1042 current_task.write_memory(name_addr, name_vec_with_nul.as_slice())?;
1043 name_list_addr =
1044 name_list_addr.checked_add(size as usize).ok_or_else(|| errno!(EINVAL))?;
1045 }
1046 let i = uapi::dm_ioctl {
1047 data_size: total_size,
1048 data_start: std::mem::size_of::<uapi::dm_ioctl>() as u32,
1049 ..Default::default()
1050 };
1051 log_trace!("DM_LIST_DEVICE returned dm_ioctl: {:?}", i);
1052 current_task.write_object(user_info, &i)?;
1053 Ok(SUCCESS)
1054 }
1055 DM_LIST_VERSIONS => {
1056 let version_list_addr = user_info.next()?.addr();
1057 let dm_versions_list = UserRef::<uapi::dm_target_versions>::new(version_list_addr);
1058 let name_c_str =
1059 std::ffi::CString::new(String::from("verity")).map_err(|_| errno!(EINVAL))?;
1060 let mut name_vec_with_nul = name_c_str.as_bytes_with_nul().to_vec();
1061 let mut size = (std::mem::size_of::<uapi::dm_target_versions>()
1062 + name_vec_with_nul.len()) as u32;
1063 let mut padding = 0;
1064 if size % 8 != 0 {
1065 padding = 8 - (size % 8);
1066 size += padding;
1067 };
1068 name_vec_with_nul.extend(vec![0; padding as usize]);
1069 if std::mem::size_of::<uapi::dm_ioctl>() as u32 + size > info.data_size {
1070 let i = uapi::dm_ioctl {
1071 data_size: DATA_SIZE,
1072 data_start: 0,
1073 flags: DeviceMapperFlags::BUFFER_FULL.bits(),
1074 ..Default::default()
1075 };
1076 log_trace!("DM_LIST_VERSIONS returned dm_ioctl: {:?}", i);
1077 current_task.write_object(user_info, &i)?;
1078 return Ok(SUCCESS);
1079 }
1080 let target_versions = uapi::dm_target_versions {
1081 next: size,
1082 version: [
1083 DM_VERITY_VERSION_MAJOR,
1084 DM_VERITY_VERSION_MINOR,
1085 DM_VERITY_VERSION_PATCHLEVEL,
1086 ],
1087 ..Default::default()
1088 };
1089 let name_addr = dm_versions_list.next()?.addr();
1090 current_task.write_object(dm_versions_list, &target_versions)?;
1091 current_task.write_memory(name_addr, name_vec_with_nul.as_slice())?;
1092
1093 let i = uapi::dm_ioctl {
1094 data_size: std::mem::size_of::<uapi::dm_ioctl>() as u32 + size,
1095 data_start: std::mem::size_of::<uapi::dm_ioctl>() as u32,
1096 ..Default::default()
1097 };
1098 log_trace!("DM_LIST_VERSIONS returned dm_ioctl: {:?}", i);
1099 current_task.write_object(user_info, &i)?;
1100 Ok(SUCCESS)
1101 }
1102 DM_TABLE_STATUS => {
1103 let dm_device = self.registry.get(&info)?;
1104 let state = dm_device.state.lock();
1105 let mut total_data_size = 0;
1106 let mut data_padding = 0;
1107 let mut target_spec_addr = user_info.next()?.addr();
1108 let mut out_flags = DeviceMapperFlags::empty();
1109 let space_for_data = info.data_size as usize
1110 - std::cmp::min(info.data_size as usize, std::mem::size_of::<uapi::dm_ioctl>());
1111 if let Some(active_table) = &state.active_table {
1112 for target in &active_table.targets {
1113 let target_spec_info =
1114 UserRef::<uapi::dm_target_spec>::new(target_spec_addr);
1115 let mut data_size = std::mem::size_of::<uapi::dm_target_spec>();
1116 if total_data_size + data_size - data_padding >= space_for_data {
1117 out_flags |= DeviceMapperFlags::BUFFER_FULL;
1118 break;
1119 }
1120 let data_addr = target_spec_info.next()?.addr();
1121 if flags.contains(DeviceMapperFlags::STATUS_TABLE) {
1122 match &target.target_type {
1123 TargetType::Verity(args) => {
1124 let param_str = std::ffi::CString::new(args.parameter_string())
1125 .map_err(|_| errno!(EINVAL))?;
1126 let mut args_bytes = param_str.into_bytes_with_nul();
1127 if args_bytes.len() % 8 != 0 {
1128 data_padding = 8 - args_bytes.len() % 8;
1129 args_bytes.extend(vec![0 as u8; data_padding]);
1130 }
1131 data_size += args_bytes.len();
1132 if total_data_size + data_size - data_padding >= space_for_data
1133 {
1134 out_flags |= DeviceMapperFlags::BUFFER_FULL;
1135 break;
1136 }
1137 current_task.write_memory(data_addr, args_bytes.as_slice())?;
1138 }
1139 TargetType::Error => {
1140 }
1142 }
1143 } else if flags.contains(DeviceMapperFlags::IMA_MEASUREMENT) {
1144 return error!(ENOTSUP);
1146 } else {
1147 match &target.target_type {
1148 TargetType::Verity(args) => {
1149 let status = if args.corrupted { "C" } else { "V" };
1150 let status_c_str = std::ffi::CString::new(String::from(status))
1151 .map_err(|_| errno!(EINVAL))?;
1152 let mut status_bytes = status_c_str.into_bytes_with_nul();
1153 if status_bytes.len() % 8 != 0 {
1154 data_padding = 8 - status_bytes.len() % 8;
1155 status_bytes.extend(vec![0 as u8; data_padding]);
1156 }
1157 data_size += status_bytes.len();
1158 if total_data_size + data_size - data_padding >= space_for_data
1159 {
1160 out_flags |= DeviceMapperFlags::BUFFER_FULL;
1161 break;
1162 }
1163 current_task
1164 .write_memory(data_addr, status_bytes.as_slice())?;
1165 }
1166 TargetType::Error => {
1167 }
1169 }
1170 }
1171 total_data_size += data_size;
1172 let target_spec = uapi::dm_target_spec {
1173 sector_start: target.sector_start,
1174 length: target.length,
1175 status: target.status,
1176 target_type: target.name,
1177 next: total_data_size as u32,
1178 };
1179 current_task.write_object(target_spec_info, &target_spec)?;
1180 target_spec_addr = target_spec_addr
1181 .checked_add(data_size)
1182 .ok_or_else(|| errno!(EINVAL))?;
1183 }
1184 } else {
1185 let i = uapi::dm_ioctl {
1186 name: state.name,
1187 version: state.version,
1188 uuid: state.uuid,
1189 dev: dm_device.number.bits(),
1190 data_size: DATA_SIZE,
1191 data_start: std::mem::size_of::<uapi::dm_ioctl>() as u32,
1192 flags: state.get_flags().bits(),
1193 ..Default::default()
1194 };
1195 log_trace!("DM_TABLE_STATUS returned dm_ioctl: {:?}", i);
1196 current_task.write_object(user_info, &i)?;
1197 return Ok(SUCCESS);
1198 }
1199 let total_size = if out_flags.contains(DeviceMapperFlags::BUFFER_FULL) {
1202 DATA_SIZE
1203 } else {
1204 (total_data_size + std::mem::size_of::<uapi::dm_ioctl>() - data_padding) as u32
1205 };
1206 out_flags |= state.get_flags();
1207
1208 let i = uapi::dm_ioctl {
1209 name: state.name,
1210 version: state.version,
1211 uuid: state.uuid,
1212 dev: dm_device.number.bits(),
1213 data_size: total_size,
1214 data_start: std::mem::size_of::<uapi::dm_ioctl>() as u32,
1215 flags: out_flags.bits(),
1216 ..Default::default()
1217 };
1218 log_trace!("DM_TABLE_STATUS returned dm_ioctl: {:?}", i);
1219 current_task.write_object(user_info, &i)?;
1220 Ok(SUCCESS)
1221 }
1222 DM_VERSION
1224 | DM_DEV_RENAME
1225 | DM_DEV_WAIT
1226 | DM_TABLE_CLEAR
1227 | DM_TABLE_DEPS
1228 | DM_REMOVE_ALL
1229 | DM_TARGET_MSG
1230 | DM_DEV_SET_GEOMETRY
1231 | DM_DEV_ARM_POLL
1232 | DM_GET_TARGET_VERSION => return error!(ENOTSUP),
1233 _ => error!(ENOTTY),
1234 }
1235 }
1236}
1237
1238pub fn create_device_mapper(
1239 current_task: &CurrentTask,
1240 _id: DeviceId,
1241 _node: &NamespaceNode,
1242 _flags: OpenFlags,
1243) -> Result<Box<dyn FileOps>, Errno> {
1244 Ok(Box::new(DeviceMapper::new(current_task.kernel().expando.get::<DeviceMapperRegistry>())))
1245}
1246
1247fn get_or_create_dm_device(
1248 current_task: &CurrentTask,
1249 id: DeviceId,
1250 _node: &NamespaceNode,
1251 _flags: OpenFlags,
1252) -> Result<Box<dyn FileOps>, Errno> {
1253 Ok(current_task
1254 .kernel()
1255 .expando
1256 .get::<DeviceMapperRegistry>()
1257 .get_or_create_by_minor(current_task, id.minor())?
1258 .create_file_ops())
1259}
1260
1261#[cfg(test)]
1262mod tests {
1263 use super::*;
1264 use starnix_core::testing::spawn_kernel_and_run;
1265
1266 #[::fuchsia::test]
1267 async fn device_mapper_init_registers_sysfs_module_parameters() {
1268 spawn_kernel_and_run(async |current_task| {
1269 let kernel = current_task.kernel();
1270 device_mapper_init(kernel).expect("device_mapper_init should succeed");
1271
1272 let root = &kernel.device_registry.objects.root;
1273 let max_age_seconds = root
1274 .lookup("module/dm_bufio/parameters/max_age_seconds".into())
1275 .expect("dm_bufio max_age_seconds should exist");
1276 assert_eq!(max_age_seconds.info().mode, mode!(IFREG, 0o644));
1277
1278 let prefetch_cluster = root
1279 .lookup("module/dm_verity/parameters/prefetch_cluster".into())
1280 .expect("dm_verity prefetch_cluster should exist");
1281 assert_eq!(prefetch_cluster.info().mode, mode!(IFREG, 0o644));
1282 })
1283 .await;
1284 }
1285}