Enable gvisor pty test

This commit is contained in:
Jianfeng Jiang
2023-08-17 14:15:35 +08:00
committed by Tate, Hongliang Tian
parent f802ff40c5
commit 40056f0692
11 changed files with 269 additions and 107 deletions

View File

@ -1,4 +1,4 @@
TESTS ?= open_test read_test statfs_test chmod_test
TESTS ?= open_test read_test statfs_test chmod_test pty_test
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))

View File

@ -0,0 +1,42 @@
PtyTrunc.Truncate
PtyTest.MasterTermiosUnchangable
PtyTest.TermiosICRNL
PtyTest.TermiosONLCR
PtyTest.TermiosINLCR
PtyTest.TermiosOCRNL
PtyTest.SwitchCanonToNonCanonNewline
PtyTest.TermiosICANONNewline
PtyTest.TermiosICANONEOF
PtyTest.CanonDiscard
PtyTest.CanonMultiline
PtyTest.SimpleEcho
PtyTest.TermiosIGNCR
PtyTest.TermiosONOCR
PtyTest.VEOLTermination
PtyTest.CanonBigWrite
PtyTest.SwitchCanonToNoncanon
PtyTest.SwitchNoncanonToCanonNewlineBig
PtyTest.SwitchNoncanonToCanonNoNewline
PtyTest.SwitchNoncanonToCanonNoNewlineBig
PtyTest.NoncanonBigWrite
PtyTest.SwitchNoncanonToCanonMultiline
PtyTest.SwitchTwiceMultiline
JobControlTest.SetTTYMaster
JobControlTest.SetTTY
JobControlTest.SetTTYNonLeader
JobControlTest.SetTTYBadArg
JobControlTest.SetTTYDifferentSession
JobControlTest.ReleaseTTY
JobControlTest.ReleaseUnsetTTY
JobControlTest.ReleaseWrongTTY
JobControlTest.ReleaseTTYNonLeader
JobControlTest.ReleaseTTYDifferentSession
JobControlTest.ReleaseTTYSignals
JobControlTest.GetForegroundProcessGroup
JobControlTest.GetForegroundProcessGroupNonControlling
JobControlTest.SetForegroundProcessGroup
JobControlTest.SetForegroundProcessGroupWrongTTY
JobControlTest.SetForegroundProcessGroupNegPgid
JobControlTest.SetForegroundProcessGroupEmptyProcessGroup
JobControlTest.SetForegroundProcessGroupDifferentSession
JobControlTest.OrphanRegression

View File

@ -55,11 +55,7 @@ impl PtyMaster {
pub(super) fn slave_push_char(&self, item: u8) -> Result<()> {
let mut buf = self.master_buffer.lock_irq_disabled();
if buf.is_full() {
return_errno_with_message!(Errno::EIO, "the buffer is full");
}
// Unwrap safety: the buf is not full, so push will always succeed.
buf.push(item).unwrap();
buf.push_overwrite(item);
self.update_state(&buf);
Ok(())
}
@ -93,13 +89,11 @@ impl PtyMaster {
}
}
fn update_state(&self, buf: &HeapRb<u8>) {
if buf.is_full() {
self.pollee.del_events(IoEvents::OUT);
} else {
self.pollee.add_events(IoEvents::OUT);
pub(super) fn slave_buf_len(&self) -> usize {
self.ldisc.buffer_len()
}
fn update_state(&self, buf: &HeapRb<u8>) {
if buf.is_empty() {
self.pollee.del_events(IoEvents::IN)
} else {
@ -139,18 +133,10 @@ impl FileLike for PtyMaster {
fn write(&self, buf: &[u8]) -> Result<usize> {
let mut master_buf = self.master_buffer.lock();
if self.ldisc.termios().contain_echo() && master_buf.len() + buf.len() > BUFFER_CAPACITY {
return_errno_with_message!(
Errno::EIO,
"the written bytes exceeds the master buf capacity"
);
}
for item in buf {
self.ldisc.push_char(*item, |content| {
for byte in content.as_bytes() {
// Unwrap safety: the master buf is ensured to have enough space.
master_buf.push(*byte).unwrap();
master_buf.push_overwrite(*byte);
}
});
}
@ -203,9 +189,18 @@ impl FileLike for PtyMaster {
};
Ok(fd)
}
IoctlCmd::TIOCGWINSZ => Ok(0),
IoctlCmd::TIOCGWINSZ => {
let winsize = self.ldisc.window_size();
write_val_to_user(arg, &winsize)?;
Ok(0)
}
IoctlCmd::TIOCSWINSZ => {
let winsize = read_val_from_user(arg)?;
self.ldisc.set_window_size(winsize);
Ok(0)
}
IoctlCmd::TIOCSCTTY => {
// TODO
// TODO: reimplement when adding session.
let foreground = {
let current = current!();
let process_group = current.process_group().lock();
@ -225,9 +220,15 @@ impl FileLike for PtyMaster {
Ok(0)
}
IoctlCmd::TIOCNOTTY => {
// TODO: reimplement when adding session.
self.ldisc.set_fg(Weak::new());
Ok(0)
}
IoctlCmd::FIONREAD => {
let len = self.master_buffer.lock().len() as i32;
write_val_to_user(arg, &len)?;
Ok(0)
}
_ => Ok(0),
}
}
@ -237,8 +238,6 @@ impl FileLike for PtyMaster {
let poll_in_mask = mask & IoEvents::IN;
loop {
let _master_buf = self.master_buffer.lock_irq_disabled();
let mut poll_status = IoEvents::empty();
if !poll_in_mask.is_empty() {

View File

@ -2,6 +2,7 @@ use crate::fs::device::{Device, DeviceId, DeviceType};
use crate::fs::file_handle::FileLike;
use crate::fs::utils::{IoEvents, IoctlCmd, Poller};
use crate::prelude::*;
use crate::util::write_val_to_user;
use super::master::PtyMaster;
@ -45,8 +46,12 @@ impl Device for PtySlave {
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
match cmd {
IoctlCmd::TCGETS | IoctlCmd::TCSETS | IoctlCmd::TIOCGPGRP => self.0.ioctl(cmd, arg),
IoctlCmd::TIOCGWINSZ => Ok(0),
IoctlCmd::TCGETS
| IoctlCmd::TCSETS
| IoctlCmd::TIOCGPGRP
| IoctlCmd::TIOCGPTN
| IoctlCmd::TIOCGWINSZ
| IoctlCmd::TIOCSWINSZ => self.0.ioctl(cmd, arg),
IoctlCmd::TIOCSCTTY => {
// TODO:
Ok(0)
@ -55,6 +60,11 @@ impl Device for PtySlave {
// TODO:
Ok(0)
}
IoctlCmd::FIONREAD => {
let buffer_len = self.0.slave_buf_len() as i32;
write_val_to_user(arg, &buffer_len)?;
Ok(0)
}
_ => Ok(0),
}
}

View File

@ -9,7 +9,7 @@ use alloc::format;
use jinux_frame::trap::disable_local;
use ringbuf::{ring_buffer::RbBase, Rb, StaticRb};
use super::termio::{KernelTermios, CC_C_CHAR};
use super::termio::{KernelTermios, WinSize, CC_C_CHAR};
// This implementation refers the implementation of linux
// https://elixir.bootlin.com/linux/latest/source/include/linux/tty_ldisc.h
@ -25,6 +25,8 @@ pub struct LineDiscipline {
foreground: SpinLock<Weak<ProcessGroup>>,
/// termios
termios: SpinLock<KernelTermios>,
/// Windows size,
winsize: SpinLock<WinSize>,
/// Pollee
pollee: Pollee,
}
@ -72,6 +74,7 @@ impl LineDiscipline {
read_buffer: SpinLock::new(StaticRb::default()),
foreground: SpinLock::new(Weak::new()),
termios: SpinLock::new(KernelTermios::default()),
winsize: SpinLock::new(WinSize::default()),
pollee: Pollee::new(IoEvents::empty()),
}
}
@ -350,6 +353,18 @@ impl LineDiscipline {
self.current_line.lock().drain();
let _: Vec<_> = self.read_buffer.lock().pop_iter().collect();
}
pub fn buffer_len(&self) -> usize {
self.read_buffer.lock().len()
}
pub fn window_size(&self) -> WinSize {
self.winsize.lock().clone()
}
pub fn set_window_size(&self, winsize: WinSize) {
*self.winsize.lock() = winsize;
}
}
fn meet_new_line(item: u8, termios: &KernelTermios) -> bool {

View File

@ -130,7 +130,13 @@ impl Device for Tty {
Ok(0)
}
IoctlCmd::TIOCGWINSZ => {
// TODO:get window size
let winsize = self.ldisc.window_size();
write_val_to_user(arg, &winsize)?;
Ok(0)
}
IoctlCmd::TIOCSWINSZ => {
let winsize = read_val_from_user(arg)?;
self.ldisc.set_window_size(winsize);
Ok(0)
}
_ => todo!(),

View File

@ -33,22 +33,76 @@ bitflags! {
}
}
impl C_IFLAGS {
pub fn new_default() -> Self {
C_IFLAGS::ICRNL | C_IFLAGS::IXON
}
}
bitflags! {
#[repr(C)]
#[derive(Pod)]
pub struct C_OFLAGS: u32 {
const OPOST = 0x01; /* Perform output processing */
const OCRNL = 0x08;
const ONOCR = 0x10;
const ONLRET= 0x20;
const OFILL = 0x40;
const OFDEL = 0x80;
const OPOST = 0x1 << 0; /* Perform output processing */
const OLCUC = 0x1 << 1;
const ONLCR = 0x1 << 2;
const OCRNL = 0x1 << 3;
const ONOCR = 0x1 << 4;
const ONLRET= 0x1 << 5;
const OFILL = 0x1 << 6;
const OFDEL = 0x1 << 7;
}
}
#[repr(u32)]
impl C_OFLAGS {
pub fn new_default() -> Self {
C_OFLAGS::OPOST | C_OFLAGS::ONLCR
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod)]
pub enum C_CFLAGS {
pub struct C_CFLAGS(u32);
impl C_CFLAGS {
pub fn new_default() -> Self {
let cbaud = C_CFLAGS_BAUD::B38400 as u32;
let csize = C_CFLAGS_CSIZE::CS8 as u32;
let c_cflags = cbaud | csize | CREAD;
Self(c_cflags)
}
pub fn cbaud(&self) -> Result<C_CFLAGS_BAUD> {
let cbaud = self.0 & CBAUD_MASK;
Ok(C_CFLAGS_BAUD::try_from(cbaud)?)
}
pub fn csize(&self) -> Result<C_CFLAGS_CSIZE> {
let csize = self.0 & CSIZE_MASK;
Ok(C_CFLAGS_CSIZE::try_from(csize)?)
}
pub fn cread(&self) -> bool {
self.0 & CREAD != 0
}
}
const CREAD: u32 = 0x00000080;
const CBAUD_MASK: u32 = 0x0000100f;
const CSIZE_MASK: u32 = 0x00000030;
#[repr(u32)]
#[derive(Clone, Copy, TryFromInt)]
pub enum C_CFLAGS_CSIZE {
CS5 = 0x00000000,
CS6 = 0x00000010,
CS7 = 0x00000020,
CS8 = 0x00000030,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, TryFromInt)]
pub enum C_CFLAGS_BAUD {
B0 = 0x00000000, /* hang up */
B50 = 0x00000001,
B75 = 0x00000002,
@ -90,6 +144,19 @@ bitflags! {
}
}
impl C_LFLAGS {
pub fn new_default() -> Self {
C_LFLAGS::ICANON
| C_LFLAGS::ECHO
| C_LFLAGS::ISIG
| C_LFLAGS::ECHOE
| C_LFLAGS::ECHOK
| C_LFLAGS::ECHOCTL
| C_LFLAGS::ECHOKE
| C_LFLAGS::IEXTEN
}
}
/* c_cc characters index*/
#[repr(u32)]
#[derive(Debug, Clone, Copy, Pod)]
@ -114,26 +181,26 @@ pub enum CC_C_CHAR {
}
impl CC_C_CHAR {
// The special char is the same as ubuntu
pub fn char(&self) -> u8 {
// The special char is from gvisor
pub fn default_char(&self) -> u8 {
match self {
CC_C_CHAR::VINTR => 3,
CC_C_CHAR::VQUIT => 28,
CC_C_CHAR::VERASE => 127,
CC_C_CHAR::VKILL => 21,
CC_C_CHAR::VEOF => 4,
CC_C_CHAR::VTIME => 0,
CC_C_CHAR::VINTR => control_character('C'),
CC_C_CHAR::VQUIT => control_character('\\'),
CC_C_CHAR::VERASE => '\x7f' as u8,
CC_C_CHAR::VKILL => control_character('U'),
CC_C_CHAR::VEOF => control_character('D'),
CC_C_CHAR::VTIME => '\0' as u8,
CC_C_CHAR::VMIN => 1,
CC_C_CHAR::VSWTC => 0,
CC_C_CHAR::VSTART => 17,
CC_C_CHAR::VSTOP => 19,
CC_C_CHAR::VSUSP => 26,
CC_C_CHAR::VEOL => 255,
CC_C_CHAR::VREPRINT => 18,
CC_C_CHAR::VDISCARD => 15,
CC_C_CHAR::VWERASE => 23,
CC_C_CHAR::VLNEXT => 22,
CC_C_CHAR::VEOL2 => 255,
CC_C_CHAR::VSWTC => '\0' as u8,
CC_C_CHAR::VSTART => control_character('Q'),
CC_C_CHAR::VSTOP => control_character('S'),
CC_C_CHAR::VSUSP => control_character('Z'),
CC_C_CHAR::VEOL => '\0' as u8,
CC_C_CHAR::VREPRINT => control_character('R'),
CC_C_CHAR::VDISCARD => control_character('O'),
CC_C_CHAR::VWERASE => control_character('W'),
CC_C_CHAR::VLNEXT => control_character('V'),
CC_C_CHAR::VEOL2 => '\0' as u8,
}
}
@ -142,28 +209,28 @@ impl CC_C_CHAR {
}
pub fn from_char(item: u8) -> Result<Self> {
if item == Self::VINTR.char() {
if item == Self::VINTR.default_char() {
return Ok(Self::VINTR);
}
if item == Self::VQUIT.char() {
if item == Self::VQUIT.default_char() {
return Ok(Self::VQUIT);
}
if item == Self::VINTR.char() {
if item == Self::VINTR.default_char() {
return Ok(Self::VINTR);
}
if item == Self::VERASE.char() {
if item == Self::VERASE.default_char() {
return Ok(Self::VERASE);
}
if item == Self::VEOF.char() {
if item == Self::VEOF.default_char() {
return Ok(Self::VEOF);
}
if item == Self::VSTART.char() {
if item == Self::VSTART.default_char() {
return Ok(Self::VSTART);
}
if item == Self::VSTOP.char() {
if item == Self::VSTOP.default_char() {
return Ok(Self::VSTOP);
}
if item == Self::VSUSP.char() {
if item == Self::VSUSP.default_char() {
return Ok(Self::VSUSP);
}
@ -185,44 +252,33 @@ pub struct KernelTermios {
impl KernelTermios {
pub fn default() -> Self {
let mut termios = Self {
c_iflags: C_IFLAGS::ICRNL,
c_oflags: C_OFLAGS::empty(),
c_cflags: C_CFLAGS::B0,
c_lflags: C_LFLAGS::ICANON | C_LFLAGS::ECHO,
c_iflags: C_IFLAGS::new_default(),
c_oflags: C_OFLAGS::new_default(),
c_cflags: C_CFLAGS::new_default(),
c_lflags: C_LFLAGS::new_default(),
c_line: 0,
c_cc: [0; KERNEL_NCCS],
};
*termios.get_special_char_mut(CC_C_CHAR::VINTR) = CC_C_CHAR::VINTR.char();
*termios.get_special_char_mut(CC_C_CHAR::VQUIT) = CC_C_CHAR::VQUIT.char();
*termios.get_special_char_mut(CC_C_CHAR::VERASE) = CC_C_CHAR::VERASE.char();
*termios.get_special_char_mut(CC_C_CHAR::VKILL) = CC_C_CHAR::VKILL.char();
*termios.get_special_char_mut(CC_C_CHAR::VEOF) = CC_C_CHAR::VEOF.char();
*termios.get_special_char_mut(CC_C_CHAR::VTIME) = CC_C_CHAR::VTIME.char();
*termios.get_special_char_mut(CC_C_CHAR::VMIN) = CC_C_CHAR::VMIN.char();
*termios.get_special_char_mut(CC_C_CHAR::VSWTC) = CC_C_CHAR::VSWTC.char();
*termios.get_special_char_mut(CC_C_CHAR::VSTART) = CC_C_CHAR::VSTART.char();
*termios.get_special_char_mut(CC_C_CHAR::VSTOP) = CC_C_CHAR::VSTOP.char();
*termios.get_special_char_mut(CC_C_CHAR::VSUSP) = CC_C_CHAR::VSUSP.char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL) = CC_C_CHAR::VEOL.char();
*termios.get_special_char_mut(CC_C_CHAR::VREPRINT) = CC_C_CHAR::VREPRINT.char();
*termios.get_special_char_mut(CC_C_CHAR::VDISCARD) = CC_C_CHAR::VDISCARD.char();
*termios.get_special_char_mut(CC_C_CHAR::VWERASE) = CC_C_CHAR::VWERASE.char();
*termios.get_special_char_mut(CC_C_CHAR::VLNEXT) = CC_C_CHAR::VLNEXT.char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL2) = CC_C_CHAR::VEOL2.char();
*termios.get_special_char_mut(CC_C_CHAR::VINTR) = CC_C_CHAR::VINTR.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VQUIT) = CC_C_CHAR::VQUIT.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VERASE) = CC_C_CHAR::VERASE.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VKILL) = CC_C_CHAR::VKILL.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VEOF) = CC_C_CHAR::VEOF.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VTIME) = CC_C_CHAR::VTIME.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VMIN) = CC_C_CHAR::VMIN.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSWTC) = CC_C_CHAR::VSWTC.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSTART) = CC_C_CHAR::VSTART.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSTOP) = CC_C_CHAR::VSTOP.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VSUSP) = CC_C_CHAR::VSUSP.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL) = CC_C_CHAR::VEOL.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VREPRINT) = CC_C_CHAR::VREPRINT.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VDISCARD) = CC_C_CHAR::VDISCARD.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VWERASE) = CC_C_CHAR::VWERASE.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VLNEXT) = CC_C_CHAR::VLNEXT.default_char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL2) = CC_C_CHAR::VEOL2.default_char();
termios
}
fn new() -> Self {
KernelTermios {
c_iflags: C_IFLAGS::empty(),
c_oflags: C_OFLAGS::empty(),
c_cflags: C_CFLAGS::B0,
c_lflags: C_LFLAGS::empty(),
c_line: 0,
c_cc: [0; KERNEL_NCCS],
}
}
pub fn get_special_char(&self, cc_c_char: CC_C_CHAR) -> &CcT {
&self.c_cc[cc_c_char.as_usize()]
}
@ -265,3 +321,17 @@ impl KernelTermios {
self.c_lflags.contains(C_LFLAGS::IEXTEN)
}
}
const fn control_character(c: char) -> u8 {
debug_assert!(c as u8 >= 'A' as u8);
c as u8 - 'A' as u8 + 1u8
}
#[derive(Debug, Clone, Copy, Default, Pod)]
#[repr(C)]
pub struct WinSize {
ws_row: u16,
ws_col: u16,
ws_xpixel: u16,
ws_ypixel: u16,
}

View File

@ -16,9 +16,10 @@ impl PtyMasterInode {
impl Drop for PtyMasterInode {
fn drop(&mut self) {
// Remove the slave from fs.
let index = self.0.index();
let fs = self.0.ptmx().fs();
let devpts = fs.downcast_ref::<DevPts>().unwrap();
let index = self.0.index();
devpts.remove_slave(index);
}
}

View File

@ -16,6 +16,8 @@ pub enum IoctlCmd {
TIOCGPGRP = 0x540f,
/// Set the foreground process group ID of this terminal.
TIOCSPGRP = 0x5410,
/// Get the number of bytes in the input buffer.
FIONREAD = 0x541B,
/// Set window size
TIOCGWINSZ = 0x5413,
TIOCSWINSZ = 0x5414,

View File

@ -146,6 +146,7 @@ impl Vnode {
if let Some(page_cache) = &inner.page_cache {
page_cache.evict_range(0..file_len);
}
inner.inode.read_at(0, &mut buf[..file_len])
}
@ -196,11 +197,13 @@ impl Vnode {
}
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
self.inner.read().inode.poll(mask, poller)
let inode = self.inner.read().inode.clone();
inode.poll(mask, poller)
}
pub fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
self.inner.read().inode.ioctl(cmd, arg)
let inode = self.inner.read().inode.clone();
inode.ioctl(cmd, arg)
}
pub fn fs(&self) -> Arc<dyn FileSystem> {

View File

@ -150,12 +150,26 @@ impl VmMapping {
}
pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
let vmo_read_offset = self.vmo_offset() + offset;
let page_idx_range = get_page_idx_range(&(vmo_read_offset..vmo_read_offset + buf.len()));
let read_perm = VmPerm::R;
for page_idx in page_idx_range {
self.check_perm(&page_idx, &read_perm)?;
}
self.vmo.read_bytes(vmo_read_offset, buf)?;
Ok(())
}
pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
let vmo_write_offset = self.vmo_offset() + offset;
let page_idx_range = get_page_idx_range(&(vmo_write_offset..vmo_write_offset + buf.len()));
let write_perm = VmPerm::W;
for page_idx in page_idx_range {
self.check_perm(&page_idx, &write_perm)?;
}
self.vmo.write_bytes(vmo_write_offset, buf)?;
Ok(())
}
@ -198,7 +212,9 @@ impl VmMapping {
} else {
self.vmo.check_rights(Rights::READ)?;
}
self.check_perm(&page_idx, write)?;
let required_perm = if write { VmPerm::W } else { VmPerm::R };
self.check_perm(&page_idx, &required_perm)?;
let frame = self.vmo.get_committed_frame(page_idx, write)?;
@ -300,8 +316,8 @@ impl VmMapping {
self.inner.lock().trim_right(vm_space, vaddr)
}
fn check_perm(&self, page_idx: &usize, write: bool) -> Result<()> {
self.inner.lock().check_perm(page_idx, write)
fn check_perm(&self, page_idx: &usize, perm: &VmPerm) -> Result<()> {
self.inner.lock().check_perm(page_idx, perm)
}
}
@ -457,16 +473,14 @@ impl VmMappingInner {
self.map_to_addr..self.map_to_addr + self.map_size
}
fn check_perm(&self, page_idx: &usize, write: bool) -> Result<()> {
fn check_perm(&self, page_idx: &usize, perm: &VmPerm) -> Result<()> {
let page_perm = self
.page_perms
.get(&page_idx)
.ok_or(Error::with_message(Errno::EINVAL, "invalid page idx"))?;
if !page_perm.contains(VmPerm::R) {
return_errno_with_message!(Errno::EINVAL, "perm should at least contain read");
}
if write && !page_perm.contains(VmPerm::W) {
return_errno_with_message!(Errno::EINVAL, "perm should contain write for write access");
if !page_perm.contains(*perm) {
return_errno_with_message!(Errno::EACCES, "perm check fails");
}
Ok(())