1use crate::device::mem::new_null_file;
6use crate::execution::{
7 create_init_child_process, create_init_process, create_system_task,
8 execute_task_with_prerun_result,
9};
10use crate::fs::fuchsia::RemoteFs;
11use crate::fs::tmpfs::TmpFs;
12use crate::mm::syscalls::{do_mmap, sys_mremap};
13use crate::mm::{MemoryAccessor, MemoryAccessorExt, MemoryManager, PAGE_SIZE};
14use crate::security;
15use crate::task::container_namespace::ContainerNamespace;
16use crate::task::{
17 CurrentTask, ExitStatus, Kernel, KernelFeatures, SchedulerManager, SystemLimits, Task,
18 TaskBuilder,
19};
20use crate::vfs::buffers::{InputBuffer, OutputBuffer};
21use crate::vfs::{
22 Anon, CacheMode, DirEntry, FdNumber, FileHandle, FileObject, FileOps, FileSystem,
23 FileSystemHandle, FileSystemOps, FileSystemOptions, FsContext, FsNode, FsNodeFlags,
24 FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, Namespace, NamespaceNode, fileops_impl_nonseekable,
25 fileops_impl_noop_sync, fs_node_impl_not_dir,
26};
27use fidl_fuchsia_io as fio;
28use fuchsia_async as fasync;
29use fuchsia_async::LocalExecutor;
30use selinux::SecurityServer;
31use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Unlocked};
32use starnix_syscalls::{SyscallArg, SyscallResult};
33use starnix_task_command::TaskCommand;
34use starnix_types::arch::ArchWidth;
35use starnix_types::vfs::default_statfs;
36use starnix_uapi::auth::{Credentials, FsCred};
37use starnix_uapi::errors::Errno;
38use starnix_uapi::file_mode::mode;
39use starnix_uapi::open_flags::OpenFlags;
40use starnix_uapi::user_address::{ArchSpecific, UserAddress};
41use starnix_uapi::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE, errno, error, statfs};
42use std::ffi::CString;
43use std::future::Future;
44use std::mem::MaybeUninit;
45use std::ops::Deref;
46use std::sync::{Arc, mpsc};
47use zerocopy::{Immutable, IntoBytes};
48
49fn create_pkgfs<L>(locked: &mut Locked<L>, kernel: &Kernel) -> FileSystemHandle
53where
54 L: LockEqualOrBefore<FileOpsCore>,
55{
56 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
57 let (server, client) = zx::Channel::create();
58 fdio::open("/pkg", rights, server).expect("failed to open /pkg");
59 RemoteFs::new_fs(
60 locked,
61 kernel,
62 client,
63 FileSystemOptions { source: "/pkg".into(), ..Default::default() },
64 rights,
65 )
66 .unwrap()
67}
68
69pub fn spawn_kernel_and_run<F, R>(callback: F) -> impl Future<Output = R>
74where
75 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
76 R: Send + Sync + 'static,
77{
78 spawn_kernel_and_run_internal(callback, None, TmpFs::new_fs, KernelFeatures::default())
79}
80
81pub fn spawn_kernel_with_features_and_run<F, R>(
83 callback: F,
84 features: KernelFeatures,
85) -> impl Future<Output = R>
86where
87 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
88 R: Send + Sync + 'static,
89{
90 spawn_kernel_and_run_internal(callback, None, TmpFs::new_fs, features)
91}
92
93pub fn spawn_kernel_and_run_sync<F, R>(callback: F) -> impl Future<Output = R>
98where
99 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
100 R: Send + Sync + 'static,
101{
102 spawn_kernel_and_run_internal_sync(
103 callback,
104 None,
105 TmpFs::new_fs,
106 KernelFeatures::default(),
107 SchedulerManager::empty_for_tests(),
108 )
109}
110
111pub fn spawn_kernel_with_scheduler_and_run_sync<F, R>(
113 scheduler: SchedulerManager,
114 callback: F,
115) -> impl Future<Output = R>
116where
117 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
118 R: Send + Sync + 'static,
119{
120 spawn_kernel_and_run_internal_sync(
121 callback,
122 None,
123 TmpFs::new_fs,
124 KernelFeatures::default(),
125 scheduler,
126 )
127}
128
129pub fn spawn_kernel_and_run_with_pkgfs<F, R>(callback: F) -> impl Future<Output = R>
135where
136 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
137 R: Send + Sync + 'static,
138{
139 spawn_kernel_and_run_internal(callback, None, create_pkgfs, KernelFeatures::default())
140}
141
142pub async fn spawn_kernel_with_selinux_and_run<F, R>(callback: F) -> R
148where
149 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask, &Arc<SecurityServer>) -> R
150 + Send
151 + Sync
152 + 'static,
153 R: Send + Sync + 'static,
154{
155 let security_server = SecurityServer::new_default();
156 let security_server_for_callback = security_server.clone();
157 spawn_kernel_and_run_internal(
158 async move |unlocked, current_task| {
159 security::selinuxfs_init_null(
160 current_task,
161 &new_null_file(unlocked, current_task, OpenFlags::empty()),
162 );
163 callback(unlocked, current_task, &security_server_for_callback).await
164 },
165 Some(security_server),
166 TmpFs::new_fs,
167 KernelFeatures::default(),
168 )
169 .await
170}
171
172fn spawn_kernel_and_run_internal<F, FS, R>(
175 callback: F,
176 security_server: Option<Arc<SecurityServer>>,
177 fs_factory: FS,
178 features: KernelFeatures,
179) -> impl Future<Output = R>
180where
181 R: Send + Sync + 'static,
182 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
183 FS: FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
184{
185 spawn_kernel_and_run_internal_sync(
186 move |locked, current_task| {
187 LocalExecutor::default().run_singlethreaded(callback(locked, current_task))
188 },
189 security_server,
190 fs_factory,
191 features,
192 SchedulerManager::empty_for_tests(),
193 )
194}
195
196fn spawn_kernel_and_run_internal_sync<F, FS, R>(
199 callback: F,
200 security_server: Option<Arc<SecurityServer>>,
201 fs_factory: FS,
202 features: KernelFeatures,
203 scheduler: SchedulerManager,
204) -> impl Future<Output = R>
205where
206 R: Send + Sync + 'static,
207 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
208 FS: FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
209{
210 #[allow(
211 clippy::undocumented_unsafe_blocks,
212 reason = "Force documented unsafe blocks in Starnix"
213 )]
214 let locked = unsafe { Unlocked::new() };
215 let kernel = create_test_kernel(locked, security_server, features, scheduler);
216 let fs = create_test_fs_context(locked, &kernel, fs_factory);
217 let init_task = create_test_init_task(locked, &kernel, fs);
218 fasync::unblock(move || {
219 let (sender, receiver) = mpsc::sync_channel(1);
220 let error = execute_task_with_prerun_result(
221 locked,
222 init_task,
223 move |locked, current_task| -> Result<(), Errno> {
224 let result = callback(locked, current_task);
225 current_task.write().set_exit_status_if_not_already(ExitStatus::Exit(0));
226 sender.send(result).map_err(|e| errno!(EIO, e))?;
227 error!(EHWPOISON)
228 },
229 |_| {},
230 None,
231 )
232 .unwrap_err();
233 assert_eq!(error, errno!(EHWPOISON));
235 receiver.recv().expect("recv")
236 })
237}
238
239#[deprecated = "Do not add new callers, use spawn_kernel_and_run() instead."]
240pub fn create_kernel_task_and_unlocked()
241-> (Arc<Kernel>, AutoReleasableTask, &'static mut Locked<Unlocked>) {
242 create_kernel_task_and_unlocked_with_fs(TmpFs::new_fs)
243}
244
245fn create_test_kernel(
246 _locked: &mut Locked<Unlocked>,
247 security_server: Option<Arc<SecurityServer>>,
248 features: KernelFeatures,
249 scheduler: SchedulerManager,
250) -> Arc<Kernel> {
251 Kernel::new(
252 b"".into(),
253 features,
254 SystemLimits::default(),
255 ContainerNamespace::new(),
256 scheduler,
257 None,
258 fuchsia_inspect::Node::default(),
259 security::testing::kernel_state(security_server),
260 None,
261 None,
262 )
263 .expect("failed to create kernel")
264}
265
266fn create_test_fs_context(
267 locked: &mut Locked<Unlocked>,
268 kernel: &Kernel,
269 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
270) -> Arc<FsContext> {
271 FsContext::new(Namespace::new(create_fs(locked, kernel)))
272}
273
274fn create_test_mm(task: &Task) -> Result<Arc<MemoryManager>, Errno> {
276 let arch_width = ArchWidth::Arch64;
277 let mm =
278 MemoryManager::new_for_test(task.thread_group().root_vmar.unowned(), ArchWidth::Arch64);
279 let fake_executable_addr = mm.get_random_base_for_executable(arch_width, 0)?;
280 mm.initialize_brk_origin(arch_width, fake_executable_addr)?;
281 task.running_state()?.mm.update(Some(mm.clone()));
282 Ok(mm)
283}
284
285fn create_test_init_task(
286 locked: &mut Locked<Unlocked>,
287 kernel: &Kernel,
288 fs: Arc<FsContext>,
289) -> TaskBuilder {
290 let init_pid = kernel.pids.write().allocate_pid();
291 assert_eq!(init_pid, 1);
292 let init_task = create_init_process(
293 locked,
294 &kernel.weak_self.upgrade().unwrap(),
295 init_pid,
296 TaskCommand::new(b"test-task"),
297 fs.fork(),
298 &[],
299 )
300 .expect("failed to create first task");
301 create_test_mm(&init_task).expect("failed to create MM");
302
303 let system_task = create_system_task(locked, &kernel.weak_self.upgrade().unwrap(), fs)
304 .expect("create system task");
305 kernel.kthreads.init(system_task).expect("failed to initialize kthreads");
306
307 let system_task = kernel.kthreads.system_task();
308 kernel.hrtimer_manager.init(&system_task).expect("init hrtimer manager worker thread");
309
310 {
313 let _l1 = init_task.thread_group().read();
314 let _l2 = init_task.read();
315 }
316 init_task
317}
318
319fn create_kernel_task_and_unlocked_with_fs(
320 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
321) -> (Arc<Kernel>, AutoReleasableTask, &'static mut Locked<Unlocked>) {
322 #[allow(
323 clippy::undocumented_unsafe_blocks,
324 reason = "Force documented unsafe blocks in Starnix"
325 )]
326 let locked = unsafe { Unlocked::new() };
327 let kernel = create_test_kernel(
328 locked,
329 None,
330 KernelFeatures::default(),
331 SchedulerManager::empty_for_tests(),
332 );
333 let fs = create_fs(locked, &kernel);
334 let fs_context = create_test_fs_context(locked, &kernel, |_, _| fs.clone());
335 let init_task = create_test_init_task(locked, &kernel, fs_context);
336 (kernel, init_task.into(), locked)
337}
338
339pub fn create_task(
349 locked: &mut Locked<Unlocked>,
350 kernel: &Kernel,
351 task_name: &str,
352) -> AutoReleasableTask {
353 create_task_with_security_context(locked, kernel, task_name, &CString::new("#kernel").unwrap())
354}
355
356pub fn create_task_with_security_context(
360 locked: &mut Locked<Unlocked>,
361 kernel: &Kernel,
362 task_name: &str,
363 security_context: &CString,
364) -> AutoReleasableTask {
365 let task = create_init_child_process(
366 locked,
367 &kernel.weak_self.upgrade().unwrap(),
368 TaskCommand::new(task_name.as_bytes()),
369 Credentials::with_ids(0, 0),
370 Some(security_context),
371 )
372 .expect("failed to create second task");
373 create_test_mm(&task).expect("failed to create MM");
374
375 {
378 let _l1 = task.thread_group().read();
379 let _l2 = task.read();
380 }
381
382 task.into()
383}
384
385pub fn map_memory_anywhere<L>(
388 locked: &mut Locked<L>,
389 current_task: &CurrentTask,
390 len: u64,
391) -> UserAddress
392where
393 L: LockEqualOrBefore<FileOpsCore> + starnix_sync::LockBefore<starnix_sync::ThreadGroupLimits>,
394{
395 map_memory(locked, current_task, UserAddress::NULL, len)
396}
397
398pub fn map_object_anywhere<L, T>(
403 locked: &mut Locked<L>,
404 current_task: &CurrentTask,
405 object: &T,
406) -> UserAddress
407where
408 L: LockEqualOrBefore<FileOpsCore> + starnix_sync::LockBefore<starnix_sync::ThreadGroupLimits>,
409 T: IntoBytes + Immutable,
410{
411 let addr = map_memory_anywhere(locked, current_task, std::mem::size_of::<T>() as u64);
412 current_task.write_object(addr.into(), object).expect("could not write object");
413 addr
414}
415
416pub fn map_memory<L>(
420 locked: &mut Locked<L>,
421 current_task: &CurrentTask,
422 address: UserAddress,
423 length: u64,
424) -> UserAddress
425where
426 L: LockEqualOrBefore<FileOpsCore> + starnix_sync::LockBefore<starnix_sync::ThreadGroupLimits>,
427{
428 map_memory_with_flags(locked, current_task, address, length, MAP_ANONYMOUS | MAP_PRIVATE)
429}
430
431pub fn map_memory_with_flags<L>(
435 locked: &mut Locked<L>,
436 current_task: &CurrentTask,
437 address: UserAddress,
438 length: u64,
439 flags: u32,
440) -> UserAddress
441where
442 L: LockEqualOrBefore<FileOpsCore> + starnix_sync::LockBefore<starnix_sync::ThreadGroupLimits>,
443{
444 do_mmap(
445 locked,
446 current_task,
447 address,
448 length as usize,
449 PROT_READ | PROT_WRITE,
450 flags,
451 FdNumber::from_raw(-1),
452 0,
453 )
454 .expect("Could not map memory")
455}
456
457pub fn remap_memory(
460 locked: &mut Locked<Unlocked>,
461 current_task: &CurrentTask,
462 old_addr: UserAddress,
463 old_length: u64,
464 new_length: u64,
465 flags: u32,
466 new_addr: UserAddress,
467) -> Result<UserAddress, Errno> {
468 sys_mremap(
469 locked,
470 current_task,
471 old_addr,
472 old_length as usize,
473 new_length as usize,
474 flags,
475 new_addr,
476 )
477}
478
479#[track_caller]
485pub fn fill_page(current_task: &CurrentTask, addr: UserAddress, data: char) {
486 let data = [data as u8].repeat(*PAGE_SIZE as usize);
487 if let Err(err) = current_task.write_memory(addr, &data) {
488 panic!("write page: failed to fill page @ {addr:?} with {data:?}: {err:?}");
489 }
490}
491
492#[track_caller]
498pub fn check_page_eq(current_task: &CurrentTask, addr: UserAddress, data: char) {
499 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
500 Ok(b) => b,
501 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
502 };
503 assert!(
504 buf.into_iter().all(|c| c == data as u8),
505 "unexpected payload: page @ {addr:?} should be filled with {data:?}"
506 );
507}
508
509#[track_caller]
515pub fn check_page_ne(current_task: &CurrentTask, addr: UserAddress, data: char) {
516 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
517 Ok(b) => b,
518 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
519 };
520 assert!(
521 !buf.into_iter().all(|c| c == data as u8),
522 "unexpected payload: page @ {addr:?} should not be filled with {data:?}"
523 );
524}
525
526#[track_caller]
532pub fn check_unmapped(current_task: &CurrentTask, addr: UserAddress) {
533 match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
534 Ok(_) => panic!("read page: page @ {addr:?} should be unmapped"),
535 Err(err) if err == starnix_uapi::errors::EFAULT => {}
536 Err(err) => {
537 panic!("read page: expected EFAULT reading page @ {addr:?} but got {err:?} instead")
538 }
539 }
540}
541
542pub struct PanickingFsNode;
545
546impl FsNodeOps for PanickingFsNode {
547 fs_node_impl_not_dir!();
548
549 fn create_file_ops(
550 &self,
551 _locked: &mut Locked<FileOpsCore>,
552 _node: &FsNode,
553 _current_task: &CurrentTask,
554 _flags: OpenFlags,
555 ) -> Result<Box<dyn FileOps>, Errno> {
556 panic!("should not be called")
557 }
558}
559
560pub struct PanickingFile;
562
563impl PanickingFile {
564 pub fn new_file(locked: &mut Locked<Unlocked>, current_task: &CurrentTask) -> FileHandle {
566 anon_test_file(locked, current_task, Box::new(PanickingFile), OpenFlags::RDWR)
567 }
568}
569
570impl FileOps for PanickingFile {
571 fileops_impl_nonseekable!();
572 fileops_impl_noop_sync!();
573
574 fn write(
575 &self,
576 _locked: &mut Locked<FileOpsCore>,
577 _file: &FileObject,
578 _current_task: &CurrentTask,
579 _offset: usize,
580 _data: &mut dyn InputBuffer,
581 ) -> Result<usize, Errno> {
582 panic!("write called on TestFile")
583 }
584
585 fn read(
586 &self,
587 _locked: &mut Locked<FileOpsCore>,
588 _file: &FileObject,
589 _current_task: &CurrentTask,
590 _offset: usize,
591 _data: &mut dyn OutputBuffer,
592 ) -> Result<usize, Errno> {
593 panic!("read called on TestFile")
594 }
595
596 fn ioctl(
597 &self,
598 _locked: &mut Locked<Unlocked>,
599 _file: &FileObject,
600 _current_task: &CurrentTask,
601 _request: u32,
602 _arg: SyscallArg,
603 ) -> Result<SyscallResult, Errno> {
604 panic!("ioctl called on TestFile")
605 }
606}
607
608pub fn anon_test_file<L>(
610 locked: &mut Locked<L>,
611 current_task: &CurrentTask,
612 ops: Box<dyn FileOps>,
613 flags: OpenFlags,
614) -> FileHandle
615where
616 L: LockEqualOrBefore<FileOpsCore>,
617{
618 Anon::new_private_file(locked, current_task, ops, flags, "[fuchsia:test_file]")
620}
621
622pub struct UserMemoryWriter<'a> {
624 mm: &'a Task,
626 current_addr: UserAddress,
628}
629
630impl<'a> UserMemoryWriter<'a> {
631 pub fn new(task: &'a Task, addr: UserAddress) -> Self {
633 Self { mm: task, current_addr: addr }
634 }
635
636 pub fn write(&mut self, data: &[u8]) -> UserAddress {
640 let bytes_written = self.mm.write_memory(self.current_addr, data).unwrap();
641 assert_eq!(bytes_written, data.len());
642 let start_addr = self.current_addr;
643 self.current_addr = (self.current_addr + bytes_written).unwrap();
644 start_addr
645 }
646
647 pub fn write_object<T: IntoBytes + Immutable>(&mut self, object: &T) -> UserAddress {
651 self.write(object.as_bytes())
652 }
653
654 pub fn current_address(&self) -> UserAddress {
656 self.current_addr
657 }
658}
659
660#[derive(Debug)]
661pub struct AutoReleasableTask(Option<CurrentTask>);
662
663impl AutoReleasableTask {
664 fn as_ref(this: &Self) -> &CurrentTask {
665 this.0.as_ref().unwrap()
666 }
667
668 fn as_mut(this: &mut Self) -> &mut CurrentTask {
669 this.0.as_mut().unwrap()
670 }
671}
672
673impl From<CurrentTask> for AutoReleasableTask {
674 fn from(task: CurrentTask) -> Self {
675 Self(Some(task))
676 }
677}
678
679impl From<TaskBuilder> for AutoReleasableTask {
680 fn from(builder: TaskBuilder) -> Self {
681 CurrentTask::from(builder).into()
682 }
683}
684
685impl Drop for AutoReleasableTask {
686 fn drop(&mut self) {
687 #[allow(
689 clippy::undocumented_unsafe_blocks,
690 reason = "Force documented unsafe blocks in Starnix"
691 )]
692 let locked = unsafe { Unlocked::new() };
693 self.0.take().unwrap().release(locked);
694 }
695}
696
697impl std::ops::Deref for AutoReleasableTask {
698 type Target = CurrentTask;
699
700 fn deref(&self) -> &Self::Target {
701 AutoReleasableTask::as_ref(self)
702 }
703}
704
705impl std::ops::DerefMut for AutoReleasableTask {
706 fn deref_mut(&mut self) -> &mut Self::Target {
707 AutoReleasableTask::as_mut(self)
708 }
709}
710
711impl std::borrow::Borrow<CurrentTask> for AutoReleasableTask {
712 fn borrow(&self) -> &CurrentTask {
713 AutoReleasableTask::as_ref(self)
714 }
715}
716
717impl std::convert::AsRef<CurrentTask> for AutoReleasableTask {
718 fn as_ref(&self) -> &CurrentTask {
719 AutoReleasableTask::as_ref(self)
720 }
721}
722
723impl ArchSpecific for AutoReleasableTask {
724 fn is_arch32(&self) -> bool {
725 self.deref().is_arch32()
726 }
727}
728
729impl MemoryAccessor for AutoReleasableTask {
730 fn read_memory<'a>(
731 &self,
732 addr: UserAddress,
733 bytes: &'a mut [MaybeUninit<u8>],
734 ) -> Result<&'a mut [u8], Errno> {
735 (**self).read_memory(addr, bytes)
736 }
737 fn read_memory_partial_until_null_byte<'a>(
738 &self,
739 addr: UserAddress,
740 bytes: &'a mut [MaybeUninit<u8>],
741 ) -> Result<&'a mut [u8], Errno> {
742 (**self).read_memory_partial_until_null_byte(addr, bytes)
743 }
744 fn read_memory_partial<'a>(
745 &self,
746 addr: UserAddress,
747 bytes: &'a mut [MaybeUninit<u8>],
748 ) -> Result<&'a mut [u8], Errno> {
749 (**self).read_memory_partial(addr, bytes)
750 }
751 fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
752 (**self).write_memory(addr, bytes)
753 }
754 fn write_memory_partial(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
755 (**self).write_memory_partial(addr, bytes)
756 }
757 fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno> {
758 (**self).zero(addr, length)
759 }
760}
761
762struct TestFs;
763impl FileSystemOps for TestFs {
764 fn statfs(
765 &self,
766 _locked: &mut Locked<FileOpsCore>,
767 _fs: &FileSystem,
768 _current_task: &CurrentTask,
769 ) -> Result<statfs, Errno> {
770 Ok(default_statfs(0))
771 }
772 fn name(&self) -> &'static FsStr {
773 "test".into()
774 }
775}
776
777pub fn create_testfs(locked: &mut Locked<Unlocked>, kernel: &Kernel) -> FileSystemHandle {
778 FileSystem::new(locked, &kernel, CacheMode::Uncached, TestFs, Default::default())
779 .expect("testfs constructed with valid options")
780}
781
782pub fn create_testfs_with_root(
783 locked: &mut Locked<Unlocked>,
784 kernel: &Kernel,
785 ops: impl FsNodeOps,
786) -> FileSystemHandle {
787 let test_fs = create_testfs(locked, kernel);
788 let root_ino = test_fs.allocate_ino();
789 test_fs.create_root(root_ino, ops);
790 test_fs
791}
792
793pub fn create_fs_node_for_testing(fs: &FileSystemHandle, ops: impl FsNodeOps) -> FsNodeHandle {
794 let ino = fs.allocate_ino();
795 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
796 FsNode::new_uncached(ino, ops, fs, info, FsNodeFlags::empty())
797}
798
799pub fn create_namespace_node_for_testing(
800 fs: &FileSystemHandle,
801 ops: impl FsNodeOps,
802) -> NamespaceNode {
803 let node = create_fs_node_for_testing(fs, ops);
804 NamespaceNode::new_anonymous(DirEntry::new_unrooted(node))
805}