use core::ffi::{c_char, c_int};
use ax_errno::{AxError, AxResult};
use ax_fs::FS_CONTEXT;
use axfs_ng_vfs::{Location, NodePermission};
use linux_raw_sys::general::{
__kernel_fsid_t, AT_EMPTY_PATH, R_OK, W_OK, X_OK, stat, statfs, statx,
};
use starry_vm::{VmMutPtr, VmPtr};
use crate::{
file::{File, FileLike, resolve_at},
mm::vm_load_string,
};
#[cfg(target_arch = "x86_64")]
pub fn sys_stat(path: *const c_char, statbuf: *mut stat) -> AxResult<isize> {
use linux_raw_sys::general::AT_FDCWD;
sys_fstatat(AT_FDCWD, path, statbuf, 0)
}
pub fn sys_fstat(fd: i32, statbuf: *mut stat) -> AxResult<isize> {
sys_fstatat(fd, core::ptr::null(), statbuf, AT_EMPTY_PATH)
}
#[cfg(target_arch = "x86_64")]
pub fn sys_lstat(path: *const c_char, statbuf: *mut stat) -> AxResult<isize> {
use linux_raw_sys::general::{AT_FDCWD, AT_SYMLINK_NOFOLLOW};
sys_fstatat(AT_FDCWD, path, statbuf, AT_SYMLINK_NOFOLLOW)
}
pub fn sys_fstatat(
dirfd: i32,
path: *const c_char,
statbuf: *mut stat,
flags: u32,
) -> AxResult<isize> {
let path = path.nullable().map(vm_load_string).transpose()?;
debug!("sys_fstatat <= dirfd: {dirfd}, path: {path:?}, flags: {flags}");
let loc = resolve_at(dirfd, path.as_deref(), flags)?;
statbuf.vm_write(loc.stat()?.into())?;
Ok(0)
}
pub fn sys_statx(
dirfd: c_int,
path: *const c_char,
flags: u32,
_mask: u32,
statxbuf: *mut statx,
) -> AxResult<isize> {
let path = path.nullable().map(vm_load_string).transpose()?;
debug!("sys_statx <= dirfd: {dirfd}, path: {path:?}, flags: {flags}");
statxbuf.vm_write(resolve_at(dirfd, path.as_deref(), flags)?.stat()?.into())?;
Ok(0)
}
#[cfg(target_arch = "x86_64")]
pub fn sys_access(path: *const c_char, mode: u32) -> AxResult<isize> {
use linux_raw_sys::general::AT_FDCWD;
sys_faccessat2(AT_FDCWD, path, mode, 0)
}
pub fn sys_faccessat2(dirfd: c_int, path: *const c_char, mode: u32, flags: u32) -> AxResult<isize> {
let path = path.nullable().map(vm_load_string).transpose()?;
debug!("sys_faccessat2 <= dirfd: {dirfd}, path: {path:?}, mode: {mode}, flags: {flags}");
let file = resolve_at(dirfd, path.as_deref(), flags)?;
if mode == 0 {
return Ok(0);
}
let mut required_mode = NodePermission::empty();
if mode & R_OK != 0 {
required_mode |= NodePermission::OWNER_READ;
}
if mode & W_OK != 0 {
required_mode |= NodePermission::OWNER_WRITE;
}
if mode & X_OK != 0 {
required_mode |= NodePermission::OWNER_EXEC;
}
let required_mode = required_mode.bits();
if (file.stat()?.mode as u16 & required_mode) != required_mode {
return Err(AxError::PermissionDenied);
}
Ok(0)
}
fn statfs(loc: &Location) -> AxResult<statfs> {
let stat = loc.filesystem().stat()?;
let mut result: statfs = unsafe { core::mem::zeroed() };
result.f_type = stat.fs_type as _;
result.f_bsize = stat.block_size as _;
result.f_blocks = stat.blocks as _;
result.f_bfree = stat.blocks_free as _;
result.f_bavail = stat.blocks_available as _;
result.f_files = stat.file_count as _;
result.f_ffree = stat.free_file_count as _;
result.f_fsid = __kernel_fsid_t {
val: [0, loc.mountpoint().device() as _],
};
result.f_namelen = stat.name_length as _;
result.f_frsize = stat.fragment_size as _;
result.f_flags = stat.mount_flags as _;
Ok(result)
}
pub fn sys_statfs(path: *const c_char, buf: *mut statfs) -> AxResult<isize> {
let path = vm_load_string(path)?;
debug!("sys_statfs <= path: {path:?}");
buf.vm_write(statfs(
&FS_CONTEXT
.lock()
.resolve(path)?
.mountpoint()
.root_location(),
)?)?;
Ok(0)
}
pub fn sys_fstatfs(fd: i32, buf: *mut statfs) -> AxResult<isize> {
debug!("sys_fstatfs <= fd: {fd}");
buf.vm_write(statfs(File::from_fd(fd)?.inner().location())?)?;
Ok(0)
}