#![forbid(unsafe_code)]
use libseccomp::ScmpNotifResp;
use nix::{errno::Errno, fcntl::AtFlags, unistd::AccessFlags, NixPath};
use crate::{
cookie::safe_faccess,
kernel::{syscall_path_handler, to_atflags},
lookup::FsFlags,
req::{PathArgs, SysArg, SysFlags, UNotifyEventRequest},
};
pub(crate) fn sys_access(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let mode = match to_accessflags(req.data.args[1]) {
Ok(mode) => mode,
Err(errno) => return request.fail_syscall(errno),
};
let argv = &[SysArg {
path: Some(0),
flags: SysFlags::CHECK_MAGIC,
..Default::default()
}];
syscall_path_handler(request, "access", argv, |path_args, request, sandbox| {
drop(sandbox); syscall_access_handler(request, path_args, mode, AtFlags::empty())
})
}
pub(crate) fn sys_faccessat(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let mode = match to_accessflags(req.data.args[2]) {
Ok(mode) => mode,
Err(errno) => return request.fail_syscall(errno),
};
let argv = &[SysArg {
dirfd: Some(0),
path: Some(1),
flags: SysFlags::CHECK_MAGIC,
..Default::default()
}];
syscall_path_handler(request, "faccessat", argv, |path_args, request, sandbox| {
drop(sandbox); syscall_access_handler(request, path_args, mode, AtFlags::empty())
})
}
pub(crate) fn sys_faccessat2(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let flags = match to_atflags(
req.data.args[3],
crate::compat::AT_EACCESS | AtFlags::AT_EMPTY_PATH | AtFlags::AT_SYMLINK_NOFOLLOW,
) {
Ok(flags) => flags,
Err(errno) => return request.fail_syscall(errno),
};
let mode = match to_accessflags(req.data.args[2]) {
Ok(mode) => mode,
Err(errno) => return request.fail_syscall(errno),
};
let mut fsflags = FsFlags::MUST_PATH;
if flags.contains(AtFlags::AT_SYMLINK_NOFOLLOW) {
fsflags.insert(FsFlags::NO_FOLLOW_LAST);
}
let argv = &[SysArg {
dirfd: Some(0),
path: Some(1),
flags: if flags.contains(AtFlags::AT_EMPTY_PATH) {
SysFlags::EMPTY_PATH
} else {
SysFlags::CHECK_MAGIC
},
fsflags,
}];
syscall_path_handler(
request,
"faccessat2",
argv,
|path_args, request, sandbox| {
drop(sandbox); syscall_access_handler(request, path_args, mode, flags)
},
)
}
fn syscall_access_handler(
request: &UNotifyEventRequest,
args: PathArgs,
mode: AccessFlags,
flags: AtFlags,
) -> Result<ScmpNotifResp, Errno> {
#[expect(clippy::disallowed_methods)]
let path = &args.0.as_ref().unwrap().path;
assert!(path.base().is_empty());
safe_faccess(path.dir(), mode, flags).map(|_| request.return_syscall(0))
}
fn to_accessflags(arg: u64) -> Result<AccessFlags, Errno> {
#[expect(clippy::cast_possible_truncation)]
AccessFlags::from_bits(arg as i32).ok_or(Errno::EINVAL)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_accessflags_zero_1() {
let flags = to_accessflags(0).unwrap();
assert_eq!(flags, AccessFlags::empty());
}
#[test]
fn test_to_accessflags_r_ok_1() {
let flags = to_accessflags(libc::R_OK as u64).unwrap();
assert!(flags.contains(AccessFlags::R_OK));
}
#[test]
fn test_to_accessflags_w_ok_1() {
let flags = to_accessflags(libc::W_OK as u64).unwrap();
assert!(flags.contains(AccessFlags::W_OK));
}
#[test]
fn test_to_accessflags_x_ok_1() {
let flags = to_accessflags(libc::X_OK as u64).unwrap();
assert!(flags.contains(AccessFlags::X_OK));
}
#[test]
fn test_to_accessflags_rwx_1() {
let arg = (libc::R_OK | libc::W_OK | libc::X_OK) as u64;
let flags = to_accessflags(arg).unwrap();
assert!(flags.contains(AccessFlags::R_OK));
assert!(flags.contains(AccessFlags::W_OK));
assert!(flags.contains(AccessFlags::X_OK));
}
#[test]
fn test_to_accessflags_invalid_1() {
let result = to_accessflags(0x80);
assert_eq!(result, Err(Errno::EINVAL));
}
}