1pub mod fd_set;
61mod file_body;
62mod file_system;
63mod io;
64mod process;
65mod select;
66mod signal;
67pub mod sigset;
68
69pub use self::file_body::*;
70pub use self::file_system::*;
71pub use self::io::*;
72pub use self::process::*;
73pub use self::signal::*;
74use self::sigset::Sigset;
75use super::AT_FDCWD;
76use super::CaughtSignals;
77use super::Chdir;
78use super::Clock;
79use super::Close;
80use super::CpuTimes;
81use super::Dir;
82use super::Disposition;
83use super::Dup;
84use super::Errno;
85use super::Exec;
86use super::Exit;
87use super::Fcntl;
88use super::FdFlag;
89use super::Fork;
90use super::Fstat;
91use super::GetCwd;
92use super::GetPid;
93use super::GetPw;
94use super::GetRlimit;
95use super::GetSigaction;
96use super::GetUid;
97use super::Gid;
98use super::IsExecutableFile;
99use super::Isatty;
100use super::OfdAccess;
101use super::Open;
102use super::OpenFlag;
103use super::Pipe;
104use super::Read;
105use super::Result;
106use super::Seek;
107use super::Select;
108use super::SendSignal;
109use super::SetPgid;
110use super::SetRlimit;
111use super::ShellPath;
112use super::Sigaction;
113use super::Sigmask;
114use super::SigmaskOp;
115use super::Signals;
116use super::Sysconf;
117use super::TcGetPgrp;
118use super::TcSetPgrp;
119use super::Times;
120use super::Uid;
121use super::Umask;
122use super::Wait;
123use super::Write;
124use super::c_string::{AsCStrArray, IntoCStrArray};
125use super::resource::INFINITY;
126use super::resource::LimitPair;
127use super::resource::Resource;
128use crate::io::Fd;
129use crate::job::Pid;
130use crate::job::ProcessState;
131use crate::path::Path;
132use crate::path::PathBuf;
133use crate::semantics::ExitStatus;
134use crate::str::UnixStr;
135use crate::str::UnixString;
136use crate::waker::ScheduledWakerQueue;
137use crate::waker::WakerSet;
138use enumset::EnumSet;
139use std::borrow::Cow;
140use std::cell::Cell;
141use std::cell::LazyCell;
142use std::cell::Ref;
143use std::cell::RefCell;
144use std::cell::RefMut;
145use std::collections::BTreeMap;
146use std::collections::HashMap;
147use std::collections::VecDeque;
148use std::convert::Infallible;
149use std::convert::TryInto;
150use std::ffi::CStr;
151use std::ffi::CString;
152use std::fmt::Debug;
153use std::future::pending;
154use std::future::poll_fn;
155use std::future::ready;
156use std::io::SeekFrom;
157use std::ops::RangeInclusive;
158use std::pin::Pin;
159use std::rc::Rc;
160use std::task::Poll;
161use std::task::Waker;
162use std::time::Duration;
163use std::time::Instant;
164
165#[derive(Clone, Debug)]
180pub struct VirtualSystem {
181 pub state: Rc<RefCell<SystemState>>,
183
184 pub process_id: Pid,
186}
187
188impl VirtualSystem {
189 pub fn new() -> VirtualSystem {
202 let mut state = SystemState::default();
203 let mut process = Process::with_parent_and_group(Pid(1), Pid(1));
204
205 let mut set_std_fd = |path, fd| {
206 let file = Rc::new(RefCell::new(Inode::new([])));
207 state.file_system.save(path, Rc::clone(&file)).unwrap();
208 let body = FdBody {
209 open_file_description: Rc::new(RefCell::new(OpenFileDescription::new(
210 file, 0, true,
211 true, true,
212 false,
213 ))),
214 flags: EnumSet::empty(),
215 };
216 process.set_fd(fd, body).unwrap();
217 };
218 set_std_fd("/dev/stdin", Fd::STDIN);
219 set_std_fd("/dev/stdout", Fd::STDOUT);
220 set_std_fd("/dev/stderr", Fd::STDERR);
221
222 state
223 .file_system
224 .save(
225 "/tmp",
226 Rc::new(RefCell::new(Inode {
227 body: FileBody::Directory {
228 files: Default::default(),
229 },
230 permissions: Mode::ALL_9,
231 })),
232 )
233 .unwrap();
234
235 let process_id = Pid(2);
236 state.processes.insert(process_id, process);
237
238 let state = Rc::new(RefCell::new(state));
239 VirtualSystem { state, process_id }
240 }
241
242 pub fn current_process(&self) -> Ref<'_, Process> {
249 Ref::map(self.state.borrow(), |state| {
250 &state.processes[&self.process_id]
251 })
252 }
253
254 pub fn current_process_mut(&self) -> RefMut<'_, Process> {
261 RefMut::map(self.state.borrow_mut(), |state| {
262 state.processes.get_mut(&self.process_id).unwrap()
263 })
264 }
265
266 pub fn get_open_file_description(&self, fd: Fd) -> Result<Rc<RefCell<OpenFileDescription>>> {
272 let process = self.current_process();
273 let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
274 Ok(Rc::clone(&body.open_file_description))
275 }
276
277 pub fn with_open_file_description<F, R>(&self, fd: Fd, f: F) -> Result<R>
288 where
289 F: FnOnce(&OpenFileDescription) -> Result<R>,
290 {
291 let ofd = self.get_open_file_description(fd)?;
292 f(&ofd.borrow())
293 }
294
295 pub fn with_open_file_description_mut<F, R>(&self, fd: Fd, f: F) -> Result<R>
306 where
307 F: FnOnce(&mut OpenFileDescription) -> Result<R>,
308 {
309 let ofd = self.get_open_file_description(fd)?;
310 f(&mut ofd.borrow_mut())
311 }
312
313 fn resolve_relative_path<'a>(&self, path: &'a Path) -> Cow<'a, Path> {
314 if path.is_absolute() {
315 Cow::Borrowed(path)
316 } else {
317 Cow::Owned(self.current_process().cwd.join(path))
318 }
319 }
320
321 fn resolve_existing_file(
322 &self,
323 _dir_fd: Fd,
324 path: &Path,
325 follow_symlinks: bool,
326 ) -> Result<Rc<RefCell<Inode>>> {
327 const _POSIX_SYMLOOP_MAX: i32 = 8;
330
331 let mut path = Cow::Borrowed(path);
332 for _count in 0.._POSIX_SYMLOOP_MAX {
333 let resolved_path = self.resolve_relative_path(&path);
334 let inode = self.state.borrow().file_system.get(&resolved_path)?;
335 if !follow_symlinks {
336 return Ok(inode);
337 }
338
339 let inode_ref = inode.borrow();
340 if let FileBody::Symlink { target } = &inode_ref.body {
341 let mut new_path = resolved_path.into_owned();
342 new_path.pop();
343 new_path.push(target);
344 path = Cow::Owned(new_path);
345 } else {
346 drop(inode_ref);
347 return Ok(inode);
348 }
349 }
350
351 Err(Errno::ELOOP)
352 }
353
354 async fn block_until_running(&self) {
356 let waker = Rc::new(Cell::new(None));
357
358 poll_fn(|cx| {
359 let mut state = self.state.borrow_mut();
360 let Some(process) = state.processes.get_mut(&self.process_id) else {
361 return Poll::Ready(());
362 };
363
364 match process.state {
365 ProcessState::Running => Poll::Ready(()),
366 ProcessState::Halted(result) => {
367 if result.is_stopped() {
368 waker.set(Some(cx.waker().clone()));
369 process.wake_on_resumption(Rc::downgrade(&waker));
370 }
371 Poll::Pending
372 }
373 }
374 })
375 .await
376 }
377
378 pub(crate) async fn block_while_stopped(&self) -> bool {
383 let waker = Rc::new(Cell::new(None));
384
385 poll_fn(|cx| {
386 let mut process = self.current_process_mut();
387 match process.state() {
388 ProcessState::Running => Poll::Ready(false),
389 ProcessState::Halted(process_result) if process_result.is_stopped() => {
390 waker.set(Some(cx.waker().clone()));
391 process.wake_on_resumption(Rc::downgrade(&waker));
392 Poll::Pending
393 }
394 ProcessState::Halted(_) => Poll::Ready(true),
395 }
396 })
397 .await
398 }
399
400 fn resolve_file(
404 &self,
405 path: &CStr,
406 access: OfdAccess,
407 flags: EnumSet<OpenFlag>,
408 mode: Mode,
409 ) -> Result<(Rc<RefCell<Inode>>, bool, bool)> {
410 let path = self.resolve_relative_path(Path::new(UnixStr::from_bytes(path.to_bytes())));
411 let umask = self.current_process().umask;
412
413 let mut state = self.state.borrow_mut();
414 let file = match state.file_system.get(&path) {
415 Ok(inode) => {
416 if flags.contains(OpenFlag::Exclusive) {
417 return Err(Errno::EEXIST);
418 }
419 if flags.contains(OpenFlag::Directory)
420 && !matches!(inode.borrow().body, FileBody::Directory { .. })
421 {
422 return Err(Errno::ENOTDIR);
423 }
424 if flags.contains(OpenFlag::Truncate)
425 && let FileBody::Regular { content, .. } = &mut inode.borrow_mut().body
426 {
427 content.clear();
428 };
429 inode
430 }
431 Err(Errno::ENOENT) if flags.contains(OpenFlag::Create) => {
432 let mut inode = Inode::new([]);
433 inode.permissions = mode.difference(umask);
434 let inode = Rc::new(RefCell::new(inode));
435 state.file_system.save(&path, Rc::clone(&inode))?;
436 inode
437 }
438 Err(errno) => return Err(errno),
439 };
440
441 let (is_readable, is_writable) = match access {
442 OfdAccess::ReadOnly => (true, false),
443 OfdAccess::WriteOnly => (false, true),
444 OfdAccess::ReadWrite => (true, true),
445 OfdAccess::Exec | OfdAccess::Search => (false, false),
446 };
447
448 Ok((file, is_readable, is_writable))
449 }
450
451 fn create_fd(
455 &self,
456 open_file_description: Rc<RefCell<OpenFileDescription>>,
457 flags: EnumSet<OpenFlag>,
458 ) -> std::result::Result<Fd, Errno> {
459 let body = FdBody {
460 open_file_description,
461 flags: if flags.contains(OpenFlag::CloseOnExec) {
462 EnumSet::only(FdFlag::CloseOnExec)
463 } else {
464 EnumSet::empty()
465 },
466 };
467 self.current_process_mut()
468 .open_fd(body)
469 .map_err(|_| Errno::EMFILE)
470 }
471
472 async fn wait_for_fifo_to_become_ready(open_file_description: &OpenFileDescription) {
482 let waker: LazyCell<Rc<Cell<Option<Waker>>>> = LazyCell::default();
484 poll_fn(|context| {
485 let mut file = open_file_description.inode().borrow_mut();
486 let FileBody::Fifo {
487 readers,
488 writers,
489 ref mut pending_open_wakers,
490 ..
491 } = file.body
492 else {
493 return Poll::Ready(());
494 };
495
496 if readers > 0 && writers > 0 {
497 return Poll::Ready(());
498 }
499
500 waker.set(Some(context.waker().clone()));
503 pending_open_wakers.insert(Rc::downgrade(&waker));
504 Poll::Pending
505 })
506 .await;
507 }
508}
509
510impl Default for VirtualSystem {
511 fn default() -> Self {
512 VirtualSystem::new()
513 }
514}
515
516impl Fstat for VirtualSystem {
517 type Stat = Stat;
518
519 fn fstat(&self, fd: Fd) -> Result<Stat> {
520 self.with_open_file_description(fd, |ofd| Ok(ofd.inode().borrow().stat()))
521 }
522
523 fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
524 let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
525 let inode = self.resolve_existing_file(dir_fd, path, follow_symlinks)?;
526 Ok(inode.borrow().stat())
527 }
528}
529
530impl IsExecutableFile for VirtualSystem {
531 fn is_executable_file(&self, path: &CStr) -> bool {
536 let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
537 self.resolve_existing_file(AT_FDCWD, path, true)
538 .is_ok_and(|inode| inode.borrow().permissions.intersects(Mode::ALL_EXEC))
539 }
540}
541
542impl Pipe for VirtualSystem {
543 fn pipe(&self) -> Result<(Fd, Fd)> {
544 let file = Rc::new(RefCell::new(Inode {
545 body: FileBody::Fifo {
546 content: VecDeque::new(),
547 readers: 0,
548 writers: 0,
549 pending_open_wakers: WakerSet::new(),
550 pending_read_wakers: WakerSet::new(),
551 pending_write_wakers: WakerSet::new(),
552 },
553 permissions: Mode::default(),
554 }));
555 let reader = OpenFileDescription::new(
556 Rc::clone(&file),
557 0,
558 true,
559 false,
560 false,
561 false,
562 );
563 let writer = OpenFileDescription::new(
564 Rc::clone(&file),
565 0,
566 false,
567 true,
568 false,
569 false,
570 );
571
572 let reader = FdBody {
573 open_file_description: Rc::new(RefCell::new(reader)),
574 flags: EnumSet::empty(),
575 };
576 let writer = FdBody {
577 open_file_description: Rc::new(RefCell::new(writer)),
578 flags: EnumSet::empty(),
579 };
580
581 let mut process = self.current_process_mut();
582 let reader = process.open_fd(reader).map_err(|_| Errno::EMFILE)?;
583 let writer = process.open_fd(writer).map_err(|_| {
584 process.close_fd(reader);
585 Errno::EMFILE
586 })?;
587 Ok((reader, writer))
588 }
589}
590
591impl Dup for VirtualSystem {
592 fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
593 let mut process = self.current_process_mut();
594 let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
595 body.flags = flags;
596 process.open_fd_ge(to_min, body).map_err(|_| Errno::EMFILE)
597 }
598
599 fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
600 let mut process = self.current_process_mut();
601 let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
602 body.flags = EnumSet::empty();
603 process.set_fd(to, body).map_err(|_| Errno::EBADF)?;
604 Ok(to)
605 }
606}
607
608impl Open for VirtualSystem {
609 fn open(
616 &self,
617 path: &CStr,
618 access: OfdAccess,
619 flags: EnumSet<OpenFlag>,
620 mode: Mode,
621 ) -> impl Future<Output = Result<Fd>> + use<> {
622 let resolution = self.resolve_file(path, access, flags, mode);
623 let system = self.clone();
624
625 async move {
626 let (file, is_readable, is_writable) = resolution?;
627
628 if let FileBody::Fifo { readers, .. } = &mut file.borrow_mut().body {
629 if flags.contains(OpenFlag::NonBlock)
632 && is_writable
633 && !is_readable
634 && *readers == 0
635 {
636 return Err(Errno::ENXIO);
637 }
638 }
639
640 let open_file_description = OpenFileDescription::new(
643 file,
644 0,
645 is_readable,
646 is_writable,
647 flags.contains(OpenFlag::Append),
648 flags.contains(OpenFlag::NonBlock),
649 );
650
651 if !flags.contains(OpenFlag::NonBlock) {
652 Self::wait_for_fifo_to_become_ready(&open_file_description).await;
653 }
654
655 system.create_fd(Rc::new(RefCell::new(open_file_description)), flags)
656 }
657 }
658
659 fn open_tmpfile(&self, _parent_dir: &Path) -> Result<Fd> {
660 let file = Rc::new(RefCell::new(Inode::new([])));
661 let open_file_description = Rc::new(RefCell::new(OpenFileDescription::new(
662 file, 0, true,
663 true, false,
664 false,
665 )));
666 let body = FdBody {
667 open_file_description,
668 flags: EnumSet::empty(),
669 };
670 self.current_process_mut()
671 .open_fd(body)
672 .map_err(|_| Errno::EMFILE)
673 }
674
675 fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
676 self.with_open_file_description(fd, |ofd| {
677 let inode = ofd.inode();
678 let dir = VirtualDir::try_from(&inode.borrow().body)?;
679 Ok(dir)
680 })
681 }
682
683 fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
684 let (file, is_readable, is_writable) = self.resolve_file(
685 path,
686 OfdAccess::ReadOnly,
687 OpenFlag::Directory.into(),
688 Mode::empty(),
689 )?;
690
691 debug_assert!(
692 matches!(file.borrow().body, FileBody::Directory { .. }),
693 "resolved file is not a directory"
694 );
695
696 let open_file_description = Rc::new(RefCell::new(OpenFileDescription::new(
697 file,
698 0,
699 is_readable,
700 is_writable,
701 false,
702 false,
703 )));
704 let fd = self.create_fd(open_file_description, OpenFlag::Directory.into())?;
705 self.fdopendir(fd)
706 }
707}
708
709impl Close for VirtualSystem {
710 fn close(&self, fd: Fd) -> Result<()> {
711 self.current_process_mut().close_fd(fd);
712 Ok(())
713 }
714}
715
716impl Fcntl for VirtualSystem {
717 fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
718 fn is_directory(file_body: &FileBody) -> bool {
719 matches!(file_body, FileBody::Directory { .. })
720 }
721
722 self.with_open_file_description(fd, |ofd| match (ofd.is_readable(), ofd.is_writable()) {
723 (true, false) => Ok(OfdAccess::ReadOnly),
724 (false, true) => Ok(OfdAccess::WriteOnly),
725 (true, true) => Ok(OfdAccess::ReadWrite),
726 (false, false) => {
727 if is_directory(&ofd.inode().borrow().body) {
728 Ok(OfdAccess::Search)
729 } else {
730 Ok(OfdAccess::Exec)
731 }
732 }
733 })
734 }
735
736 fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
737 self.with_open_file_description_mut(fd, |ofd| {
738 let was_nonblocking = ofd.is_nonblocking();
739 ofd.set_nonblocking(nonblocking);
740 Ok(was_nonblocking)
741 })
742 }
743
744 fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
745 let process = self.current_process();
746 let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
747 Ok(body.flags)
748 }
749
750 fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
751 let mut process = self.current_process_mut();
752 let body = process.get_fd_mut(fd).ok_or(Errno::EBADF)?;
753 body.flags = flags;
754 Ok(())
755 }
756}
757
758impl VirtualSystem {
759 fn signal_interrupted(
765 &self,
766 initial_signal_count: usize,
767 waker: &Rc<Cell<Option<Waker>>>,
768 context: &mut std::task::Context<'_>,
769 ) -> bool {
770 let mut proc = self.current_process_mut();
771 if proc.caught_signals_count != initial_signal_count {
772 return true;
773 }
774 waker.set(Some(context.waker().clone()));
775 proc.register_signal_waker(Rc::downgrade(waker));
776 false
777 }
778}
779
780impl Read for VirtualSystem {
781 fn read<'a>(
786 &self,
787 fd: Fd,
788 buffer: &'a mut [u8],
789 ) -> impl Future<Output = Result<usize>> + use<'a> {
790 let ofd = self.get_open_file_description(fd);
791 let system = self.clone();
792 async move {
793 let ofd = ofd?;
794 let initial_signal_count = system.current_process().caught_signals_count;
795 let waker: LazyCell<Rc<Cell<Option<Waker>>>> = LazyCell::default();
796 poll_fn(|context| {
797 let get_waker = || {
798 waker.set(Some(context.waker().clone()));
799 Rc::downgrade(&waker)
800 };
801 let result = ofd.borrow_mut().poll_read(buffer, get_waker);
802 if result.is_pending()
803 && system.signal_interrupted(initial_signal_count, &waker, context)
804 {
805 return Poll::Ready(Err(Errno::EINTR));
806 }
807 result
808 })
809 .await
810 }
811 }
812}
813
814impl Write for VirtualSystem {
815 fn write<'a>(&self, fd: Fd, buffer: &'a [u8]) -> impl Future<Output = Result<usize>> + use<'a> {
836 let ofd = self.get_open_file_description(fd);
837 let system = self.clone();
838 async move {
839 let ofd = ofd?;
840 let initial_signal_count = system.current_process().caught_signals_count;
841 let mut bytes_written = 0usize;
842 let waker: LazyCell<Rc<Cell<Option<Waker>>>> = LazyCell::default();
843 let interrupted = |bytes_written: usize| -> Poll<Result<usize>> {
846 Poll::Ready(if bytes_written > 0 {
847 Ok(bytes_written)
848 } else {
849 Err(Errno::EINTR)
850 })
851 };
852 poll_fn(|context| {
853 if system.current_process().caught_signals_count != initial_signal_count {
854 return interrupted(bytes_written);
855 }
856 let get_waker = || {
857 waker.set(Some(context.waker().clone()));
858 Rc::downgrade(&waker)
859 };
860 let result =
861 ofd.borrow_mut()
862 .poll_write_full(buffer, &mut bytes_written, get_waker);
863 if result.is_pending()
864 && system.signal_interrupted(initial_signal_count, &waker, context)
865 {
866 return interrupted(bytes_written);
867 }
868 result
869 })
870 .await
871 }
872 }
873}
874
875impl Seek for VirtualSystem {
876 fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
877 self.with_open_file_description_mut(fd, |ofd| ofd.seek(position))
878 .and_then(|new_offset| new_offset.try_into().map_err(|_| Errno::EOVERFLOW))
879 }
880}
881
882impl Umask for VirtualSystem {
883 fn umask(&self, new_mask: Mode) -> Mode {
884 std::mem::replace(&mut self.current_process_mut().umask, new_mask)
885 }
886}
887
888impl GetCwd for VirtualSystem {
889 fn getcwd(&self) -> Result<PathBuf> {
890 Ok(self.current_process().cwd.clone())
891 }
892}
893
894impl Chdir for VirtualSystem {
895 fn chdir(&self, path: &CStr) -> Result<()> {
896 let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
897 let inode = self.resolve_existing_file(AT_FDCWD, path, true)?;
898 if matches!(&inode.borrow().body, FileBody::Directory { .. }) {
899 let mut process = self.current_process_mut();
900 let new_path = process.cwd.join(path);
901 process.chdir(new_path);
902 Ok(())
903 } else {
904 Err(Errno::ENOTDIR)
905 }
906 }
907}
908
909impl Clock for VirtualSystem {
910 fn now(&self) -> Instant {
914 self.state
915 .borrow()
916 .now
917 .expect("`SystemState::now` should be assigned")
918 }
919}
920
921impl Times for VirtualSystem {
922 fn times(&self) -> Result<CpuTimes> {
924 Ok(self.state.borrow().times)
925 }
926}
927
928impl Signals for VirtualSystem {
929 const SIGABRT: signal::Number = signal::SIGABRT;
930 const SIGALRM: signal::Number = signal::SIGALRM;
931 const SIGBUS: signal::Number = signal::SIGBUS;
932 const SIGCHLD: signal::Number = signal::SIGCHLD;
933 const SIGCLD: Option<signal::Number> = Some(signal::SIGCLD);
934 const SIGCONT: signal::Number = signal::SIGCONT;
935 const SIGEMT: Option<signal::Number> = Some(signal::SIGEMT);
936 const SIGFPE: signal::Number = signal::SIGFPE;
937 const SIGHUP: signal::Number = signal::SIGHUP;
938 const SIGILL: signal::Number = signal::SIGILL;
939 const SIGINFO: Option<signal::Number> = Some(signal::SIGINFO);
940 const SIGINT: signal::Number = signal::SIGINT;
941 const SIGIO: Option<signal::Number> = Some(signal::SIGIO);
942 const SIGIOT: signal::Number = signal::SIGIOT;
943 const SIGKILL: signal::Number = signal::SIGKILL;
944 const SIGLOST: Option<signal::Number> = Some(signal::SIGLOST);
945 const SIGPIPE: signal::Number = signal::SIGPIPE;
946 const SIGPOLL: Option<signal::Number> = Some(signal::SIGPOLL);
947 const SIGPROF: signal::Number = signal::SIGPROF;
948 const SIGPWR: Option<signal::Number> = Some(signal::SIGPWR);
949 const SIGQUIT: signal::Number = signal::SIGQUIT;
950 const SIGSEGV: signal::Number = signal::SIGSEGV;
951 const SIGSTKFLT: Option<signal::Number> = Some(signal::SIGSTKFLT);
952 const SIGSTOP: signal::Number = signal::SIGSTOP;
953 const SIGSYS: signal::Number = signal::SIGSYS;
954 const SIGTERM: signal::Number = signal::SIGTERM;
955 const SIGTHR: Option<signal::Number> = Some(signal::SIGTHR);
956 const SIGTRAP: signal::Number = signal::SIGTRAP;
957 const SIGTSTP: signal::Number = signal::SIGTSTP;
958 const SIGTTIN: signal::Number = signal::SIGTTIN;
959 const SIGTTOU: signal::Number = signal::SIGTTOU;
960 const SIGURG: signal::Number = signal::SIGURG;
961 const SIGUSR1: signal::Number = signal::SIGUSR1;
962 const SIGUSR2: signal::Number = signal::SIGUSR2;
963 const SIGVTALRM: signal::Number = signal::SIGVTALRM;
964 const SIGWINCH: signal::Number = signal::SIGWINCH;
965 const SIGXCPU: signal::Number = signal::SIGXCPU;
966 const SIGXFSZ: signal::Number = signal::SIGXFSZ;
967
968 fn sigrt_range(&self) -> Option<RangeInclusive<Number>> {
969 Some(signal::SIGRTMIN..=signal::SIGRTMAX)
970 }
971}
972
973impl GetPid for VirtualSystem {
974 fn getsid(&self, pid: Pid) -> Result<Pid> {
976 self.state
977 .borrow()
978 .processes
979 .get(&pid)
980 .map_or(Err(Errno::ESRCH), |_| Ok(Pid(2)))
981 }
982
983 fn getpid(&self) -> Pid {
984 self.process_id
985 }
986
987 fn getppid(&self) -> Pid {
988 self.current_process().ppid
989 }
990
991 fn getpgrp(&self) -> Pid {
992 self.current_process().pgid
993 }
994}
995
996impl SetPgid for VirtualSystem {
997 fn setpgid(&self, mut pid: Pid, mut pgid: Pid) -> Result<()> {
1001 if pgid.0 < 0 {
1002 return Err(Errno::EINVAL);
1003 }
1004 if pid.0 == 0 {
1005 pid = self.process_id;
1006 }
1007 if pgid.0 == 0 {
1008 pgid = pid;
1009 }
1010
1011 let mut state = self.state.borrow_mut();
1012 if pgid != pid && !state.processes.values().any(|p| p.pgid == pgid) {
1013 return Err(Errno::EPERM);
1014 }
1015 let process = state.processes.get_mut(&pid).ok_or(Errno::ESRCH)?;
1016 if pid != self.process_id && process.ppid != self.process_id {
1017 return Err(Errno::ESRCH);
1018 }
1019 if process.last_exec.is_some() {
1020 return Err(Errno::EACCES);
1021 }
1022
1023 process.pgid = pgid;
1024 Ok(())
1025 }
1027}
1028
1029impl Sigmask for VirtualSystem {
1030 type Sigset = Sigset;
1031
1032 fn sigmask(
1033 &self,
1034 op: Option<(SigmaskOp, &Sigset)>,
1035 old_mask: Option<&mut Sigset>,
1036 ) -> impl Future<Output = Result<()>> + use<> {
1037 let state_changed = {
1038 let mut state = self.state.borrow_mut();
1039 let process = state
1040 .processes
1041 .get_mut(&self.process_id)
1042 .expect("the current process should be in the system state");
1043
1044 if let Some(old_mask) = old_mask {
1045 old_mask.clone_from(process.blocked_signals());
1046 }
1047
1048 if let Some((op, mask)) = op {
1049 let result = process.block_signals(op, mask.iter().copied());
1050 if result.process_state_changed {
1051 let parent_pid = process.ppid;
1052 raise_sigchld(&mut state, parent_pid);
1053 }
1054 result.process_state_changed
1055 } else {
1056 false
1057 }
1058 };
1059
1060 let system = self.clone();
1061 async move {
1062 if state_changed {
1063 system.block_until_running().await;
1064 }
1065 Ok(())
1066 }
1067 }
1068}
1069
1070impl GetSigaction for VirtualSystem {
1071 fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
1072 let process = self.current_process();
1073 Ok(process.disposition(signal))
1074 }
1075}
1076
1077impl Sigaction for VirtualSystem {
1078 fn sigaction(&self, signal: signal::Number, disposition: Disposition) -> Result<Disposition> {
1079 let mut process = self.current_process_mut();
1080 Ok(process.set_disposition(signal, disposition))
1081 }
1082}
1083
1084impl CaughtSignals for VirtualSystem {
1085 fn caught_signals(&self) -> Vec<signal::Number> {
1086 std::mem::take(&mut self.current_process_mut().caught_signals)
1087 }
1088}
1089
1090impl SendSignal for VirtualSystem {
1091 fn kill(
1104 &self,
1105 target: Pid,
1106 signal: Option<signal::Number>,
1107 ) -> impl Future<Output = Result<()>> + use<> {
1108 let result = 'result: {
1109 if let Some(signal) = signal {
1110 if signal.as_raw() < 0 {
1112 break 'result Err(Errno::EINVAL);
1113 }
1114 }
1115
1116 match target {
1117 Pid::MY_PROCESS_GROUP => {
1118 let target_pgid = self.current_process().pgid;
1119 send_signal_to_processes(
1120 &mut self.state.borrow_mut(),
1121 Some(target_pgid),
1122 signal,
1123 )
1124 }
1125
1126 Pid::ALL => send_signal_to_processes(&mut self.state.borrow_mut(), None, signal),
1127
1128 Pid(raw_pid) if raw_pid >= 0 => {
1129 let mut state = self.state.borrow_mut();
1130 match state.processes.get_mut(&target) {
1131 Some(process) => {
1132 if let Some(signal) = signal {
1133 let result = process.raise_signal(signal);
1134 if result.process_state_changed {
1135 let parent_pid = process.ppid;
1136 raise_sigchld(&mut state, parent_pid);
1137 }
1138 }
1139 Ok(())
1140 }
1141 None => Err(Errno::ESRCH),
1142 }
1143 }
1144
1145 Pid(negative_pgid) => {
1146 let target_pgid = Pid(-negative_pgid);
1147 send_signal_to_processes(
1148 &mut self.state.borrow_mut(),
1149 Some(target_pgid),
1150 signal,
1151 )
1152 }
1153 }
1154 };
1155
1156 let system = self.clone();
1157 async move {
1158 system.block_until_running().await;
1159 result
1160 }
1161 }
1162
1163 fn raise(&self, signal: signal::Number) -> impl Future<Output = Result<()>> + use<> {
1164 let target = self.process_id;
1165 self.kill(target, Some(signal))
1166 }
1167}
1168
1169impl Isatty for VirtualSystem {
1170 fn isatty(&self, fd: Fd) -> bool {
1171 self.with_open_file_description(fd, |ofd| {
1172 Ok(matches!(
1173 &ofd.inode().borrow().body,
1174 FileBody::Terminal { .. }
1175 ))
1176 })
1177 .unwrap_or(false)
1178 }
1179}
1180
1181impl TcGetPgrp for VirtualSystem {
1182 fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
1187 self.with_open_file_description(fd, |_| Ok(()))?;
1189
1190 self.state.borrow().foreground.ok_or(Errno::ENOTTY)
1191 }
1192}
1193
1194impl TcSetPgrp for VirtualSystem {
1195 fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<> {
1200 fn inner(system: &VirtualSystem, fd: Fd, pgid: Pid) -> Result<()> {
1201 system.with_open_file_description(fd, |_| Ok(()))?;
1203
1204 let mut state = system.state.borrow_mut();
1206 if !state.processes.values().any(|p| p.pgid == pgid) {
1207 return Err(Errno::EPERM);
1208 }
1209
1210 state.foreground = Some(pgid);
1214 Ok(())
1215 }
1216
1217 ready(inner(self, fd, pgid))
1218 }
1219}
1220
1221impl Fork for VirtualSystem {
1222 fn run_in_child_process<D, F>(&self, shared_data: D, child_task: F) -> (Result<Pid>, D)
1240 where
1241 D: Clone + 'static,
1242 F: AsyncFnOnce(Self, D) + 'static,
1243 {
1244 let mut state = self.state.borrow_mut();
1245 let Some(executor) = state.executor.as_ref() else {
1246 return (Err(Errno::ENOSYS), shared_data);
1247 };
1248 let child_process_id = state
1249 .processes
1250 .keys()
1251 .max()
1252 .map_or(Pid(2), |pid| Pid(pid.0 + 1));
1253 let child_system = VirtualSystem {
1254 state: Rc::clone(&self.state),
1255 process_id: child_process_id,
1256 };
1257 let data_clone = shared_data.clone();
1258 let child_task = async move {
1259 child_task(child_system.clone(), data_clone).await;
1260 assert!(
1261 !child_system.current_process().state().is_alive(),
1262 "the child task should have terminated itself by exiting or signaling itself: pid={}",
1263 child_system.process_id
1264 );
1265 };
1266
1267 if executor.spawn(Box::pin(child_task)).is_err() {
1268 return (Err(Errno::EAGAIN), shared_data);
1269 }
1270
1271 let parent_process = &state.processes[&self.process_id];
1272 let child_process = Process::fork_from(self.process_id, parent_process);
1273 state.processes.insert(child_process_id, child_process);
1274
1275 (Ok(child_process_id), shared_data)
1276 }
1277}
1278
1279impl Wait for VirtualSystem {
1280 fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
1284 let parent_pid = self.process_id;
1285 let mut state = self.state.borrow_mut();
1286 if let Some((pid, process)) = state.child_to_wait_for(parent_pid, target) {
1287 if process.state_has_changed() {
1288 Ok(Some((pid, process.take_state())))
1289 } else if process.state().is_alive() {
1290 Ok(None)
1291 } else {
1292 Err(Errno::ECHILD)
1293 }
1294 } else {
1295 Err(Errno::ECHILD)
1296 }
1297 }
1298}
1299
1300impl Exec for VirtualSystem {
1301 fn execve<A, E>(
1307 &self,
1308 path: &CStr,
1309 args: A,
1310 envs: E,
1311 ) -> impl Future<Output = Result<Infallible>> + use<A, E>
1312 where
1313 A: IntoCStrArray,
1314 E: IntoCStrArray,
1315 {
1316 fn inner<A, E>(
1317 this: &VirtualSystem,
1318 path: &CStr,
1319 args: A,
1320 envs: E,
1321 ) -> impl Future<Output = Result<Infallible>> + use<A, E>
1322 where
1323 A: AsCStrArray,
1324 E: AsCStrArray,
1325 {
1326 let os_path = UnixStr::from_bytes(path.to_bytes());
1327 let mut state = this.state.borrow_mut();
1328 let fs = &state.file_system;
1329 let file = match fs.get(os_path) {
1330 Ok(file) => file,
1331 Err(e) => return ready(Err(e)),
1332 };
1333 let is_executable = matches!(
1335 &file.borrow().body,
1336 FileBody::Regular {
1337 is_native_executable: true,
1338 ..
1339 }
1340 );
1341 if is_executable {
1342 let process = state.processes.get_mut(&this.process_id).unwrap();
1344 let path = path.to_owned();
1345 let args = args.into_c_str_array().to_vec();
1346 let envs = envs.into_c_str_array().to_vec();
1347 process.last_exec = Some((path, args, envs));
1348
1349 ready(Err(Errno::ENOSYS))
1353 } else {
1354 ready(Err(Errno::ENOEXEC))
1355 }
1356 }
1357
1358 inner(self, path, args.into_c_str_array(), envs.into_c_str_array())
1359 }
1360}
1361
1362impl Exit for VirtualSystem {
1363 fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<> {
1364 let mut myself = self.current_process_mut();
1365 let parent_pid = myself.ppid;
1366 let exited = myself.set_state(ProcessState::exited(exit_status));
1367 drop(myself);
1368 if exited {
1369 raise_sigchld(&mut self.state.borrow_mut(), parent_pid);
1370 }
1371
1372 pending()
1373 }
1374}
1375
1376impl GetUid for VirtualSystem {
1377 fn getuid(&self) -> Uid {
1378 self.current_process().uid()
1379 }
1380
1381 fn geteuid(&self) -> Uid {
1382 self.current_process().euid()
1383 }
1384
1385 fn getgid(&self) -> Gid {
1386 self.current_process().gid()
1387 }
1388
1389 fn getegid(&self) -> Gid {
1390 self.current_process().egid()
1391 }
1392}
1393
1394impl GetPw for VirtualSystem {
1395 fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
1396 let state = self.state.borrow();
1397 let name = match name.to_str() {
1398 Ok(name) => name,
1399 Err(_utf8_error) => return Ok(None),
1400 };
1401 Ok(state.home_dirs.get(name).cloned())
1402 }
1403}
1404
1405impl Sysconf for VirtualSystem {
1406 fn confstr_path(&self) -> Result<UnixString> {
1411 let path = self.state.borrow().path.clone();
1412 if path.is_empty() {
1413 Err(Errno::ENOSYS)
1414 } else {
1415 Ok(path)
1416 }
1417 }
1418}
1419
1420impl ShellPath for VirtualSystem {
1421 fn shell_path(&self) -> CString {
1425 c"/bin/sh".to_owned()
1426 }
1427}
1428
1429impl GetRlimit for VirtualSystem {
1430 fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
1431 Ok(self
1432 .current_process()
1433 .resource_limits
1434 .get(&resource)
1435 .copied()
1436 .unwrap_or(LimitPair {
1437 soft: INFINITY,
1438 hard: INFINITY,
1439 }))
1440 }
1441}
1442
1443impl SetRlimit for VirtualSystem {
1444 fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1445 if limits.soft_exceeds_hard() {
1446 return Err(Errno::EINVAL);
1447 }
1448
1449 let mut process = self.current_process_mut();
1450 use std::collections::hash_map::Entry::{Occupied, Vacant};
1451 match process.resource_limits.entry(resource) {
1452 Occupied(occupied) => {
1453 let occupied = occupied.into_mut();
1454 if limits.hard > occupied.hard {
1455 return Err(Errno::EPERM);
1456 }
1457 *occupied = limits;
1458 }
1459 Vacant(vacant) => {
1460 vacant.insert(limits);
1461 }
1462 }
1463 Ok(())
1464 }
1465}
1466
1467fn send_signal_to_processes(
1468 state: &mut SystemState,
1469 target_pgid: Option<Pid>,
1470 signal: Option<signal::Number>,
1471) -> Result<()> {
1472 let mut results = Vec::new();
1473
1474 for (&_pid, process) in &mut state.processes {
1475 if target_pgid.is_none_or(|target_pgid| process.pgid == target_pgid) {
1476 let result = if let Some(signal) = signal {
1477 process.raise_signal(signal)
1478 } else {
1479 SignalResult::default()
1480 };
1481 results.push((result, process.ppid));
1482 }
1483 }
1484
1485 if results.is_empty() {
1486 Err(Errno::ESRCH)
1487 } else {
1488 for (result, ppid) in results {
1489 if result.process_state_changed {
1490 raise_sigchld(state, ppid);
1491 }
1492 }
1493 Ok(())
1494 }
1495}
1496
1497fn raise_sigchld(state: &mut SystemState, target_pid: Pid) {
1498 if let Some(target) = state.processes.get_mut(&target_pid) {
1499 let result = target.raise_signal(signal::SIGCHLD);
1500 assert!(!result.process_state_changed);
1501 }
1502}
1503
1504#[derive(Clone, Debug, Default)]
1506pub struct SystemState {
1507 pub now: Option<Instant>,
1513
1514 pub scheduled_wakers: ScheduledWakerQueue,
1516
1517 pub times: CpuTimes,
1519
1520 pub executor: Option<Rc<dyn Executor>>,
1526
1527 pub processes: BTreeMap<Pid, Process>,
1529
1530 pub foreground: Option<Pid>,
1536
1537 pub file_system: FileSystem,
1539
1540 pub home_dirs: HashMap<String, PathBuf>,
1545
1546 pub path: UnixString,
1548}
1549
1550impl SystemState {
1551 pub fn advance_time(&mut self, new_current_time: Instant) {
1554 self.now = Some(new_current_time);
1555 self.scheduled_wakers.wake(new_current_time);
1556 }
1557
1558 fn child_to_wait_for(&mut self, parent_pid: Pid, target: Pid) -> Option<(Pid, &mut Process)> {
1562 match target.0 {
1563 0 => todo!("wait target {}", target),
1564 -1 => {
1565 let mut result = None;
1567 for (pid, process) in &mut self.processes {
1568 if process.ppid == parent_pid {
1569 let changed = process.state_has_changed();
1570 result = Some((*pid, process));
1571 if changed {
1572 break;
1573 }
1574 }
1575 }
1576 result
1577 }
1578 raw if raw >= 0 => {
1579 let process = self.processes.get_mut(&target)?;
1580 if process.ppid == parent_pid {
1581 Some((target, process))
1582 } else {
1583 None
1584 }
1585 }
1586 _target => todo!("wait target {}", target),
1587 }
1588 }
1589}
1590
1591pub trait Executor: Debug {
1599 fn spawn(
1604 &self,
1605 task: Pin<Box<dyn Future<Output = ()>>>,
1606 ) -> std::result::Result<(), Box<dyn std::error::Error>>;
1607}
1608
1609#[cfg(test)]
1610mod tests {
1611 use super::*;
1612 use crate::Env;
1613 use crate::job::ProcessResult;
1614 use crate::system::FileType;
1615 use crate::system::r#virtual::PIPE_SIZE;
1616 use crate::test_helper::WakeFlag;
1617 use assert_matches::assert_matches;
1618 use futures_executor::LocalPool;
1619 use futures_util::FutureExt as _;
1620 use std::future::pending;
1621 use std::pin::pin;
1622 use std::sync::Arc;
1623 use std::task::Context;
1624 use std::task::Poll::{Pending, Ready};
1625
1626 #[test]
1627 fn fstatat_non_existent_file() {
1628 let system = VirtualSystem::new();
1629 assert_matches!(
1630 system.fstatat(Fd(0), c"/no/such/file", true),
1631 Err(Errno::ENOENT)
1632 );
1633 }
1634
1635 #[test]
1636 fn fstatat_regular_file() {
1637 let system = VirtualSystem::new();
1638 let path = "/some/file";
1639 let content = Rc::new(RefCell::new(Inode::new([1, 2, 3, 42, 100])));
1640 let mut state = system.state.borrow_mut();
1641 state.file_system.save(path, content).unwrap();
1642 drop(state);
1643
1644 let stat = system.fstatat(Fd(0), c"/some/file", true).unwrap();
1645 assert_eq!(stat.mode, Mode::default());
1646 assert_eq!(stat.r#type, FileType::Regular);
1647 assert_eq!(stat.size, 5);
1648 }
1650
1651 #[test]
1652 fn fstatat_directory() {
1653 let system = VirtualSystem::new();
1654 let path = "/some/file";
1655 let content = Rc::new(RefCell::new(Inode::new([])));
1656 let mut state = system.state.borrow_mut();
1657 state.file_system.save(path, content).unwrap();
1658 drop(state);
1659
1660 let stat = system.fstatat(Fd(0), c"/some/", true).unwrap();
1661 assert_eq!(stat.mode, Mode::from_bits_retain(0o755));
1662 assert_eq!(stat.r#type, FileType::Directory);
1663 }
1665
1666 #[test]
1667 fn fstatat_fifo() {
1668 let system = VirtualSystem::new();
1669 let path = "/some/fifo";
1670 let content = Rc::new(RefCell::new(Inode {
1671 body: FileBody::Fifo {
1672 content: [17; 42].into(),
1673 readers: 0,
1674 writers: 0,
1675 pending_open_wakers: WakerSet::new(),
1676 pending_read_wakers: WakerSet::new(),
1677 pending_write_wakers: WakerSet::new(),
1678 },
1679 permissions: Mode::default(),
1680 }));
1681 let mut state = system.state.borrow_mut();
1682 state.file_system.save(path, content).unwrap();
1683 drop(state);
1684
1685 let stat = system.fstatat(Fd(0), c"/some/fifo", true).unwrap();
1686 assert_eq!(stat.mode, Mode::default());
1687 assert_eq!(stat.r#type, FileType::Fifo);
1688 assert_eq!(stat.size, 42);
1689 }
1690
1691 fn system_with_symlink() -> VirtualSystem {
1692 let system = VirtualSystem::new();
1693 let mut state = system.state.borrow_mut();
1694 state
1695 .file_system
1696 .save("/some/file", Rc::new(RefCell::new(Inode::new([]))))
1697 .unwrap();
1698 state
1699 .file_system
1700 .save(
1701 "/link",
1702 Rc::new(RefCell::new(Inode {
1703 body: FileBody::Symlink {
1704 target: "some/file".into(),
1705 },
1706 permissions: Mode::default(),
1707 })),
1708 )
1709 .unwrap();
1710 drop(state);
1711 system
1712 }
1713
1714 #[test]
1715 fn fstatat_symlink_to_regular_file() {
1716 let system = system_with_symlink();
1717 let stat = system.fstatat(Fd(0), c"/link", true).unwrap();
1718 assert_eq!(stat.r#type, FileType::Regular);
1719 }
1720
1721 #[test]
1722 fn fstatat_symlink_no_follow() {
1723 let system = system_with_symlink();
1724 let stat = system.fstatat(Fd(0), c"/link", false).unwrap();
1725 assert_eq!(stat.r#type, FileType::Symlink);
1726 }
1727
1728 #[test]
1729 fn is_executable_file_non_existing_file() {
1730 let system = VirtualSystem::new();
1731 assert!(!system.is_executable_file(c"/no/such/file"));
1732 }
1733
1734 #[test]
1735 fn is_executable_file_existing_but_non_executable_file() {
1736 let system = VirtualSystem::new();
1737 let path = "/some/file";
1738 let content = Rc::new(RefCell::new(Inode::default()));
1739 let mut state = system.state.borrow_mut();
1740 state.file_system.save(path, content).unwrap();
1741 drop(state);
1742 assert!(!system.is_executable_file(c"/some/file"));
1743 }
1744
1745 #[test]
1746 fn is_executable_file_with_executable_file() {
1747 let system = VirtualSystem::new();
1748 let path = "/some/file";
1749 let mut content = Inode::default();
1750 content.permissions.set(Mode::USER_EXEC, true);
1751 let content = Rc::new(RefCell::new(content));
1752 let mut state = system.state.borrow_mut();
1753 state.file_system.save(path, content).unwrap();
1754 drop(state);
1755 assert!(system.is_executable_file(c"/some/file"));
1756 }
1757
1758 #[test]
1759 fn pipe_read_write() {
1760 let system = VirtualSystem::new();
1761 let (reader, writer) = system.pipe().unwrap();
1762 let result = system.write(writer, &[5, 42, 29]).now_or_never().unwrap();
1763 assert_eq!(result, Ok(3));
1764
1765 let mut buffer = [1; 4];
1766 let result = system.read(reader, &mut buffer).now_or_never().unwrap();
1767 assert_eq!(result, Ok(3));
1768 assert_eq!(buffer, [5, 42, 29, 1]);
1769
1770 let result = system.close(writer);
1771 assert_eq!(result, Ok(()));
1772
1773 let result = system.read(reader, &mut buffer).now_or_never().unwrap();
1774 assert_eq!(result, Ok(0));
1775 }
1776
1777 #[test]
1778 fn read_returns_eintr_when_interrupted_by_signal() {
1779 let system = VirtualSystem::new();
1780 system.sigaction(SIGINT, Disposition::Catch).unwrap();
1781 let (reader, _writer) = system.pipe().unwrap();
1782
1783 let woken = Arc::new(WakeFlag::new());
1784 let waker = std::task::Waker::from(woken.clone());
1785 let mut context = Context::from_waker(&waker);
1786
1787 let mut buffer = [0; 4];
1788 let mut read_fut = pin!(system.read(reader, &mut buffer));
1789
1790 assert_eq!(read_fut.as_mut().poll(&mut context), Pending);
1792
1793 let _ = system.current_process_mut().raise_signal(SIGINT);
1795 assert!(woken.is_woken(), "signal should have woken the read task");
1796
1797 let woken = Arc::new(WakeFlag::new());
1799 let waker = std::task::Waker::from(woken.clone());
1800 let mut context = Context::from_waker(&waker);
1801 assert_eq!(read_fut.poll(&mut context), Ready(Err(Errno::EINTR)));
1802 }
1803
1804 #[test]
1805 fn read_does_not_return_eintr_if_data_available_despite_pending_signal() {
1806 let system = VirtualSystem::new();
1807 system.sigaction(SIGINT, Disposition::Catch).unwrap();
1808 let (reader, writer) = system.pipe().unwrap();
1809 system
1811 .write(writer, &[1, 2, 3])
1812 .now_or_never()
1813 .unwrap()
1814 .unwrap();
1815 let _ = system.current_process_mut().raise_signal(SIGINT);
1817
1818 let woken = Arc::new(WakeFlag::new());
1819 let waker = std::task::Waker::from(woken.clone());
1820 let mut context = Context::from_waker(&waker);
1821
1822 let mut buffer = [0; 4];
1823 let read_fut = pin!(system.read(reader, &mut buffer));
1824 assert_eq!(read_fut.poll(&mut context), Ready(Ok(3)));
1826 }
1827
1828 #[test]
1829 fn write_returns_eintr_when_interrupted_by_signal() {
1830 let system = VirtualSystem::new();
1831 system.sigaction(SIGINT, Disposition::Catch).unwrap();
1832 let (_reader, writer) = system.pipe().unwrap();
1833 system
1835 .write(writer, &[0u8; PIPE_SIZE])
1836 .now_or_never()
1837 .unwrap()
1838 .unwrap();
1839
1840 let woken = Arc::new(WakeFlag::new());
1841 let waker = std::task::Waker::from(woken.clone());
1842 let mut context = Context::from_waker(&waker);
1843
1844 let mut write_fut = pin!(system.write(writer, &[1]));
1845
1846 assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1848
1849 let _ = system.current_process_mut().raise_signal(SIGINT);
1851 assert!(woken.is_woken(), "signal should have woken the write task");
1852
1853 let woken = Arc::new(WakeFlag::new());
1855 let waker = std::task::Waker::from(woken.clone());
1856 let mut context = Context::from_waker(&waker);
1857 assert_eq!(write_fut.poll(&mut context), Ready(Err(Errno::EINTR)));
1858 }
1859
1860 #[test]
1861 fn write_returns_partial_count_when_interrupted_by_signal_after_partial_write() {
1862 let system = VirtualSystem::new();
1868 system.sigaction(SIGINT, Disposition::Catch).unwrap();
1869 let (reader, writer) = system.pipe().unwrap();
1870
1871 let big_buf: Vec<u8> = (0..2 * PIPE_SIZE).map(|i| i as u8).collect();
1874
1875 let woken = Arc::new(WakeFlag::new());
1876 let waker = std::task::Waker::from(woken.clone());
1877 let mut context = Context::from_waker(&waker);
1878 let mut write_fut = pin!(system.write(writer, &big_buf));
1879
1880 assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1882
1883 let _ = system.current_process_mut().raise_signal(SIGINT);
1885 assert!(woken.is_woken(), "signal should have woken the write task");
1886
1887 let woken = Arc::new(WakeFlag::new());
1889 let waker = std::task::Waker::from(woken.clone());
1890 let mut context = Context::from_waker(&waker);
1891 assert_eq!(
1892 write_fut.poll(&mut context),
1893 Ready(Ok(PIPE_SIZE)),
1894 "expected partial byte count written before the signal"
1895 );
1896
1897 let mut read_buf = [0u8; PIPE_SIZE];
1899 assert_eq!(
1900 system.read(reader, &mut read_buf).now_or_never().unwrap(),
1901 Ok(PIPE_SIZE)
1902 );
1903 assert_eq!(read_buf, big_buf[..PIPE_SIZE]);
1904
1905 assert_eq!(
1907 system.current_process().caught_signals,
1908 [SIGINT],
1909 "SIGINT should be in the caught signals list"
1910 );
1911 }
1912
1913 #[test]
1914 fn write_large_buffer_completes_fully_without_signal() {
1915 let big_buf: Vec<u8> = (0..2 * PIPE_SIZE).map(|i| i as u8).collect();
1920 let system = VirtualSystem::new();
1921 let (reader, writer) = system.pipe().unwrap();
1922
1923 let woken = Arc::new(WakeFlag::new());
1924 let waker = std::task::Waker::from(woken.clone());
1925 let mut context = Context::from_waker(&waker);
1926 let mut write_fut = pin!(system.write(writer, &big_buf));
1927
1928 assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1930
1931 let mut read_buf = [0u8; PIPE_SIZE];
1933 assert_eq!(
1934 system.read(reader, &mut read_buf).now_or_never().unwrap(),
1935 Ok(PIPE_SIZE)
1936 );
1937 assert_eq!(read_buf, big_buf[..PIPE_SIZE]);
1938 assert!(
1939 woken.is_woken(),
1940 "draining the pipe should wake the write task"
1941 );
1942
1943 let woken = Arc::new(WakeFlag::new());
1945 let waker = std::task::Waker::from(woken.clone());
1946 let mut context = Context::from_waker(&waker);
1947 assert_eq!(
1948 write_fut.poll(&mut context),
1949 Ready(Ok(2 * PIPE_SIZE)),
1950 "large write without signal must complete the full buffer"
1951 );
1952
1953 let mut read_buf2 = [0u8; PIPE_SIZE];
1955 assert_eq!(
1956 system.read(reader, &mut read_buf2).now_or_never().unwrap(),
1957 Ok(PIPE_SIZE)
1958 );
1959 assert_eq!(read_buf2, big_buf[PIPE_SIZE..]);
1960 }
1961
1962 #[test]
1963 fn write_returns_eintr_when_space_and_signal_arrive_simultaneously() {
1964 let system = VirtualSystem::new();
1970 system.sigaction(SIGINT, Disposition::Catch).unwrap();
1971 let (reader, writer) = system.pipe().unwrap();
1972 system
1974 .write(writer, &[0u8; PIPE_SIZE])
1975 .now_or_never()
1976 .unwrap()
1977 .unwrap();
1978
1979 let woken = Arc::new(WakeFlag::new());
1980 let waker = std::task::Waker::from(woken.clone());
1981 let mut context = Context::from_waker(&waker);
1982 let mut write_fut = pin!(system.write(writer, &[1]));
1983
1984 assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
1986
1987 let mut drain_buf = [0u8; PIPE_SIZE];
1990 system
1991 .read(reader, &mut drain_buf)
1992 .now_or_never()
1993 .unwrap()
1994 .unwrap();
1995 let _ = system.current_process_mut().raise_signal(SIGINT);
1996 assert!(woken.is_woken(), "write task should have been woken");
1997
1998 let woken = Arc::new(WakeFlag::new());
2000 let waker = std::task::Waker::from(woken.clone());
2001 let mut context = Context::from_waker(&waker);
2002 assert_eq!(
2003 write_fut.as_mut().poll(&mut context),
2004 Ready(Err(Errno::EINTR)),
2005 "signal should interrupt write even when space is available"
2006 );
2007
2008 let mut probe_buf = [0u8; 1];
2012 assert_eq!(
2013 system.read(reader, &mut probe_buf).now_or_never(),
2014 None,
2015 "no bytes must be transferred when EINTR is returned"
2016 );
2017 }
2018
2019 #[test]
2020 fn write_does_not_check_signals_before_first_block() {
2021 let system = VirtualSystem::new();
2026 system.sigaction(SIGINT, Disposition::Catch).unwrap();
2027 let (_reader, writer) = system.pipe().unwrap();
2028 let _ = system.current_process_mut().raise_signal(SIGINT);
2029
2030 let result = system.write(writer, &[42]).now_or_never().unwrap();
2031 assert_eq!(
2032 result,
2033 Ok(1),
2034 "write that does not block must succeed even with a pending signal"
2035 );
2036 }
2037
2038 #[test]
2039 fn write_returns_partial_count_when_space_and_signal_arrive_simultaneously() {
2040 let system = VirtualSystem::new();
2045 system.sigaction(SIGINT, Disposition::Catch).unwrap();
2046 let (reader, writer) = system.pipe().unwrap();
2047
2048 let big_buf: Vec<u8> = (0..2 * PIPE_SIZE).map(|i| i as u8).collect();
2049
2050 let woken = Arc::new(WakeFlag::new());
2051 let waker = std::task::Waker::from(woken.clone());
2052 let mut context = Context::from_waker(&waker);
2053 let mut write_fut = pin!(system.write(writer, &big_buf));
2054
2055 assert_eq!(write_fut.as_mut().poll(&mut context), Pending);
2057
2058 let mut drain_buf = [0u8; PIPE_SIZE];
2060 system
2061 .read(reader, &mut drain_buf)
2062 .now_or_never()
2063 .unwrap()
2064 .unwrap();
2065 let _ = system.current_process_mut().raise_signal(SIGINT);
2066 assert!(woken.is_woken(), "write task should have been woken");
2067
2068 let woken = Arc::new(WakeFlag::new());
2071 let waker = std::task::Waker::from(woken.clone());
2072 let mut context = Context::from_waker(&waker);
2073 assert_eq!(
2074 write_fut.poll(&mut context),
2075 Ready(Ok(PIPE_SIZE)),
2076 "signal should interrupt large write and return bytes written so far"
2077 );
2078 }
2079
2080 #[test]
2081 fn write_to_non_blocking_pipe_does_not_block() {
2082 let system = VirtualSystem::new();
2085 let (_reader, writer) = system.pipe().unwrap();
2086 system.get_and_set_nonblocking(writer, true).unwrap();
2087
2088 let big_buf = vec![0u8; PIPE_SIZE * 2];
2091 let result = system.write(writer, &big_buf).now_or_never();
2092 assert_eq!(
2093 result,
2094 Some(Ok(PIPE_SIZE)),
2095 "non-blocking write should return immediately with bytes that fit"
2096 );
2097
2098 let result = system.write(writer, &[1]).now_or_never();
2100 assert_eq!(
2101 result,
2102 Some(Err(Errno::EAGAIN)),
2103 "non-blocking write to full pipe should return EAGAIN"
2104 );
2105 }
2106
2107 #[test]
2108 fn dup_shares_open_file_description() {
2109 let system = VirtualSystem::new();
2110 let result = system.dup(Fd::STDOUT, Fd::STDERR, EnumSet::empty());
2111 assert_eq!(result, Ok(Fd(3)));
2112
2113 let process = system.current_process();
2114 let fd1 = process.fds.get(&Fd(1)).unwrap();
2115 let fd3 = process.fds.get(&Fd(3)).unwrap();
2116 assert_eq!(fd1, fd3);
2117 }
2118
2119 #[test]
2120 fn dup_can_set_cloexec() {
2121 let system = VirtualSystem::new();
2122 let result = system.dup(Fd::STDOUT, Fd::STDERR, FdFlag::CloseOnExec.into());
2123 assert_eq!(result, Ok(Fd(3)));
2124
2125 let process = system.current_process();
2126 let fd3 = process.fds.get(&Fd(3)).unwrap();
2127 assert_eq!(fd3.flags, EnumSet::only(FdFlag::CloseOnExec));
2128 }
2129
2130 #[test]
2131 fn dup2_shares_open_file_description() {
2132 let system = VirtualSystem::new();
2133 let result = system.dup2(Fd::STDOUT, Fd(5));
2134 assert_eq!(result, Ok(Fd(5)));
2135
2136 let process = system.current_process();
2137 let fd1 = process.fds.get(&Fd(1)).unwrap();
2138 let fd5 = process.fds.get(&Fd(5)).unwrap();
2139 assert_eq!(fd1, fd5);
2140 }
2141
2142 #[test]
2143 fn dup2_clears_cloexec() {
2144 let system = VirtualSystem::new();
2145 let mut process = system.current_process_mut();
2146 process.fds.get_mut(&Fd::STDOUT).unwrap().flags = FdFlag::CloseOnExec.into();
2147 drop(process);
2148
2149 let result = system.dup2(Fd::STDOUT, Fd(6));
2150 assert_eq!(result, Ok(Fd(6)));
2151
2152 let process = system.current_process();
2153 let fd6 = process.fds.get(&Fd(6)).unwrap();
2154 assert_eq!(fd6.flags, EnumSet::empty());
2155 }
2156
2157 #[test]
2158 fn open_non_existing_file_no_creation() {
2159 let system = VirtualSystem::new();
2160 let result = system
2161 .open(
2162 c"/no/such/file",
2163 OfdAccess::ReadOnly,
2164 EnumSet::empty(),
2165 Mode::empty(),
2166 )
2167 .now_or_never()
2168 .unwrap();
2169 assert_eq!(result, Err(Errno::ENOENT));
2170 }
2171
2172 #[test]
2173 fn open_creating_non_existing_file() {
2174 let system = VirtualSystem::new();
2175 let result = system
2176 .open(
2177 c"new_file",
2178 OfdAccess::WriteOnly,
2179 OpenFlag::Create.into(),
2180 Mode::empty(),
2181 )
2182 .now_or_never()
2183 .unwrap();
2184 assert_eq!(result, Ok(Fd(3)));
2185
2186 system
2187 .write(Fd(3), &[42, 123])
2188 .now_or_never()
2189 .unwrap()
2190 .unwrap();
2191 let file = system.state.borrow().file_system.get("new_file").unwrap();
2192 let file = file.borrow();
2193 assert_eq!(file.permissions, Mode::empty());
2194 assert_matches!(&file.body, FileBody::Regular { content, .. } => {
2195 assert_eq!(content[..], [42, 123]);
2196 });
2197 }
2198
2199 #[test]
2200 fn open_creating_non_existing_file_umask() {
2201 let system = VirtualSystem::new();
2202 system.umask(Mode::from_bits_retain(0o125));
2203 system
2204 .open(
2205 c"file",
2206 OfdAccess::WriteOnly,
2207 OpenFlag::Create.into(),
2208 Mode::ALL_9,
2209 )
2210 .now_or_never()
2211 .unwrap()
2212 .unwrap();
2213
2214 let file = system.state.borrow().file_system.get("file").unwrap();
2215 let file = file.borrow();
2216 assert_eq!(file.permissions, Mode::from_bits_retain(0o652));
2217 }
2218
2219 #[test]
2220 fn open_existing_file() {
2221 let system = VirtualSystem::new();
2222 let fd = system
2223 .open(
2224 c"file",
2225 OfdAccess::WriteOnly,
2226 OpenFlag::Create.into(),
2227 Mode::empty(),
2228 )
2229 .now_or_never()
2230 .unwrap()
2231 .unwrap();
2232 system
2233 .write(fd, &[75, 96, 133])
2234 .now_or_never()
2235 .unwrap()
2236 .unwrap();
2237
2238 let result = system
2239 .open(
2240 c"file",
2241 OfdAccess::ReadOnly,
2242 EnumSet::empty(),
2243 Mode::empty(),
2244 )
2245 .now_or_never()
2246 .unwrap();
2247 assert_eq!(result, Ok(Fd(4)));
2248
2249 let mut buffer = [0; 5];
2250 let count = system
2251 .read(Fd(4), &mut buffer)
2252 .now_or_never()
2253 .unwrap()
2254 .unwrap();
2255 assert_eq!(count, 3);
2256 assert_eq!(buffer, [75, 96, 133, 0, 0]);
2257 let count = system
2258 .read(Fd(4), &mut buffer)
2259 .now_or_never()
2260 .unwrap()
2261 .unwrap();
2262 assert_eq!(count, 0);
2263 }
2264
2265 #[test]
2266 fn open_existing_file_excl() {
2267 let system = VirtualSystem::new();
2268 let first = system
2269 .open(
2270 c"my_file",
2271 OfdAccess::WriteOnly,
2272 OpenFlag::Create | OpenFlag::Exclusive,
2273 Mode::empty(),
2274 )
2275 .now_or_never()
2276 .unwrap();
2277 assert_eq!(first, Ok(Fd(3)));
2278
2279 let second = system
2280 .open(
2281 c"my_file",
2282 OfdAccess::WriteOnly,
2283 OpenFlag::Create | OpenFlag::Exclusive,
2284 Mode::empty(),
2285 )
2286 .now_or_never()
2287 .unwrap();
2288 assert_eq!(second, Err(Errno::EEXIST));
2289 }
2290
2291 #[test]
2292 fn open_truncating() {
2293 let system = VirtualSystem::new();
2294 let fd = system
2295 .open(
2296 c"file",
2297 OfdAccess::WriteOnly,
2298 OpenFlag::Create.into(),
2299 Mode::ALL_9,
2300 )
2301 .now_or_never()
2302 .unwrap()
2303 .unwrap();
2304 system
2305 .write(fd, &[1, 2, 3])
2306 .now_or_never()
2307 .unwrap()
2308 .unwrap();
2309
2310 let result = system
2311 .open(
2312 c"file",
2313 OfdAccess::WriteOnly,
2314 OpenFlag::Truncate.into(),
2315 Mode::empty(),
2316 )
2317 .now_or_never()
2318 .unwrap();
2319 assert_eq!(result, Ok(Fd(4)));
2320
2321 let reader = system
2322 .open(
2323 c"file",
2324 OfdAccess::ReadOnly,
2325 EnumSet::empty(),
2326 Mode::empty(),
2327 )
2328 .now_or_never()
2329 .unwrap()
2330 .unwrap();
2331 let count = system
2332 .read(reader, &mut [0; 1])
2333 .now_or_never()
2334 .unwrap()
2335 .unwrap();
2336 assert_eq!(count, 0);
2337 }
2338
2339 #[test]
2340 fn open_appending() {
2341 let system = VirtualSystem::new();
2342 let fd = system
2343 .open(
2344 c"file",
2345 OfdAccess::WriteOnly,
2346 OpenFlag::Create.into(),
2347 Mode::ALL_9,
2348 )
2349 .now_or_never()
2350 .unwrap()
2351 .unwrap();
2352 system
2353 .write(fd, &[1, 2, 3])
2354 .now_or_never()
2355 .unwrap()
2356 .unwrap();
2357
2358 let result = system
2359 .open(
2360 c"file",
2361 OfdAccess::WriteOnly,
2362 OpenFlag::Append.into(),
2363 Mode::empty(),
2364 )
2365 .now_or_never()
2366 .unwrap();
2367 assert_eq!(result, Ok(Fd(4)));
2368 system
2369 .write(Fd(4), &[4, 5, 6])
2370 .now_or_never()
2371 .unwrap()
2372 .unwrap();
2373
2374 let reader = system
2375 .open(
2376 c"file",
2377 OfdAccess::ReadOnly,
2378 EnumSet::empty(),
2379 Mode::empty(),
2380 )
2381 .now_or_never()
2382 .unwrap()
2383 .unwrap();
2384 let mut buffer = [0; 7];
2385 let count = system
2386 .read(reader, &mut buffer)
2387 .now_or_never()
2388 .unwrap()
2389 .unwrap();
2390 assert_eq!(count, 6);
2391 assert_eq!(buffer, [1, 2, 3, 4, 5, 6, 0]);
2392 }
2393
2394 #[test]
2395 fn open_directory() {
2396 let system = VirtualSystem::new();
2397
2398 let _ = system
2400 .open(
2401 c"/dir/file",
2402 OfdAccess::WriteOnly,
2403 OpenFlag::Create.into(),
2404 Mode::empty(),
2405 )
2406 .now_or_never()
2407 .unwrap();
2408
2409 let result = system
2410 .open(
2411 c"/dir",
2412 OfdAccess::ReadOnly,
2413 OpenFlag::Directory.into(),
2414 Mode::empty(),
2415 )
2416 .now_or_never()
2417 .unwrap();
2418 assert_eq!(result, Ok(Fd(4)));
2419 }
2420
2421 #[test]
2422 fn open_non_directory_path_prefix() {
2423 let system = VirtualSystem::new();
2424
2425 let _ = system
2427 .open(
2428 c"/file",
2429 OfdAccess::WriteOnly,
2430 OpenFlag::Create.into(),
2431 Mode::empty(),
2432 )
2433 .now_or_never()
2434 .unwrap();
2435
2436 let result = system
2437 .open(
2438 c"/file/file",
2439 OfdAccess::WriteOnly,
2440 OpenFlag::Create.into(),
2441 Mode::empty(),
2442 )
2443 .now_or_never()
2444 .unwrap();
2445 assert_eq!(result, Err(Errno::ENOTDIR));
2446 }
2447
2448 #[test]
2449 fn open_non_directory_file() {
2450 let system = VirtualSystem::new();
2451
2452 let _ = system
2454 .open(
2455 c"/file",
2456 OfdAccess::WriteOnly,
2457 OpenFlag::Create.into(),
2458 Mode::empty(),
2459 )
2460 .now_or_never()
2461 .unwrap();
2462
2463 let result = system
2464 .open(
2465 c"/file",
2466 OfdAccess::ReadOnly,
2467 OpenFlag::Directory.into(),
2468 Mode::empty(),
2469 )
2470 .now_or_never()
2471 .unwrap();
2472 assert_eq!(result, Err(Errno::ENOTDIR));
2473 }
2474
2475 #[test]
2476 fn open_default_working_directory() {
2477 let system = VirtualSystem::new();
2479
2480 let writer = system
2481 .open(
2482 c"/dir/file",
2483 OfdAccess::WriteOnly,
2484 OpenFlag::Create.into(),
2485 Mode::ALL_9,
2486 )
2487 .now_or_never()
2488 .unwrap();
2489 system
2490 .write(writer.unwrap(), &[1, 2, 3, 42])
2491 .now_or_never()
2492 .unwrap()
2493 .unwrap();
2494
2495 let reader = system
2496 .open(
2497 c"./dir/file",
2498 OfdAccess::ReadOnly,
2499 EnumSet::empty(),
2500 Mode::empty(),
2501 )
2502 .now_or_never()
2503 .unwrap();
2504 let mut buffer = [0; 10];
2505 let count = system
2506 .read(reader.unwrap(), &mut buffer)
2507 .now_or_never()
2508 .unwrap()
2509 .unwrap();
2510 assert_eq!(count, 4);
2511 assert_eq!(buffer[0..4], [1, 2, 3, 42]);
2512 }
2513
2514 #[test]
2515 fn open_tmpfile() {
2516 let system = VirtualSystem::new();
2517 let fd = system.open_tmpfile(Path::new("")).unwrap();
2518 system
2519 .write(fd, &[42, 17, 75])
2520 .now_or_never()
2521 .unwrap()
2522 .unwrap();
2523 system.lseek(fd, SeekFrom::Start(0)).unwrap();
2524 let mut buffer = [0; 4];
2525 let count = system
2526 .read(fd, &mut buffer)
2527 .now_or_never()
2528 .unwrap()
2529 .unwrap();
2530 assert_eq!(count, 3);
2531 assert_eq!(buffer[..3], [42, 17, 75]);
2532 }
2533
2534 #[test]
2535 fn close() {
2536 let system = VirtualSystem::new();
2537
2538 let result = system.close(Fd::STDERR);
2539 assert_eq!(result, Ok(()));
2540 assert_eq!(system.current_process().fds.get(&Fd::STDERR), None);
2541
2542 let result = system.close(Fd::STDERR);
2543 assert_eq!(result, Ok(()));
2544 }
2545
2546 #[test]
2547 fn fcntl_getfd_and_setfd() {
2548 let system = VirtualSystem::new();
2549
2550 let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
2551 assert_eq!(flags, EnumSet::empty());
2552
2553 system
2554 .fcntl_setfd(Fd::STDIN, FdFlag::CloseOnExec.into())
2555 .unwrap();
2556
2557 let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
2558 assert_eq!(flags, EnumSet::only(FdFlag::CloseOnExec));
2559
2560 let flags = system.fcntl_getfd(Fd::STDOUT).unwrap();
2561 assert_eq!(flags, EnumSet::empty());
2562
2563 system.fcntl_setfd(Fd::STDIN, EnumSet::empty()).unwrap();
2564
2565 let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
2566 assert_eq!(flags, EnumSet::empty());
2567 }
2568
2569 #[test]
2570 fn opendir_default_working_directory() {
2571 let system = VirtualSystem::new();
2573
2574 let _ = system
2575 .open(
2576 c"/dir/file",
2577 OfdAccess::WriteOnly,
2578 OpenFlag::Create.into(),
2579 Mode::ALL_9,
2580 )
2581 .now_or_never()
2582 .unwrap();
2583
2584 let mut dir = system.opendir(c"./dir").unwrap();
2585 let mut files = Vec::new();
2586 while let Some(entry) = dir.next().unwrap() {
2587 files.push(entry.name.to_unix_string());
2588 }
2589 files.sort_unstable();
2590 assert_eq!(
2591 files[..],
2592 [
2593 UnixString::from("."),
2594 UnixString::from(".."),
2595 UnixString::from("file")
2596 ]
2597 );
2598 }
2599
2600 #[test]
2603 fn sigmask_suspends_on_pending_stop_signal() {
2604 let system = VirtualSystem::new();
2605 system
2607 .sigmask(Some((SigmaskOp::Add, &Sigset::from(SIGTSTP))), None)
2608 .now_or_never()
2609 .unwrap()
2610 .unwrap();
2611 let _ = system.current_process_mut().raise_signal(SIGTSTP);
2613 let result = system
2615 .sigmask(Some((SigmaskOp::Remove, &Sigset::from(SIGTSTP))), None)
2616 .now_or_never();
2617 assert_eq!(result, None);
2619 assert_eq!(
2620 system.current_process().state(),
2621 ProcessState::stopped(SIGTSTP),
2622 );
2623 }
2624
2625 #[test]
2626 fn sigmask_resumes_after_sigcont() {
2627 let (system, mut executor) = virtual_system_with_executor();
2628 let state = Rc::clone(&system.state);
2629 let mut env = Env::with_system(system);
2630 let pid = env
2631 .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
2632 child_env
2634 .system
2635 .sigmask(Some((SigmaskOp::Add, &Sigset::from(SIGTSTP))), None)
2636 .await
2637 .unwrap();
2638 child_env.system.raise(SIGTSTP).await.unwrap();
2640 child_env
2643 .system
2644 .sigmask(Some((SigmaskOp::Remove, &Sigset::from(SIGTSTP))), None)
2645 .await
2646 .unwrap();
2647 child_env.system.exit(ExitStatus(0)).await;
2649 })
2650 .0
2651 .unwrap();
2652 executor.run_until_stalled();
2653
2654 assert_eq!(
2656 state.borrow().processes[&pid].state,
2657 ProcessState::stopped(SIGTSTP),
2658 );
2659
2660 env.system
2662 .kill(pid, Some(SIGCONT))
2663 .now_or_never()
2664 .unwrap()
2665 .unwrap();
2666
2667 executor.run_until_stalled();
2668
2669 let result = env.system.wait(pid);
2671 assert_eq!(result, Ok(Some((pid, ProcessState::exited(0)))));
2672 }
2673
2674 #[test]
2675 fn kill_process() {
2676 let system = VirtualSystem::new();
2677 system
2678 .kill(system.process_id, None)
2679 .now_or_never()
2680 .unwrap()
2681 .unwrap();
2682 assert_eq!(system.current_process().state(), ProcessState::Running);
2683
2684 let result = system.kill(system.process_id, Some(SIGINT)).now_or_never();
2685 assert_eq!(result, None);
2687 assert_eq!(
2688 system.current_process().state(),
2689 ProcessState::Halted(ProcessResult::Signaled {
2690 signal: SIGINT,
2691 core_dump: false
2692 })
2693 );
2694
2695 let system = VirtualSystem::new();
2696 let state = system.state.borrow();
2697 let max_pid = *state.processes.keys().max().unwrap();
2698 drop(state);
2699 let e = system
2700 .kill(Pid(max_pid.0 + 1), Some(SIGINT))
2701 .now_or_never()
2702 .unwrap()
2703 .unwrap_err();
2704 assert_eq!(e, Errno::ESRCH);
2705 }
2706
2707 #[test]
2708 fn kill_all_processes() {
2709 let system = VirtualSystem::new();
2710 let pgid = system.current_process().pgid;
2711 let mut state = system.state.borrow_mut();
2712 state.processes.insert(
2713 Pid(10),
2714 Process::with_parent_and_group(system.process_id, pgid),
2715 );
2716 state.processes.insert(
2717 Pid(11),
2718 Process::with_parent_and_group(system.process_id, pgid),
2719 );
2720 state
2721 .processes
2722 .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2723 drop(state);
2724
2725 let result = system.kill(Pid::ALL, Some(SIGTERM)).now_or_never();
2726 assert_eq!(result, None);
2728 let state = system.state.borrow();
2729 for process in state.processes.values() {
2730 assert_eq!(
2731 process.state,
2732 ProcessState::Halted(ProcessResult::Signaled {
2733 signal: SIGTERM,
2734 core_dump: false
2735 })
2736 );
2737 }
2738 }
2739
2740 #[test]
2741 fn kill_processes_in_same_group() {
2742 let system = VirtualSystem::new();
2743 let pgid = system.current_process().pgid;
2744 let mut state = system.state.borrow_mut();
2745 state.processes.insert(
2746 Pid(10),
2747 Process::with_parent_and_group(system.process_id, pgid),
2748 );
2749 state.processes.insert(
2750 Pid(11),
2751 Process::with_parent_and_group(system.process_id, pgid),
2752 );
2753 state
2754 .processes
2755 .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2756 drop(state);
2757
2758 let result = system
2759 .kill(Pid::MY_PROCESS_GROUP, Some(SIGQUIT))
2760 .now_or_never();
2761 assert_eq!(result, None);
2763 let state = system.state.borrow();
2764 assert_eq!(
2765 state.processes[&system.process_id].state,
2766 ProcessState::Halted(ProcessResult::Signaled {
2767 signal: SIGQUIT,
2768 core_dump: true
2769 })
2770 );
2771 assert_eq!(
2772 state.processes[&Pid(10)].state,
2773 ProcessState::Halted(ProcessResult::Signaled {
2774 signal: SIGQUIT,
2775 core_dump: true
2776 })
2777 );
2778 assert_eq!(
2779 state.processes[&Pid(11)].state,
2780 ProcessState::Halted(ProcessResult::Signaled {
2781 signal: SIGQUIT,
2782 core_dump: true
2783 })
2784 );
2785 assert_eq!(state.processes[&Pid(21)].state, ProcessState::Running);
2786 }
2787
2788 #[test]
2789 fn kill_process_group() {
2790 let system = VirtualSystem::new();
2791 let pgid = system.current_process().pgid;
2792 let mut state = system.state.borrow_mut();
2793 state.processes.insert(
2794 Pid(10),
2795 Process::with_parent_and_group(system.process_id, pgid),
2796 );
2797 state.processes.insert(
2798 Pid(11),
2799 Process::with_parent_and_group(system.process_id, Pid(21)),
2800 );
2801 state
2802 .processes
2803 .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2804 drop(state);
2805
2806 system
2807 .kill(Pid(-21), Some(SIGHUP))
2808 .now_or_never()
2809 .unwrap()
2810 .unwrap();
2811 let state = system.state.borrow();
2812 assert_eq!(
2813 state.processes[&system.process_id].state,
2814 ProcessState::Running
2815 );
2816 assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2817 assert_eq!(
2818 state.processes[&Pid(11)].state,
2819 ProcessState::Halted(ProcessResult::Signaled {
2820 signal: SIGHUP,
2821 core_dump: false
2822 })
2823 );
2824 assert_eq!(
2825 state.processes[&Pid(21)].state,
2826 ProcessState::Halted(ProcessResult::Signaled {
2827 signal: SIGHUP,
2828 core_dump: false
2829 })
2830 );
2831 }
2832
2833 #[test]
2834 fn kill_returns_success_even_if_process_state_did_not_change() {
2835 let system = VirtualSystem::new();
2836 let pgid = system.current_process().pgid;
2837 let mut state = system.state.borrow_mut();
2838 state.processes.insert(
2839 Pid(10),
2840 Process::with_parent_and_group(system.process_id, pgid),
2841 );
2842 drop(state);
2843
2844 system
2845 .kill(-pgid, Some(SIGCONT))
2846 .now_or_never()
2847 .unwrap()
2848 .unwrap();
2849 let state = system.state.borrow();
2850 assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2851 }
2852
2853 #[test]
2854 fn kill_dummy_signal_to_my_group() {
2855 let system = VirtualSystem::new();
2856
2857 let result = system
2858 .kill(Pid::MY_PROCESS_GROUP, None)
2859 .now_or_never()
2860 .unwrap();
2861
2862 assert_eq!(result, Ok(()));
2863 assert_eq!(system.current_process().state(), ProcessState::Running);
2864 }
2865
2866 #[test]
2867 fn kill_dummy_signal_to_non_existent_group() {
2868 let system = VirtualSystem::new();
2869 let result = system.kill(Pid(-9999), None).now_or_never().unwrap();
2870 assert_eq!(result, Err(Errno::ESRCH));
2871 }
2872
2873 pub(super) fn virtual_system_with_executor() -> (VirtualSystem, LocalPool) {
2874 let system = VirtualSystem::new();
2875 let executor = LocalPool::new();
2876 system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
2877 (system, executor)
2878 }
2879
2880 #[test]
2881 fn setpgid_creating_new_group_from_parent() {
2882 let (system, _executor) = virtual_system_with_executor();
2883 let state = Rc::clone(&system.state);
2884 let mut env = Env::with_system(system);
2885 let pid = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2886
2887 let result = env.system.setpgid(pid, pid);
2888 assert_eq!(result, Ok(()));
2889
2890 let pgid = state.borrow().processes[&pid].pgid();
2891 assert_eq!(pgid, pid);
2892 }
2893
2894 #[test]
2895 fn setpgid_creating_new_group_from_child() {
2896 let (system, mut executor) = virtual_system_with_executor();
2897 let state = Rc::clone(&system.state);
2898 let mut env = Env::with_system(system);
2899 let pid = env
2900 .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
2901 let result = child_env.system.setpgid(Pid(0), Pid(0));
2902 assert_eq!(result, Ok(()));
2903 child_env.system.exit(child_env.exit_status).await;
2904 })
2905 .0
2906 .unwrap();
2907 executor.run_until_stalled();
2908
2909 let pgid = state.borrow().processes[&pid].pgid();
2910 assert_eq!(pgid, pid);
2911 }
2912
2913 #[test]
2914 fn setpgid_extending_existing_group_from_parent() {
2915 let (system, _executor) = virtual_system_with_executor();
2916 let state = Rc::clone(&system.state);
2917 let mut env = Env::with_system(system);
2918 let pid_1 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2919 env.system.setpgid(pid_1, pid_1).unwrap();
2920 let pid_2 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2921
2922 let result = env.system.setpgid(pid_2, pid_1);
2923 assert_eq!(result, Ok(()));
2924
2925 let pgid = state.borrow().processes[&pid_2].pgid();
2926 assert_eq!(pgid, pid_1);
2927 }
2928
2929 #[test]
2930 fn setpgid_with_nonexisting_pid() {
2931 let (system, _executor) = virtual_system_with_executor();
2932 let state = Rc::clone(&system.state);
2933 let mut env = Env::with_system(system);
2934 let pid = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
2935
2936 let dummy_pid = Pid(123);
2937 let result = env.system.setpgid(dummy_pid, dummy_pid);
2938 assert_eq!(result, Err(Errno::ESRCH));
2939
2940 let pgid = state.borrow().processes[&pid].pgid();
2941 assert_eq!(pgid, Pid(1));
2942 }
2943
2944 #[test]
2945 fn setpgid_with_unrelated_pid() {
2946 let (system, mut executor) = virtual_system_with_executor();
2947 let parent_pid = system.process_id;
2948 let state = Rc::clone(&system.state);
2949 let mut env = Env::with_system(system);
2950 let _pid = env
2951 .run_in_child_process((), move |child_env: Env<VirtualSystem>, ()| async move {
2952 let result = child_env.system.setpgid(parent_pid, Pid(0));
2953 assert_eq!(result, Err(Errno::ESRCH));
2954 child_env.system.exit(child_env.exit_status).await;
2955 })
2956 .0
2957 .unwrap();
2958 executor.run_until_stalled();
2959
2960 let pgid = state.borrow().processes[&parent_pid].pgid();
2961 assert_eq!(pgid, Pid(1));
2962 }
2963
2964 #[test]
2965 fn setpgid_with_execed_child() {
2966 let (system, mut executor) = virtual_system_with_executor();
2967 let path = "/some/file";
2968 let mut content = Inode::default();
2969 content.body = FileBody::Regular {
2970 content: vec![],
2971 is_native_executable: true,
2972 };
2973 content.permissions.set(Mode::USER_EXEC, true);
2974 let content = Rc::new(RefCell::new(content));
2975 let state = Rc::clone(&system.state);
2976 state.borrow_mut().file_system.save(path, content).unwrap();
2977 let mut env = Env::with_system(system);
2978 let pid = env
2979 .run_in_child_process((), move |child_env: Env<VirtualSystem>, ()| async move {
2980 let path = CString::new(path).unwrap();
2981 child_env
2982 .system
2983 .execve(&path, &[] as &[CString], &[] as &[CString])
2984 .await
2985 .ok();
2986 child_env.system.exit(child_env.exit_status).await;
2987 })
2988 .0
2989 .unwrap();
2990 executor.run_until_stalled();
2991
2992 let result = env.system.setpgid(pid, pid);
2993 assert_eq!(result, Err(Errno::EACCES));
2994
2995 let pgid = state.borrow().processes[&pid].pgid();
2996 assert_eq!(pgid, Pid(1));
2997 }
2998
2999 #[test]
3000 fn setpgid_with_nonexisting_pgid() {
3001 let (system, mut executor) = virtual_system_with_executor();
3002 let state = Rc::clone(&system.state);
3003 let mut env = Env::with_system(system);
3004 let pid_1 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
3005 let pid_2 = env.run_in_child_process((), |_, ()| pending()).0.unwrap();
3007 executor.run_until_stalled();
3008
3009 let result = env.system.setpgid(pid_2, pid_1);
3010 assert_eq!(result, Err(Errno::EPERM));
3011
3012 let pgid = state.borrow().processes[&pid_2].pgid();
3013 assert_eq!(pgid, Pid(1));
3014 }
3015
3016 #[test]
3017 fn tcsetpgrp_success() {
3018 let system = VirtualSystem::new();
3019 let pid = Pid(10);
3020 let ppid = system.process_id;
3021 let pgid = Pid(9);
3022 system
3023 .state
3024 .borrow_mut()
3025 .processes
3026 .insert(pid, Process::with_parent_and_group(ppid, pgid));
3027
3028 system
3029 .tcsetpgrp(Fd::STDIN, pgid)
3030 .now_or_never()
3031 .unwrap()
3032 .unwrap();
3033
3034 let foreground = system.state.borrow().foreground;
3035 assert_eq!(foreground, Some(pgid));
3036 }
3037
3038 #[test]
3039 fn tcsetpgrp_with_invalid_fd() {
3040 let system = VirtualSystem::new();
3041 let result = system.tcsetpgrp(Fd(100), Pid(2)).now_or_never().unwrap();
3042 assert_eq!(result, Err(Errno::EBADF));
3043 }
3044
3045 #[test]
3046 fn tcsetpgrp_with_nonexisting_pgrp() {
3047 let system = VirtualSystem::new();
3048 let result = system
3049 .tcsetpgrp(Fd::STDIN, Pid(100))
3050 .now_or_never()
3051 .unwrap();
3052 assert_eq!(result, Err(Errno::EPERM));
3053 }
3054
3055 #[test]
3056 fn run_in_child_process_shares_data() {
3057 let (system, mut executor) = virtual_system_with_executor();
3058 let data = "foo".to_string();
3059 let (_pid_or_error, data) =
3060 system.run_in_child_process(data, |child_system: VirtualSystem, data| async move {
3061 assert_eq!(data, "foo");
3062 child_system.exit(ExitStatus(0)).await;
3063 });
3064
3065 assert_eq!(data, "foo");
3066 executor.run_until_stalled();
3067 }
3068
3069 #[test]
3070 fn run_in_child_process_chooses_minimal_unused_pid() {
3071 let (system, _executor) = virtual_system_with_executor();
3072 let dummy_process = Process::with_parent_and_group(system.process_id, Pid(1));
3073 system
3074 .state
3075 .borrow_mut()
3076 .processes
3077 .insert(Pid(42), dummy_process);
3078
3079 let (pid_or_error, ()) = system.run_in_child_process((), |_, ()| pending());
3080 assert_eq!(pid_or_error, Ok(Pid(43)));
3081 }
3082
3083 #[test]
3084 fn run_in_child_process_adds_child_to_process_table() {
3085 let (system, mut executor) = virtual_system_with_executor();
3086 let child_pid = Rc::new(Cell::new(None));
3087
3088 let (pid_or_error, child_pid) = system.run_in_child_process(
3089 child_pid,
3090 |child_system: VirtualSystem, child_pid: Rc<Cell<Option<Pid>>>| async move {
3091 child_pid.set(Some(child_system.getpid()));
3092 child_system.exit(ExitStatus(0)).await;
3093 },
3094 );
3095 executor.run_until_stalled();
3096
3097 let child_pid = child_pid.get().expect("child task should have set pid");
3098 assert_eq!(pid_or_error, Ok(child_pid));
3099 let state = system.state.borrow();
3100 let child_process = state
3101 .processes
3102 .get(&child_pid)
3103 .expect("child process should be in process table");
3104 assert_eq!(child_process.ppid(), system.process_id);
3105 }
3106
3107 #[test]
3108 #[should_panic = "the child task should have terminated itself by exiting or signaling itself: pid=3"]
3109 fn run_in_child_process_panics_on_child_task_not_exiting() {
3110 let (system, mut executor) = virtual_system_with_executor();
3111
3112 system
3113 .run_in_child_process((), |_, ()| async { })
3114 .0
3115 .unwrap();
3116
3117 executor.run_until_stalled();
3118 }
3119
3120 #[test]
3121 fn run_in_child_process_fails_without_executor() {
3122 let system = VirtualSystem::new();
3123 let result = system
3124 .run_in_child_process((), |_, ()| {
3125 unreachable!("child task should not run without executor")
3126 as std::future::Pending<()>
3127 })
3128 .0;
3129 assert_eq!(result, Err(Errno::ENOSYS));
3130 }
3131
3132 #[test]
3133 fn wait_for_running_child() {
3134 let (system, _executor) = virtual_system_with_executor();
3135 let mut env = Env::with_system(system);
3136
3137 let pid = env
3138 .run_in_child_process((), |_, ()| async {
3139 unreachable!("child process does not progress unless executor is used")
3140 })
3141 .0
3142 .unwrap();
3143
3144 let result = env.system.wait(pid);
3145 assert_eq!(result, Ok(None))
3146 }
3147
3148 #[test]
3149 fn wait_for_exited_child() {
3150 let (system, mut executor) = virtual_system_with_executor();
3151 let mut env = Env::with_system(system);
3152
3153 let pid = env
3154 .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3155 child_env.system.exit(ExitStatus(5)).await;
3156 })
3157 .0
3158 .unwrap();
3159 executor.run_until_stalled();
3160
3161 let result = env.system.wait(pid);
3162 assert_eq!(result, Ok(Some((pid, ProcessState::exited(5)))));
3163 }
3164
3165 #[test]
3166 fn wait_for_signaled_child() {
3167 let (system, mut executor) = virtual_system_with_executor();
3168 let mut env = Env::with_system(system);
3169
3170 let pid = env
3171 .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3172 let pid = child_env.system.getpid();
3173 let result = child_env.system.kill(pid, Some(SIGKILL)).await;
3174 unreachable!("kill returned {result:?}");
3175 })
3176 .0
3177 .unwrap();
3178 executor.run_until_stalled();
3179
3180 let result = env.system.wait(pid);
3181 assert_eq!(
3182 result,
3183 Ok(Some((
3184 pid,
3185 ProcessState::Halted(ProcessResult::Signaled {
3186 signal: SIGKILL,
3187 core_dump: false
3188 })
3189 )))
3190 );
3191 }
3192
3193 #[test]
3194 fn wait_for_stopped_child() {
3195 let (system, mut executor) = virtual_system_with_executor();
3196 let mut env = Env::with_system(system);
3197
3198 let pid = env
3199 .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3200 let pid = child_env.system.getpid();
3201 let result = child_env.system.kill(pid, Some(SIGSTOP)).await;
3202 unreachable!("kill returned {result:?}");
3203 })
3204 .0
3205 .unwrap();
3206 executor.run_until_stalled();
3207
3208 let result = env.system.wait(pid);
3209 assert_eq!(result, Ok(Some((pid, ProcessState::stopped(SIGSTOP)))));
3210 }
3211
3212 #[test]
3213 fn wait_for_resumed_child() {
3214 let (system, mut executor) = virtual_system_with_executor();
3215 let mut env = Env::with_system(system);
3216
3217 let pid = env
3218 .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3219 let pid = child_env.system.getpid();
3220 child_env.system.kill(pid, Some(SIGSTOP)).await.unwrap();
3221 child_env.system.exit(ExitStatus(123)).await;
3222 })
3223 .0
3224 .unwrap();
3225 executor.run_until_stalled();
3226
3227 env.system
3228 .kill(pid, Some(SIGCONT))
3229 .now_or_never()
3230 .unwrap()
3231 .unwrap();
3232
3233 let result = env.system.wait(pid);
3234 assert_eq!(result, Ok(Some((pid, ProcessState::Running))));
3235
3236 executor.run_until_stalled();
3237
3238 let result = env.system.wait(pid);
3239 assert_eq!(result, Ok(Some((pid, ProcessState::exited(123)))));
3240 }
3241
3242 #[test]
3243 fn wait_without_child() {
3244 let system = VirtualSystem::new();
3245 let result = system.wait(Pid::ALL);
3246 assert_eq!(result, Err(Errno::ECHILD));
3247 let result = system.wait(system.process_id);
3251 assert_eq!(result, Err(Errno::ECHILD));
3252 let result = system.wait(Pid(1234));
3253 assert_eq!(result, Err(Errno::ECHILD));
3254 }
3258
3259 #[test]
3260 fn execve_returns_enosys_for_executable_file() {
3261 let system = VirtualSystem::new();
3262 let path = "/some/file";
3263 let mut content = Inode::default();
3264 content.body = FileBody::Regular {
3265 content: vec![],
3266 is_native_executable: true,
3267 };
3268 content.permissions.set(Mode::USER_EXEC, true);
3269 let content = Rc::new(RefCell::new(content));
3270 let mut state = system.state.borrow_mut();
3271 state.file_system.save(path, content).unwrap();
3272 drop(state);
3273 let path = CString::new(path).unwrap();
3274 let result = system
3275 .execve(&path, &[] as &[CString], &[] as &[CString])
3276 .now_or_never()
3277 .unwrap();
3278 assert_eq!(result, Err(Errno::ENOSYS));
3279 }
3280
3281 #[test]
3282 fn execve_saves_arguments() {
3283 let system = VirtualSystem::new();
3284 let path = "/some/file";
3285 let mut content = Inode::default();
3286 content.body = FileBody::Regular {
3287 content: vec![],
3288 is_native_executable: true,
3289 };
3290 content.permissions.set(Mode::USER_EXEC, true);
3291 let content = Rc::new(RefCell::new(content));
3292 let mut state = system.state.borrow_mut();
3293 state.file_system.save(path, content).unwrap();
3294 drop(state);
3295 let path = CString::new(path).unwrap();
3296 let args = [c"file", c"bar"];
3297 let envs = [c"foo=FOO", c"baz"];
3298 system
3299 .execve(&path, args.as_slice(), envs.as_slice())
3300 .now_or_never();
3301
3302 let process = system.current_process();
3303 let arguments = process.last_exec.as_ref().unwrap();
3304 assert_eq!(arguments.0, path);
3305 assert_eq!(arguments.1, args);
3306 assert_eq!(arguments.2, envs);
3307 }
3308
3309 #[test]
3310 fn execve_returns_enoexec_for_non_executable_file() {
3311 let system = VirtualSystem::new();
3312 let path = "/some/file";
3313 let mut content = Inode::default();
3314 content.permissions.set(Mode::USER_EXEC, true);
3315 let content = Rc::new(RefCell::new(content));
3316 let mut state = system.state.borrow_mut();
3317 state.file_system.save(path, content).unwrap();
3318 drop(state);
3319 let path = CString::new(path).unwrap();
3320 let result = system
3321 .execve(&path, &[] as &[CString], &[] as &[CString])
3322 .now_or_never()
3323 .unwrap();
3324 assert_eq!(result, Err(Errno::ENOEXEC));
3325 }
3326
3327 #[test]
3328 fn execve_returns_enoent_on_file_not_found() {
3329 let system = VirtualSystem::new();
3330 let result = system
3331 .execve(c"/no/such/file", &[] as &[CString], &[] as &[CString])
3332 .now_or_never()
3333 .unwrap();
3334 assert_eq!(result, Err(Errno::ENOENT));
3335 }
3336
3337 #[test]
3338 fn exit_sets_current_process_state_to_exited() {
3339 let system = VirtualSystem::new();
3340 system.exit(ExitStatus(42)).now_or_never();
3341
3342 assert!(system.current_process().state_has_changed());
3343 assert_eq!(
3344 system.current_process().state(),
3345 ProcessState::exited(ExitStatus(42))
3346 );
3347 }
3348
3349 #[test]
3350 fn exit_sends_sigchld_to_parent() {
3351 let (system, mut executor) = virtual_system_with_executor();
3352 system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
3353 let system2 = system.clone();
3354 let mut env = Env::with_system(system);
3355
3356 let _pid = env
3357 .run_in_child_process((), |child_env: Env<VirtualSystem>, ()| async move {
3358 child_env.system.exit(ExitStatus(123)).await;
3359 })
3360 .0
3361 .unwrap();
3362 executor.run_until_stalled();
3363
3364 assert_eq!(system2.current_process().caught_signals, [SIGCHLD]);
3365 }
3366
3367 #[test]
3368 fn chdir_changes_directory() {
3369 let system = VirtualSystem::new();
3370
3371 let _ = system
3373 .open(
3374 c"/dir/file",
3375 OfdAccess::WriteOnly,
3376 OpenFlag::Create.into(),
3377 Mode::empty(),
3378 )
3379 .now_or_never()
3380 .unwrap();
3381
3382 let result = system.chdir(c"/dir");
3383 assert_eq!(result, Ok(()));
3384 assert_eq!(system.current_process().cwd, Path::new("/dir"));
3385 }
3386
3387 #[test]
3388 fn chdir_fails_with_non_existing_directory() {
3389 let system = VirtualSystem::new();
3390
3391 let result = system.chdir(c"/no/such/dir");
3392 assert_eq!(result, Err(Errno::ENOENT));
3393 }
3394
3395 #[test]
3396 fn chdir_fails_with_non_directory_file() {
3397 let system = VirtualSystem::new();
3398
3399 let _ = system
3401 .open(
3402 c"/dir/file",
3403 OfdAccess::WriteOnly,
3404 OpenFlag::Create.into(),
3405 Mode::empty(),
3406 )
3407 .now_or_never()
3408 .unwrap();
3409
3410 let result = system.chdir(c"/dir/file");
3411 assert_eq!(result, Err(Errno::ENOTDIR));
3412 }
3413
3414 #[test]
3415 fn getrlimit_for_unset_resource_returns_infinity() {
3416 let system = VirtualSystem::new();
3417 let result = system.getrlimit(Resource::CPU).unwrap();
3418 assert_eq!(
3419 result,
3420 LimitPair {
3421 soft: INFINITY,
3422 hard: INFINITY,
3423 },
3424 );
3425 }
3426
3427 #[test]
3428 fn setrlimit_and_getrlimit_with_finite_limits() {
3429 let system = VirtualSystem::new();
3430 system
3431 .setrlimit(
3432 Resource::CORE,
3433 LimitPair {
3434 soft: 4096,
3435 hard: 8192,
3436 },
3437 )
3438 .unwrap();
3439 system
3440 .setrlimit(Resource::CPU, LimitPair { soft: 10, hard: 30 })
3441 .unwrap();
3442
3443 let result = system.getrlimit(Resource::CORE).unwrap();
3444 assert_eq!(
3445 result,
3446 LimitPair {
3447 soft: 4096,
3448 hard: 8192,
3449 },
3450 );
3451 let result = system.getrlimit(Resource::CPU).unwrap();
3452 assert_eq!(result, LimitPair { soft: 10, hard: 30 },);
3453 }
3454
3455 #[test]
3456 fn setrlimit_rejects_soft_limit_higher_than_hard_limit() {
3457 let system = VirtualSystem::new();
3458 let result = system.setrlimit(Resource::CPU, LimitPair { soft: 2, hard: 1 });
3459 assert_eq!(result, Err(Errno::EINVAL));
3460
3461 let result = system.getrlimit(Resource::CPU).unwrap();
3463 assert_eq!(
3464 result,
3465 LimitPair {
3466 soft: INFINITY,
3467 hard: INFINITY,
3468 },
3469 );
3470 }
3471
3472 #[test]
3473 fn setrlimit_refuses_raising_hard_limit() {
3474 let system = VirtualSystem::new();
3475 system
3476 .setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 1 })
3477 .unwrap();
3478 let result = system.setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 2 });
3479 assert_eq!(result, Err(Errno::EPERM));
3480
3481 let result = system.getrlimit(Resource::CPU).unwrap();
3483 assert_eq!(result, LimitPair { soft: 1, hard: 1 });
3484 }
3485}
3486
3487#[cfg(test)]
3488mod fifo_tests;