use libseccomp::{ScmpArch, ScmpNotifResp};
use nix::errno::Errno;
use crate::{
compat::{fstatfs64, statfs32, statfs64, Statfs},
confine::{is_valid_ptr, scmp_arch_is_compat_long32},
fd::is_valid_fd,
kernel::syscall_path_handler,
lookup::FsFlags,
req::{SysArg, UNotifyEventRequest},
};
pub(crate) fn sys_statfs(request: UNotifyEventRequest) -> ScmpNotifResp {
let argv = &[SysArg {
path: Some(0),
fsflags: FsFlags::MUST_PATH,
..Default::default()
}];
syscall_statfs_handler(request, "statfs", argv, 1, false)
}
pub(crate) fn sys_statfs64(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let is32 = scmp_arch_is_compat_long32(req.data.arch);
let size = if is32 {
size_of::<statfs64>()
} else {
size_of::<Statfs>()
};
let sz = match usize::try_from(req.data.args[1]) {
Ok(sz) => arm_oabi_statfs64_size_fixup(req.data.arch, sz),
Err(_) => return request.fail_syscall(Errno::EINVAL),
};
if sz != size {
return request.fail_syscall(Errno::EINVAL);
}
let argv = &[SysArg {
path: Some(0),
fsflags: FsFlags::MUST_PATH,
..Default::default()
}];
syscall_statfs_handler(request, "statfs64", argv, 2, true)
}
pub(crate) fn sys_fstatfs(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
if !is_valid_fd(req.data.args[0]) {
return request.fail_syscall(Errno::EBADF);
}
let argv = &[SysArg {
dirfd: Some(0),
..Default::default()
}];
syscall_statfs_handler(request, "fstatfs", argv, 1, false)
}
pub(crate) fn sys_fstatfs64(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let is32 = scmp_arch_is_compat_long32(req.data.arch);
let size = if is32 {
size_of::<statfs64>()
} else {
size_of::<Statfs>()
};
let sz = match usize::try_from(req.data.args[1]) {
Ok(sz) => arm_oabi_statfs64_size_fixup(req.data.arch, sz),
Err(_) => return request.fail_syscall(Errno::EINVAL),
};
if sz != size {
return request.fail_syscall(Errno::EINVAL);
}
if !is_valid_fd(req.data.args[0]) {
return request.fail_syscall(Errno::EBADF);
}
let argv = &[SysArg {
dirfd: Some(0),
..Default::default()
}];
syscall_statfs_handler(request, "fstatfs64", argv, 2, true)
}
fn syscall_statfs_handler(
request: UNotifyEventRequest,
syscall_name: &str,
argv: &[SysArg],
arg_statfs: usize,
compat64: bool,
) -> ScmpNotifResp {
syscall_path_handler(
request,
syscall_name,
argv,
|path_args, request, sandbox| {
let req = request.scmpreq;
drop(sandbox);
#[expect(clippy::disallowed_methods)]
let fd = path_args.0.as_ref().unwrap().path.dir();
let addr = req.data.args[arg_statfs];
if !is_valid_ptr(addr, req.data.arch) {
return Err(Errno::EFAULT);
}
request.cache.add_sys_block(req, false)?;
let result = fstatfs64(fd);
request.cache.del_sys_block(req.id)?;
let result = result?;
let is32 = scmp_arch_is_compat_long32(req.data.arch);
if is32 && compat64 {
let statfs64: statfs64 = result.into();
let statfs = unsafe {
std::slice::from_raw_parts(
std::ptr::addr_of!(statfs64).cast::<u8>(),
size_of_val(&statfs64),
)
};
request.write_mem_all(statfs, addr)?;
} else if is32 {
let statfs32: statfs32 = result.try_into()?;
let statfs = unsafe {
std::slice::from_raw_parts(
std::ptr::addr_of!(statfs32).cast::<u8>(),
size_of_val(&statfs32),
)
};
request.write_mem_all(statfs, addr)?;
} else {
let statfs = unsafe {
std::slice::from_raw_parts(
std::ptr::addr_of!(result).cast::<u8>(),
size_of_val(&result),
)
};
request.write_mem_all(statfs, addr)?;
}
Ok(request.return_syscall(0))
},
)
}
fn arm_oabi_statfs64_size_fixup(arch: ScmpArch, sz: usize) -> usize {
if arch == ScmpArch::Arm && sz == 88 {
84
} else {
sz
}
}