Skip to main content

starnix_modules_loop/
lib.rs

1// Copyright 2023 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 = "256"]
6
7use bitflags::bitflags;
8use starnix_core::device::DeviceMode;
9use starnix_core::device::block::canonicalize_ioctl_request;
10use starnix_core::device::kobject::{Device, DeviceMetadata};
11use starnix_core::fs::sysfs::{BlockDeviceInfo, build_block_device_directory};
12use starnix_core::mm::memory::MemoryObject;
13use starnix_core::mm::{MemoryAccessorExt, PAGE_SIZE, ProtectionFlags};
14use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
15use starnix_core::task::{CurrentTask, Kernel};
16use starnix_core::vfs::buffers::{InputBuffer, OutputBuffer};
17use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
18use starnix_core::vfs::{
19    Buffer, FdNumber, FileHandle, FileObject, FileOps, FsNodeOps, FsString, InputBufferCallback,
20    NamespaceNode, PeekBufferSegmentsCallback, fileops_impl_dataless, fileops_impl_noop_sync,
21    fileops_impl_seekable, fileops_impl_seekless,
22};
23use starnix_ext::map_ext::EntryExt;
24use starnix_logging::track_stub;
25use starnix_sync::{LockDepMutex, LoopDeviceStateLock, LoopDevicesLock};
26use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
27use starnix_types::user_buffer::UserBuffer;
28use starnix_uapi::device_id::{DeviceId, LOOP_MAJOR};
29use starnix_uapi::errors::Errno;
30use starnix_uapi::open_flags::OpenFlags;
31use starnix_uapi::user_address::{MultiArchUserRef, UserRef};
32use starnix_uapi::{
33    __kernel_old_dev_t, BLKFLSBUF, BLKGETSIZE, BLKGETSIZE64, BLKRASET, LO_FLAGS_AUTOCLEAR,
34    LO_FLAGS_DIRECT_IO, LO_FLAGS_PARTSCAN, LO_FLAGS_READ_ONLY, LO_KEY_SIZE, LOOP_CHANGE_FD,
35    LOOP_CLR_FD, LOOP_CONFIGURE, LOOP_CTL_ADD, LOOP_CTL_GET_FREE, LOOP_CTL_REMOVE, LOOP_GET_STATUS,
36    LOOP_GET_STATUS64, LOOP_SET_BLOCK_SIZE, LOOP_SET_CAPACITY, LOOP_SET_DIRECT_IO, LOOP_SET_FD,
37    LOOP_SET_STATUS, LOOP_SET_STATUS64, errno, error, loop_info, loop_info64, mode, uapi,
38};
39use std::borrow::Cow;
40use std::collections::btree_map::{BTreeMap, Entry};
41use std::sync::{Arc, Weak};
42use zx::VmoChildOptions;
43
44// See LOOP_SET_BLOCK_SIZE in <https://man7.org/linux/man-pages/man4/loop.4.html>.
45const MIN_BLOCK_SIZE: u32 = 512;
46
47bitflags! {
48    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
49    struct LoopDeviceFlags: u32 {
50        const READ_ONLY = LO_FLAGS_READ_ONLY;
51        const AUTOCLEAR = LO_FLAGS_AUTOCLEAR;
52        const PARTSCAN = LO_FLAGS_PARTSCAN;
53        const DIRECT_IO = LO_FLAGS_DIRECT_IO;
54    }
55}
56
57#[derive(Debug)]
58struct LoopDeviceState {
59    backing_file: Option<FileHandle>,
60    block_size: u32,
61    k_device: Option<Device>,
62
63    // See struct loop_info64 for details about these fields.
64    offset: u64,
65    size_limit: u64,
66    flags: LoopDeviceFlags,
67
68    // Encryption is not implemented.
69    encrypt_type: u32,
70    encrypt_key: Vec<u8>,
71    init: [u64; 2],
72}
73
74impl Default for LoopDeviceState {
75    fn default() -> Self {
76        LoopDeviceState {
77            backing_file: Default::default(),
78            block_size: MIN_BLOCK_SIZE,
79            k_device: Default::default(),
80            offset: Default::default(),
81            size_limit: Default::default(),
82            flags: Default::default(),
83            encrypt_type: Default::default(),
84            encrypt_key: Default::default(),
85            init: Default::default(),
86        }
87    }
88}
89
90impl LoopDeviceState {
91    fn check_bound(&self) -> Result<(), Errno> {
92        if self.backing_file.is_none() { error!(ENXIO) } else { Ok(()) }
93    }
94
95    fn set_backing_file(
96        &mut self,
97        current_task: &CurrentTask,
98        backing_file: FileHandle,
99    ) -> Result<(), Errno> {
100        if self.backing_file.is_some() {
101            return error!(EBUSY);
102        }
103        self.backing_file = Some(backing_file);
104        self.update_size_limit(current_task)?;
105        Ok(())
106    }
107
108    fn set_info(&mut self, info: &uapi::loop_info64) {
109        let encrypt_key_size = info.lo_encrypt_key_size.clamp(0, LO_KEY_SIZE);
110        self.offset = info.lo_offset;
111        self.size_limit = info.lo_sizelimit;
112        self.flags = LoopDeviceFlags::from_bits_truncate(info.lo_flags);
113        self.encrypt_type = info.lo_encrypt_type;
114        self.encrypt_key = info.lo_encrypt_key[0..(encrypt_key_size as usize)].to_owned();
115        self.init = info.lo_init;
116    }
117
118    fn update_size_limit(&mut self, current_task: &CurrentTask) -> Result<(), Errno> {
119        if let Some(backing_file) = &self.backing_file {
120            let backing_stat = backing_file.node().stat(current_task)?;
121            self.size_limit = backing_stat.st_size as u64;
122        }
123        Ok(())
124    }
125
126    fn set_k_device(&mut self, k_device: Device) {
127        self.k_device = Some(k_device);
128    }
129}
130
131#[derive(Debug, Default)]
132struct LoopDevice {
133    number: u32,
134    state: LockDepMutex<LoopDeviceState, LoopDeviceStateLock>,
135}
136
137struct LoopDeviceBackingFile(Weak<LoopDevice>);
138
139impl LoopDeviceBackingFile {
140    pub fn new_node(device: Weak<LoopDevice>) -> impl FsNodeOps {
141        BytesFile::new_node(Self(device))
142    }
143}
144
145impl BytesFileOps for LoopDeviceBackingFile {
146    fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
147        let mut path = self
148            .0
149            .upgrade()
150            .ok_or_else(|| errno!(EINVAL))?
151            .state
152            .lock()
153            .backing_file
154            .as_ref()
155            .ok_or_else(|| errno!(EINVAL))
156            .map(|file| file.name.to_passive().path(&current_task.fs()))?;
157        path.push(b'\n');
158        Ok(Cow::Owned(path.into()))
159    }
160}
161
162impl LoopDevice {
163    fn new<'a>(kernel: &Kernel, minor: u32) -> Result<Arc<Self>, Errno> {
164        let registry = &kernel.device_registry;
165        let loop_device_name = FsString::from(format!("loop{minor}"));
166        let virtual_block_class = registry.objects.virtual_block_class();
167        let device = Arc::new(Self { number: minor, state: Default::default() });
168        let device_weak = Arc::<LoopDevice>::downgrade(&device);
169        let k_device = registry.add_device(
170            kernel,
171            loop_device_name.as_ref(),
172            DeviceMetadata::new(
173                loop_device_name.clone(),
174                DeviceId::new(LOOP_MAJOR, minor),
175                DeviceMode::Block,
176            )
177            // It is not generally true that all loop devices are disks, but it is true for
178            // the ones we currently support. Future work should allow us to set this dynamically.
179            .with_devtype("disk"),
180            virtual_block_class,
181            |device, dir| {
182                let device_weak_clone = device_weak.clone();
183                build_block_device_directory(device, device_weak, dir);
184                dir.subdir("loop", 0o755, |dir| {
185                    dir.entry(
186                        "backing_file",
187                        LoopDeviceBackingFile::new_node(device_weak_clone),
188                        mode!(IFREG, 0o644),
189                    );
190                });
191            },
192        )?;
193        {
194            let mut state = device.state.lock();
195            state.set_k_device(k_device);
196        }
197        Ok(device)
198    }
199
200    fn create_file_ops(self: &Arc<Self>) -> Box<dyn FileOps> {
201        Box::new(LoopDeviceFile { device: self.clone() })
202    }
203
204    fn backing_file(&self) -> Option<FileHandle> {
205        self.state.lock().backing_file.clone()
206    }
207
208    fn is_bound(&self) -> bool {
209        self.state.lock().backing_file.is_some()
210    }
211
212    fn offset_for_backing_file(&self, offset: usize) -> usize {
213        self.state.lock().offset.saturating_add(offset as u64) as usize
214    }
215}
216
217fn check_block_size(block_size: u32) -> Result<(), Errno> {
218    let page_size = *PAGE_SIZE as u32;
219    let mut allowed_size = MIN_BLOCK_SIZE;
220    while allowed_size <= page_size {
221        if block_size == allowed_size {
222            return Ok(());
223        }
224        allowed_size *= 2;
225    }
226    error!(EINVAL)
227}
228
229impl BlockDeviceInfo for LoopDevice {
230    fn size(&self) -> Result<usize, Errno> {
231        Ok(self.state.lock().size_limit as usize)
232    }
233}
234
235#[derive(Debug)]
236struct CroppedInputBuffer<'a> {
237    base: &'a mut dyn InputBuffer,
238    size: usize,
239    drained: bool,
240}
241
242impl<'a> CroppedInputBuffer<'a> {
243    fn new(base: &'a mut dyn InputBuffer, size: usize) -> Self {
244        debug_assert!(size <= base.bytes_read() + base.available());
245        CroppedInputBuffer { base, size, drained: false }
246    }
247}
248
249impl<'a> Buffer for CroppedInputBuffer<'a> {
250    fn segments_count(&self) -> Result<usize, Errno> {
251        error!(ENOTSUP)
252    }
253
254    fn peek_each_segment(
255        &mut self,
256        callback: &mut PeekBufferSegmentsCallback<'_>,
257    ) -> Result<(), Errno> {
258        let mut pos = 0;
259        self.base.peek_each_segment(&mut |buffer: &UserBuffer| {
260            if pos >= self.size {
261                return;
262            } else if pos + buffer.length > self.size {
263                let cropped_size = self.size - pos;
264                pos += buffer.length;
265                callback(&UserBuffer { address: buffer.address, length: cropped_size });
266            } else {
267                pos += buffer.length;
268                callback(buffer);
269            }
270        })
271    }
272}
273
274impl<'a> InputBuffer for CroppedInputBuffer<'a> {
275    fn peek_each(&mut self, callback: &mut InputBufferCallback<'_>) -> Result<usize, Errno> {
276        if self.drained {
277            return Ok(0);
278        }
279        let mut pos = self.base.bytes_read();
280        self.base.peek_each(&mut |buf: &[u8]| {
281            if pos >= self.size {
282                return Ok(0);
283            }
284            let size = std::cmp::min(buf.len(), self.size - pos);
285            pos += size;
286            callback(&buf[..size])
287        })
288    }
289    fn advance(&mut self, length: usize) -> Result<(), Errno> {
290        if length > self.available() {
291            return error!(EINVAL);
292        }
293        self.base.advance(length)
294    }
295    fn available(&self) -> usize {
296        if self.drained || self.size < self.bytes_read() {
297            0
298        } else {
299            self.size - self.bytes_read()
300        }
301    }
302    fn bytes_read(&self) -> usize {
303        self.base.bytes_read()
304    }
305    fn drain(&mut self) -> usize {
306        let size = self.available();
307        self.drained = true;
308        size
309    }
310}
311
312struct LoopDeviceFile {
313    device: Arc<LoopDevice>,
314}
315
316impl FileOps for LoopDeviceFile {
317    fileops_impl_seekable!();
318
319    fn read(
320        &self,
321        _file: &FileObject,
322        current_task: &CurrentTask,
323        offset: usize,
324        data: &mut dyn OutputBuffer,
325    ) -> Result<usize, Errno> {
326        if let Some(backing_file) = self.device.backing_file() {
327            backing_file.read_at(current_task, self.device.offset_for_backing_file(offset), data)
328        } else {
329            Ok(0)
330        }
331    }
332
333    fn write(
334        &self,
335        _file: &FileObject,
336        current_task: &CurrentTask,
337        offset: usize,
338        data: &mut dyn InputBuffer,
339    ) -> Result<usize, Errno> {
340        if let Some(backing_file) = self.device.backing_file() {
341            let limit = self.device.state.lock().size_limit as usize;
342            if offset >= limit {
343                // Can't write past the size limit.
344                return Ok(0);
345            }
346            let mut cropped_buf;
347            let data = if offset + data.available() > limit {
348                // If the write would exceed the size limit, then crop the input buffer to write
349                // to the limit without exceeding it.
350                let bytes_to_write = limit - offset;
351                let cropped_size = data.bytes_read() + bytes_to_write;
352                cropped_buf = CroppedInputBuffer::new(data, cropped_size);
353                &mut cropped_buf
354            } else {
355                data
356            };
357            let r = backing_file.write_at(
358                current_task,
359                self.device.offset_for_backing_file(offset),
360                data,
361            );
362            r
363        } else {
364            error!(ENOSPC)
365        }
366    }
367
368    fn get_memory(
369        &self,
370        _file: &FileObject,
371        current_task: &CurrentTask,
372        requested_length: Option<usize>,
373        prot: ProtectionFlags,
374    ) -> Result<Arc<MemoryObject>, Errno> {
375        let backing_file = self.device.backing_file().ok_or_else(|| errno!(EBADF))?;
376
377        let state = self.device.state.lock();
378        let configured_offset = state.offset;
379        let configured_size_limit = match state.size_limit {
380            // If the size limit is 0, use all available bytes from the backing file.
381            0 => None,
382            n => Some(n),
383        };
384
385        let backing_memory = backing_file.get_memory(
386            current_task,
387            requested_length.map(|l| l + configured_offset as usize),
388            prot,
389        )?;
390        let backing_memory_size = backing_memory.get_size();
391
392        let clone_len = backing_memory_size
393            .min(configured_size_limit.unwrap_or(u64::MAX))
394            .min(requested_length.unwrap_or(usize::MAX) as u64);
395
396        let backing_content_size = backing_memory.get_content_size();
397
398        let mem = if backing_content_size < clone_len {
399            // If we need to set a content size then the backing memory must not be writable since
400            // we are going to create a snapshot clone, which will prevent any writes from becoming
401            // visible in the backing memory.
402            if backing_file.can_write() {
403                track_stub!(
404                    TODO("https://fxbug.dev/408048145"),
405                    "Loop device mutable loop backing files with smaller content"
406                );
407                return error!(EINVAL);
408            }
409            let memory_clone = backing_memory
410                .create_child(
411                    VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE,
412                    configured_offset,
413                    clone_len,
414                )
415                .map_err(|e| errno!(EINVAL, e))?;
416            let new_content_size = backing_content_size.saturating_sub(configured_offset);
417            memory_clone.set_content_size(new_content_size).map_err(|e| errno!(EINVAL, e))?;
418            memory_clone
419        } else {
420            backing_memory
421                .create_child(VmoChildOptions::SLICE, configured_offset, clone_len)
422                .map_err(|e| errno!(EINVAL, e))?
423        };
424        Ok(Arc::new(mem))
425    }
426
427    fn sync(&self, _file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
428        if let Some(f) = self.device.backing_file() { f.sync(current_task) } else { Ok(()) }
429    }
430
431    fn ioctl(
432        &self,
433        file: &FileObject,
434        current_task: &CurrentTask,
435        request: u32,
436        arg: SyscallArg,
437    ) -> Result<SyscallResult, Errno> {
438        match canonicalize_ioctl_request(current_task, request) {
439            BLKGETSIZE => {
440                let user_size = MultiArchUserRef::<u64, u32>::new(current_task, arg);
441                let state = self.device.state.lock();
442                state.check_bound()?;
443                let size = state.size_limit / (state.block_size as u64);
444                std::mem::drop(state);
445                current_task.write_multi_arch_object(user_size, size)?;
446                Ok(SUCCESS)
447            }
448            BLKGETSIZE64 => {
449                let user_size = UserRef::<u64>::from(arg);
450                let state = self.device.state.lock();
451                state.check_bound()?;
452                let size = state.size_limit;
453                std::mem::drop(state);
454                current_task.write_object(user_size, &size)?;
455                Ok(SUCCESS)
456            }
457            BLKFLSBUF => {
458                track_stub!(TODO("https://fxbug.dev/322873756"), "Loop device BLKFLSBUF");
459                Ok(SUCCESS)
460            }
461            BLKRASET => {
462                track_stub!(TODO("https://fxbug.dev/408500542"), "Loop device BLKRASET");
463                Ok(SUCCESS)
464            }
465            LOOP_SET_FD => {
466                let fd = arg.into();
467                let backing_file = current_task.files().get(fd)?;
468                let mut state = self.device.state.lock();
469                state.set_backing_file(current_task, backing_file)?;
470                Ok(SUCCESS)
471            }
472            LOOP_CLR_FD => {
473                let mut state = self.device.state.lock();
474                state.check_bound()?;
475                *state = Default::default();
476                Ok(SUCCESS)
477            }
478            LOOP_SET_STATUS => {
479                let modifiable_flags = LoopDeviceFlags::AUTOCLEAR | LoopDeviceFlags::PARTSCAN;
480
481                let user_info = UserRef::<uapi::loop_info>::from(arg);
482                let info = current_task.read_object(user_info)?;
483                let flags = LoopDeviceFlags::from_bits_truncate(info.lo_flags as u32);
484                let encrypt_key_size = info.lo_encrypt_key_size.clamp(0, LO_KEY_SIZE as i32);
485                let mut state = self.device.state.lock();
486                state.check_bound()?;
487                state.flags = (state.flags & !modifiable_flags) | (flags & modifiable_flags);
488                state.encrypt_type = info.lo_encrypt_type as u32;
489                state.encrypt_key = info.lo_encrypt_key[0..(encrypt_key_size as usize)].to_owned();
490                state.init = info.lo_init;
491                state.offset = info.lo_offset as u64;
492                std::mem::drop(state);
493                Ok(SUCCESS)
494            }
495            LOOP_GET_STATUS => {
496                let user_info = UserRef::<uapi::loop_info>::from(arg);
497                let node = file.node();
498                let rdev = node.info().rdev;
499                let state = self.device.state.lock();
500                state.check_bound()?;
501                let info = loop_info {
502                    lo_number: self.device.number as i32,
503                    lo_device: node.dev().bits() as __kernel_old_dev_t,
504                    lo_inode: node.ino,
505                    lo_rdevice: rdev.bits() as __kernel_old_dev_t,
506                    lo_offset: state.offset as i32,
507                    lo_encrypt_type: state.encrypt_type as i32,
508                    lo_flags: state.flags.bits() as i32,
509                    lo_init: state.init,
510                    ..Default::default()
511                };
512                std::mem::drop(state);
513                current_task.write_object(user_info, &info)?;
514                Ok(SUCCESS)
515            }
516            LOOP_CHANGE_FD => {
517                let fd = arg.into();
518                let backing_file = current_task.files().get(fd)?;
519                let mut state = self.device.state.lock();
520                if let Some(_existing_file) = &state.backing_file {
521                    // https://man7.org/linux/man-pages/man4/loop.4.html says:
522                    //
523                    //   This operation is possible only if the loop device is read-only and the
524                    //   new backing store is the same size and type as the old backing store.
525                    if !state.flags.contains(LoopDeviceFlags::READ_ONLY) {
526                        return error!(EINVAL);
527                    }
528                    track_stub!(
529                        TODO("https://fxbug.dev/322874313"),
530                        "check backing store size before change loop fd"
531                    );
532                    state.backing_file = Some(backing_file);
533                    Ok(SUCCESS)
534                } else {
535                    error!(EINVAL)
536                }
537            }
538            LOOP_SET_CAPACITY => {
539                let mut state = self.device.state.lock();
540                state.check_bound()?;
541                state.update_size_limit(current_task)?;
542                Ok(SUCCESS)
543            }
544            LOOP_SET_DIRECT_IO => {
545                track_stub!(TODO("https://fxbug.dev/322873418"), "Loop device LOOP_SET_DIRECT_IO");
546                error!(ENOTTY)
547            }
548            LOOP_SET_BLOCK_SIZE => {
549                let block_size = arg.into();
550                check_block_size(block_size)?;
551                let mut state = self.device.state.lock();
552                state.check_bound()?;
553                state.block_size = block_size;
554                Ok(SUCCESS)
555            }
556            LOOP_CONFIGURE => {
557                let user_config = UserRef::<uapi::loop_config>::from(arg);
558                let config = current_task.read_object(user_config)?;
559                let fd = FdNumber::from_raw(config.fd as i32);
560                check_block_size(config.block_size)?;
561                let mut state = self.device.state.lock();
562                if let Ok(backing_file) = current_task.files().get(fd) {
563                    state.set_backing_file(current_task, backing_file)?;
564                }
565                state.block_size = config.block_size;
566                state.set_info(&config.info);
567                std::mem::drop(state);
568                Ok(SUCCESS)
569            }
570            LOOP_SET_STATUS64 => {
571                let user_info = UserRef::<uapi::loop_info64>::from(arg);
572                let info = current_task.read_object(user_info)?;
573                let mut state = self.device.state.lock();
574                state.check_bound()?;
575                state.set_info(&info);
576                std::mem::drop(state);
577                Ok(SUCCESS)
578            }
579            LOOP_GET_STATUS64 => {
580                let user_info = UserRef::<uapi::loop_info64>::from(arg);
581                let node = file.node();
582                let rdev = node.info().rdev;
583                let state = self.device.state.lock();
584                state.check_bound()?;
585                let info = loop_info64 {
586                    lo_device: node.dev().bits(),
587                    lo_inode: node.ino,
588                    lo_rdevice: rdev.bits(),
589                    lo_offset: state.offset as u64,
590                    lo_sizelimit: state.size_limit,
591                    lo_number: self.device.number,
592                    lo_encrypt_type: state.encrypt_type,
593                    lo_flags: state.flags.bits(),
594                    lo_init: state.init,
595                    ..Default::default()
596                };
597                std::mem::drop(state);
598                current_task.write_object(user_info, &info)?;
599                Ok(SUCCESS)
600            }
601            _ => error!(ENOTTY),
602        }
603    }
604}
605
606pub fn loop_device_init(kernel: &Kernel) -> Result<(), Errno> {
607    // Device registry.
608    kernel
609        .device_registry
610        .register_major("loop".into(), DeviceMode::Block, LOOP_MAJOR, get_or_create_loop_device)
611        .expect("loop device register failed.");
612
613    // Ensure initial loop devices.
614    kernel.expando.get::<LoopDeviceRegistry>().ensure_initial_devices(kernel)
615}
616
617#[derive(Debug, Default)]
618pub struct LoopDeviceRegistry {
619    devices: LockDepMutex<BTreeMap<u32, Arc<LoopDevice>>, LoopDevicesLock>,
620}
621
622impl LoopDeviceRegistry {
623    /// Ensure initial loop devices.
624    fn ensure_initial_devices(&self, kernel: &Kernel) -> Result<(), Errno> {
625        for minor in 0..8 {
626            self.get_or_create(kernel, minor)?;
627        }
628        Ok(())
629    }
630
631    fn get(&self, minor: u32) -> Result<Arc<LoopDevice>, Errno> {
632        self.devices.lock().get(&minor).ok_or_else(|| errno!(ENODEV)).cloned()
633    }
634
635    fn get_or_create<'a>(&self, kernel: &Kernel, minor: u32) -> Result<Arc<LoopDevice>, Errno> {
636        self.devices
637            .lock()
638            .entry(minor)
639            .or_insert_with_fallible(|| LoopDevice::new(kernel, minor))
640            .cloned()
641    }
642
643    fn find(&self, current_task: &CurrentTask) -> Result<u32, Errno> {
644        let mut devices = self.devices.lock();
645        for minor in 0..u32::MAX {
646            match devices.entry(minor) {
647                Entry::Vacant(e) => {
648                    e.insert(LoopDevice::new(current_task.kernel(), minor)?);
649                    return Ok(minor);
650                }
651                Entry::Occupied(e) => {
652                    if !e.get().is_bound() {
653                        return Ok(minor);
654                    }
655                }
656            }
657        }
658        error!(ENODEV)
659    }
660
661    fn add(&self, current_task: &CurrentTask, minor: u32) -> Result<(), Errno> {
662        match self.devices.lock().entry(minor) {
663            Entry::Vacant(e) => {
664                e.insert(LoopDevice::new(current_task.kernel(), minor)?);
665                Ok(())
666            }
667            Entry::Occupied(_) => {
668                error!(EEXIST)
669            }
670        }
671    }
672
673    fn remove(
674        &self,
675        current_task: &CurrentTask,
676        k_device: Option<Device>,
677        minor: u32,
678    ) -> Result<(), Errno> {
679        match self.devices.lock().entry(minor) {
680            Entry::Vacant(_) => Ok(()),
681            Entry::Occupied(e) => {
682                if e.get().is_bound() {
683                    return error!(EBUSY);
684                }
685                e.remove();
686                let kernel = current_task.kernel();
687                let registry = &kernel.device_registry;
688                if let Some(dev) = &k_device {
689                    registry.remove_device(current_task, dev.clone());
690                } else {
691                    return error!(EINVAL);
692                }
693                Ok(())
694            }
695        }
696    }
697}
698
699pub fn create_loop_control_device(
700    current_task: &CurrentTask,
701    _id: DeviceId,
702    _node: &NamespaceNode,
703    _flags: OpenFlags,
704) -> Result<Box<dyn FileOps>, Errno> {
705    Ok(Box::new(LoopControlDevice::new(current_task.kernel().expando.get::<LoopDeviceRegistry>())))
706}
707
708struct LoopControlDevice {
709    registry: Arc<LoopDeviceRegistry>,
710}
711
712impl LoopControlDevice {
713    pub fn new(registry: Arc<LoopDeviceRegistry>) -> Self {
714        Self { registry }
715    }
716}
717
718impl FileOps for LoopControlDevice {
719    fileops_impl_seekless!();
720    fileops_impl_dataless!();
721    fileops_impl_noop_sync!();
722
723    fn ioctl(
724        &self,
725        _file: &FileObject,
726        current_task: &CurrentTask,
727        request: u32,
728        arg: SyscallArg,
729    ) -> Result<SyscallResult, Errno> {
730        match request {
731            LOOP_CTL_GET_FREE => Ok(self.registry.find(current_task)?.into()),
732            LOOP_CTL_ADD => {
733                let minor = arg.into();
734                let registry = Arc::clone(&self.registry);
735                // Delegate to the system task to have the permission to create the loop device.
736                let closure = move |task: &CurrentTask| registry.add(task, minor);
737                let (result, req) = SpawnRequestBuilder::new()
738                    .with_debug_name("loop-control-add")
739                    .with_sync_closure(closure)
740                    .build_with_sync_result();
741                current_task.kernel().kthreads.spawner().spawn_from_request(req);
742
743                result()??;
744
745                Ok(minor.into())
746            }
747            LOOP_CTL_REMOVE => {
748                let minor = arg.into();
749                let device = self.registry.get(minor)?;
750                let k_device = {
751                    let state = device.state.lock();
752                    state.k_device.clone()
753                };
754                self.registry.remove(current_task, k_device, minor)?;
755                Ok(minor.into())
756            }
757            _ => error!(ENOTTY),
758        }
759    }
760}
761
762fn get_or_create_loop_device(
763    current_task: &CurrentTask,
764    id: DeviceId,
765    _node: &NamespaceNode,
766    _flags: OpenFlags,
767) -> Result<Box<dyn FileOps>, Errno> {
768    Ok(current_task
769        .kernel()
770        .expando
771        .get::<LoopDeviceRegistry>()
772        .get_or_create(current_task.kernel(), id.minor())?
773        .create_file_ops())
774}
775
776#[cfg(test)]
777mod tests {
778    use super::*;
779    use fidl::endpoints::Proxy;
780    use fidl_fuchsia_io as fio;
781    use starnix_core::fs::fuchsia::new_remote_file;
782    use starnix_core::testing::*;
783    use starnix_core::vfs::buffers::*;
784    use starnix_core::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource};
785    use starnix_core::vfs::{FdFlags, FsNodeOps};
786
787    #[derive(Clone)]
788    struct PassthroughTestFile(Vec<u8>);
789
790    impl PassthroughTestFile {
791        pub fn new_node(bytes: &[u8]) -> impl FsNodeOps {
792            DynamicFile::new_node(Self(bytes.to_owned()))
793        }
794    }
795
796    impl DynamicFileSource for PassthroughTestFile {
797        fn generate(
798            &self,
799            _current_task: &CurrentTask,
800            sink: &mut DynamicFileBuf,
801        ) -> Result<(), Errno> {
802            sink.write(&self.0);
803            Ok(())
804        }
805    }
806
807    fn bind_simple_loop_device(
808        current_task: &CurrentTask,
809        backing_file: FileHandle,
810        open_flags: OpenFlags,
811    ) -> FileHandle {
812        let backing_fd = current_task.add_file(backing_file, FdFlags::empty()).unwrap();
813
814        let loop_file = anon_test_file(
815            &current_task,
816            Box::new(LoopDeviceFile { device: Arc::new(LoopDevice::default()) }),
817            open_flags,
818        );
819
820        let config_addr = map_object_anywhere(
821            &current_task,
822            &uapi::loop_config {
823                block_size: MIN_BLOCK_SIZE,
824                fd: backing_fd.raw() as u32,
825                ..Default::default()
826            },
827        );
828        loop_file.ioctl(&current_task, LOOP_CONFIGURE, config_addr.into()).unwrap();
829
830        loop_file
831    }
832
833    #[::fuchsia::test]
834    async fn basic_read() {
835        spawn_kernel_and_run(async |current_task| {
836            let fs = create_testfs(&current_task.kernel());
837            let expected_contents = b"hello, world!";
838
839            let ops = PassthroughTestFile::new_node(expected_contents);
840            let backing_node = create_fs_node_for_testing(&fs, ops);
841            let file_ops = backing_node.create_file_ops(current_task, OpenFlags::RDONLY).unwrap();
842            let backing_file = anon_test_file(current_task, file_ops, OpenFlags::RDONLY);
843            let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
844
845            let mut buf = VecOutputBuffer::new(expected_contents.len());
846            loop_file.read(current_task, &mut buf).unwrap();
847
848            assert_eq!(buf.data(), expected_contents);
849        })
850        .await;
851    }
852
853    #[::fuchsia::test]
854    async fn offset_works() {
855        spawn_kernel_and_run(async |current_task| {
856            let fs = create_testfs(&current_task.kernel());
857            let ops = PassthroughTestFile::new_node(b"hello, world!");
858            let backing_node = create_fs_node_for_testing(&fs, ops);
859            let file_ops = backing_node.create_file_ops(current_task, OpenFlags::RDONLY).unwrap();
860            let backing_file = anon_test_file(current_task, file_ops, OpenFlags::RDONLY);
861            let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
862
863            let info_addr = map_object_anywhere(
864                current_task,
865                &uapi::loop_info64 { lo_offset: 3, ..Default::default() },
866            );
867            loop_file.ioctl(current_task, LOOP_SET_STATUS64, info_addr.into()).unwrap();
868
869            let mut buf = VecOutputBuffer::new(25);
870            loop_file.read(current_task, &mut buf).unwrap();
871
872            assert_eq!(buf.data(), b"lo, world!");
873        })
874        .await;
875    }
876
877    #[::fuchsia::test]
878    async fn basic_get_memory() {
879        let test_data_path = "/pkg/data/testfile.txt";
880        let expected_contents = std::fs::read(test_data_path).unwrap();
881
882        let txt_channel: zx::Channel =
883            fuchsia_fs::file::open_in_namespace(test_data_path, fio::PERM_READABLE)
884                .unwrap()
885                .into_channel()
886                .unwrap()
887                .into();
888
889        spawn_kernel_and_run(async move |current_task| {
890            let backing_file =
891                new_remote_file(current_task, txt_channel.into(), OpenFlags::RDONLY).unwrap();
892            let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
893
894            let memory = loop_file.get_memory(current_task, None, ProtectionFlags::READ).unwrap();
895            let size = memory.get_content_size();
896            let memory_contents = memory.read_to_vec(0, size).unwrap();
897            assert_eq!(memory_contents, expected_contents);
898        })
899        .await;
900    }
901
902    #[::fuchsia::test]
903    async fn get_memory_offset_and_size_limit_work() {
904        // VMO slice children require a page-aligned offset, so we need a file that's big enough to
905        // have multiple pages to support creating a child with a meaningful offset, our own
906        // binary should do the trick.
907        let test_data_path = std::env::args().next().unwrap();
908        let expected_offset = *PAGE_SIZE;
909        let expected_size_limit = *PAGE_SIZE;
910        let expected_contents = std::fs::read(&test_data_path).unwrap();
911        let expected_contents = expected_contents
912            [expected_offset as usize..(expected_offset + expected_size_limit) as usize]
913            .to_vec();
914
915        let txt_channel: zx::Channel =
916            fuchsia_fs::file::open_in_namespace(&test_data_path, fio::PERM_READABLE)
917                .unwrap()
918                .into_channel()
919                .unwrap()
920                .into();
921
922        spawn_kernel_and_run(async move |current_task| {
923            let backing_file =
924                new_remote_file(current_task, txt_channel.into(), OpenFlags::RDONLY).unwrap();
925            let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
926
927            let info_addr = map_object_anywhere(
928                current_task,
929                &uapi::loop_info64 {
930                    lo_offset: expected_offset,
931                    lo_sizelimit: expected_size_limit,
932                    ..Default::default()
933                },
934            );
935            loop_file.ioctl(current_task, LOOP_SET_STATUS64, info_addr.into()).unwrap();
936
937            let memory = loop_file.get_memory(current_task, None, ProtectionFlags::READ).unwrap();
938            let size = memory.get_content_size();
939            let memory_contents = memory.read_to_vec(0, size).unwrap();
940            assert_eq!(memory_contents, expected_contents);
941        })
942        .await;
943    }
944}