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