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