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