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