Skip to main content

starnix_modules_device_mapper/
lib.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![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;
50// The value of the data_size field in the output dm_ioctl struct when no data is returned as per
51// Linux 6.6.15.
52const DATA_SIZE: u32 = 305;
53// Observed version values for the dm-verity target as per Linux 6.6.15.
54const 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    /// Looks up a dm-device based on strictly one of the following: name, uuid, or dev number.
91    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    /// Finds a free minor number in the DeviceMapperRegistry. Returns that minor number along with
147    /// a new DmDevice associated with that minor number.
148    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    /// Removes `device` from both the Device and DeviceMapper registries.
164    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    // Writes aren't supported for these files, no need to sync the data.
247    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(&params, 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
600// Returns the FileHandle and minor number of the device found at `device path` formatted as
601// either /dev/loop# of MAJOR:MINOR
602
603fn 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
638// Parse the parameter string into a TargetType.
639
640fn 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    // The version field of the input dm-ioctl struct should represent the version of the interface
730    // that the client was compiled with. The major number must match the kernel's, the minor
731    // number is backwards compatible, and the patchlevel is forwards and backwards compatible.
732    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                // Expect name and version to be set. This should not fail if uuid is not set.
758                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                // Update the metadata of the dm device
838                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                    // For the event_nr and flags.
952                    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                                    // Error target has no parameters.
1073                                }
1074                            }
1075                        } else if flags.contains(DeviceMapperFlags::IMA_MEASUREMENT) {
1076                            // Linux 6.6.15 does not currently support IMA for dm-verity.
1077                            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                                    // Status for error target is not used much but we can provide something.
1100                                }
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                // Linux removes the size of the data padding when calculating the data size field
1132                // returned.
1133                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            // These dm ioctls are not used by Android
1155            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}