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