use crate::abi::errno::Errno;
use crate::vcpu::GuestMemory;
use super::{AT_FDCWD, Kernel, err, io_errno, read_path, stat};
const AT_REMOVEDIR: u64 = 0x200;
impl Kernel {
pub(super) fn sys_statfs(&mut self, pathptr: u64, buf: u64, mem: &mut GuestMemory) -> i64 {
let Some(rel) = read_path(mem, pathptr) else {
return err(Errno::EFAULT);
};
let abs = self.resolve_path(AT_FDCWD, &rel);
let abs = self.follow_symlinks(&abs).unwrap_or(abs);
if self.mounts.stat(&abs).is_none() {
return err(Errno::ENOENT);
}
write_statfs_or_fault(mem, buf)
}
pub(super) fn sys_fstatfs(&mut self, fd: u64, buf: u64, mem: &mut GuestMemory) -> i64 {
if self.cur.fds.get(fd as i32).is_none() {
return err(Errno::EBADF);
}
write_statfs_or_fault(mem, buf)
}
pub(super) fn sys_readlinkat(
&mut self,
dirfd: i64,
pathptr: u64,
buf: u64,
bufsz: u64,
mem: &mut GuestMemory,
) -> i64 {
let Some(rel) = read_path(mem, pathptr) else {
return err(Errno::EFAULT);
};
let abs = self.resolve_path(dirfd, &rel);
let target = match self.mounts.readlink(&abs) {
Ok(t) => t,
Err(e) => return io_errno(&e),
};
let bytes = target.as_bytes();
let n = bytes.len().min(bufsz as usize);
if mem.write(buf, &bytes[..n]).is_err() {
return err(Errno::EFAULT);
}
n as i64
}
pub(super) fn sys_symlinkat(
&mut self,
targetptr: u64,
newdirfd: i64,
linkptr: u64,
mem: &GuestMemory,
) -> i64 {
let (Some(target), Some(link)) = (read_path(mem, targetptr), read_path(mem, linkptr))
else {
return err(Errno::EFAULT);
};
let abs = self.resolve_path(newdirfd, &link);
match self.mounts.symlink(&target, &abs) {
Ok(()) => 0,
Err(e) => io_errno(&e),
}
}
pub(super) fn sys_mkdirat(
&mut self,
dirfd: i64,
pathptr: u64,
mode: u64,
mem: &GuestMemory,
) -> i64 {
let Some(rel) = read_path(mem, pathptr) else {
return err(Errno::EFAULT);
};
let abs = self.resolve_path(dirfd, &rel);
match self.mounts.mkdir(&abs, (mode & 0o777) as u32) {
Ok(()) => 0,
Err(e) => io_errno(&e),
}
}
pub(super) fn sys_unlinkat(
&mut self,
dirfd: i64,
pathptr: u64,
flags: u64,
mem: &GuestMemory,
) -> i64 {
let Some(rel) = read_path(mem, pathptr) else {
return err(Errno::EFAULT);
};
let abs = self.resolve_path(dirfd, &rel);
let r = if flags & AT_REMOVEDIR != 0 {
self.mounts.rmdir(&abs)
} else {
self.mounts.unlink(&abs)
};
match r {
Ok(()) => 0,
Err(e) => io_errno(&e),
}
}
pub(super) fn sys_renameat(
&mut self,
olddirfd: i64,
oldptr: u64,
newdirfd: i64,
newptr: u64,
mem: &GuestMemory,
) -> i64 {
let (Some(old), Some(new)) = (read_path(mem, oldptr), read_path(mem, newptr)) else {
return err(Errno::EFAULT);
};
let from = self.resolve_path(olddirfd, &old);
let to = self.resolve_path(newdirfd, &new);
match self.mounts.rename(&from, &to) {
Ok(()) => 0,
Err(e) => io_errno(&e),
}
}
pub(super) fn sys_faccessat(&mut self, dirfd: i64, pathptr: u64, mem: &GuestMemory) -> i64 {
let Some(rel) = read_path(mem, pathptr) else {
return err(Errno::EFAULT);
};
let abs = self.resolve_path(dirfd, &rel);
let abs = self.follow_symlinks(&abs).unwrap_or(abs);
if self.mounts.stat(&abs).is_some() {
0
} else {
err(Errno::ENOENT)
}
}
pub(super) fn sys_umask(&mut self, mask: u64) -> i64 {
let old = self.umask;
self.umask = (mask & 0o777) as u32;
i64::from(old)
}
}
fn write_statfs_or_fault(mem: &mut GuestMemory, addr: u64) -> i64 {
let buf = stat::encode_statfs();
if mem.write(addr, &buf).is_err() {
err(Errno::EFAULT)
} else {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::Arch;
use crate::fs::{MountTable, NodeKind, TmpFs};
use crate::vcpu::GuestMemory;
use crate::vcpu::mem::Prot;
const PAGE: u64 = 4096;
fn setup() -> (Kernel, GuestMemory) {
let mut mounts = MountTable::new();
mounts.mount("/", Box::new(TmpFs::new()));
let mut kernel = Kernel::new(Arch::Aarch64, mounts);
kernel.cur.pid = 1;
let mut mem = GuestMemory::new(0x1_0000, 16 * PAGE);
mem.map(0x1_0000, 4 * PAGE, Prot::rw()).unwrap();
(kernel, mem)
}
#[test]
fn mkdirat_then_faccessat_and_stat() {
let (mut k, mut mem) = setup();
let path = 0x1_0000;
mem.write_init(path, b"/d\0").unwrap();
assert_eq!(k.sys_mkdirat(AT_FDCWD, path, 0o755, &mem), 0);
assert_eq!(k.sys_faccessat(AT_FDCWD, path, &mem), 0);
assert_eq!(k.mounts.stat("/d").unwrap().kind, NodeKind::Dir);
}
#[test]
fn symlinkat_then_readlinkat() {
let (mut k, mut mem) = setup();
let target = 0x1_0000;
let link = 0x1_0100;
let buf = 0x1_1000;
mem.write_init(target, b"/target\0").unwrap();
mem.write_init(link, b"/l\0").unwrap();
assert_eq!(k.sys_symlinkat(target, AT_FDCWD, link, &mem), 0);
assert_eq!(k.sys_readlinkat(AT_FDCWD, link, buf, 64, &mut mem), 7);
assert_eq!(mem.read_vec(buf, 7).unwrap(), b"/target");
}
#[test]
fn statfs_writes_bsize() {
let (mut k, mut mem) = setup();
let path = 0x1_0000;
let buf = 0x1_1000;
mem.write_init(path, b"/\0").unwrap();
assert_eq!(k.sys_statfs(path, buf, &mut mem), 0);
assert_eq!(mem.read_u64(buf + 8).unwrap(), 4096); }
#[test]
fn unlinkat_removes_file() {
let (mut k, mut mem) = setup();
k.mounts.create("/f", 0o644).unwrap();
let path = 0x1_0000;
mem.write_init(path, b"/f\0").unwrap();
assert_eq!(k.sys_unlinkat(AT_FDCWD, path, 0, &mem), 0);
assert!(k.mounts.stat("/f").is_none());
}
#[test]
fn umask_returns_previous() {
let (mut k, _mem) = setup();
assert_eq!(k.sys_umask(0o077), 0o022);
assert_eq!(k.sys_umask(0o022), 0o077);
}
}