#![forbid(unsafe_code)]
use libseccomp::ScmpNotifResp;
use nix::{errno::Errno, fcntl::AtFlags, NixPath};
use crate::{
compat::TimeSpec64,
confine::scmp_arch_is_compat_long32,
cookie::safe_utimensat,
kernel::{syscall_path_handler, to_atflags},
lookup::FsFlags,
req::{PathArgs, SysArg, SysFlags, UNotifyEventRequest},
};
pub(crate) fn sys_utime(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let is32 = scmp_arch_is_compat_long32(req.data.arch);
let times = match request.remote_utimbuf(req.data.args[1], is32) {
Ok(times) => times,
Err(errno) => return request.fail_syscall(errno),
};
let argv = &[SysArg {
path: Some(0),
fsflags: FsFlags::MUST_PATH,
..Default::default()
}];
syscall_path_handler(request, "utime", argv, |path_args, request, sandbox| {
drop(sandbox); let (atime, mtime) = times;
syscall_utime_handler(request, path_args, &atime, &mtime)
})
}
pub(crate) fn sys_utimes(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let is32 = scmp_arch_is_compat_long32(req.data.arch);
let times = match request.remote_timeval(req.data.args[1], is32) {
Ok(times) => times,
Err(errno) => return request.fail_syscall(errno),
};
let argv = &[SysArg {
path: Some(0),
fsflags: FsFlags::MUST_PATH,
..Default::default()
}];
syscall_path_handler(request, "utimes", argv, |path_args, request, sandbox| {
drop(sandbox); let (atime, mtime) = times;
syscall_utime_handler(request, path_args, &atime, &mtime)
})
}
pub(crate) fn sys_futimesat(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let is32 = scmp_arch_is_compat_long32(req.data.arch);
let times = match request.remote_timeval(req.data.args[2], is32) {
Ok(times) => times,
Err(errno) => return request.fail_syscall(errno),
};
#[expect(clippy::cast_possible_truncation)]
let is_fd = req.data.args[1] == 0 && req.data.args[0] as libc::c_int != libc::AT_FDCWD;
let fsflags = FsFlags::MUST_PATH;
let path = if is_fd { None } else { Some(1) };
let argv = &[SysArg {
dirfd: Some(0),
path,
fsflags,
..Default::default()
}];
syscall_path_handler(request, "futimesat", argv, |path_args, request, sandbox| {
drop(sandbox); let (atime, mtime) = times;
syscall_utime_handler(request, path_args, &atime, &mtime)
})
}
pub(crate) fn sys_utimensat(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let is32 = scmp_arch_is_compat_long32(req.data.arch);
let times = match request.remote_timespec_2(req.data.args[2], is32) {
Ok(times) => times,
Err(errno) => return request.fail_syscall(errno),
};
if times.0.tv_nsec() == TimeSpec64::UTIME_OMIT.tv_nsec()
&& times.1.tv_nsec() == TimeSpec64::UTIME_OMIT.tv_nsec()
{
return request.return_syscall(0);
}
#[expect(clippy::cast_possible_truncation)]
let is_fd = req.data.args[1] == 0 && req.data.args[0] as libc::c_int != libc::AT_FDCWD;
let atflags = if is_fd {
AtFlags::empty()
} else {
AtFlags::AT_EMPTY_PATH | AtFlags::AT_SYMLINK_NOFOLLOW
};
let atflags = match to_atflags(req.data.args[3], atflags) {
Ok(atflags) => atflags,
Err(errno) => return request.fail_syscall(errno),
};
let mut flags = SysFlags::empty();
let mut fsflags = FsFlags::MUST_PATH;
if atflags.contains(AtFlags::AT_EMPTY_PATH) {
flags |= SysFlags::EMPTY_PATH;
}
if atflags.contains(AtFlags::AT_SYMLINK_NOFOLLOW) {
fsflags |= FsFlags::NO_FOLLOW_LAST;
}
let argv = &[SysArg {
dirfd: Some(0),
path: if is_fd { None } else { Some(1) },
flags,
fsflags,
}];
syscall_path_handler(request, "utimensat", argv, |path_args, request, sandbox| {
drop(sandbox); let (atime, mtime) = times;
syscall_utime_handler(request, path_args, &atime, &mtime)
})
}
pub(crate) fn sys_utimensat64(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let times = match request.remote_timespec_2(req.data.args[2], false) {
Ok(times) => times,
Err(errno) => return request.fail_syscall(errno),
};
if times.0.tv_nsec() == TimeSpec64::UTIME_OMIT.tv_nsec()
&& times.1.tv_nsec() == TimeSpec64::UTIME_OMIT.tv_nsec()
{
return request.return_syscall(0);
}
#[expect(clippy::cast_possible_truncation)]
let is_fd = req.data.args[1] == 0 && req.data.args[0] as libc::c_int != libc::AT_FDCWD;
let atflags = if is_fd {
AtFlags::empty()
} else {
AtFlags::AT_EMPTY_PATH | AtFlags::AT_SYMLINK_NOFOLLOW
};
let atflags = match to_atflags(req.data.args[3], atflags) {
Ok(atflags) => atflags,
Err(errno) => return request.fail_syscall(errno),
};
let mut flags = SysFlags::empty();
let mut fsflags = FsFlags::MUST_PATH;
if atflags.contains(AtFlags::AT_EMPTY_PATH) {
flags |= SysFlags::EMPTY_PATH;
}
if atflags.contains(AtFlags::AT_SYMLINK_NOFOLLOW) {
fsflags |= FsFlags::NO_FOLLOW_LAST;
}
let argv = &[SysArg {
dirfd: Some(0),
path: if is_fd { None } else { Some(1) },
flags,
fsflags,
}];
syscall_path_handler(
request,
"utimensat_time64",
argv,
|path_args, request, sandbox| {
drop(sandbox); let (atime, mtime) = times;
syscall_utime_handler(request, path_args, &atime, &mtime)
},
)
}
fn syscall_utime_handler(
request: &UNotifyEventRequest,
args: PathArgs,
atime: &TimeSpec64,
mtime: &TimeSpec64,
) -> Result<ScmpNotifResp, Errno> {
#[expect(clippy::disallowed_methods)]
let path = &args.0.as_ref().unwrap().path;
assert!(path.base().is_empty()); let fd = path.dir();
let req = request.scmpreq;
request.cache.add_sys_block(req, false)?;
let result = safe_utimensat(fd, atime, mtime);
request.cache.del_sys_block(req.id)?;
result.map(|_| request.return_syscall(0))
}