occlum/src/libos/src/fs/syscalls.rs
2023-05-30 10:16:48 +08:00

857 lines
25 KiB
Rust

use super::event_file::EventCreationFlags;
use super::file_ops;
use super::file_ops::{
get_abs_path_by_fd, get_utimes, AccessibilityCheckFlags, AccessibilityCheckMode, ChownFlags,
FcntlCmd, FsPath, LinkFlags, StatFlags, UnlinkFlags, Utime, UtimeFlags, AT_FDCWD, UTIME_OMIT,
};
use super::fs_ops;
use super::fs_ops::{MountFlags, MountOptions, UmountFlags};
use super::time::{clockid_t, itimerspec_t, timespec_t, timeval_t, ClockID};
use super::timer_file::{TimerCreationFlags, TimerSetFlags};
use super::*;
use crate::config::{user_rootfs_config, ConfigApp, ConfigMountFsType};
use util::mem_util::from_user;
#[allow(non_camel_case_types)]
pub struct iovec_t {
base: *const c_void,
len: size_t,
}
pub fn do_eventfd(init_val: u32) -> Result<isize> {
do_eventfd2(init_val, 0)
}
pub fn do_eventfd2(init_val: u32, flags: i32) -> Result<isize> {
info!("eventfd: initval {}, flags {} ", init_val, flags);
let inner_flags =
EventCreationFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let file_ref: Arc<dyn File> = {
let event = EventFile::new(init_val, inner_flags)?;
Arc::new(event)
};
let fd = current!().add_file(
file_ref,
inner_flags.contains(EventCreationFlags::EFD_CLOEXEC),
);
Ok(fd as isize)
}
pub fn do_timerfd_create(clockid: clockid_t, flags: i32) -> Result<isize> {
debug!("timerfd: clockid {}, flags {} ", clockid, flags);
let clockid = ClockID::from_raw(clockid)?;
match clockid {
ClockID::CLOCK_REALTIME | ClockID::CLOCK_MONOTONIC => {}
_ => {
return_errno!(EINVAL, "invalid clockid");
}
}
let timer_create_flags =
TimerCreationFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let file_ref: Arc<dyn File> = {
let timer = TimerFile::new(clockid, timer_create_flags)?;
Arc::new(timer)
};
let fd = current!().add_file(
file_ref,
timer_create_flags.contains(TimerCreationFlags::TFD_CLOEXEC),
);
Ok(fd as isize)
}
pub fn do_timerfd_settime(
fd: FileDesc,
flags: i32,
new_value_ptr: *const itimerspec_t,
old_value_ptr: *mut itimerspec_t,
) -> Result<isize> {
from_user::check_ptr(new_value_ptr)?;
let new_value = itimerspec_t::from_raw_ptr(new_value_ptr)?;
let timer_set_flags =
TimerSetFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let current = current!();
let file = current.file(fd)?;
let timerfile = file.as_timer()?;
let old_value = timerfile.set_time(timer_set_flags, &new_value)?;
if !old_value_ptr.is_null() {
from_user::check_mut_ptr(old_value_ptr)?;
unsafe {
old_value_ptr.write(old_value);
}
}
Ok(0)
}
pub fn do_timerfd_gettime(fd: FileDesc, curr_value_ptr: *mut itimerspec_t) -> Result<isize> {
from_user::check_mut_ptr(curr_value_ptr)?;
let current = current!();
let file = current.file(fd)?;
let timerfile = file.as_timer()?;
let curr_value = timerfile.time()?;
unsafe {
curr_value_ptr.write(curr_value);
}
Ok(0)
}
pub fn do_creat(path: *const i8, mode: u16) -> Result<isize> {
let flags =
AccessMode::O_WRONLY as u32 | (CreationFlags::O_CREAT | CreationFlags::O_TRUNC).bits();
self::do_open(path, flags, mode)
}
pub fn do_open(path: *const i8, flags: u32, mode: u16) -> Result<isize> {
self::do_openat(AT_FDCWD, path, flags, mode)
}
pub fn do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u16) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let fs_path = FsPath::new(&path, dirfd, false)?;
let mode = FileMode::from_bits_truncate(mode);
let fd = file_ops::do_openat(&fs_path, flags, mode)?;
Ok(fd as isize)
}
pub fn do_umask(mask: u16) -> Result<isize> {
let new_mask = FileMode::from_bits_truncate(mask).to_umask();
let old_mask = current!().process().set_umask(new_mask);
Ok(old_mask.bits() as isize)
}
pub fn do_close(fd: FileDesc) -> Result<isize> {
file_ops::do_close(fd)?;
Ok(0)
}
pub fn do_read(fd: FileDesc, buf: *mut u8, size: usize) -> Result<isize> {
let safe_buf = {
from_user::check_mut_array(buf, size)?;
unsafe { std::slice::from_raw_parts_mut(buf, size) }
};
let len = file_ops::do_read(fd, safe_buf)?;
Ok(len as isize)
}
pub fn do_write(fd: FileDesc, buf: *const u8, size: usize) -> Result<isize> {
let safe_buf = {
from_user::check_array(buf, size)?;
unsafe { std::slice::from_raw_parts(buf, size) }
};
let len = file_ops::do_write(fd, safe_buf)?;
Ok(len as isize)
}
fn do_writev_offset(
fd: FileDesc,
iov: *const iovec_t,
count: i32,
offset: Option<off_t>,
) -> Result<isize> {
let count = {
if count < 0 {
return_errno!(EINVAL, "Invalid count of iovec");
}
count as usize
};
from_user::check_array(iov, count)?;
let bufs_vec = {
let mut bufs_vec = Vec::with_capacity(count);
for iov_i in 0..count {
let iov_ptr = unsafe { iov.offset(iov_i as isize) };
let iov = unsafe { &*iov_ptr };
let buf = unsafe { std::slice::from_raw_parts(iov.base as *const u8, iov.len) };
bufs_vec.push(buf);
}
bufs_vec
};
let bufs = &bufs_vec[..];
let len = if let Some(offset) = offset {
file_ops::do_pwritev(fd, bufs, offset)?
} else {
file_ops::do_writev(fd, bufs)?
};
Ok(len as isize)
}
pub fn do_writev(fd: FileDesc, iov: *const iovec_t, count: i32) -> Result<isize> {
do_writev_offset(fd, iov, count, None)
}
fn do_readv_offset(
fd: FileDesc,
iov: *mut iovec_t,
count: i32,
offset: Option<off_t>,
) -> Result<isize> {
let count = {
if count < 0 {
return_errno!(EINVAL, "Invalid count of iovec");
}
count as usize
};
from_user::check_array(iov, count)?;
let mut bufs_vec = {
let mut bufs_vec = Vec::with_capacity(count);
for iov_i in 0..count {
let iov_ptr = unsafe { iov.offset(iov_i as isize) };
let iov = unsafe { &*iov_ptr };
let buf = unsafe { std::slice::from_raw_parts_mut(iov.base as *mut u8, iov.len) };
bufs_vec.push(buf);
}
bufs_vec
};
let bufs = &mut bufs_vec[..];
let len = if let Some(offset) = offset {
file_ops::do_preadv(fd, bufs, offset)?
} else {
file_ops::do_readv(fd, bufs)?
};
Ok(len as isize)
}
pub fn do_readv(fd: FileDesc, iov: *mut iovec_t, count: i32) -> Result<isize> {
do_readv_offset(fd, iov, count, None)
}
pub fn do_pread(fd: FileDesc, buf: *mut u8, size: usize, offset: off_t) -> Result<isize> {
let safe_buf = {
from_user::check_mut_array(buf, size)?;
unsafe { std::slice::from_raw_parts_mut(buf, size) }
};
let len = file_ops::do_pread(fd, safe_buf, offset)?;
Ok(len as isize)
}
pub fn do_preadv(fd: FileDesc, iov: *mut iovec_t, count: i32, offset: off_t) -> Result<isize> {
if offset < 0 {
return_errno!(EINVAL, "Invalid offset");
}
do_readv_offset(fd, iov, count, Some(offset))
}
pub fn do_pwrite(fd: FileDesc, buf: *const u8, size: usize, offset: off_t) -> Result<isize> {
let safe_buf = {
from_user::check_array(buf, size)?;
unsafe { std::slice::from_raw_parts(buf, size) }
};
let len = file_ops::do_pwrite(fd, safe_buf, offset)?;
Ok(len as isize)
}
pub fn do_pwritev(fd: FileDesc, iov: *const iovec_t, count: i32, offset: off_t) -> Result<isize> {
if offset < 0 {
return_errno!(EINVAL, "Invalid offset");
}
do_writev_offset(fd, iov, count, Some(offset))
}
pub fn do_fstat(fd: FileDesc, stat_buf: *mut Stat) -> Result<isize> {
from_user::check_mut_ptr(stat_buf)?;
let stat = file_ops::do_fstat(fd)?;
unsafe {
stat_buf.write(stat);
}
Ok(0)
}
pub fn do_stat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
self::do_fstatat(AT_FDCWD, path, stat_buf, 0)
}
pub fn do_lstat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
self::do_fstatat(
AT_FDCWD,
path,
stat_buf,
StatFlags::AT_SYMLINK_NOFOLLOW.bits(),
)
}
pub fn do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let flags = StatFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let fs_path = FsPath::new(&path, dirfd, flags.contains(StatFlags::AT_EMPTY_PATH))?;
from_user::check_mut_ptr(stat_buf)?;
let stat = file_ops::do_fstatat(&fs_path, flags)?;
unsafe {
stat_buf.write(stat);
}
Ok(0)
}
pub fn do_access(path: *const i8, mode: u32) -> Result<isize> {
self::do_faccessat(AT_FDCWD, path, mode, 0)
}
pub fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let fs_path = FsPath::new(&path, dirfd, false)?;
let mode = AccessibilityCheckMode::from_u32(mode)?;
let flags = AccessibilityCheckFlags::from_u32(flags)?;
file_ops::do_faccessat(&fs_path, mode, flags).map(|_| 0)
}
pub fn do_lseek(fd: FileDesc, offset: off_t, whence: i32) -> Result<isize> {
let seek_from = match whence {
0 => {
// SEEK_SET
if offset < 0 {
return_errno!(EINVAL, "Invalid offset");
}
SeekFrom::Start(offset as u64)
}
1 => {
// SEEK_CUR
SeekFrom::Current(offset)
}
2 => {
// SEEK_END
SeekFrom::End(offset)
}
_ => {
return_errno!(EINVAL, "Invalid whence");
}
};
let offset = file_ops::do_lseek(fd, seek_from)?;
Ok(offset as isize)
}
pub fn do_fsync(fd: FileDesc) -> Result<isize> {
file_ops::do_fsync(fd)?;
Ok(0)
}
pub fn do_fdatasync(fd: FileDesc) -> Result<isize> {
file_ops::do_fdatasync(fd)?;
Ok(0)
}
pub fn do_truncate(path: *const i8, len: usize) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
file_ops::do_truncate(&path, len)?;
Ok(0)
}
pub fn do_ftruncate(fd: FileDesc, len: usize) -> Result<isize> {
file_ops::do_ftruncate(fd, len)?;
Ok(0)
}
pub fn do_getdents64(fd: FileDesc, buf: *mut u8, buf_size: usize) -> Result<isize> {
let safe_buf = {
from_user::check_mut_array(buf, buf_size)?;
unsafe { std::slice::from_raw_parts_mut(buf, buf_size) }
};
let len = file_ops::do_getdents64(fd, safe_buf)?;
Ok(len as isize)
}
pub fn do_getdents(fd: FileDesc, buf: *mut u8, buf_size: usize) -> Result<isize> {
let safe_buf = {
from_user::check_mut_array(buf, buf_size)?;
unsafe { std::slice::from_raw_parts_mut(buf, buf_size) }
};
let len = file_ops::do_getdents(fd, safe_buf)?;
Ok(len as isize)
}
pub fn do_sync() -> Result<isize> {
fs_ops::do_sync()?;
Ok(0)
}
pub fn do_pipe(fds_u: *mut i32) -> Result<isize> {
do_pipe2(fds_u, 0)
}
pub fn do_pipe2(fds_u: *mut i32, flags: u32) -> Result<isize> {
from_user::check_mut_array(fds_u, 2)?;
// TODO: how to deal with open flags???
let fds = pipe::do_pipe2(flags as u32)?;
unsafe {
*fds_u.offset(0) = fds[0] as c_int;
*fds_u.offset(1) = fds[1] as c_int;
}
Ok(0)
}
pub fn do_dup(old_fd: FileDesc) -> Result<isize> {
let new_fd = file_ops::do_dup(old_fd)?;
Ok(new_fd as isize)
}
pub fn do_dup2(old_fd: FileDesc, new_fd: FileDesc) -> Result<isize> {
let new_fd = file_ops::do_dup2(old_fd, new_fd)?;
Ok(new_fd as isize)
}
pub fn do_dup3(old_fd: FileDesc, new_fd: FileDesc, flags: u32) -> Result<isize> {
let new_fd = file_ops::do_dup3(old_fd, new_fd, flags)?;
Ok(new_fd as isize)
}
pub fn do_chdir(path: *const i8) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
if path.is_empty() {
return_errno!(ENOENT, "path is an empty string");
} else if path.len() > PATH_MAX {
return_errno!(ENAMETOOLONG, "path name too long");
}
fs_ops::do_chdir(&path)?;
Ok(0)
}
pub fn do_fchdir(fd: FileDesc) -> Result<isize> {
let path = get_abs_path_by_fd(fd)?;
fs_ops::do_chdir(&path)?;
Ok(0)
}
pub fn do_getcwd(buf_ptr: *mut u8, size: usize) -> Result<isize> {
let buf = {
from_user::check_mut_array(buf_ptr, size)?;
unsafe { std::slice::from_raw_parts_mut(buf_ptr, size) }
};
let cwd = fs_ops::do_getcwd()?;
if cwd.len() + 1 > buf.len() {
return_errno!(ERANGE, "buf is not long enough");
}
buf[..cwd.len()].copy_from_slice(cwd.as_bytes());
buf[cwd.len()] = b'\0';
// The user-level library returns the pointer of buffer, the kernel just returns
// the length of the buffer filled (which includes the ending '\0' character).
Ok((cwd.len() + 1) as isize)
}
pub fn do_rename(oldpath: *const i8, newpath: *const i8) -> Result<isize> {
self::do_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath)
}
pub fn do_renameat(
olddirfd: i32,
oldpath: *const i8,
newdirfd: i32,
newpath: *const i8,
) -> Result<isize> {
let oldpath = from_user::clone_cstring_safely(oldpath)?
.to_string_lossy()
.into_owned();
let newpath = from_user::clone_cstring_safely(newpath)?
.to_string_lossy()
.into_owned();
let old_fs_path = FsPath::new(&oldpath, olddirfd, false)?;
let new_fs_path = FsPath::new(&newpath, newdirfd, false)?;
file_ops::do_renameat(&old_fs_path, &new_fs_path)?;
Ok(0)
}
pub fn do_mkdir(path: *const i8, mode: u16) -> Result<isize> {
self::do_mkdirat(AT_FDCWD, path, mode)
}
pub fn do_mkdirat(dirfd: i32, path: *const i8, mode: u16) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let fs_path = FsPath::new(&path, dirfd, false)?;
let mode = FileMode::from_bits_truncate(mode);
file_ops::do_mkdirat(&fs_path, mode)?;
Ok(0)
}
pub fn do_rmdir(path: *const i8) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
if path.is_empty() {
return_errno!(ENOENT, "path is an empty string");
} else if path.len() > PATH_MAX {
return_errno!(ENAMETOOLONG, "path name too long");
}
file_ops::do_rmdir(&path)?;
Ok(0)
}
pub fn do_link(oldpath: *const i8, newpath: *const i8) -> Result<isize> {
self::do_linkat(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0)
}
pub fn do_linkat(
olddirfd: i32,
oldpath: *const i8,
newdirfd: i32,
newpath: *const i8,
flags: i32,
) -> Result<isize> {
let oldpath = from_user::clone_cstring_safely(oldpath)?
.to_string_lossy()
.into_owned();
let newpath = from_user::clone_cstring_safely(newpath)?
.to_string_lossy()
.into_owned();
let flags = LinkFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let old_fs_path = FsPath::new(&oldpath, olddirfd, flags.contains(LinkFlags::AT_EMPTY_PATH))?;
let new_fs_path = FsPath::new(&newpath, newdirfd, false)?;
file_ops::do_linkat(&old_fs_path, &new_fs_path, flags)?;
Ok(0)
}
pub fn do_unlink(path: *const i8) -> Result<isize> {
self::do_unlinkat(AT_FDCWD, path, 0)
}
pub fn do_unlinkat(dirfd: i32, path: *const i8, flags: i32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let fs_path = FsPath::new(&path, dirfd, false)?;
let flags =
UnlinkFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flag value"))?;
file_ops::do_unlinkat(&fs_path, flags)?;
Ok(0)
}
pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize> {
self::do_readlinkat(AT_FDCWD, path, buf, size)
}
pub fn do_readlinkat(dirfd: i32, path: *const i8, buf: *mut u8, size: usize) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let buf = {
from_user::check_array(buf, size)?;
unsafe { std::slice::from_raw_parts_mut(buf, size) }
};
let fs_path = FsPath::new(&path, dirfd, false)?;
let len = file_ops::do_readlinkat(&fs_path, buf)?;
Ok(len as isize)
}
pub fn do_symlink(target: *const i8, link_path: *const i8) -> Result<isize> {
self::do_symlinkat(target, AT_FDCWD, link_path)
}
pub fn do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8) -> Result<isize> {
let target = from_user::clone_cstring_safely(target)?
.to_string_lossy()
.into_owned();
let link_path = from_user::clone_cstring_safely(link_path)?
.to_string_lossy()
.into_owned();
let fs_path = FsPath::new(&link_path, new_dirfd, false)?;
file_ops::do_symlinkat(&target, &fs_path)?;
Ok(0)
}
pub fn do_chmod(path: *const i8, mode: u16) -> Result<isize> {
self::do_fchmodat(AT_FDCWD, path, mode)
}
pub fn do_fchmod(fd: FileDesc, mode: u16) -> Result<isize> {
let mode = FileMode::from_bits_truncate(mode);
file_ops::do_fchmod(fd, mode)?;
Ok(0)
}
pub fn do_fchmodat(dirfd: i32, path: *const i8, mode: u16) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let mode = FileMode::from_bits_truncate(mode);
let fs_path = FsPath::new(&path, dirfd, false)?;
file_ops::do_fchmodat(&fs_path, mode)?;
Ok(0)
}
pub fn do_chown(path: *const i8, uid: u32, gid: u32) -> Result<isize> {
self::do_fchownat(AT_FDCWD, path, uid, gid, 0)
}
pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<isize> {
file_ops::do_fchown(fd, uid, gid)?;
Ok(0)
}
pub fn do_fchownat(dirfd: i32, path: *const i8, uid: u32, gid: u32, flags: i32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let flags = ChownFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let fs_path = FsPath::new(&path, dirfd, flags.contains(ChownFlags::AT_EMPTY_PATH))?;
file_ops::do_fchownat(&fs_path, uid, gid, flags)?;
Ok(0)
}
pub fn do_lchown(path: *const i8, uid: u32, gid: u32) -> Result<isize> {
self::do_fchownat(
AT_FDCWD,
path,
uid,
gid,
ChownFlags::AT_SYMLINK_NOFOLLOW.bits(),
)
}
pub fn do_sendfile(
out_fd: FileDesc,
in_fd: FileDesc,
offset_ptr: *mut off_t,
count: usize,
) -> Result<isize> {
let offset = if offset_ptr.is_null() {
None
} else {
from_user::check_mut_ptr(offset_ptr)?;
Some(unsafe { offset_ptr.read() })
};
let (len, offset) = file_ops::do_sendfile(out_fd, in_fd, offset, count)?;
if !offset_ptr.is_null() {
unsafe {
offset_ptr.write(offset as off_t);
}
}
Ok(len as isize)
}
pub fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result<isize> {
let mut cmd = FcntlCmd::from_raw(cmd, arg)?;
file_ops::do_fcntl(fd, &mut cmd)
}
pub fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result<isize> {
let mut ioctl_cmd = unsafe {
if argp.is_null() == false {
from_user::check_mut_ptr(argp)?;
}
IoctlCmd::new(cmd, argp)?
};
file_ops::do_ioctl(fd, &mut ioctl_cmd)?;
Ok(0)
}
pub fn do_mount_rootfs(
key_ptr: *const sgx_key_128bit_t,
rootfs_config_ptr: *const user_rootfs_config,
) -> Result<isize> {
let key = if key_ptr.is_null() {
None
} else {
Some(unsafe { key_ptr.read() })
};
// If user provided valid parameters, do runtime mount and boot
// Otherwise, do general mount and boot
if !rootfs_config_ptr.is_null() {
from_user::check_ptr(rootfs_config_ptr)?;
let rootfs_config = unsafe { *rootfs_config_ptr };
let app_config = ConfigApp::from_user(&rootfs_config)?;
debug!("user provided app config: {:?}", app_config);
fs_ops::do_mount_rootfs(&app_config, &key)?;
} else {
fs_ops::do_mount_rootfs(&config::LIBOS_CONFIG.get_app_config("app").unwrap(), &key)?;
}
Ok((0))
}
pub fn do_fallocate(fd: FileDesc, mode: u32, offset: off_t, len: off_t) -> Result<isize> {
if offset < 0 || len <= 0 {
return_errno!(
EINVAL,
"offset was less than 0, or len was less than or equal to 0"
);
}
let flags = FallocateFlags::from_u32(mode)?;
file_ops::do_fallocate(fd, flags, offset as usize, len as usize)?;
Ok(0)
}
pub fn do_fstatfs(fd: FileDesc, statfs_buf: *mut Statfs) -> Result<isize> {
from_user::check_mut_ptr(statfs_buf)?;
let statfs = fs_ops::do_fstatfs(fd)?;
unsafe {
statfs_buf.write(statfs);
}
Ok(0)
}
pub fn do_statfs(path: *const i8, statfs_buf: *mut Statfs) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
if path.is_empty() {
return_errno!(ENOENT, "path is an empty string");
} else if path.len() > PATH_MAX {
return_errno!(ENAMETOOLONG, "path name too long");
}
from_user::check_mut_ptr(statfs_buf)?;
let statfs = fs_ops::do_statfs(&path)?;
unsafe {
statfs_buf.write(statfs);
}
Ok(0)
}
pub fn do_mount(
source: *const i8,
target: *const i8,
fs_type: *const i8,
flags: u32,
options: *const i8,
) -> Result<isize> {
let source = from_user::clone_cstring_safely(source)?
.to_string_lossy()
.into_owned();
let target = from_user::clone_cstring_safely(target)?
.to_string_lossy()
.into_owned();
if target.is_empty() {
return_errno!(ENOENT, "target is an empty string");
} else if target.len() > PATH_MAX {
return_errno!(ENAMETOOLONG, "target name too long");
}
let flags = MountFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let mount_options = {
let fs_type = {
let fs_type = from_user::clone_cstring_safely(fs_type)?
.to_string_lossy()
.into_owned();
ConfigMountFsType::from_input(fs_type.as_str())?
};
MountOptions::from_fs_type_and_options(&fs_type, options)?
};
fs_ops::do_mount(&source, &target, flags, mount_options)?;
Ok(0)
}
pub fn do_umount(target: *const i8, flags: u32) -> Result<isize> {
let target = from_user::clone_cstring_safely(target)?
.to_string_lossy()
.into_owned();
let flags = UmountFlags::from_u32(flags)?;
fs_ops::do_umount(&target, flags)?;
Ok(0)
}
pub fn do_flock(fd: FileDesc, operation: i32) -> Result<isize> {
let flock_ops = FlockOps::from_i32(operation)?;
file_ops::do_flock(fd, flock_ops)?;
Ok(0)
}
pub fn do_utime(path: *const i8, times_u: *const utimbuf_t) -> Result<isize> {
let times = if !times_u.is_null() {
from_user::check_ptr(times_u)?;
let utimbuf = unsafe { *times_u };
let atime = timespec_t::from(utimbuf.atime());
atime.validate()?;
let mtime = timespec_t::from(utimbuf.mtime());
mtime.validate()?;
Some((atime, mtime))
} else {
None
};
let (atime, mtime) = file_ops::get_utimes(times)?;
self::do_utimes_wrapper(AT_FDCWD, path, atime, mtime, 0)
}
pub fn do_utimes(path: *const i8, times: *const timeval_t) -> Result<isize> {
self::do_futimesat(AT_FDCWD, path, times)
}
pub fn do_futimesat(dirfd: i32, path: *const i8, times_u: *const timeval_t) -> Result<isize> {
let times = if !times_u.is_null() {
from_user::check_array(times_u, 2)?;
let atime_ptr = unsafe { times_u.offset(0) };
let atime = unsafe { *atime_ptr };
let atime = timespec_t::from(atime);
atime.validate()?;
let mtime_ptr = unsafe { times_u.offset(1) };
let mtime = unsafe { *mtime_ptr };
let mtime = timespec_t::from(mtime);
mtime.validate()?;
Some((atime, mtime))
} else {
None
};
let (atime, mtime) = file_ops::get_utimes(times)?;
self::do_utimes_wrapper(dirfd, path, atime, mtime, 0)
}
pub fn do_utimensat(
dirfd: i32,
path: *const i8,
times_u: *const timespec_t,
flags: i32,
) -> Result<isize> {
let times = if !times_u.is_null() {
from_user::check_array(times_u, 2)?;
let atime_ptr = unsafe { times_u.offset(0) };
let atime = unsafe { *atime_ptr };
let mtime_ptr = unsafe { times_u.offset(1) };
let mtime = unsafe { *mtime_ptr };
if atime.nsec() == UTIME_OMIT && mtime.nsec() == UTIME_OMIT {
return Ok(0);
}
Some((atime, mtime))
} else {
None
};
let (atime, mtime) = file_ops::get_utimes(times)?;
self::do_utimes_wrapper(dirfd, path, atime, mtime, flags)
}
fn do_utimes_wrapper(
dirfd: i32,
path: *const i8,
atime: Utime,
mtime: Utime,
flags: i32,
) -> Result<isize> {
if path.is_null() && dirfd != AT_FDCWD {
file_ops::do_utimes_fd(dirfd as FileDesc, atime, mtime, flags)
} else {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let flags = UtimeFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let fs_path = FsPath::new(&path, dirfd, false)?;
file_ops::do_utimes_path(&fs_path, atime, mtime, flags)
}
}