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, get_sysfs};
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, FileToken, FsNodeOps, FsString,
20 InputBufferCallback, NamespaceNode, PeekBufferSegmentsCallback, 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::{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 device: &Arc<LoopDevice>,
99 backing_file: FileHandle,
100 ) -> Result<(), Errno> {
101 if self.backing_file.is_some() {
102 return error!(EBUSY);
103 }
104 let backing_stat = backing_file.node().stat(current_task)?;
105 self.backing_file = Some(backing_file);
106 self.size_limit = backing_stat.st_size as u64;
107 if let Some(sysfs_dir) = self.k_device.as_ref().and_then(Device::dir) {
108 let device_weak = Arc::downgrade(device);
109 sysfs_dir.edit(&get_sysfs(current_task.kernel()), |dir| {
110 dir.subdir("loop", 0o755, |dir| {
111 dir.entry(
112 "backing_file",
113 LoopDeviceBackingFile::new_node(device_weak),
114 mode!(IFREG, 0o644),
115 );
116 });
117 });
118 }
119 Ok(())
120 }
121
122 fn set_info(&mut self, info: &uapi::loop_info64) {
123 let encrypt_key_size = info.lo_encrypt_key_size.clamp(0, LO_KEY_SIZE);
124 self.offset = info.lo_offset;
125 self.size_limit = info.lo_sizelimit;
126 self.flags = LoopDeviceFlags::from_bits_truncate(info.lo_flags);
127 self.encrypt_type = info.lo_encrypt_type;
128 self.encrypt_key = info.lo_encrypt_key[0..(encrypt_key_size as usize)].to_owned();
129 self.init = info.lo_init;
130 }
131
132 fn update_size_limit(&mut self, current_task: &CurrentTask) -> Result<(), Errno> {
133 if let Some(backing_file) = &self.backing_file {
134 let backing_stat = backing_file.node().stat(current_task)?;
135 self.size_limit = backing_stat.st_size as u64;
136 }
137 Ok(())
138 }
139
140 fn set_k_device(&mut self, k_device: Device) {
141 self.k_device = Some(k_device);
142 }
143
144 fn clear(&mut self) {
145 if let Some(sysfs_dir) = self.k_device.as_ref().and_then(Device::dir) {
146 sysfs_dir.remove(b"loop".into());
147 }
148 *self = Self { k_device: self.k_device.take(), ..Default::default() };
149 }
150}
151
152#[derive(Debug, Default)]
153struct LoopDevice {
154 number: u32,
155 state: LockDepMutex<LoopDeviceState, LoopDeviceStateLock>,
156}
157
158struct LoopDeviceBackingFile(Weak<LoopDevice>);
159
160impl LoopDeviceBackingFile {
161 pub fn new_node(device: Weak<LoopDevice>) -> impl FsNodeOps {
162 BytesFile::new_node(Self(device))
163 }
164}
165
166impl BytesFileOps for LoopDeviceBackingFile {
167 fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
168 let mut path = self
169 .0
170 .upgrade()
171 .ok_or_else(|| errno!(EINVAL))?
172 .state
173 .lock()
174 .backing_file
175 .as_ref()
176 .ok_or_else(|| errno!(EINVAL))
177 .map(|file| file.name.to_passive().path(¤t_task.fs()))?;
178 path.push(b'\n');
179 Ok(Cow::Owned(path.into()))
180 }
181}
182
183impl LoopDevice {
184 fn new<'a>(kernel: &Kernel, minor: u32) -> Result<Arc<Self>, Errno> {
185 let registry = &kernel.device_registry;
186 let loop_device_name = FsString::from(format!("loop{minor}"));
187 let block_class = registry.objects.block_class();
188 let device = Arc::new(Self { number: minor, state: Default::default() });
189 let device_weak = Arc::<LoopDevice>::downgrade(&device);
190 let k_device = registry.add_major_device(
191 kernel,
192 loop_device_name.as_ref(),
193 DeviceMetadata::new(
194 loop_device_name.clone(),
195 DeviceId::new(LOOP_MAJOR, minor),
196 DeviceMode::Block,
197 )
198 .with_devtype("disk"),
201 None,
202 block_class,
203 |k_device, dir| build_block_device_directory(k_device, device_weak, dir),
204 );
205 {
206 let mut state = device.state.lock();
207 state.set_k_device(k_device);
208 }
209 Ok(device)
210 }
211
212 fn create_file_ops(self: &Arc<Self>) -> Box<dyn FileOps> {
213 Box::new(LoopDeviceFile { device: self.clone() })
214 }
215
216 fn backing_file(&self) -> Option<FileHandle> {
217 self.state.lock().backing_file.clone()
218 }
219
220 fn is_bound(&self) -> bool {
221 self.state.lock().backing_file.is_some()
222 }
223
224 fn offset_for_backing_file(&self, offset: usize) -> usize {
225 self.state.lock().offset.saturating_add(offset as u64) as usize
226 }
227}
228
229fn check_block_size(block_size: u32) -> Result<(), Errno> {
230 let page_size = *PAGE_SIZE as u32;
231 let mut allowed_size = MIN_BLOCK_SIZE;
232 while allowed_size <= page_size {
233 if block_size == allowed_size {
234 return Ok(());
235 }
236 allowed_size *= 2;
237 }
238 error!(EINVAL)
239}
240
241impl BlockDeviceInfo for LoopDevice {
242 fn size(&self) -> Result<usize, Errno> {
243 Ok(self.state.lock().size_limit as usize)
244 }
245}
246
247#[derive(Debug)]
248struct CroppedInputBuffer<'a> {
249 base: &'a mut dyn InputBuffer,
250 size: usize,
251 drained: bool,
252}
253
254impl<'a> CroppedInputBuffer<'a> {
255 fn new(base: &'a mut dyn InputBuffer, size: usize) -> Self {
256 debug_assert!(size <= base.bytes_read() + base.available());
257 CroppedInputBuffer { base, size, drained: false }
258 }
259}
260
261impl<'a> Buffer for CroppedInputBuffer<'a> {
262 fn segments_count(&self) -> Result<usize, Errno> {
263 error!(ENOTSUP)
264 }
265
266 fn peek_each_segment(
267 &mut self,
268 callback: &mut PeekBufferSegmentsCallback<'_>,
269 ) -> Result<(), Errno> {
270 let mut pos = 0;
271 self.base.peek_each_segment(&mut |buffer: &UserBuffer| {
272 if pos >= self.size {
273 return;
274 } else if pos + buffer.length > self.size {
275 let cropped_size = self.size - pos;
276 pos += buffer.length;
277 callback(&UserBuffer { address: buffer.address, length: cropped_size });
278 } else {
279 pos += buffer.length;
280 callback(buffer);
281 }
282 })
283 }
284}
285
286impl<'a> InputBuffer for CroppedInputBuffer<'a> {
287 fn peek_each(&mut self, callback: &mut InputBufferCallback<'_>) -> Result<usize, Errno> {
288 if self.drained {
289 return Ok(0);
290 }
291 let mut pos = self.base.bytes_read();
292 self.base.peek_each(&mut |buf: &[u8]| {
293 if pos >= self.size {
294 return Ok(0);
295 }
296 let size = std::cmp::min(buf.len(), self.size - pos);
297 pos += size;
298 callback(&buf[..size])
299 })
300 }
301 fn advance(&mut self, length: usize) -> Result<(), Errno> {
302 if length > self.available() {
303 return error!(EINVAL);
304 }
305 self.base.advance(length)
306 }
307 fn available(&self) -> usize {
308 if self.drained || self.size < self.bytes_read() {
309 0
310 } else {
311 self.size - self.bytes_read()
312 }
313 }
314 fn bytes_read(&self) -> usize {
315 self.base.bytes_read()
316 }
317 fn drain(&mut self) -> usize {
318 let size = self.available();
319 self.drained = true;
320 size
321 }
322}
323
324struct LoopDeviceFile {
325 device: Arc<LoopDevice>,
326}
327
328impl FileOps for LoopDeviceFile {
329 fileops_impl_seekable!();
330
331 fn read(
332 &self,
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(current_task, self.device.offset_for_backing_file(offset), data)
340 } else {
341 Ok(0)
342 }
343 }
344
345 fn write(
346 &self,
347 _file: &FileObject,
348 current_task: &CurrentTask,
349 offset: usize,
350 data: &mut dyn InputBuffer,
351 ) -> Result<usize, Errno> {
352 if let Some(backing_file) = self.device.backing_file() {
353 let limit = self.device.state.lock().size_limit as usize;
354 if offset >= limit {
355 return Ok(0);
357 }
358 let mut cropped_buf;
359 let data = if offset + data.available() > limit {
360 let bytes_to_write = limit - offset;
363 let cropped_size = data.bytes_read() + bytes_to_write;
364 cropped_buf = CroppedInputBuffer::new(data, cropped_size);
365 &mut cropped_buf
366 } else {
367 data
368 };
369 let r = backing_file.write_at(
370 current_task,
371 self.device.offset_for_backing_file(offset),
372 data,
373 );
374 r
375 } else {
376 error!(ENOSPC)
377 }
378 }
379
380 fn get_memory(
381 &self,
382 _file: &FileObject,
383 current_task: &CurrentTask,
384 requested_length: Option<usize>,
385 prot: ProtectionFlags,
386 ) -> Result<Arc<MemoryObject>, Errno> {
387 let backing_file = self.device.backing_file().ok_or_else(|| errno!(EBADF))?;
388
389 let state = self.device.state.lock();
390 let configured_offset = state.offset;
391 let configured_size_limit = match state.size_limit {
392 0 => None,
394 n => Some(n),
395 };
396
397 let backing_memory = backing_file.get_memory(
398 current_task,
399 requested_length.map(|l| l + configured_offset as usize),
400 prot,
401 )?;
402 let backing_memory_size = backing_memory.get_size();
403
404 let clone_len = backing_memory_size
405 .min(configured_size_limit.unwrap_or(u64::MAX))
406 .min(requested_length.unwrap_or(usize::MAX) as u64);
407
408 let backing_content_size = backing_memory.get_content_size();
409
410 let mem = if backing_content_size < clone_len {
411 if backing_file.can_write() {
415 track_stub!(
416 TODO("https://fxbug.dev/408048145"),
417 "Loop device mutable loop backing files with smaller content"
418 );
419 return error!(EINVAL);
420 }
421 let memory_clone = backing_memory
422 .create_child(
423 VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE,
424 configured_offset,
425 clone_len,
426 )
427 .map_err(|e| errno!(EINVAL, e))?;
428 let new_content_size = backing_content_size.saturating_sub(configured_offset);
429 memory_clone.set_content_size(new_content_size).map_err(|e| errno!(EINVAL, e))?;
430 memory_clone
431 } else {
432 backing_memory
433 .create_child(VmoChildOptions::SLICE, configured_offset, clone_len)
434 .map_err(|e| errno!(EINVAL, e))?
435 };
436 Ok(Arc::new(mem))
437 }
438
439 fn get_file_token(
440 &self,
441 _file: &FileObject,
442 current_task: &CurrentTask,
443 ) -> Result<FileToken, Errno> {
444 let backing_file = self.device.backing_file().ok_or_else(|| errno!(EBADF))?;
445 let configured_offset = self.device.state.lock().offset;
446 backing_file.get_file_token(current_task)?.add_offset(configured_offset)
447 }
448
449 fn sync(&self, _file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
450 if let Some(f) = self.device.backing_file() { f.sync(current_task) } else { Ok(()) }
451 }
452
453 fn ioctl(
454 &self,
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(current_task, &self.device, backing_file)?;
492 Ok(SUCCESS)
493 }
494 LOOP_CLR_FD => {
495 let mut state = self.device.state.lock();
496 state.check_bound()?;
497 state.clear();
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(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(current_task, &self.device, 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 _ => error!(ENOTTY),
624 }
625 }
626}
627
628pub fn loop_device_init(kernel: &Kernel) -> Result<(), Errno> {
629 kernel
631 .device_registry
632 .register_major("loop".into(), DeviceMode::Block, LOOP_MAJOR, get_or_create_loop_device)
633 .expect("loop device register failed.");
634
635 kernel.expando.get::<LoopDeviceRegistry>().ensure_initial_devices(kernel)
637}
638
639#[derive(Debug, Default)]
640pub struct LoopDeviceRegistry {
641 devices: LockDepMutex<BTreeMap<u32, Arc<LoopDevice>>, LoopDevicesLock>,
642}
643
644impl LoopDeviceRegistry {
645 fn ensure_initial_devices(&self, kernel: &Kernel) -> Result<(), Errno> {
647 for minor in 0..8 {
648 self.get_or_create(kernel, minor)?;
649 }
650 Ok(())
651 }
652
653 #[cfg(test)]
654 fn get(&self, minor: u32) -> Result<Arc<LoopDevice>, Errno> {
655 self.devices.lock().get(&minor).ok_or_else(|| errno!(ENODEV)).cloned()
656 }
657
658 fn get_or_create<'a>(&self, kernel: &Kernel, minor: u32) -> Result<Arc<LoopDevice>, Errno> {
659 self.devices
660 .lock()
661 .entry(minor)
662 .or_insert_with_fallible(|| LoopDevice::new(kernel, minor))
663 .cloned()
664 }
665
666 fn find(&self, current_task: &CurrentTask) -> Result<u32, Errno> {
667 let mut devices = self.devices.lock();
668 for minor in 0..u32::MAX {
669 match devices.entry(minor) {
670 Entry::Vacant(e) => {
671 e.insert(LoopDevice::new(current_task.kernel(), minor)?);
672 return Ok(minor);
673 }
674 Entry::Occupied(e) => {
675 if !e.get().is_bound() {
676 return Ok(minor);
677 }
678 }
679 }
680 }
681 error!(ENODEV)
682 }
683
684 fn add(&self, current_task: &CurrentTask, minor: u32) -> Result<(), Errno> {
685 match self.devices.lock().entry(minor) {
686 Entry::Vacant(e) => {
687 e.insert(LoopDevice::new(current_task.kernel(), minor)?);
688 Ok(())
689 }
690 Entry::Occupied(_) => {
691 error!(EEXIST)
692 }
693 }
694 }
695
696 fn remove(&self, current_task: &CurrentTask, minor: u32) -> Result<(), Errno> {
697 match self.devices.lock().entry(minor) {
698 Entry::Vacant(_) => error!(ENODEV),
699 Entry::Occupied(e) => {
700 let dev = {
701 let mut state = e.get().state.lock();
702 if state.backing_file.is_some() {
703 return error!(EBUSY);
704 }
705 state.k_device.take().ok_or_else(|| errno!(EINVAL))?
706 };
707 e.remove();
708 let kernel = current_task.kernel();
709 let registry = &kernel.device_registry;
710 registry.remove_device(current_task, dev);
711 Ok(())
712 }
713 }
714 }
715}
716
717pub fn create_loop_control_device(
718 current_task: &CurrentTask,
719 _id: DeviceId,
720 _node: &NamespaceNode,
721 _flags: OpenFlags,
722) -> Result<Box<dyn FileOps>, Errno> {
723 Ok(Box::new(LoopControlDevice::new(current_task.kernel().expando.get::<LoopDeviceRegistry>())))
724}
725
726struct LoopControlDevice {
727 registry: Arc<LoopDeviceRegistry>,
728}
729
730impl LoopControlDevice {
731 pub fn new(registry: Arc<LoopDeviceRegistry>) -> Self {
732 Self { registry }
733 }
734}
735
736impl FileOps for LoopControlDevice {
737 fileops_impl_seekless!();
738 fileops_impl_dataless!();
739 fileops_impl_noop_sync!();
740
741 fn ioctl(
742 &self,
743 _file: &FileObject,
744 current_task: &CurrentTask,
745 request: u32,
746 arg: SyscallArg,
747 ) -> Result<SyscallResult, Errno> {
748 match request {
749 LOOP_CTL_GET_FREE => Ok(self.registry.find(current_task)?.into()),
750 LOOP_CTL_ADD => {
751 let minor = arg.into();
752 let registry = Arc::clone(&self.registry);
753 let closure = move |task: &CurrentTask| registry.add(task, minor);
755 let (result, req) = SpawnRequestBuilder::new()
756 .with_debug_name("loop-control-add")
757 .with_sync_closure(closure)
758 .build_with_sync_result();
759 current_task.kernel().kthreads.spawner().spawn_from_request(req);
760
761 result()??;
762
763 Ok(minor.into())
764 }
765 LOOP_CTL_REMOVE => {
766 let minor = arg.into();
767 self.registry.remove(current_task, minor)?;
768 Ok(minor.into())
769 }
770 _ => error!(ENOTTY),
771 }
772 }
773}
774
775fn get_or_create_loop_device(
776 current_task: &CurrentTask,
777 id: DeviceId,
778 _node: &NamespaceNode,
779 _flags: OpenFlags,
780) -> Result<Box<dyn FileOps>, Errno> {
781 Ok(current_task
782 .kernel()
783 .expando
784 .get::<LoopDeviceRegistry>()
785 .get_or_create(current_task.kernel(), id.minor())?
786 .create_file_ops())
787}
788
789#[cfg(test)]
790mod tests {
791 use super::*;
792 use fidl::endpoints::Proxy;
793 use fidl_fuchsia_io as fio;
794 use starnix_core::fs::fuchsia::new_remote_file;
795 use starnix_core::testing::*;
796 use starnix_core::vfs::buffers::*;
797 use starnix_core::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource};
798 use starnix_core::vfs::{FdFlags, FsNodeOps};
799
800 #[derive(Clone)]
801 struct PassthroughTestFile(Vec<u8>);
802
803 impl PassthroughTestFile {
804 pub fn new_node(bytes: &[u8]) -> impl FsNodeOps {
805 DynamicFile::new_node(Self(bytes.to_owned()))
806 }
807 }
808
809 impl DynamicFileSource for PassthroughTestFile {
810 fn generate(
811 &self,
812 _current_task: &CurrentTask,
813 sink: &mut DynamicFileBuf,
814 ) -> Result<(), Errno> {
815 sink.write(&self.0);
816 Ok(())
817 }
818 }
819
820 fn bind_simple_loop_device(
821 current_task: &CurrentTask,
822 backing_file: FileHandle,
823 open_flags: OpenFlags,
824 ) -> FileHandle {
825 let backing_fd = current_task.add_file(backing_file, FdFlags::empty()).unwrap();
826
827 let loop_file = anon_test_file(
828 ¤t_task,
829 Box::new(LoopDeviceFile { device: Arc::new(LoopDevice::default()) }),
830 open_flags,
831 );
832
833 let config_addr = map_object_anywhere(
834 ¤t_task,
835 &uapi::loop_config {
836 block_size: MIN_BLOCK_SIZE,
837 fd: backing_fd.raw() as u32,
838 ..Default::default()
839 },
840 );
841 loop_file.ioctl(¤t_task, LOOP_CONFIGURE, config_addr.into()).unwrap();
842
843 loop_file
844 }
845
846 #[::fuchsia::test]
847 async fn basic_read() {
848 spawn_kernel_and_run(async |current_task| {
849 let fs = create_testfs(¤t_task.kernel());
850 let expected_contents = b"hello, world!";
851
852 let ops = PassthroughTestFile::new_node(expected_contents);
853 let backing_node = create_fs_node_for_testing(&fs, ops);
854 let file_ops = backing_node.create_file_ops(current_task, OpenFlags::RDONLY).unwrap();
855 let backing_file = anon_test_file(current_task, file_ops, OpenFlags::RDONLY);
856 let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
857
858 let mut buf = VecOutputBuffer::new(expected_contents.len());
859 loop_file.read(current_task, &mut buf).unwrap();
860
861 assert_eq!(buf.data(), expected_contents);
862 })
863 .await;
864 }
865
866 #[::fuchsia::test]
867 async fn offset_works() {
868 spawn_kernel_and_run(async |current_task| {
869 let fs = create_testfs(¤t_task.kernel());
870 let ops = PassthroughTestFile::new_node(b"hello, world!");
871 let backing_node = create_fs_node_for_testing(&fs, ops);
872 let file_ops = backing_node.create_file_ops(current_task, OpenFlags::RDONLY).unwrap();
873 let backing_file = anon_test_file(current_task, file_ops, OpenFlags::RDONLY);
874 let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
875
876 let info_addr = map_object_anywhere(
877 current_task,
878 &uapi::loop_info64 { lo_offset: 3, ..Default::default() },
879 );
880 loop_file.ioctl(current_task, LOOP_SET_STATUS64, info_addr.into()).unwrap();
881
882 let mut buf = VecOutputBuffer::new(25);
883 loop_file.read(current_task, &mut buf).unwrap();
884
885 assert_eq!(buf.data(), b"lo, world!");
886 })
887 .await;
888 }
889
890 #[::fuchsia::test]
891 async fn basic_get_memory() {
892 let test_data_path = "/pkg/data/testfile.txt";
893 let expected_contents = std::fs::read(test_data_path).unwrap();
894
895 let txt_channel: zx::Channel =
896 fuchsia_fs::file::open_in_namespace(test_data_path, fio::PERM_READABLE)
897 .unwrap()
898 .into_channel()
899 .unwrap()
900 .into();
901
902 spawn_kernel_and_run(async move |current_task| {
903 let backing_file =
904 new_remote_file(current_task, txt_channel.into(), OpenFlags::RDONLY).unwrap();
905 let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
906
907 let memory = loop_file.get_memory(current_task, None, ProtectionFlags::READ).unwrap();
908 let size = memory.get_content_size();
909 let memory_contents = memory.read_to_vec(0, size).unwrap();
910 assert_eq!(memory_contents, expected_contents);
911 })
912 .await;
913 }
914
915 #[::fuchsia::test]
916 async fn get_memory_offset_and_size_limit_work() {
917 let test_data_path = std::env::args().next().unwrap();
921 let expected_offset = *PAGE_SIZE;
922 let expected_size_limit = *PAGE_SIZE;
923 let expected_contents = std::fs::read(&test_data_path).unwrap();
924 let expected_contents = expected_contents
925 [expected_offset as usize..(expected_offset + expected_size_limit) as usize]
926 .to_vec();
927
928 let txt_channel: zx::Channel =
929 fuchsia_fs::file::open_in_namespace(&test_data_path, fio::PERM_READABLE)
930 .unwrap()
931 .into_channel()
932 .unwrap()
933 .into();
934
935 spawn_kernel_and_run(async move |current_task| {
936 let backing_file =
937 new_remote_file(current_task, txt_channel.into(), OpenFlags::RDONLY).unwrap();
938 let loop_file = bind_simple_loop_device(current_task, backing_file, OpenFlags::RDONLY);
939
940 let info_addr = map_object_anywhere(
941 current_task,
942 &uapi::loop_info64 {
943 lo_offset: expected_offset,
944 lo_sizelimit: expected_size_limit,
945 ..Default::default()
946 },
947 );
948 loop_file.ioctl(current_task, LOOP_SET_STATUS64, info_addr.into()).unwrap();
949
950 let memory = loop_file.get_memory(current_task, None, ProtectionFlags::READ).unwrap();
951 let size = memory.get_content_size();
952 let memory_contents = memory.read_to_vec(0, size).unwrap();
953 assert_eq!(memory_contents, expected_contents);
954 })
955 .await;
956 }
957
958 #[::fuchsia::test]
959 async fn remove_without_k_device_preserves_registry_entry() {
960 spawn_kernel_and_run(async |current_task| {
961 let registry = LoopDeviceRegistry::default();
962 let minor = 42;
963 registry
964 .devices
965 .lock()
966 .insert(minor, Arc::new(LoopDevice { number: minor, state: Default::default() }));
967
968 assert_eq!(registry.remove(current_task, minor), error!(EINVAL));
969 assert!(registry.get(minor).is_ok());
970 })
971 .await;
972 }
973}