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::{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
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 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(¤t_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 .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 return Ok(0);
345 }
346 let mut cropped_buf;
347 let data = if offset + data.available() > limit {
348 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 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 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 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 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 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 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 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 ¤t_task,
816 Box::new(LoopDeviceFile { device: Arc::new(LoopDevice::default()) }),
817 open_flags,
818 );
819
820 let config_addr = map_object_anywhere(
821 ¤t_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(¤t_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(¤t_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(¤t_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 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}