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