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