Skip to main content

yash_env/system/
virtual.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! System simulated in Rust
18//!
19//! [`VirtualSystem`] is a pure Rust implementation of system traits that
20//! simulates the behavior of the underlying system without any interaction with
21//! the actual system. `VirtualSystem` is used for testing the behavior of the
22//! shell in unit tests.
23//!
24//! # Features and components
25//!
26//! This module also defines elements that compose a virtual system.
27//!
28//! ## File system
29//!
30//! Basic file operations are supported in the virtual system. Regular files,
31//! directories, named pipes, and symbolic links can be created in the file
32//! system. The file system is shared among all processes in the system.
33//!
34//! ## Processes
35//!
36//! A virtual system initially has one process, but can have more processes as a
37//! result of simulating fork. Each process has its own state.
38//!
39//! ## I/O
40//!
41//! Currently, read and write operations on files and unnamed pipes are
42//! supported.
43//!
44//! ## Signals
45//!
46//! The virtual system can simulate sending signals to processes. Processes can
47//! block, ignore, and catch signals.
48//!
49//! # Concurrency
50//!
51//! `VirtualSystem` is designed to allow multiple virtual processes to run
52//! concurrently. To achieve this, some trait methods return futures that enable
53//! the executor to suspend the calling thread until the process is ready to
54//! proceed. This allows the executor to switch between multiple virtual
55//! processes on a single thread.
56//! See also the [`system` module](super) documentation.
57//!
58//! TBD: Explain how to use `VirtualSystem` with an executor.
59
60mod file_system;
61mod io;
62mod process;
63mod signal;
64
65pub use self::file_system::*;
66pub use self::io::*;
67pub use self::process::*;
68pub use self::signal::*;
69use super::AT_FDCWD;
70use super::CaughtSignals;
71use super::Chdir;
72use super::Clock;
73use super::Close;
74use super::CpuTimes;
75use super::Dir;
76use super::Disposition;
77use super::Dup;
78use super::Errno;
79use super::Exec;
80use super::Exit;
81use super::Fcntl;
82use super::FdFlag;
83use super::Fork;
84use super::Fstat;
85use super::GetCwd;
86use super::GetPid;
87use super::GetPw;
88use super::GetRlimit;
89use super::GetSigaction;
90use super::GetUid;
91use super::Gid;
92use super::IsExecutableFile;
93use super::Isatty;
94use super::OfdAccess;
95use super::Open;
96use super::OpenFlag;
97use super::Pipe;
98use super::Read;
99use super::Result;
100use super::Seek;
101use super::Select;
102use super::SendSignal;
103use super::SetPgid;
104use super::SetRlimit;
105use super::ShellPath;
106use super::Sigaction;
107use super::Sigmask;
108use super::SigmaskOp;
109use super::Signals;
110use super::Stat;
111use super::Sysconf;
112use super::TcGetPgrp;
113use super::TcSetPgrp;
114use super::Times;
115use super::Uid;
116use super::Umask;
117use super::Wait;
118use super::Write;
119use super::resource::INFINITY;
120use super::resource::LimitPair;
121use super::resource::Resource;
122#[cfg(doc)]
123use crate::System;
124use crate::io::Fd;
125use crate::job::Pid;
126use crate::job::ProcessState;
127use crate::path::Path;
128use crate::path::PathBuf;
129use crate::semantics::ExitStatus;
130use crate::str::UnixStr;
131use crate::str::UnixString;
132use crate::system::ChildProcessStarter;
133use enumset::EnumSet;
134use std::borrow::Cow;
135use std::cell::Cell;
136use std::cell::Ref;
137use std::cell::RefCell;
138use std::cell::RefMut;
139use std::collections::BTreeMap;
140use std::collections::HashMap;
141use std::collections::VecDeque;
142use std::convert::Infallible;
143use std::convert::TryInto;
144use std::ffi::CStr;
145use std::ffi::CString;
146use std::ffi::c_int;
147use std::fmt::Debug;
148use std::future::pending;
149use std::future::poll_fn;
150use std::future::ready;
151use std::io::SeekFrom;
152use std::ops::DerefMut as _;
153use std::ops::RangeInclusive;
154use std::pin::Pin;
155use std::rc::Rc;
156use std::task::Context;
157use std::task::Poll;
158use std::task::Waker;
159use std::time::Duration;
160use std::time::Instant;
161
162/// Simulated system
163///
164/// See the [module-level documentation](self) to grasp a basic understanding of
165/// `VirtualSystem`.
166///
167/// A `VirtualSystem` instance has two members: `state` and `process_id`. The
168/// former is a [`SystemState`] that effectively contains the whole state of the
169/// system. The state is contained in `Rc` so that virtual processes can share
170/// the same state. The latter is a process ID that identifies a process calling
171/// the system interfaces.
172///
173/// When you clone a virtual system, the clone will have the same `process_id`
174/// and `state` as the original. To simulate the `fork` system call, you should
175/// call a method of the [`Fork`] trait implemented by `VirtualSystem`.
176#[derive(Clone, Debug)]
177pub struct VirtualSystem {
178    /// State of the system.
179    pub state: Rc<RefCell<SystemState>>,
180
181    /// Process ID of the process that is interacting with the system.
182    pub process_id: Pid,
183}
184
185impl VirtualSystem {
186    /// Creates a virtual system with an almost empty state.
187    ///
188    /// The `process_id` of the returned `VirtualSystem` will be 2.
189    /// (Process ID 1 has special meaning in some system calls, so we don't use
190    /// it as a default value.)
191    ///
192    /// The `state` of the returned `VirtualSystem` will have a [`Process`] with
193    /// process ID 2 in the process set ([`SystemState::processes`]). The file
194    /// system will contain files named `/dev/stdin`, `/dev/stdout`, and
195    /// `/dev/stderr` that are opened in the process with file descriptor 0, 1,
196    /// and 2, respectively. The file system also contains an empty directory
197    /// `/tmp`.
198    pub fn new() -> VirtualSystem {
199        let mut state = SystemState::default();
200        let mut process = Process::with_parent_and_group(Pid(1), Pid(1));
201
202        let mut set_std_fd = |path, fd| {
203            let file = Rc::new(RefCell::new(Inode::new([])));
204            state.file_system.save(path, Rc::clone(&file)).unwrap();
205            let body = FdBody {
206                open_file_description: Rc::new(RefCell::new(OpenFileDescription {
207                    file,
208                    offset: 0,
209                    is_readable: true,
210                    is_writable: true,
211                    is_appending: true,
212                })),
213                flags: EnumSet::empty(),
214            };
215            process.set_fd(fd, body).unwrap();
216        };
217        set_std_fd("/dev/stdin", Fd::STDIN);
218        set_std_fd("/dev/stdout", Fd::STDOUT);
219        set_std_fd("/dev/stderr", Fd::STDERR);
220
221        state
222            .file_system
223            .save(
224                "/tmp",
225                Rc::new(RefCell::new(Inode {
226                    body: FileBody::Directory {
227                        files: Default::default(),
228                    },
229                    permissions: Mode::ALL_9,
230                })),
231            )
232            .unwrap();
233
234        let process_id = Pid(2);
235        state.processes.insert(process_id, process);
236
237        let state = Rc::new(RefCell::new(state));
238        VirtualSystem { state, process_id }
239    }
240
241    /// Finds the current process from the system state.
242    ///
243    /// # Panics
244    ///
245    /// This function will panic if it cannot find a process having
246    /// `self.process_id`.
247    pub fn current_process(&self) -> Ref<'_, Process> {
248        Ref::map(self.state.borrow(), |state| {
249            &state.processes[&self.process_id]
250        })
251    }
252
253    /// Finds the current process from the system state.
254    ///
255    /// # Panics
256    ///
257    /// This function will panic if it cannot find a process having
258    /// `self.process_id`.
259    pub fn current_process_mut(&self) -> RefMut<'_, Process> {
260        RefMut::map(self.state.borrow_mut(), |state| {
261            state.processes.get_mut(&self.process_id).unwrap()
262        })
263    }
264
265    /// Calls the given closure passing the open file description for the FD.
266    ///
267    /// Returns `Err(Errno::EBADF)` if the FD is not open.
268    pub fn with_open_file_description<F, R>(&self, fd: Fd, f: F) -> Result<R>
269    where
270        F: FnOnce(&OpenFileDescription) -> Result<R>,
271    {
272        let process = self.current_process();
273        let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
274        let ofd = body.open_file_description.borrow();
275        f(&ofd)
276    }
277
278    /// Calls the given closure passing the open file description for the FD.
279    ///
280    /// Returns `Err(Errno::EBADF)` if the FD is not open.
281    pub fn with_open_file_description_mut<F, R>(&self, fd: Fd, f: F) -> Result<R>
282    where
283        F: FnOnce(&mut OpenFileDescription) -> Result<R>,
284    {
285        let mut process = self.current_process_mut();
286        let body = process.get_fd_mut(fd).ok_or(Errno::EBADF)?;
287        let mut ofd = body.open_file_description.borrow_mut();
288        f(&mut ofd)
289    }
290
291    fn resolve_relative_path<'a>(&self, path: &'a Path) -> Cow<'a, Path> {
292        if path.is_absolute() {
293            Cow::Borrowed(path)
294        } else {
295            Cow::Owned(self.current_process().cwd.join(path))
296        }
297    }
298
299    fn resolve_existing_file(
300        &self,
301        _dir_fd: Fd,
302        path: &Path,
303        follow_symlinks: bool,
304    ) -> Result<Rc<RefCell<Inode>>> {
305        // TODO Resolve relative to dir_fd
306        // TODO Support AT_FDCWD
307        const _POSIX_SYMLOOP_MAX: i32 = 8;
308
309        let mut path = Cow::Borrowed(path);
310        for _count in 0.._POSIX_SYMLOOP_MAX {
311            let resolved_path = self.resolve_relative_path(&path);
312            let inode = self.state.borrow().file_system.get(&resolved_path)?;
313            if !follow_symlinks {
314                return Ok(inode);
315            }
316
317            let inode_ref = inode.borrow();
318            if let FileBody::Symlink { target } = &inode_ref.body {
319                let mut new_path = resolved_path.into_owned();
320                new_path.pop();
321                new_path.push(target);
322                path = Cow::Owned(new_path);
323            } else {
324                drop(inode_ref);
325                return Ok(inode);
326            }
327        }
328
329        Err(Errno::ELOOP)
330    }
331
332    /// Blocks the calling thread until the current process is running.
333    async fn block_until_running(&self) {
334        let waker = Rc::new(Cell::new(None));
335
336        poll_fn(|cx| {
337            let mut state = self.state.borrow_mut();
338            let Some(process) = state.processes.get_mut(&self.process_id) else {
339                return Poll::Ready(());
340            };
341
342            match process.state {
343                ProcessState::Running => Poll::Ready(()),
344                ProcessState::Halted(result) => {
345                    if result.is_stopped() {
346                        waker.set(Some(cx.waker().clone()));
347                        process.wake_on_resumption(Rc::downgrade(&waker));
348                    }
349                    Poll::Pending
350                }
351            }
352        })
353        .await
354    }
355}
356
357impl Default for VirtualSystem {
358    fn default() -> Self {
359        VirtualSystem::new()
360    }
361}
362
363impl Fstat for VirtualSystem {
364    fn fstat(&self, fd: Fd) -> Result<Stat> {
365        self.with_open_file_description(fd, |ofd| Ok(ofd.file.borrow().stat()))
366    }
367
368    fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
369        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
370        let inode = self.resolve_existing_file(dir_fd, path, follow_symlinks)?;
371        Ok(inode.borrow().stat())
372    }
373}
374
375impl IsExecutableFile for VirtualSystem {
376    /// Tests whether the specified file is executable or not.
377    ///
378    /// The current implementation only checks if the file has any executable
379    /// bit in the permissions. The file owner and group are not considered.
380    fn is_executable_file(&self, path: &CStr) -> bool {
381        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
382        self.resolve_existing_file(AT_FDCWD, path, /* follow symlinks */ true)
383            .is_ok_and(|inode| inode.borrow().permissions.intersects(Mode::ALL_EXEC))
384    }
385}
386
387impl Pipe for VirtualSystem {
388    fn pipe(&self) -> Result<(Fd, Fd)> {
389        let file = Rc::new(RefCell::new(Inode {
390            body: FileBody::Fifo {
391                content: VecDeque::new(),
392                readers: 1,
393                writers: 1,
394            },
395            permissions: Mode::default(),
396        }));
397        let reader = OpenFileDescription {
398            file: Rc::clone(&file),
399            offset: 0,
400            is_readable: true,
401            is_writable: false,
402            is_appending: false,
403        };
404        let writer = OpenFileDescription {
405            file: Rc::clone(&file),
406            offset: 0,
407            is_readable: false,
408            is_writable: true,
409            is_appending: false,
410        };
411
412        let reader = FdBody {
413            open_file_description: Rc::new(RefCell::new(reader)),
414            flags: EnumSet::empty(),
415        };
416        let writer = FdBody {
417            open_file_description: Rc::new(RefCell::new(writer)),
418            flags: EnumSet::empty(),
419        };
420
421        let mut process = self.current_process_mut();
422        let reader = process.open_fd(reader).map_err(|_| Errno::EMFILE)?;
423        let writer = process.open_fd(writer).map_err(|_| {
424            process.close_fd(reader);
425            Errno::EMFILE
426        })?;
427        Ok((reader, writer))
428    }
429}
430
431impl Dup for VirtualSystem {
432    fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
433        let mut process = self.current_process_mut();
434        let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
435        body.flags = flags;
436        process.open_fd_ge(to_min, body).map_err(|_| Errno::EMFILE)
437    }
438
439    fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
440        let mut process = self.current_process_mut();
441        let mut body = process.fds.get(&from).ok_or(Errno::EBADF)?.clone();
442        body.flags = EnumSet::empty();
443        process.set_fd(to, body).map_err(|_| Errno::EBADF)?;
444        Ok(to)
445    }
446}
447
448impl Open for VirtualSystem {
449    fn open(
450        &self,
451        path: &CStr,
452        access: OfdAccess,
453        flags: EnumSet<OpenFlag>,
454        mode: Mode,
455    ) -> Result<Fd> {
456        let path = self.resolve_relative_path(Path::new(UnixStr::from_bytes(path.to_bytes())));
457        let umask = self.current_process().umask;
458
459        let mut state = self.state.borrow_mut();
460        let file = match state.file_system.get(&path) {
461            Ok(inode) => {
462                if flags.contains(OpenFlag::Exclusive) {
463                    return Err(Errno::EEXIST);
464                }
465                if flags.contains(OpenFlag::Directory)
466                    && !matches!(inode.borrow().body, FileBody::Directory { .. })
467                {
468                    return Err(Errno::ENOTDIR);
469                }
470                if flags.contains(OpenFlag::Truncate) {
471                    if let FileBody::Regular { content, .. } = &mut inode.borrow_mut().body {
472                        content.clear();
473                    };
474                }
475                inode
476            }
477            Err(Errno::ENOENT) if flags.contains(OpenFlag::Create) => {
478                let mut inode = Inode::new([]);
479                inode.permissions = mode.difference(umask);
480                let inode = Rc::new(RefCell::new(inode));
481                state.file_system.save(&path, Rc::clone(&inode))?;
482                inode
483            }
484            Err(errno) => return Err(errno),
485        };
486
487        let (is_readable, is_writable) = match access {
488            OfdAccess::ReadOnly => (true, false),
489            OfdAccess::WriteOnly => (false, true),
490            OfdAccess::ReadWrite => (true, true),
491            OfdAccess::Exec | OfdAccess::Search => (false, false),
492        };
493
494        if let FileBody::Fifo {
495            readers, writers, ..
496        } = &mut file.borrow_mut().body
497        {
498            if is_readable {
499                *readers += 1;
500            }
501            if is_writable {
502                *writers += 1;
503            }
504        }
505
506        let open_file_description = Rc::new(RefCell::new(OpenFileDescription {
507            file,
508            offset: 0,
509            is_readable,
510            is_writable,
511            is_appending: flags.contains(OpenFlag::Append),
512        }));
513        let body = FdBody {
514            open_file_description,
515            flags: if flags.contains(OpenFlag::CloseOnExec) {
516                EnumSet::only(FdFlag::CloseOnExec)
517            } else {
518                EnumSet::empty()
519            },
520        };
521        let process = state.processes.get_mut(&self.process_id).unwrap();
522        process.open_fd(body).map_err(|_| Errno::EMFILE)
523    }
524
525    fn open_tmpfile(&self, _parent_dir: &Path) -> Result<Fd> {
526        let file = Rc::new(RefCell::new(Inode::new([])));
527        let open_file_description = Rc::new(RefCell::new(OpenFileDescription {
528            file,
529            offset: 0,
530            is_readable: true,
531            is_writable: true,
532            is_appending: false,
533        }));
534        let body = FdBody {
535            open_file_description,
536            flags: EnumSet::empty(),
537        };
538        let mut state = self.state.borrow_mut();
539        let process = state.processes.get_mut(&self.process_id).unwrap();
540        process.open_fd(body).map_err(|_| Errno::EMFILE)
541    }
542
543    fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
544        self.with_open_file_description(fd, |ofd| {
545            let inode = ofd.inode();
546            let dir = VirtualDir::try_from(&inode.borrow().body)?;
547            Ok(dir)
548        })
549    }
550
551    fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
552        let fd = self.open(
553            path,
554            OfdAccess::ReadOnly,
555            OpenFlag::Directory.into(),
556            Mode::empty(),
557        )?;
558        self.fdopendir(fd)
559    }
560}
561
562impl Close for VirtualSystem {
563    fn close(&self, fd: Fd) -> Result<()> {
564        self.current_process_mut().close_fd(fd);
565        Ok(())
566    }
567}
568
569impl Fcntl for VirtualSystem {
570    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
571        fn is_directory(file_body: &FileBody) -> bool {
572            matches!(file_body, FileBody::Directory { .. })
573        }
574
575        self.with_open_file_description(fd, |ofd| match (ofd.is_readable, ofd.is_writable) {
576            (true, false) => Ok(OfdAccess::ReadOnly),
577            (false, true) => Ok(OfdAccess::WriteOnly),
578            (true, true) => Ok(OfdAccess::ReadWrite),
579            (false, false) => {
580                if is_directory(&ofd.inode().borrow().body) {
581                    Ok(OfdAccess::Search)
582                } else {
583                    Ok(OfdAccess::Exec)
584                }
585            }
586        })
587    }
588
589    fn get_and_set_nonblocking(&self, fd: Fd, _nonblocking: bool) -> Result<bool> {
590        self.with_open_file_description(fd, |_ofd| {
591            // TODO Implement non-blocking I/O
592            Ok(false)
593        })
594    }
595
596    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
597        let process = self.current_process();
598        let body = process.get_fd(fd).ok_or(Errno::EBADF)?;
599        Ok(body.flags)
600    }
601
602    fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
603        let mut process = self.current_process_mut();
604        let body = process.get_fd_mut(fd).ok_or(Errno::EBADF)?;
605        body.flags = flags;
606        Ok(())
607    }
608}
609
610impl Read for VirtualSystem {
611    fn read(&self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
612        self.with_open_file_description_mut(fd, |ofd| ofd.read(buffer))
613    }
614}
615
616impl Write for VirtualSystem {
617    fn write(&self, fd: Fd, buffer: &[u8]) -> Result<usize> {
618        self.with_open_file_description_mut(fd, |ofd| ofd.write(buffer))
619    }
620}
621
622impl Seek for VirtualSystem {
623    fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
624        self.with_open_file_description_mut(fd, |ofd| ofd.seek(position))
625            .and_then(|new_offset| new_offset.try_into().map_err(|_| Errno::EOVERFLOW))
626    }
627}
628
629impl Umask for VirtualSystem {
630    fn umask(&self, new_mask: Mode) -> Mode {
631        std::mem::replace(&mut self.current_process_mut().umask, new_mask)
632    }
633}
634
635impl GetCwd for VirtualSystem {
636    fn getcwd(&self) -> Result<PathBuf> {
637        Ok(self.current_process().cwd.clone())
638    }
639}
640
641impl Chdir for VirtualSystem {
642    fn chdir(&self, path: &CStr) -> Result<()> {
643        let path = Path::new(UnixStr::from_bytes(path.to_bytes()));
644        let inode = self.resolve_existing_file(AT_FDCWD, path, /* follow links */ true)?;
645        if matches!(&inode.borrow().body, FileBody::Directory { .. }) {
646            let mut process = self.current_process_mut();
647            let new_path = process.cwd.join(path);
648            process.chdir(new_path);
649            Ok(())
650        } else {
651            Err(Errno::ENOTDIR)
652        }
653    }
654}
655
656impl Clock for VirtualSystem {
657    /// Returns `now` in [`SystemState`].
658    ///
659    /// Panics if it is `None`.
660    fn now(&self) -> Instant {
661        self.state
662            .borrow()
663            .now
664            .expect("SystemState::now not assigned")
665    }
666}
667
668impl Times for VirtualSystem {
669    /// Returns `times` in [`SystemState`].
670    fn times(&self) -> Result<CpuTimes> {
671        Ok(self.state.borrow().times)
672    }
673}
674
675impl Signals for VirtualSystem {
676    const SIGABRT: signal::Number = signal::SIGABRT;
677    const SIGALRM: signal::Number = signal::SIGALRM;
678    const SIGBUS: signal::Number = signal::SIGBUS;
679    const SIGCHLD: signal::Number = signal::SIGCHLD;
680    const SIGCLD: Option<signal::Number> = Some(signal::SIGCLD);
681    const SIGCONT: signal::Number = signal::SIGCONT;
682    const SIGEMT: Option<signal::Number> = Some(signal::SIGEMT);
683    const SIGFPE: signal::Number = signal::SIGFPE;
684    const SIGHUP: signal::Number = signal::SIGHUP;
685    const SIGILL: signal::Number = signal::SIGILL;
686    const SIGINFO: Option<signal::Number> = Some(signal::SIGINFO);
687    const SIGINT: signal::Number = signal::SIGINT;
688    const SIGIO: Option<signal::Number> = Some(signal::SIGIO);
689    const SIGIOT: signal::Number = signal::SIGIOT;
690    const SIGKILL: signal::Number = signal::SIGKILL;
691    const SIGLOST: Option<signal::Number> = Some(signal::SIGLOST);
692    const SIGPIPE: signal::Number = signal::SIGPIPE;
693    const SIGPOLL: Option<signal::Number> = Some(signal::SIGPOLL);
694    const SIGPROF: signal::Number = signal::SIGPROF;
695    const SIGPWR: Option<signal::Number> = Some(signal::SIGPWR);
696    const SIGQUIT: signal::Number = signal::SIGQUIT;
697    const SIGSEGV: signal::Number = signal::SIGSEGV;
698    const SIGSTKFLT: Option<signal::Number> = Some(signal::SIGSTKFLT);
699    const SIGSTOP: signal::Number = signal::SIGSTOP;
700    const SIGSYS: signal::Number = signal::SIGSYS;
701    const SIGTERM: signal::Number = signal::SIGTERM;
702    const SIGTHR: Option<signal::Number> = Some(signal::SIGTHR);
703    const SIGTRAP: signal::Number = signal::SIGTRAP;
704    const SIGTSTP: signal::Number = signal::SIGTSTP;
705    const SIGTTIN: signal::Number = signal::SIGTTIN;
706    const SIGTTOU: signal::Number = signal::SIGTTOU;
707    const SIGURG: signal::Number = signal::SIGURG;
708    const SIGUSR1: signal::Number = signal::SIGUSR1;
709    const SIGUSR2: signal::Number = signal::SIGUSR2;
710    const SIGVTALRM: signal::Number = signal::SIGVTALRM;
711    const SIGWINCH: signal::Number = signal::SIGWINCH;
712    const SIGXCPU: signal::Number = signal::SIGXCPU;
713    const SIGXFSZ: signal::Number = signal::SIGXFSZ;
714
715    fn sigrt_range(&self) -> Option<RangeInclusive<Number>> {
716        Some(signal::SIGRTMIN..=signal::SIGRTMAX)
717    }
718}
719
720impl GetPid for VirtualSystem {
721    /// Currently, this function always returns `Pid(2)` if the process exists.
722    fn getsid(&self, pid: Pid) -> Result<Pid> {
723        self.state
724            .borrow()
725            .processes
726            .get(&pid)
727            .map_or(Err(Errno::ESRCH), |_| Ok(Pid(2)))
728    }
729
730    fn getpid(&self) -> Pid {
731        self.process_id
732    }
733
734    fn getppid(&self) -> Pid {
735        self.current_process().ppid
736    }
737
738    fn getpgrp(&self) -> Pid {
739        self.current_process().pgid
740    }
741}
742
743impl SetPgid for VirtualSystem {
744    /// Modifies the process group ID of a process.
745    ///
746    /// The current implementation does not yet support the concept of sessions.
747    fn setpgid(&self, mut pid: Pid, mut pgid: Pid) -> Result<()> {
748        if pgid.0 < 0 {
749            return Err(Errno::EINVAL);
750        }
751        if pid.0 == 0 {
752            pid = self.process_id;
753        }
754        if pgid.0 == 0 {
755            pgid = pid;
756        }
757
758        let mut state = self.state.borrow_mut();
759        if pgid != pid && !state.processes.values().any(|p| p.pgid == pgid) {
760            return Err(Errno::EPERM);
761        }
762        let process = state.processes.get_mut(&pid).ok_or(Errno::ESRCH)?;
763        if pid != self.process_id && process.ppid != self.process_id {
764            return Err(Errno::ESRCH);
765        }
766        if process.last_exec.is_some() {
767            return Err(Errno::EACCES);
768        }
769
770        process.pgid = pgid;
771        Ok(())
772        // TODO Support sessions
773    }
774}
775
776impl Sigmask for VirtualSystem {
777    fn sigmask(
778        &self,
779        op: Option<(SigmaskOp, &[signal::Number])>,
780        old_mask: Option<&mut Vec<signal::Number>>,
781    ) -> Result<()> {
782        let mut state = self.state.borrow_mut();
783        let process = state
784            .processes
785            .get_mut(&self.process_id)
786            .expect("current process not found");
787
788        if let Some(old_mask) = old_mask {
789            old_mask.clear();
790            old_mask.extend(process.blocked_signals());
791        }
792
793        if let Some((op, mask)) = op {
794            let result = process.block_signals(op, mask);
795            if result.process_state_changed {
796                let parent_pid = process.ppid;
797                raise_sigchld(&mut state, parent_pid);
798            }
799        }
800
801        Ok(())
802    }
803}
804
805impl GetSigaction for VirtualSystem {
806    fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
807        let process = self.current_process();
808        Ok(process.disposition(signal))
809    }
810}
811
812impl Sigaction for VirtualSystem {
813    fn sigaction(&self, signal: signal::Number, disposition: Disposition) -> Result<Disposition> {
814        let mut process = self.current_process_mut();
815        Ok(process.set_disposition(signal, disposition))
816    }
817}
818
819impl CaughtSignals for VirtualSystem {
820    fn caught_signals(&self) -> Vec<signal::Number> {
821        std::mem::take(&mut self.current_process_mut().caught_signals)
822    }
823}
824
825impl SendSignal for VirtualSystem {
826    /// Sends a signal to the target process.
827    ///
828    /// The current implementation accepts any positive signal number and `None`
829    /// (no signal) for `signal`. Negative signal numbers are rejected with
830    /// `Errno::EINVAL`.
831    ///
832    /// This function returns a future that enables the executor to block the
833    /// calling thread until the current process is ready to proceed. If the
834    /// signal is sent to the current process and it causes the process to stop,
835    /// the future will be ready only when the process is resumed. Similarly, if
836    /// the signal causes the current process to terminate, the future will
837    /// never be ready.
838    fn kill(
839        &self,
840        target: Pid,
841        signal: Option<signal::Number>,
842    ) -> impl Future<Output = Result<()>> + use<> {
843        let result = 'result: {
844            if let Some(signal) = signal {
845                // Validate the signal number
846                if signal.as_raw() < 0 {
847                    break 'result Err(Errno::EINVAL);
848                }
849            }
850
851            match target {
852                Pid::MY_PROCESS_GROUP => {
853                    let target_pgid = self.current_process().pgid;
854                    send_signal_to_processes(
855                        &mut self.state.borrow_mut(),
856                        Some(target_pgid),
857                        signal,
858                    )
859                }
860
861                Pid::ALL => send_signal_to_processes(&mut self.state.borrow_mut(), None, signal),
862
863                Pid(raw_pid) if raw_pid >= 0 => {
864                    let mut state = self.state.borrow_mut();
865                    match state.processes.get_mut(&target) {
866                        Some(process) => {
867                            if let Some(signal) = signal {
868                                let result = process.raise_signal(signal);
869                                if result.process_state_changed {
870                                    let parent_pid = process.ppid;
871                                    raise_sigchld(&mut state, parent_pid);
872                                }
873                            }
874                            Ok(())
875                        }
876                        None => Err(Errno::ESRCH),
877                    }
878                }
879
880                Pid(negative_pgid) => {
881                    let target_pgid = Pid(-negative_pgid);
882                    send_signal_to_processes(
883                        &mut self.state.borrow_mut(),
884                        Some(target_pgid),
885                        signal,
886                    )
887                }
888            }
889        };
890
891        let system = self.clone();
892        async move {
893            system.block_until_running().await;
894            result
895        }
896    }
897
898    fn raise(&self, signal: signal::Number) -> impl Future<Output = Result<()>> + use<> {
899        let target = self.process_id;
900        self.kill(target, Some(signal))
901    }
902}
903
904impl Select for VirtualSystem {
905    /// Waits for a next event.
906    ///
907    /// The `VirtualSystem` implementation for this method does not actually
908    /// block the calling thread. The method returns immediately in any case.
909    ///
910    /// The `timeout` is ignored if this function returns because of a ready FD
911    /// or a caught signal. Otherwise, the timeout is added to
912    /// [`SystemState::now`], which must not be `None` then.
913    fn select(
914        &self,
915        readers: &mut Vec<Fd>,
916        writers: &mut Vec<Fd>,
917        timeout: Option<Duration>,
918        signal_mask: Option<&[signal::Number]>,
919    ) -> Result<c_int> {
920        let mut process = self.current_process_mut();
921
922        // Detect invalid FDs first. POSIX requires that the arguments are
923        // not modified if an error occurs.
924        let fds = readers.iter().chain(writers.iter());
925        if { fds }.any(|fd| !process.fds().contains_key(fd)) {
926            return Err(Errno::EBADF);
927        }
928
929        if let Some(signal_mask) = signal_mask {
930            let save_mask = process
931                .blocked_signals()
932                .iter()
933                .copied()
934                .collect::<Vec<signal::Number>>();
935            let result_1 = process.block_signals(SigmaskOp::Set, signal_mask);
936            let result_2 = process.block_signals(SigmaskOp::Set, &save_mask);
937            assert!(!result_2.delivered);
938            if result_1.caught {
939                return Err(Errno::EINTR);
940            }
941        }
942
943        readers.retain(|fd| {
944            // We already checked that the FD is open, so it's safe to access by index.
945            let ofd = process.fds()[fd].open_file_description.borrow();
946            !ofd.is_readable() || ofd.is_ready_for_reading()
947        });
948        writers.retain(|fd| {
949            let ofd = process.fds()[fd].open_file_description.borrow();
950            !ofd.is_writable() || ofd.is_ready_for_writing()
951        });
952
953        drop(process);
954
955        let count = (readers.len() + writers.len()).try_into().unwrap();
956        if count == 0 {
957            if let Some(duration) = timeout {
958                if !duration.is_zero() {
959                    let mut state = self.state.borrow_mut();
960                    let now = state.now.as_mut();
961                    let now = now.expect("now time unspecified; cannot add timeout duration");
962                    *now += duration;
963                }
964            }
965        }
966        Ok(count)
967    }
968}
969
970impl Isatty for VirtualSystem {
971    fn isatty(&self, fd: Fd) -> bool {
972        self.with_open_file_description(fd, |ofd| {
973            Ok(matches!(&ofd.file.borrow().body, FileBody::Terminal { .. }))
974        })
975        .unwrap_or(false)
976    }
977}
978
979impl TcGetPgrp for VirtualSystem {
980    /// Returns the current foreground process group ID.
981    ///
982    /// The current implementation does not yet support the concept of
983    /// controlling terminals and sessions. It accepts any open file descriptor.
984    fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
985        // Make sure the FD is open
986        self.with_open_file_description(fd, |_| Ok(()))?;
987
988        self.state.borrow().foreground.ok_or(Errno::ENOTTY)
989    }
990}
991
992impl TcSetPgrp for VirtualSystem {
993    /// Switches the foreground process.
994    ///
995    /// The current implementation does not yet support the concept of
996    /// controlling terminals and sessions. It accepts any open file descriptor.
997    fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<> {
998        fn inner(system: &VirtualSystem, fd: Fd, pgid: Pid) -> Result<()> {
999            // Make sure the FD is open
1000            system.with_open_file_description(fd, |_| Ok(()))?;
1001
1002            // Make sure the process group exists
1003            let mut state = system.state.borrow_mut();
1004            if !state.processes.values().any(|p| p.pgid == pgid) {
1005                return Err(Errno::EPERM);
1006            }
1007
1008            // TODO: Suspend the calling process group if it is in the background
1009            // and not ignoring or blocking SIGTTOU.
1010
1011            state.foreground = Some(pgid);
1012            Ok(())
1013        }
1014
1015        ready(inner(self, fd, pgid))
1016    }
1017}
1018
1019impl Fork for VirtualSystem {
1020    /// Creates a new child process.
1021    ///
1022    /// This implementation does not create any real child process. Instead,
1023    /// it returns a child process starter that runs its task concurrently in
1024    /// the same process.
1025    ///
1026    /// To run the concurrent task, this function needs an executor that has
1027    /// been set in the [`SystemState`]. If the system state does not have an
1028    /// executor, this function fails with `Errno::ENOSYS`.
1029    ///
1030    /// The process ID of the child will be the maximum of existing process IDs
1031    /// plus 1. If there are no other processes, it will be 2.
1032    fn new_child_process(&self) -> Result<ChildProcessStarter<Self>> {
1033        let mut state = self.state.borrow_mut();
1034        let executor = state.executor.clone().ok_or(Errno::ENOSYS)?;
1035        let process_id = state
1036            .processes
1037            .keys()
1038            .max()
1039            .map_or(Pid(2), |pid| Pid(pid.0 + 1));
1040        let parent_process = &state.processes[&self.process_id];
1041        let child_process = Process::fork_from(self.process_id, parent_process);
1042        state.processes.insert(process_id, child_process);
1043        drop(state);
1044
1045        let state = Rc::clone(&self.state);
1046        Ok(Box::new(move |parent_env, task| {
1047            let system = VirtualSystem { state, process_id };
1048            let mut child_env = parent_env.clone_with_system(system.clone());
1049
1050            {
1051                let mut process = system.current_process_mut();
1052                process.selector = Rc::downgrade(&child_env.system.0);
1053            }
1054
1055            let run_task_and_set_exit_status = Box::pin(async move {
1056                let runner = ProcessRunner {
1057                    task: task(&mut child_env),
1058                    system,
1059                    waker: Rc::new(Cell::new(None)),
1060                };
1061                runner.await;
1062            });
1063
1064            executor
1065                .spawn(run_task_and_set_exit_status)
1066                .expect("the executor failed to start the child process task");
1067
1068            process_id
1069        }))
1070    }
1071}
1072
1073impl Wait for VirtualSystem {
1074    /// Waits for a child.
1075    ///
1076    /// TODO: Currently, this function only supports `target == -1 || target > 0`.
1077    fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
1078        let parent_pid = self.process_id;
1079        let mut state = self.state.borrow_mut();
1080        if let Some((pid, process)) = state.child_to_wait_for(parent_pid, target) {
1081            if process.state_has_changed() {
1082                Ok(Some((pid, process.take_state())))
1083            } else if process.state().is_alive() {
1084                Ok(None)
1085            } else {
1086                Err(Errno::ECHILD)
1087            }
1088        } else {
1089            Err(Errno::ECHILD)
1090        }
1091    }
1092}
1093
1094impl Exec for VirtualSystem {
1095    /// Stub for the `execve` system call.
1096    ///
1097    /// The `execve` system call cannot be simulated in the userland. This
1098    /// function returns `ENOSYS` if the file at `path` is a native executable,
1099    /// `ENOEXEC` if a non-executable file, and `ENOENT` otherwise.
1100    fn execve(
1101        &self,
1102        path: &CStr,
1103        args: &[CString],
1104        envs: &[CString],
1105    ) -> impl Future<Output = Result<Infallible>> + use<> {
1106        let os_path = UnixStr::from_bytes(path.to_bytes());
1107        let mut state = self.state.borrow_mut();
1108        let fs = &state.file_system;
1109        let file = match fs.get(os_path) {
1110            Ok(file) => file,
1111            Err(e) => return ready(Err(e)),
1112        };
1113        // TODO Check file permissions
1114        let is_executable = matches!(
1115            &file.borrow().body,
1116            FileBody::Regular {
1117                is_native_executable: true,
1118                ..
1119            }
1120        );
1121        if is_executable {
1122            // Save arguments in the Process
1123            let process = state.processes.get_mut(&self.process_id).unwrap();
1124            let path = path.to_owned();
1125            let args = args.to_owned();
1126            let envs = envs.to_owned();
1127            process.last_exec = Some((path, args, envs));
1128
1129            // TODO: We should abort the currently running task and start the new one.
1130            // Just returning `pending()` would break existing tests that rely on
1131            // the current behavior.
1132            ready(Err(Errno::ENOSYS))
1133        } else {
1134            ready(Err(Errno::ENOEXEC))
1135        }
1136    }
1137}
1138
1139impl Exit for VirtualSystem {
1140    fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<> {
1141        let mut myself = self.current_process_mut();
1142        let parent_pid = myself.ppid;
1143        let exited = myself.set_state(ProcessState::exited(exit_status));
1144        drop(myself);
1145        if exited {
1146            raise_sigchld(&mut self.state.borrow_mut(), parent_pid);
1147        }
1148
1149        pending()
1150    }
1151}
1152
1153impl GetUid for VirtualSystem {
1154    fn getuid(&self) -> Uid {
1155        self.current_process().uid()
1156    }
1157
1158    fn geteuid(&self) -> Uid {
1159        self.current_process().euid()
1160    }
1161
1162    fn getgid(&self) -> Gid {
1163        self.current_process().gid()
1164    }
1165
1166    fn getegid(&self) -> Gid {
1167        self.current_process().egid()
1168    }
1169}
1170
1171impl GetPw for VirtualSystem {
1172    fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
1173        let state = self.state.borrow();
1174        let name = match name.to_str() {
1175            Ok(name) => name,
1176            Err(_utf8_error) => return Ok(None),
1177        };
1178        Ok(state.home_dirs.get(name).cloned())
1179    }
1180}
1181
1182impl Sysconf for VirtualSystem {
1183    /// Returns the standard path for the system.
1184    ///
1185    /// This function returns the value of [`SystemState::path`]. If it is empty,
1186    /// it returns the `ENOSYS` error.
1187    fn confstr_path(&self) -> Result<UnixString> {
1188        let path = self.state.borrow().path.clone();
1189        if path.is_empty() {
1190            Err(Errno::ENOSYS)
1191        } else {
1192            Ok(path)
1193        }
1194    }
1195}
1196
1197impl ShellPath for VirtualSystem {
1198    /// Returns the path to the shell.
1199    ///
1200    /// The current implementation returns "/bin/sh".
1201    fn shell_path(&self) -> CString {
1202        c"/bin/sh".to_owned()
1203    }
1204}
1205
1206impl GetRlimit for VirtualSystem {
1207    fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
1208        Ok(self
1209            .current_process()
1210            .resource_limits
1211            .get(&resource)
1212            .copied()
1213            .unwrap_or(LimitPair {
1214                soft: INFINITY,
1215                hard: INFINITY,
1216            }))
1217    }
1218}
1219
1220impl SetRlimit for VirtualSystem {
1221    fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1222        if limits.soft_exceeds_hard() {
1223            return Err(Errno::EINVAL);
1224        }
1225
1226        let mut process = self.current_process_mut();
1227        use std::collections::hash_map::Entry::{Occupied, Vacant};
1228        match process.resource_limits.entry(resource) {
1229            Occupied(occupied) => {
1230                let occupied = occupied.into_mut();
1231                if limits.hard > occupied.hard {
1232                    return Err(Errno::EPERM);
1233                }
1234                *occupied = limits;
1235            }
1236            Vacant(vacant) => {
1237                vacant.insert(limits);
1238            }
1239        }
1240        Ok(())
1241    }
1242}
1243
1244fn send_signal_to_processes(
1245    state: &mut SystemState,
1246    target_pgid: Option<Pid>,
1247    signal: Option<signal::Number>,
1248) -> Result<()> {
1249    let mut results = Vec::new();
1250
1251    for (&_pid, process) in &mut state.processes {
1252        if target_pgid.is_none_or(|target_pgid| process.pgid == target_pgid) {
1253            let result = if let Some(signal) = signal {
1254                process.raise_signal(signal)
1255            } else {
1256                SignalResult::default()
1257            };
1258            results.push((result, process.ppid));
1259        }
1260    }
1261
1262    if results.is_empty() {
1263        Err(Errno::ESRCH)
1264    } else {
1265        for (result, ppid) in results {
1266            if result.process_state_changed {
1267                raise_sigchld(state, ppid);
1268            }
1269        }
1270        Ok(())
1271    }
1272}
1273
1274fn raise_sigchld(state: &mut SystemState, target_pid: Pid) {
1275    if let Some(target) = state.processes.get_mut(&target_pid) {
1276        let result = target.raise_signal(signal::SIGCHLD);
1277        assert!(!result.process_state_changed);
1278    }
1279}
1280
1281/// State of the virtual system.
1282#[derive(Clone, Debug, Default)]
1283pub struct SystemState {
1284    /// Current time
1285    pub now: Option<Instant>,
1286
1287    /// Consumed CPU time statistics
1288    pub times: CpuTimes,
1289
1290    /// Task manager that can execute asynchronous tasks
1291    ///
1292    /// The virtual system uses this executor to run (virtual) child processes.
1293    /// If `executor` is `None`, [`VirtualSystem::new_child_process`] will fail.
1294    pub executor: Option<Rc<dyn Executor>>,
1295
1296    /// Processes running in the system
1297    pub processes: BTreeMap<Pid, Process>,
1298
1299    /// Process group ID of the foreground process group
1300    ///
1301    /// Note: The current implementation does not support the notion of
1302    /// controlling terminals and sessions. This item may be replaced with a
1303    /// more _correct_ implementation in the future.
1304    pub foreground: Option<Pid>,
1305
1306    /// Collection of files existing in the virtual system
1307    pub file_system: FileSystem,
1308
1309    /// Map from user names to their home directory paths
1310    ///
1311    /// [`VirtualSystem::getpwnam_dir`] looks up its argument in this
1312    /// dictionary.
1313    pub home_dirs: HashMap<String, PathBuf>,
1314
1315    /// Standard path returned by [`VirtualSystem::confstr_path`]
1316    pub path: UnixString,
1317}
1318
1319impl SystemState {
1320    /// Performs [`select`](crate::system::SharedSystem::select) on all
1321    /// processes in the system.
1322    ///
1323    /// Any errors are ignored.
1324    ///
1325    /// The `RefCell` must not have been borrowed, or this function will panic
1326    /// with a double borrow.
1327    pub fn select_all(this: &RefCell<Self>) {
1328        let mut selectors = Vec::new();
1329        for process in this.borrow().processes.values() {
1330            if let Some(selector) = process.selector.upgrade() {
1331                selectors.push(selector);
1332            }
1333        }
1334        // To avoid double borrowing, SelectSystem::select must be called after
1335        // dropping the borrow for `this`
1336        for selector in selectors {
1337            // TODO merge advances of `now` performed by each select
1338            selector.borrow_mut().select(false).ok();
1339        }
1340    }
1341
1342    /// Finds a child process to wait for.
1343    ///
1344    /// This is a helper function for `VirtualSystem::wait`.
1345    fn child_to_wait_for(&mut self, parent_pid: Pid, target: Pid) -> Option<(Pid, &mut Process)> {
1346        match target.0 {
1347            0 => todo!("wait target {}", target),
1348            -1 => {
1349                // any child
1350                let mut result = None;
1351                for (pid, process) in &mut self.processes {
1352                    if process.ppid == parent_pid {
1353                        let changed = process.state_has_changed();
1354                        result = Some((*pid, process));
1355                        if changed {
1356                            break;
1357                        }
1358                    }
1359                }
1360                result
1361            }
1362            raw if raw >= 0 => {
1363                let process = self.processes.get_mut(&target)?;
1364                if process.ppid == parent_pid {
1365                    Some((target, process))
1366                } else {
1367                    None
1368                }
1369            }
1370            _target => todo!("wait target {}", target),
1371        }
1372    }
1373}
1374
1375/// Executor that can start new async tasks.
1376///
1377/// This trait abstracts the executor interface so that [`SystemState`] does not
1378/// depend on a specific executor implementation.
1379///
1380/// Note that [`VirtualSystem`] does not support multi-threading. The executor
1381/// should run concurrent tasks on a single thread.
1382pub trait Executor: Debug {
1383    /// Starts a new async task.
1384    ///
1385    /// Returns `Ok(())` if the task has been started successfully and `Err(_)`
1386    /// otherwise.
1387    fn spawn(
1388        &self,
1389        task: Pin<Box<dyn Future<Output = ()>>>,
1390    ) -> std::result::Result<(), Box<dyn std::error::Error>>;
1391}
1392
1393/// Concurrent task that manages the execution of a process.
1394///
1395/// This struct is a helper for [`VirtualSystem::new_child_process`].
1396/// It basically runs the given task, but pauses or cancels it depending on
1397/// the state of the process.
1398struct ProcessRunner<'a> {
1399    task: Pin<Box<dyn Future<Output = Infallible> + 'a>>,
1400    system: VirtualSystem,
1401
1402    /// Waker that is woken up when the process is resumed.
1403    waker: Rc<Cell<Option<Waker>>>,
1404}
1405
1406impl Future for ProcessRunner<'_> {
1407    type Output = ();
1408
1409    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
1410        let this = self.deref_mut();
1411
1412        let process_state = this.system.current_process().state;
1413        if process_state == ProcessState::Running {
1414            // Let the task make progress
1415            let poll = this.task.as_mut().poll(cx);
1416            match poll {
1417                // unreachable: Poll::Ready(_) => todo!(),
1418                Poll::Pending => (),
1419            }
1420        }
1421
1422        let mut process = this.system.current_process_mut();
1423        match process.state {
1424            ProcessState::Running => Poll::Pending,
1425            ProcessState::Halted(result) => {
1426                if result.is_stopped() {
1427                    this.waker.set(Some(cx.waker().clone()));
1428                    process.wake_on_resumption(Rc::downgrade(&this.waker));
1429                    Poll::Pending
1430                } else {
1431                    Poll::Ready(())
1432                }
1433            }
1434        }
1435    }
1436}
1437
1438#[cfg(test)]
1439mod tests {
1440    use super::*;
1441    use crate::Env;
1442    use crate::job::ProcessResult;
1443    use crate::system::FileType;
1444    use assert_matches::assert_matches;
1445    use futures_executor::LocalPool;
1446    use futures_util::FutureExt as _;
1447    use std::future::pending;
1448
1449    impl Executor for futures_executor::LocalSpawner {
1450        fn spawn(
1451            &self,
1452            task: Pin<Box<dyn Future<Output = ()>>>,
1453        ) -> std::result::Result<(), Box<dyn std::error::Error>> {
1454            use futures_util::task::LocalSpawnExt;
1455            self.spawn_local(task)
1456                .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
1457        }
1458    }
1459
1460    #[test]
1461    fn fstatat_non_existent_file() {
1462        let system = VirtualSystem::new();
1463        assert_matches!(
1464            system.fstatat(Fd(0), c"/no/such/file", true),
1465            Err(Errno::ENOENT)
1466        );
1467    }
1468
1469    #[test]
1470    fn fstatat_regular_file() {
1471        let system = VirtualSystem::new();
1472        let path = "/some/file";
1473        let content = Rc::new(RefCell::new(Inode::new([1, 2, 3, 42, 100])));
1474        let mut state = system.state.borrow_mut();
1475        state.file_system.save(path, content).unwrap();
1476        drop(state);
1477
1478        let stat = system.fstatat(Fd(0), c"/some/file", true).unwrap();
1479        assert_eq!(stat.mode, Mode::default());
1480        assert_eq!(stat.r#type, FileType::Regular);
1481        assert_eq!(stat.size, 5);
1482        // TODO Other stat properties
1483    }
1484
1485    #[test]
1486    fn fstatat_directory() {
1487        let system = VirtualSystem::new();
1488        let path = "/some/file";
1489        let content = Rc::new(RefCell::new(Inode::new([])));
1490        let mut state = system.state.borrow_mut();
1491        state.file_system.save(path, content).unwrap();
1492        drop(state);
1493
1494        let stat = system.fstatat(Fd(0), c"/some/", true).unwrap();
1495        assert_eq!(stat.mode, Mode::from_bits_retain(0o755));
1496        assert_eq!(stat.r#type, FileType::Directory);
1497        // TODO Other stat properties
1498    }
1499
1500    #[test]
1501    fn fstatat_fifo() {
1502        let system = VirtualSystem::new();
1503        let path = "/some/fifo";
1504        let content = Rc::new(RefCell::new(Inode {
1505            body: FileBody::Fifo {
1506                content: [17; 42].into(),
1507                readers: 0,
1508                writers: 0,
1509            },
1510            permissions: Mode::default(),
1511        }));
1512        let mut state = system.state.borrow_mut();
1513        state.file_system.save(path, content).unwrap();
1514        drop(state);
1515
1516        let stat = system.fstatat(Fd(0), c"/some/fifo", true).unwrap();
1517        assert_eq!(stat.mode, Mode::default());
1518        assert_eq!(stat.r#type, FileType::Fifo);
1519        assert_eq!(stat.size, 42);
1520    }
1521
1522    fn system_with_symlink() -> VirtualSystem {
1523        let system = VirtualSystem::new();
1524        let mut state = system.state.borrow_mut();
1525        state
1526            .file_system
1527            .save("/some/file", Rc::new(RefCell::new(Inode::new([]))))
1528            .unwrap();
1529        state
1530            .file_system
1531            .save(
1532                "/link",
1533                Rc::new(RefCell::new(Inode {
1534                    body: FileBody::Symlink {
1535                        target: "some/file".into(),
1536                    },
1537                    permissions: Mode::default(),
1538                })),
1539            )
1540            .unwrap();
1541        drop(state);
1542        system
1543    }
1544
1545    #[test]
1546    fn fstatat_symlink_to_regular_file() {
1547        let system = system_with_symlink();
1548        let stat = system.fstatat(Fd(0), c"/link", true).unwrap();
1549        assert_eq!(stat.r#type, FileType::Regular);
1550    }
1551
1552    #[test]
1553    fn fstatat_symlink_no_follow() {
1554        let system = system_with_symlink();
1555        let stat = system.fstatat(Fd(0), c"/link", false).unwrap();
1556        assert_eq!(stat.r#type, FileType::Symlink);
1557    }
1558
1559    #[test]
1560    fn is_executable_file_non_existing_file() {
1561        let system = VirtualSystem::new();
1562        assert!(!system.is_executable_file(c"/no/such/file"));
1563    }
1564
1565    #[test]
1566    fn is_executable_file_existing_but_non_executable_file() {
1567        let system = VirtualSystem::new();
1568        let path = "/some/file";
1569        let content = Rc::new(RefCell::new(Inode::default()));
1570        let mut state = system.state.borrow_mut();
1571        state.file_system.save(path, content).unwrap();
1572        drop(state);
1573        assert!(!system.is_executable_file(c"/some/file"));
1574    }
1575
1576    #[test]
1577    fn is_executable_file_with_executable_file() {
1578        let system = VirtualSystem::new();
1579        let path = "/some/file";
1580        let mut content = Inode::default();
1581        content.permissions.set(Mode::USER_EXEC, true);
1582        let content = Rc::new(RefCell::new(content));
1583        let mut state = system.state.borrow_mut();
1584        state.file_system.save(path, content).unwrap();
1585        drop(state);
1586        assert!(system.is_executable_file(c"/some/file"));
1587    }
1588
1589    #[test]
1590    fn pipe_read_write() {
1591        let system = VirtualSystem::new();
1592        let (reader, writer) = system.pipe().unwrap();
1593        let result = system.write(writer, &[5, 42, 29]);
1594        assert_eq!(result, Ok(3));
1595
1596        let mut buffer = [1; 4];
1597        let result = system.read(reader, &mut buffer);
1598        assert_eq!(result, Ok(3));
1599        assert_eq!(buffer, [5, 42, 29, 1]);
1600
1601        let result = system.close(writer);
1602        assert_eq!(result, Ok(()));
1603
1604        let result = system.read(reader, &mut buffer);
1605        assert_eq!(result, Ok(0));
1606    }
1607
1608    #[test]
1609    fn dup_shares_open_file_description() {
1610        let system = VirtualSystem::new();
1611        let result = system.dup(Fd::STDOUT, Fd::STDERR, EnumSet::empty());
1612        assert_eq!(result, Ok(Fd(3)));
1613
1614        let process = system.current_process();
1615        let fd1 = process.fds.get(&Fd(1)).unwrap();
1616        let fd3 = process.fds.get(&Fd(3)).unwrap();
1617        assert_eq!(fd1, fd3);
1618    }
1619
1620    #[test]
1621    fn dup_can_set_cloexec() {
1622        let system = VirtualSystem::new();
1623        let result = system.dup(Fd::STDOUT, Fd::STDERR, FdFlag::CloseOnExec.into());
1624        assert_eq!(result, Ok(Fd(3)));
1625
1626        let process = system.current_process();
1627        let fd3 = process.fds.get(&Fd(3)).unwrap();
1628        assert_eq!(fd3.flags, EnumSet::only(FdFlag::CloseOnExec));
1629    }
1630
1631    #[test]
1632    fn dup2_shares_open_file_description() {
1633        let system = VirtualSystem::new();
1634        let result = system.dup2(Fd::STDOUT, Fd(5));
1635        assert_eq!(result, Ok(Fd(5)));
1636
1637        let process = system.current_process();
1638        let fd1 = process.fds.get(&Fd(1)).unwrap();
1639        let fd5 = process.fds.get(&Fd(5)).unwrap();
1640        assert_eq!(fd1, fd5);
1641    }
1642
1643    #[test]
1644    fn dup2_clears_cloexec() {
1645        let system = VirtualSystem::new();
1646        let mut process = system.current_process_mut();
1647        process.fds.get_mut(&Fd::STDOUT).unwrap().flags = FdFlag::CloseOnExec.into();
1648        drop(process);
1649
1650        let result = system.dup2(Fd::STDOUT, Fd(6));
1651        assert_eq!(result, Ok(Fd(6)));
1652
1653        let process = system.current_process();
1654        let fd6 = process.fds.get(&Fd(6)).unwrap();
1655        assert_eq!(fd6.flags, EnumSet::empty());
1656    }
1657
1658    #[test]
1659    fn open_non_existing_file_no_creation() {
1660        let system = VirtualSystem::new();
1661        let result = system.open(
1662            c"/no/such/file",
1663            OfdAccess::ReadOnly,
1664            EnumSet::empty(),
1665            Mode::empty(),
1666        );
1667        assert_eq!(result, Err(Errno::ENOENT));
1668    }
1669
1670    #[test]
1671    fn open_creating_non_existing_file() {
1672        let system = VirtualSystem::new();
1673        let result = system.open(
1674            c"new_file",
1675            OfdAccess::WriteOnly,
1676            OpenFlag::Create.into(),
1677            Mode::empty(),
1678        );
1679        assert_eq!(result, Ok(Fd(3)));
1680
1681        system.write(Fd(3), &[42, 123]).unwrap();
1682        let file = system.state.borrow().file_system.get("new_file").unwrap();
1683        let file = file.borrow();
1684        assert_eq!(file.permissions, Mode::empty());
1685        assert_matches!(&file.body, FileBody::Regular { content, .. } => {
1686            assert_eq!(content[..], [42, 123]);
1687        });
1688    }
1689
1690    #[test]
1691    fn open_creating_non_existing_file_umask() {
1692        let system = VirtualSystem::new();
1693        system.umask(Mode::from_bits_retain(0o125));
1694        system
1695            .open(
1696                c"file",
1697                OfdAccess::WriteOnly,
1698                OpenFlag::Create.into(),
1699                Mode::ALL_9,
1700            )
1701            .unwrap();
1702
1703        let file = system.state.borrow().file_system.get("file").unwrap();
1704        let file = file.borrow();
1705        assert_eq!(file.permissions, Mode::from_bits_retain(0o652));
1706    }
1707
1708    #[test]
1709    fn open_existing_file() {
1710        let system = VirtualSystem::new();
1711        let fd = system
1712            .open(
1713                c"file",
1714                OfdAccess::WriteOnly,
1715                OpenFlag::Create.into(),
1716                Mode::empty(),
1717            )
1718            .unwrap();
1719        system.write(fd, &[75, 96, 133]).unwrap();
1720
1721        let result = system.open(
1722            c"file",
1723            OfdAccess::ReadOnly,
1724            EnumSet::empty(),
1725            Mode::empty(),
1726        );
1727        assert_eq!(result, Ok(Fd(4)));
1728
1729        let mut buffer = [0; 5];
1730        let count = system.read(Fd(4), &mut buffer).unwrap();
1731        assert_eq!(count, 3);
1732        assert_eq!(buffer, [75, 96, 133, 0, 0]);
1733        let count = system.read(Fd(4), &mut buffer).unwrap();
1734        assert_eq!(count, 0);
1735    }
1736
1737    #[test]
1738    fn open_existing_file_excl() {
1739        let system = VirtualSystem::new();
1740        let first = system.open(
1741            c"my_file",
1742            OfdAccess::WriteOnly,
1743            OpenFlag::Create | OpenFlag::Exclusive,
1744            Mode::empty(),
1745        );
1746        assert_eq!(first, Ok(Fd(3)));
1747
1748        let second = system.open(
1749            c"my_file",
1750            OfdAccess::WriteOnly,
1751            OpenFlag::Create | OpenFlag::Exclusive,
1752            Mode::empty(),
1753        );
1754        assert_eq!(second, Err(Errno::EEXIST));
1755    }
1756
1757    #[test]
1758    fn open_truncating() {
1759        let system = VirtualSystem::new();
1760        let fd = system
1761            .open(
1762                c"file",
1763                OfdAccess::WriteOnly,
1764                OpenFlag::Create.into(),
1765                Mode::ALL_9,
1766            )
1767            .unwrap();
1768        system.write(fd, &[1, 2, 3]).unwrap();
1769
1770        let result = system.open(
1771            c"file",
1772            OfdAccess::WriteOnly,
1773            OpenFlag::Truncate.into(),
1774            Mode::empty(),
1775        );
1776        assert_eq!(result, Ok(Fd(4)));
1777
1778        let reader = system
1779            .open(
1780                c"file",
1781                OfdAccess::ReadOnly,
1782                EnumSet::empty(),
1783                Mode::empty(),
1784            )
1785            .unwrap();
1786        let count = system.read(reader, &mut [0; 1]).unwrap();
1787        assert_eq!(count, 0);
1788    }
1789
1790    #[test]
1791    fn open_appending() {
1792        let system = VirtualSystem::new();
1793        let fd = system
1794            .open(
1795                c"file",
1796                OfdAccess::WriteOnly,
1797                OpenFlag::Create.into(),
1798                Mode::ALL_9,
1799            )
1800            .unwrap();
1801        system.write(fd, &[1, 2, 3]).unwrap();
1802
1803        let result = system.open(
1804            c"file",
1805            OfdAccess::WriteOnly,
1806            OpenFlag::Append.into(),
1807            Mode::empty(),
1808        );
1809        assert_eq!(result, Ok(Fd(4)));
1810        system.write(Fd(4), &[4, 5, 6]).unwrap();
1811
1812        let reader = system
1813            .open(
1814                c"file",
1815                OfdAccess::ReadOnly,
1816                EnumSet::empty(),
1817                Mode::empty(),
1818            )
1819            .unwrap();
1820        let mut buffer = [0; 7];
1821        let count = system.read(reader, &mut buffer).unwrap();
1822        assert_eq!(count, 6);
1823        assert_eq!(buffer, [1, 2, 3, 4, 5, 6, 0]);
1824    }
1825
1826    #[test]
1827    fn open_directory() {
1828        let system = VirtualSystem::new();
1829
1830        // Create a regular file and its parent directory
1831        let _ = system.open(
1832            c"/dir/file",
1833            OfdAccess::WriteOnly,
1834            OpenFlag::Create.into(),
1835            Mode::empty(),
1836        );
1837
1838        let result = system.open(
1839            c"/dir",
1840            OfdAccess::ReadOnly,
1841            OpenFlag::Directory.into(),
1842            Mode::empty(),
1843        );
1844        assert_eq!(result, Ok(Fd(4)));
1845    }
1846
1847    #[test]
1848    fn open_non_directory_path_prefix() {
1849        let system = VirtualSystem::new();
1850
1851        // Create a regular file
1852        let _ = system.open(
1853            c"/file",
1854            OfdAccess::WriteOnly,
1855            OpenFlag::Create.into(),
1856            Mode::empty(),
1857        );
1858
1859        let result = system.open(
1860            c"/file/file",
1861            OfdAccess::WriteOnly,
1862            OpenFlag::Create.into(),
1863            Mode::empty(),
1864        );
1865        assert_eq!(result, Err(Errno::ENOTDIR));
1866    }
1867
1868    #[test]
1869    fn open_non_directory_file() {
1870        let system = VirtualSystem::new();
1871
1872        // Create a regular file
1873        let _ = system.open(
1874            c"/file",
1875            OfdAccess::WriteOnly,
1876            OpenFlag::Create.into(),
1877            Mode::empty(),
1878        );
1879
1880        let result = system.open(
1881            c"/file",
1882            OfdAccess::ReadOnly,
1883            OpenFlag::Directory.into(),
1884            Mode::empty(),
1885        );
1886        assert_eq!(result, Err(Errno::ENOTDIR));
1887    }
1888
1889    #[test]
1890    fn open_default_working_directory() {
1891        // The default working directory is the root directory.
1892        let system = VirtualSystem::new();
1893
1894        let writer = system.open(
1895            c"/dir/file",
1896            OfdAccess::WriteOnly,
1897            OpenFlag::Create.into(),
1898            Mode::ALL_9,
1899        );
1900        system.write(writer.unwrap(), &[1, 2, 3, 42]).unwrap();
1901
1902        let reader = system.open(
1903            c"./dir/file",
1904            OfdAccess::ReadOnly,
1905            EnumSet::empty(),
1906            Mode::empty(),
1907        );
1908        let mut buffer = [0; 10];
1909        let count = system.read(reader.unwrap(), &mut buffer).unwrap();
1910        assert_eq!(count, 4);
1911        assert_eq!(buffer[0..4], [1, 2, 3, 42]);
1912    }
1913
1914    #[test]
1915    fn open_tmpfile() {
1916        let system = VirtualSystem::new();
1917        let fd = system.open_tmpfile(Path::new("")).unwrap();
1918        system.write(fd, &[42, 17, 75]).unwrap();
1919        system.lseek(fd, SeekFrom::Start(0)).unwrap();
1920        let mut buffer = [0; 4];
1921        let count = system.read(fd, &mut buffer).unwrap();
1922        assert_eq!(count, 3);
1923        assert_eq!(buffer[..3], [42, 17, 75]);
1924    }
1925
1926    #[test]
1927    fn close() {
1928        let system = VirtualSystem::new();
1929
1930        let result = system.close(Fd::STDERR);
1931        assert_eq!(result, Ok(()));
1932        assert_eq!(system.current_process().fds.get(&Fd::STDERR), None);
1933
1934        let result = system.close(Fd::STDERR);
1935        assert_eq!(result, Ok(()));
1936    }
1937
1938    #[test]
1939    fn fcntl_getfd_and_setfd() {
1940        let system = VirtualSystem::new();
1941
1942        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
1943        assert_eq!(flags, EnumSet::empty());
1944
1945        system
1946            .fcntl_setfd(Fd::STDIN, FdFlag::CloseOnExec.into())
1947            .unwrap();
1948
1949        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
1950        assert_eq!(flags, EnumSet::only(FdFlag::CloseOnExec));
1951
1952        let flags = system.fcntl_getfd(Fd::STDOUT).unwrap();
1953        assert_eq!(flags, EnumSet::empty());
1954
1955        system.fcntl_setfd(Fd::STDIN, EnumSet::empty()).unwrap();
1956
1957        let flags = system.fcntl_getfd(Fd::STDIN).unwrap();
1958        assert_eq!(flags, EnumSet::empty());
1959    }
1960
1961    #[test]
1962    fn opendir_default_working_directory() {
1963        // The default working directory is the root directory.
1964        let system = VirtualSystem::new();
1965
1966        let _ = system.open(
1967            c"/dir/file",
1968            OfdAccess::WriteOnly,
1969            OpenFlag::Create.into(),
1970            Mode::ALL_9,
1971        );
1972
1973        let mut dir = system.opendir(c"./dir").unwrap();
1974        let mut files = Vec::new();
1975        while let Some(entry) = dir.next().unwrap() {
1976            files.push(entry.name.to_unix_string());
1977        }
1978        files.sort_unstable();
1979        assert_eq!(
1980            files[..],
1981            [
1982                UnixString::from("."),
1983                UnixString::from(".."),
1984                UnixString::from("file")
1985            ]
1986        );
1987    }
1988
1989    // TODO Test sigmask
1990
1991    #[test]
1992    fn kill_process() {
1993        let system = VirtualSystem::new();
1994        system
1995            .kill(system.process_id, None)
1996            .now_or_never()
1997            .unwrap()
1998            .unwrap();
1999        assert_eq!(system.current_process().state(), ProcessState::Running);
2000
2001        let result = system.kill(system.process_id, Some(SIGINT)).now_or_never();
2002        // The future should be pending because the current process has been killed
2003        assert_eq!(result, None);
2004        assert_eq!(
2005            system.current_process().state(),
2006            ProcessState::Halted(ProcessResult::Signaled {
2007                signal: SIGINT,
2008                core_dump: false
2009            })
2010        );
2011
2012        let system = VirtualSystem::new();
2013        let state = system.state.borrow();
2014        let max_pid = *state.processes.keys().max().unwrap();
2015        drop(state);
2016        let e = system
2017            .kill(Pid(max_pid.0 + 1), Some(SIGINT))
2018            .now_or_never()
2019            .unwrap()
2020            .unwrap_err();
2021        assert_eq!(e, Errno::ESRCH);
2022    }
2023
2024    #[test]
2025    fn kill_all_processes() {
2026        let system = VirtualSystem::new();
2027        let pgid = system.current_process().pgid;
2028        let mut state = system.state.borrow_mut();
2029        state.processes.insert(
2030            Pid(10),
2031            Process::with_parent_and_group(system.process_id, pgid),
2032        );
2033        state.processes.insert(
2034            Pid(11),
2035            Process::with_parent_and_group(system.process_id, pgid),
2036        );
2037        state
2038            .processes
2039            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2040        drop(state);
2041
2042        let result = system.kill(Pid::ALL, Some(SIGTERM)).now_or_never();
2043        // The future should be pending because the current process has been killed
2044        assert_eq!(result, None);
2045        let state = system.state.borrow();
2046        for process in state.processes.values() {
2047            assert_eq!(
2048                process.state,
2049                ProcessState::Halted(ProcessResult::Signaled {
2050                    signal: SIGTERM,
2051                    core_dump: false
2052                })
2053            );
2054        }
2055    }
2056
2057    #[test]
2058    fn kill_processes_in_same_group() {
2059        let system = VirtualSystem::new();
2060        let pgid = system.current_process().pgid;
2061        let mut state = system.state.borrow_mut();
2062        state.processes.insert(
2063            Pid(10),
2064            Process::with_parent_and_group(system.process_id, pgid),
2065        );
2066        state.processes.insert(
2067            Pid(11),
2068            Process::with_parent_and_group(system.process_id, pgid),
2069        );
2070        state
2071            .processes
2072            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2073        drop(state);
2074
2075        let result = system
2076            .kill(Pid::MY_PROCESS_GROUP, Some(SIGQUIT))
2077            .now_or_never();
2078        // The future should be pending because the current process has been killed
2079        assert_eq!(result, None);
2080        let state = system.state.borrow();
2081        assert_eq!(
2082            state.processes[&system.process_id].state,
2083            ProcessState::Halted(ProcessResult::Signaled {
2084                signal: SIGQUIT,
2085                core_dump: true
2086            })
2087        );
2088        assert_eq!(
2089            state.processes[&Pid(10)].state,
2090            ProcessState::Halted(ProcessResult::Signaled {
2091                signal: SIGQUIT,
2092                core_dump: true
2093            })
2094        );
2095        assert_eq!(
2096            state.processes[&Pid(11)].state,
2097            ProcessState::Halted(ProcessResult::Signaled {
2098                signal: SIGQUIT,
2099                core_dump: true
2100            })
2101        );
2102        assert_eq!(state.processes[&Pid(21)].state, ProcessState::Running);
2103    }
2104
2105    #[test]
2106    fn kill_process_group() {
2107        let system = VirtualSystem::new();
2108        let pgid = system.current_process().pgid;
2109        let mut state = system.state.borrow_mut();
2110        state.processes.insert(
2111            Pid(10),
2112            Process::with_parent_and_group(system.process_id, pgid),
2113        );
2114        state.processes.insert(
2115            Pid(11),
2116            Process::with_parent_and_group(system.process_id, Pid(21)),
2117        );
2118        state
2119            .processes
2120            .insert(Pid(21), Process::with_parent_and_group(Pid(10), Pid(21)));
2121        drop(state);
2122
2123        system
2124            .kill(Pid(-21), Some(SIGHUP))
2125            .now_or_never()
2126            .unwrap()
2127            .unwrap();
2128        let state = system.state.borrow();
2129        assert_eq!(
2130            state.processes[&system.process_id].state,
2131            ProcessState::Running
2132        );
2133        assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2134        assert_eq!(
2135            state.processes[&Pid(11)].state,
2136            ProcessState::Halted(ProcessResult::Signaled {
2137                signal: SIGHUP,
2138                core_dump: false
2139            })
2140        );
2141        assert_eq!(
2142            state.processes[&Pid(21)].state,
2143            ProcessState::Halted(ProcessResult::Signaled {
2144                signal: SIGHUP,
2145                core_dump: false
2146            })
2147        );
2148    }
2149
2150    #[test]
2151    fn kill_returns_success_even_if_process_state_did_not_change() {
2152        let system = VirtualSystem::new();
2153        let pgid = system.current_process().pgid;
2154        let mut state = system.state.borrow_mut();
2155        state.processes.insert(
2156            Pid(10),
2157            Process::with_parent_and_group(system.process_id, pgid),
2158        );
2159        drop(state);
2160
2161        system
2162            .kill(-pgid, Some(SIGCONT))
2163            .now_or_never()
2164            .unwrap()
2165            .unwrap();
2166        let state = system.state.borrow();
2167        assert_eq!(state.processes[&Pid(10)].state, ProcessState::Running);
2168    }
2169
2170    #[test]
2171    fn kill_dummy_signal_to_my_group() {
2172        let system = VirtualSystem::new();
2173
2174        let result = system
2175            .kill(Pid::MY_PROCESS_GROUP, None)
2176            .now_or_never()
2177            .unwrap();
2178
2179        assert_eq!(result, Ok(()));
2180        assert_eq!(system.current_process().state(), ProcessState::Running);
2181    }
2182
2183    #[test]
2184    fn kill_dummy_signal_to_non_existent_group() {
2185        let system = VirtualSystem::new();
2186        let result = system.kill(Pid(-9999), None).now_or_never().unwrap();
2187        assert_eq!(result, Err(Errno::ESRCH));
2188    }
2189
2190    #[test]
2191    fn select_regular_file_is_always_ready() {
2192        let system = VirtualSystem::new();
2193        let mut readers = vec![Fd::STDIN];
2194        let mut writers = vec![Fd::STDOUT, Fd::STDERR];
2195
2196        let result = system.select(&mut readers, &mut writers, None, None);
2197        assert_eq!(result, Ok(3));
2198        assert_eq!(readers, [Fd::STDIN]);
2199        assert_eq!(writers, [Fd::STDOUT, Fd::STDERR]);
2200    }
2201
2202    #[test]
2203    fn select_pipe_reader_is_ready_if_writer_is_closed() {
2204        let system = VirtualSystem::new();
2205        let (reader, writer) = system.pipe().unwrap();
2206        system.close(writer).unwrap();
2207        let mut readers = vec![reader];
2208        let mut writers = vec![];
2209
2210        let result = system.select(&mut readers, &mut writers, None, None);
2211        assert_eq!(result, Ok(1));
2212        assert_eq!(readers, [reader]);
2213        assert_eq!(writers, []);
2214    }
2215
2216    #[test]
2217    fn select_pipe_reader_is_ready_if_something_has_been_written() {
2218        let system = VirtualSystem::new();
2219        let (reader, writer) = system.pipe().unwrap();
2220        system.write(writer, &[0]).unwrap();
2221        let mut readers = vec![reader];
2222        let mut writers = vec![];
2223
2224        let result = system.select(&mut readers, &mut writers, None, None);
2225        assert_eq!(result, Ok(1));
2226        assert_eq!(readers, [reader]);
2227        assert_eq!(writers, []);
2228    }
2229
2230    #[test]
2231    fn select_pipe_reader_is_not_ready_if_writer_has_written_nothing() {
2232        let system = VirtualSystem::new();
2233        let (reader, _writer) = system.pipe().unwrap();
2234        let mut readers = vec![reader];
2235        let mut writers = vec![];
2236
2237        let result = system.select(&mut readers, &mut writers, None, None);
2238        assert_eq!(result, Ok(0));
2239        assert_eq!(readers, []);
2240        assert_eq!(writers, []);
2241    }
2242
2243    #[test]
2244    fn select_pipe_writer_is_ready_if_pipe_is_not_full() {
2245        let system = VirtualSystem::new();
2246        let (_reader, writer) = system.pipe().unwrap();
2247        let mut readers = vec![];
2248        let mut writers = vec![writer];
2249
2250        let result = system.select(&mut readers, &mut writers, None, None);
2251        assert_eq!(result, Ok(1));
2252        assert_eq!(readers, []);
2253        assert_eq!(writers, [writer]);
2254    }
2255
2256    #[test]
2257    fn select_on_unreadable_fd() {
2258        let system = VirtualSystem::new();
2259        let (_reader, writer) = system.pipe().unwrap();
2260        let mut fds = vec![writer];
2261        let result = system.select(&mut fds, &mut vec![], None, None);
2262        assert_eq!(result, Ok(1));
2263        assert_eq!(fds, [writer]);
2264    }
2265
2266    #[test]
2267    fn select_on_unwritable_fd() {
2268        let system = VirtualSystem::new();
2269        let (reader, _writer) = system.pipe().unwrap();
2270        let mut fds = vec![reader];
2271        let result = system.select(&mut vec![], &mut fds, None, None);
2272        assert_eq!(result, Ok(1));
2273        assert_eq!(fds, [reader]);
2274    }
2275
2276    #[test]
2277    fn select_on_closed_fd() {
2278        let system = VirtualSystem::new();
2279        let result = system.select(&mut vec![Fd(17)], &mut vec![], None, None);
2280        assert_eq!(result, Err(Errno::EBADF));
2281
2282        let result = system.select(&mut vec![], &mut vec![Fd(17)], None, None);
2283        assert_eq!(result, Err(Errno::EBADF));
2284    }
2285
2286    fn system_for_catching_sigchld() -> VirtualSystem {
2287        let system = VirtualSystem::new();
2288        system
2289            .sigmask(Some((SigmaskOp::Add, &[SIGCHLD])), None)
2290            .unwrap();
2291        system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
2292        system
2293    }
2294
2295    #[test]
2296    fn select_on_non_pending_signal() {
2297        let system = system_for_catching_sigchld();
2298        let result = system.select(&mut vec![], &mut vec![], None, Some(&[]));
2299        assert_eq!(result, Ok(0));
2300        assert_eq!(system.caught_signals(), []);
2301    }
2302
2303    #[test]
2304    fn select_on_pending_signal() {
2305        let system = system_for_catching_sigchld();
2306        let _ = system.current_process_mut().raise_signal(SIGCHLD);
2307        let result = system.select(&mut vec![], &mut vec![], None, Some(&[]));
2308        assert_eq!(result, Err(Errno::EINTR));
2309        assert_eq!(system.caught_signals(), [SIGCHLD]);
2310    }
2311
2312    #[test]
2313    fn select_timeout() {
2314        let system = VirtualSystem::new();
2315        let now = Instant::now();
2316        system.state.borrow_mut().now = Some(now);
2317
2318        let (reader, _writer) = system.pipe().unwrap();
2319        let mut readers = vec![reader];
2320        let mut writers = vec![];
2321        let timeout = Duration::new(42, 195);
2322
2323        let result = system.select(&mut readers, &mut writers, Some(timeout), None);
2324        assert_eq!(result, Ok(0));
2325        assert_eq!(readers, []);
2326        assert_eq!(writers, []);
2327        assert_eq!(
2328            system.state.borrow().now,
2329            Some(now + Duration::new(42, 195))
2330        );
2331    }
2332
2333    fn virtual_system_with_executor() -> (VirtualSystem, LocalPool) {
2334        let system = VirtualSystem::new();
2335        let executor = LocalPool::new();
2336        system.state.borrow_mut().executor = Some(Rc::new(executor.spawner()));
2337        (system, executor)
2338    }
2339
2340    #[test]
2341    fn setpgid_creating_new_group_from_parent() {
2342        let (system, _executor) = virtual_system_with_executor();
2343        let state = Rc::clone(&system.state);
2344        let mut env = Env::with_system(system);
2345        let child = env.system.new_child_process().unwrap();
2346        let pid = child(&mut env, Box::new(|_env| Box::pin(pending())));
2347
2348        let result = env.system.setpgid(pid, pid);
2349        assert_eq!(result, Ok(()));
2350
2351        let pgid = state.borrow().processes[&pid].pgid();
2352        assert_eq!(pgid, pid);
2353    }
2354
2355    #[test]
2356    fn setpgid_creating_new_group_from_child() {
2357        let (system, mut executor) = virtual_system_with_executor();
2358        let state = Rc::clone(&system.state);
2359        let mut env = Env::with_system(system);
2360        let child = env.system.new_child_process().unwrap();
2361        let pid = child(
2362            &mut env,
2363            Box::new(|child_env| {
2364                Box::pin(async move {
2365                    let result = child_env.system.setpgid(Pid(0), Pid(0));
2366                    assert_eq!(result, Ok(()));
2367                    child_env.system.exit(child_env.exit_status).await
2368                })
2369            }),
2370        );
2371        executor.run_until_stalled();
2372
2373        let pgid = state.borrow().processes[&pid].pgid();
2374        assert_eq!(pgid, pid);
2375    }
2376
2377    #[test]
2378    fn setpgid_extending_existing_group_from_parent() {
2379        let (system, _executor) = virtual_system_with_executor();
2380        let state = Rc::clone(&system.state);
2381        let mut env = Env::with_system(system);
2382        let child_1 = env.system.new_child_process().unwrap();
2383        let pid_1 = child_1(&mut env, Box::new(|_env| Box::pin(pending())));
2384        env.system.setpgid(pid_1, pid_1).unwrap();
2385        let child_2 = env.system.new_child_process().unwrap();
2386        let pid_2 = child_2(&mut env, Box::new(|_env| Box::pin(pending())));
2387
2388        let result = env.system.setpgid(pid_2, pid_1);
2389        assert_eq!(result, Ok(()));
2390
2391        let pgid = state.borrow().processes[&pid_2].pgid();
2392        assert_eq!(pgid, pid_1);
2393    }
2394
2395    #[test]
2396    fn setpgid_with_nonexisting_pid() {
2397        let (system, _executor) = virtual_system_with_executor();
2398        let state = Rc::clone(&system.state);
2399        let mut env = Env::with_system(system);
2400        let child = env.system.new_child_process().unwrap();
2401        let pid = child(&mut env, Box::new(|_env| Box::pin(pending())));
2402
2403        let dummy_pid = Pid(123);
2404        let result = env.system.setpgid(dummy_pid, dummy_pid);
2405        assert_eq!(result, Err(Errno::ESRCH));
2406
2407        let pgid = state.borrow().processes[&pid].pgid();
2408        assert_eq!(pgid, Pid(1));
2409    }
2410
2411    #[test]
2412    fn setpgid_with_unrelated_pid() {
2413        let (system, mut executor) = virtual_system_with_executor();
2414        let parent_pid = system.process_id;
2415        let state = Rc::clone(&system.state);
2416        let mut env = Env::with_system(system);
2417        let child = env.system.new_child_process().unwrap();
2418        let _pid = child(
2419            &mut env,
2420            Box::new(move |child_env| {
2421                Box::pin(async move {
2422                    let result = child_env.system.setpgid(parent_pid, Pid(0));
2423                    assert_eq!(result, Err(Errno::ESRCH));
2424                    child_env.system.exit(child_env.exit_status).await
2425                })
2426            }),
2427        );
2428        executor.run_until_stalled();
2429
2430        let pgid = state.borrow().processes[&parent_pid].pgid();
2431        assert_eq!(pgid, Pid(1));
2432    }
2433
2434    #[test]
2435    fn setpgid_with_execed_child() {
2436        let (system, mut executor) = virtual_system_with_executor();
2437        let path = "/some/file";
2438        let mut content = Inode::default();
2439        content.body = FileBody::Regular {
2440            content: vec![],
2441            is_native_executable: true,
2442        };
2443        content.permissions.set(Mode::USER_EXEC, true);
2444        let content = Rc::new(RefCell::new(content));
2445        let state = Rc::clone(&system.state);
2446        state.borrow_mut().file_system.save(path, content).unwrap();
2447        let mut env = Env::with_system(system);
2448        let child = env.system.new_child_process().unwrap();
2449        let pid = child(
2450            &mut env,
2451            Box::new(move |child_env| {
2452                Box::pin(async move {
2453                    let path = CString::new(path).unwrap();
2454                    child_env.system.execve(&path, &[], &[]).await.ok();
2455                    child_env.system.exit(child_env.exit_status).await
2456                })
2457            }),
2458        );
2459        executor.run_until_stalled();
2460
2461        let result = env.system.setpgid(pid, pid);
2462        assert_eq!(result, Err(Errno::EACCES));
2463
2464        let pgid = state.borrow().processes[&pid].pgid();
2465        assert_eq!(pgid, Pid(1));
2466    }
2467
2468    #[test]
2469    fn setpgid_with_nonexisting_pgid() {
2470        let (system, mut executor) = virtual_system_with_executor();
2471        let state = Rc::clone(&system.state);
2472        let mut env = Env::with_system(system);
2473        let child_1 = env.system.new_child_process().unwrap();
2474        let pid_1 = child_1(&mut env, Box::new(|_env| Box::pin(pending())));
2475        // env.system.setpgid(pid_1, pid_1).unwrap();
2476        let child_2 = env.system.new_child_process().unwrap();
2477        let pid_2 = child_2(&mut env, Box::new(|_env| Box::pin(pending())));
2478        executor.run_until_stalled();
2479
2480        let result = env.system.setpgid(pid_2, pid_1);
2481        assert_eq!(result, Err(Errno::EPERM));
2482
2483        let pgid = state.borrow().processes[&pid_2].pgid();
2484        assert_eq!(pgid, Pid(1));
2485    }
2486
2487    #[test]
2488    fn tcsetpgrp_success() {
2489        let system = VirtualSystem::new();
2490        let pid = Pid(10);
2491        let ppid = system.process_id;
2492        let pgid = Pid(9);
2493        system
2494            .state
2495            .borrow_mut()
2496            .processes
2497            .insert(pid, Process::with_parent_and_group(ppid, pgid));
2498
2499        system
2500            .tcsetpgrp(Fd::STDIN, pgid)
2501            .now_or_never()
2502            .unwrap()
2503            .unwrap();
2504
2505        let foreground = system.state.borrow().foreground;
2506        assert_eq!(foreground, Some(pgid));
2507    }
2508
2509    #[test]
2510    fn tcsetpgrp_with_invalid_fd() {
2511        let system = VirtualSystem::new();
2512        let result = system.tcsetpgrp(Fd(100), Pid(2)).now_or_never().unwrap();
2513        assert_eq!(result, Err(Errno::EBADF));
2514    }
2515
2516    #[test]
2517    fn tcsetpgrp_with_nonexisting_pgrp() {
2518        let system = VirtualSystem::new();
2519        let result = system
2520            .tcsetpgrp(Fd::STDIN, Pid(100))
2521            .now_or_never()
2522            .unwrap();
2523        assert_eq!(result, Err(Errno::EPERM));
2524    }
2525
2526    #[test]
2527    fn new_child_process_without_executor() {
2528        let system = VirtualSystem::new();
2529        let result = system.new_child_process();
2530        match result {
2531            Ok(_) => panic!("unexpected Ok value"),
2532            Err(e) => assert_eq!(e, Errno::ENOSYS),
2533        }
2534    }
2535
2536    #[test]
2537    fn new_child_process_with_executor() {
2538        let (system, _executor) = virtual_system_with_executor();
2539
2540        let result = system.new_child_process();
2541
2542        let state = system.state.borrow();
2543        assert_eq!(state.processes.len(), 2);
2544        drop(state);
2545
2546        let mut env = Env::with_system(system);
2547        let child_process = result.unwrap();
2548        let pid = child_process(
2549            &mut env,
2550            Box::new(|env| Box::pin(async move { env.system.exit(env.exit_status).await })),
2551        );
2552        assert_eq!(pid, Pid(3));
2553    }
2554
2555    #[test]
2556    fn wait_for_running_child() {
2557        let (system, _executor) = virtual_system_with_executor();
2558
2559        let child_process = system.new_child_process();
2560
2561        let mut env = Env::with_system(system);
2562        let child_process = child_process.unwrap();
2563        let pid = child_process(
2564            &mut env,
2565            Box::new(|_env| {
2566                Box::pin(async {
2567                    unreachable!("child process does not progress unless executor is used")
2568                })
2569            }),
2570        );
2571
2572        let result = env.system.wait(pid);
2573        assert_eq!(result, Ok(None))
2574    }
2575
2576    #[test]
2577    fn wait_for_exited_child() {
2578        let (system, mut executor) = virtual_system_with_executor();
2579
2580        let child_process = system.new_child_process();
2581
2582        let mut env = Env::with_system(system);
2583        let child_process = child_process.unwrap();
2584        let pid = child_process(
2585            &mut env,
2586            Box::new(|env| Box::pin(async move { env.system.exit(ExitStatus(5)).await })),
2587        );
2588        executor.run_until_stalled();
2589
2590        let result = env.system.wait(pid);
2591        assert_eq!(result, Ok(Some((pid, ProcessState::exited(5)))));
2592    }
2593
2594    #[test]
2595    fn wait_for_signaled_child() {
2596        let (system, mut executor) = virtual_system_with_executor();
2597
2598        let child_process = system.new_child_process();
2599
2600        let mut env = Env::with_system(system);
2601        let child_process = child_process.unwrap();
2602        let pid = child_process(
2603            &mut env,
2604            Box::new(|env| {
2605                Box::pin(async move {
2606                    let pid = env.system.getpid();
2607                    let result = env.system.kill(pid, Some(SIGKILL)).await;
2608                    unreachable!("kill returned {result:?}");
2609                })
2610            }),
2611        );
2612        executor.run_until_stalled();
2613
2614        let result = env.system.wait(pid);
2615        assert_eq!(
2616            result,
2617            Ok(Some((
2618                pid,
2619                ProcessState::Halted(ProcessResult::Signaled {
2620                    signal: SIGKILL,
2621                    core_dump: false
2622                })
2623            )))
2624        );
2625    }
2626
2627    #[test]
2628    fn wait_for_stopped_child() {
2629        let (system, mut executor) = virtual_system_with_executor();
2630
2631        let child_process = system.new_child_process();
2632
2633        let mut env = Env::with_system(system);
2634        let child_process = child_process.unwrap();
2635        let pid = child_process(
2636            &mut env,
2637            Box::new(|env| {
2638                Box::pin(async move {
2639                    let pid = env.system.getpid();
2640                    let result = env.system.kill(pid, Some(SIGSTOP)).await;
2641                    unreachable!("kill returned {result:?}");
2642                })
2643            }),
2644        );
2645        executor.run_until_stalled();
2646
2647        let result = env.system.wait(pid);
2648        assert_eq!(result, Ok(Some((pid, ProcessState::stopped(SIGSTOP)))));
2649    }
2650
2651    #[test]
2652    fn wait_for_resumed_child() {
2653        let (system, mut executor) = virtual_system_with_executor();
2654
2655        let child_process = system.new_child_process();
2656
2657        let mut env = Env::with_system(system);
2658        let child_process = child_process.unwrap();
2659        let pid = child_process(
2660            &mut env,
2661            Box::new(|env| {
2662                Box::pin(async move {
2663                    let pid = env.system.getpid();
2664                    let result = env.system.kill(pid, Some(SIGSTOP)).await;
2665                    assert_eq!(result, Ok(()));
2666                    env.system.exit(ExitStatus(123)).await
2667                })
2668            }),
2669        );
2670        executor.run_until_stalled();
2671
2672        env.system
2673            .kill(pid, Some(SIGCONT))
2674            .now_or_never()
2675            .unwrap()
2676            .unwrap();
2677
2678        let result = env.system.wait(pid);
2679        assert_eq!(result, Ok(Some((pid, ProcessState::Running))));
2680
2681        executor.run_until_stalled();
2682
2683        let result = env.system.wait(pid);
2684        assert_eq!(result, Ok(Some((pid, ProcessState::exited(123)))));
2685    }
2686
2687    #[test]
2688    fn wait_without_child() {
2689        let system = VirtualSystem::new();
2690        let result = system.wait(Pid::ALL);
2691        assert_eq!(result, Err(Errno::ECHILD));
2692        // TODO
2693        // let result = system.wait(Pid::MY_PROCESS_GROUP);
2694        // assert_eq!(result, Err(Errno::ECHILD));
2695        let result = system.wait(system.process_id);
2696        assert_eq!(result, Err(Errno::ECHILD));
2697        let result = system.wait(Pid(1234));
2698        assert_eq!(result, Err(Errno::ECHILD));
2699        // TODO
2700        // let result = system.wait(Pid(-1234));
2701        // assert_eq!(result, Err(Errno::ECHILD));
2702    }
2703
2704    #[test]
2705    fn exiting_child_sends_sigchld_to_parent() {
2706        let (system, mut executor) = virtual_system_with_executor();
2707        system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
2708
2709        let child_process = system.new_child_process().unwrap();
2710
2711        let mut env = Env::with_system(system);
2712        let _pid = child_process(
2713            &mut env,
2714            Box::new(|env| Box::pin(async { env.system.exit(ExitStatus(0)).await })),
2715        );
2716        executor.run_until_stalled();
2717
2718        assert_eq!(env.system.caught_signals(), [SIGCHLD]);
2719    }
2720
2721    #[test]
2722    fn execve_returns_enosys_for_executable_file() {
2723        let system = VirtualSystem::new();
2724        let path = "/some/file";
2725        let mut content = Inode::default();
2726        content.body = FileBody::Regular {
2727            content: vec![],
2728            is_native_executable: true,
2729        };
2730        content.permissions.set(Mode::USER_EXEC, true);
2731        let content = Rc::new(RefCell::new(content));
2732        let mut state = system.state.borrow_mut();
2733        state.file_system.save(path, content).unwrap();
2734        drop(state);
2735        let path = CString::new(path).unwrap();
2736        let result = system.execve(&path, &[], &[]).now_or_never().unwrap();
2737        assert_eq!(result, Err(Errno::ENOSYS));
2738    }
2739
2740    #[test]
2741    fn execve_saves_arguments() {
2742        let system = VirtualSystem::new();
2743        let path = "/some/file";
2744        let mut content = Inode::default();
2745        content.body = FileBody::Regular {
2746            content: vec![],
2747            is_native_executable: true,
2748        };
2749        content.permissions.set(Mode::USER_EXEC, true);
2750        let content = Rc::new(RefCell::new(content));
2751        let mut state = system.state.borrow_mut();
2752        state.file_system.save(path, content).unwrap();
2753        drop(state);
2754        let path = CString::new(path).unwrap();
2755        let args = [c"file".to_owned(), c"bar".to_owned()];
2756        let envs = [c"foo=FOO".to_owned(), c"baz".to_owned()];
2757        system.execve(&path, &args, &envs).now_or_never();
2758
2759        let process = system.current_process();
2760        let arguments = process.last_exec.as_ref().unwrap();
2761        assert_eq!(arguments.0, path);
2762        assert_eq!(arguments.1, args);
2763        assert_eq!(arguments.2, envs);
2764    }
2765
2766    #[test]
2767    fn execve_returns_enoexec_for_non_executable_file() {
2768        let system = VirtualSystem::new();
2769        let path = "/some/file";
2770        let mut content = Inode::default();
2771        content.permissions.set(Mode::USER_EXEC, true);
2772        let content = Rc::new(RefCell::new(content));
2773        let mut state = system.state.borrow_mut();
2774        state.file_system.save(path, content).unwrap();
2775        drop(state);
2776        let path = CString::new(path).unwrap();
2777        let result = system.execve(&path, &[], &[]).now_or_never().unwrap();
2778        assert_eq!(result, Err(Errno::ENOEXEC));
2779    }
2780
2781    #[test]
2782    fn execve_returns_enoent_on_file_not_found() {
2783        let system = VirtualSystem::new();
2784        let result = system
2785            .execve(c"/no/such/file", &[], &[])
2786            .now_or_never()
2787            .unwrap();
2788        assert_eq!(result, Err(Errno::ENOENT));
2789    }
2790
2791    #[test]
2792    fn exit_sets_current_process_state_to_exited() {
2793        let system = VirtualSystem::new();
2794        system.exit(ExitStatus(42)).now_or_never();
2795
2796        assert!(system.current_process().state_has_changed());
2797        assert_eq!(
2798            system.current_process().state(),
2799            ProcessState::exited(ExitStatus(42))
2800        );
2801    }
2802
2803    #[test]
2804    fn exit_sends_sigchld_to_parent() {
2805        let (system, mut executor) = virtual_system_with_executor();
2806        system.sigaction(SIGCHLD, Disposition::Catch).unwrap();
2807
2808        let child_process = system.new_child_process().unwrap();
2809
2810        let mut env = Env::with_system(system);
2811        let _pid = child_process(
2812            &mut env,
2813            Box::new(|env| Box::pin(async { env.system.exit(ExitStatus(123)).await })),
2814        );
2815        executor.run_until_stalled();
2816
2817        assert_eq!(env.system.caught_signals(), [SIGCHLD]);
2818    }
2819
2820    #[test]
2821    fn chdir_changes_directory() {
2822        let system = VirtualSystem::new();
2823
2824        // Create a regular file and its parent directory
2825        let _ = system.open(
2826            c"/dir/file",
2827            OfdAccess::WriteOnly,
2828            OpenFlag::Create.into(),
2829            Mode::empty(),
2830        );
2831
2832        let result = system.chdir(c"/dir");
2833        assert_eq!(result, Ok(()));
2834        assert_eq!(system.current_process().cwd, Path::new("/dir"));
2835    }
2836
2837    #[test]
2838    fn chdir_fails_with_non_existing_directory() {
2839        let system = VirtualSystem::new();
2840
2841        let result = system.chdir(c"/no/such/dir");
2842        assert_eq!(result, Err(Errno::ENOENT));
2843    }
2844
2845    #[test]
2846    fn chdir_fails_with_non_directory_file() {
2847        let system = VirtualSystem::new();
2848
2849        // Create a regular file and its parent directory
2850        let _ = system.open(
2851            c"/dir/file",
2852            OfdAccess::WriteOnly,
2853            OpenFlag::Create.into(),
2854            Mode::empty(),
2855        );
2856
2857        let result = system.chdir(c"/dir/file");
2858        assert_eq!(result, Err(Errno::ENOTDIR));
2859    }
2860
2861    #[test]
2862    fn getrlimit_for_unset_resource_returns_infinity() {
2863        let system = VirtualSystem::new();
2864        let result = system.getrlimit(Resource::CPU).unwrap();
2865        assert_eq!(
2866            result,
2867            LimitPair {
2868                soft: INFINITY,
2869                hard: INFINITY,
2870            },
2871        );
2872    }
2873
2874    #[test]
2875    fn setrlimit_and_getrlimit_with_finite_limits() {
2876        let system = VirtualSystem::new();
2877        system
2878            .setrlimit(
2879                Resource::CORE,
2880                LimitPair {
2881                    soft: 4096,
2882                    hard: 8192,
2883                },
2884            )
2885            .unwrap();
2886        system
2887            .setrlimit(Resource::CPU, LimitPair { soft: 10, hard: 30 })
2888            .unwrap();
2889
2890        let result = system.getrlimit(Resource::CORE).unwrap();
2891        assert_eq!(
2892            result,
2893            LimitPair {
2894                soft: 4096,
2895                hard: 8192,
2896            },
2897        );
2898        let result = system.getrlimit(Resource::CPU).unwrap();
2899        assert_eq!(result, LimitPair { soft: 10, hard: 30 },);
2900    }
2901
2902    #[test]
2903    fn setrlimit_rejects_soft_limit_higher_than_hard_limit() {
2904        let system = VirtualSystem::new();
2905        let result = system.setrlimit(Resource::CPU, LimitPair { soft: 2, hard: 1 });
2906        assert_eq!(result, Err(Errno::EINVAL));
2907
2908        // The limits should not have been changed
2909        let result = system.getrlimit(Resource::CPU).unwrap();
2910        assert_eq!(
2911            result,
2912            LimitPair {
2913                soft: INFINITY,
2914                hard: INFINITY,
2915            },
2916        );
2917    }
2918
2919    #[test]
2920    fn setrlimit_refuses_raising_hard_limit() {
2921        let system = VirtualSystem::new();
2922        system
2923            .setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 1 })
2924            .unwrap();
2925        let result = system.setrlimit(Resource::CPU, LimitPair { soft: 1, hard: 2 });
2926        assert_eq!(result, Err(Errno::EPERM));
2927
2928        // The limits should not have been changed
2929        let result = system.getrlimit(Resource::CPU).unwrap();
2930        assert_eq!(result, LimitPair { soft: 1, hard: 1 });
2931    }
2932}