use libseccomp::ScmpNotifResp;
use nix::{errno::Errno, NixPath};
use crate::{
compat::RenameFlags,
cookie::safe_renameat2,
kernel::{syscall_path_handler, to_renameflags},
lookup::FsFlags,
req::{PathArgs, SysArg, UNotifyEventRequest},
};
pub(crate) fn sys_rename(request: UNotifyEventRequest) -> ScmpNotifResp {
let argv = &[
SysArg {
path: Some(0),
fsflags: FsFlags::MUST_PATH
| FsFlags::NO_FOLLOW_LAST
| FsFlags::WANT_BASE
| FsFlags::DOTLAST_EBUSY,
..Default::default()
},
SysArg {
path: Some(1),
fsflags: FsFlags::NO_FOLLOW_LAST | FsFlags::WANT_BASE | FsFlags::DOTLAST_EBUSY,
..Default::default()
},
];
syscall_path_handler(request, "rename", argv, |path_args, request, sandbox| {
drop(sandbox);
syscall_rename_handler(request, path_args, RenameFlags::empty())
})
}
pub(crate) fn sys_renameat(request: UNotifyEventRequest) -> ScmpNotifResp {
let argv = &[
SysArg {
dirfd: Some(0),
path: Some(1),
fsflags: FsFlags::MUST_PATH
| FsFlags::NO_FOLLOW_LAST
| FsFlags::WANT_BASE
| FsFlags::DOTLAST_EBUSY,
..Default::default()
},
SysArg {
dirfd: Some(2),
path: Some(3),
fsflags: FsFlags::NO_FOLLOW_LAST | FsFlags::WANT_BASE | FsFlags::DOTLAST_EBUSY,
..Default::default()
},
];
syscall_path_handler(request, "renameat", argv, |path_args, request, sandbox| {
drop(sandbox);
syscall_rename_handler(request, path_args, RenameFlags::empty())
})
}
pub(crate) fn sys_renameat2(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let flags = match to_renameflags(req.data.args[4]) {
Ok(flags) => flags,
Err(errno) => return request.fail_syscall(errno),
};
let mut fsflags_new = FsFlags::NO_FOLLOW_LAST | FsFlags::WANT_BASE | FsFlags::DOTLAST_EBUSY;
if flags.contains(RenameFlags::RENAME_EXCHANGE) {
fsflags_new.insert(FsFlags::MUST_PATH);
} else if flags.contains(RenameFlags::RENAME_NOREPLACE) {
fsflags_new.insert(FsFlags::MISS_LAST);
}
let argv = &[
SysArg {
dirfd: Some(0),
path: Some(1),
fsflags: FsFlags::MUST_PATH
| FsFlags::NO_FOLLOW_LAST
| FsFlags::WANT_BASE
| FsFlags::DOTLAST_EBUSY,
..Default::default()
},
SysArg {
dirfd: Some(2),
path: Some(3),
fsflags: fsflags_new,
..Default::default()
},
];
syscall_path_handler(request, "renameat2", argv, |path_args, request, sandbox| {
drop(sandbox);
syscall_rename_handler(request, path_args, flags)
})
}
fn syscall_rename_handler(
request: &UNotifyEventRequest,
args: PathArgs,
flags: RenameFlags,
) -> Result<ScmpNotifResp, Errno> {
#[expect(clippy::disallowed_methods)]
let old_path = &args.0.as_ref().unwrap().path;
#[expect(clippy::disallowed_methods)]
let new_path = &args.1.as_ref().unwrap().path;
if old_path.base().is_empty() || new_path.base().is_empty() {
return Err(Errno::EBUSY);
}
let req = request.scmpreq;
request.cache.add_sys_block(req, false)?;
let result = safe_renameat2(
old_path.dir(),
old_path.base(),
new_path.dir(),
new_path.base(),
flags,
);
request.cache.del_sys_block(req.id)?;
result.map(|_| request.return_syscall(0))
}