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