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