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