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