Add fstatat and openat system calls

This commit is contained in:
LI Qing 2020-03-03 07:59:06 +00:00
parent 66dec604e4
commit 23817fc659
13 changed files with 461 additions and 29 deletions

@ -27,10 +27,8 @@ impl AccessibilityCheckFlags {
} }
} }
pub const AT_FDCWD: i32 = -100;
pub fn do_faccessat( pub fn do_faccessat(
dirfd: Option<FileDesc>, dirfd: DirFd,
path: &str, path: &str,
mode: AccessibilityCheckMode, mode: AccessibilityCheckMode,
flags: AccessibilityCheckFlags, flags: AccessibilityCheckFlags,
@ -41,8 +39,8 @@ pub fn do_faccessat(
); );
match dirfd { match dirfd {
// TODO: handle dirfd // TODO: handle dirfd
Some(dirfd) => return_errno!(ENOSYS, "cannot accept dirfd"), DirFd::Fd(dirfd) => return_errno!(ENOSYS, "cannot accept dirfd"),
None => do_access(path, mode), DirFd::Cwd => do_access(path, mode),
} }
} }

@ -0,0 +1,40 @@
use super::*;
pub const AT_FDCWD: i32 = -100;
#[derive(Debug)]
pub enum DirFd {
Cwd,
Fd(FileDesc),
}
impl DirFd {
pub fn from_i32(fd: i32) -> Result<DirFd> {
let dirfd = if fd >= 0 {
DirFd::Fd(fd as FileDesc)
} else if fd == AT_FDCWD {
DirFd::Cwd
} else {
return_errno!(EINVAL, "invalid dirfd");
};
Ok(dirfd)
}
}
// Get the absolute path of directory
pub fn get_dir_path(dirfd: FileDesc) -> Result<String> {
let dir_path = {
let current_ref = process::get_current();
let proc = current_ref.lock().unwrap();
let file_ref = proc.get_files().lock().unwrap().get(dirfd)?;
if let Ok(inode_file) = file_ref.as_inode_file() {
if inode_file.metadata()?.type_ != FileType::Dir {
return_errno!(ENOTDIR, "not a directory");
}
inode_file.get_abs_path().to_owned()
} else {
return_errno!(EBADF, "not an inode file");
}
};
Ok(dir_path)
}

@ -2,12 +2,11 @@ use super::dev_fs::{DevNull, DevRandom, DevSgx, DevZero};
use super::*; use super::*;
use process::Process; use process::Process;
pub use self::access::{ pub use self::access::{do_access, do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode};
do_access, do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode, AT_FDCWD,
};
pub use self::chdir::do_chdir; pub use self::chdir::do_chdir;
pub use self::close::do_close; pub use self::close::do_close;
pub use self::dirent::do_getdents64; pub use self::dirent::do_getdents64;
pub use self::dirfd::{get_dir_path, DirFd};
pub use self::dup::{do_dup, do_dup2, do_dup3}; pub use self::dup::{do_dup, do_dup2, do_dup3};
pub use self::fcntl::{do_fcntl, FcntlCmd}; pub use self::fcntl::{do_fcntl, FcntlCmd};
pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags}; pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags};
@ -17,12 +16,12 @@ pub use self::ioctl::{do_ioctl, IoctlCmd, StructuredIoctlArgType, StructuredIoct
pub use self::link::do_link; pub use self::link::do_link;
pub use self::lseek::do_lseek; pub use self::lseek::do_lseek;
pub use self::mkdir::do_mkdir; pub use self::mkdir::do_mkdir;
pub use self::open::do_open; pub use self::open::do_openat;
pub use self::read::{do_pread, do_read, do_readv}; pub use self::read::{do_pread, do_read, do_readv};
pub use self::rename::do_rename; pub use self::rename::do_rename;
pub use self::rmdir::do_rmdir; pub use self::rmdir::do_rmdir;
pub use self::sendfile::do_sendfile; pub use self::sendfile::do_sendfile;
pub use self::stat::{do_fstat, do_lstat, do_stat, Stat}; pub use self::stat::{do_fstat, do_fstatat, do_lstat, Stat, StatFlags};
pub use self::symlink::do_readlink; pub use self::symlink::do_readlink;
pub use self::truncate::{do_ftruncate, do_truncate}; pub use self::truncate::{do_ftruncate, do_truncate};
pub use self::unlink::do_unlink; pub use self::unlink::do_unlink;
@ -32,6 +31,7 @@ mod access;
mod chdir; mod chdir;
mod close; mod close;
mod dirent; mod dirent;
mod dirfd;
mod dup; mod dup;
mod fcntl; mod fcntl;
mod file_flags; mod file_flags;

@ -1,11 +1,6 @@
use super::*; use super::*;
pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> { fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
info!(
"open: path: {:?}, flags: {:#o}, mode: {:#o}",
path, flags, mode
);
let current_ref = process::get_current(); let current_ref = process::get_current();
let mut proc = current_ref.lock().unwrap(); let mut proc = current_ref.lock().unwrap();
@ -21,3 +16,22 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
}; };
Ok(fd) Ok(fd)
} }
pub fn do_openat(dirfd: DirFd, path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
info!(
"openat: dirfd: {:?}, path: {:?}, flags: {:#o}, mode: {:#o}",
dirfd, path, flags, mode
);
if Path::new(path).is_absolute() {
// Path is absolute, so dirfd is ignored
return Ok(do_open(path, flags, mode)?);
}
let path = match dirfd {
DirFd::Fd(dirfd) => {
let dir_path = get_dir_path(dirfd)?;
dir_path + "/" + path
}
DirFd::Cwd => path.to_owned(),
};
do_open(&path, flags, mode)
}

@ -105,6 +105,14 @@ impl StatMode {
} }
} }
bitflags! {
pub struct StatFlags: u32 {
const AT_EMPTY_PATH = 1 << 12;
const AT_NO_AUTOMOUNT = 1 << 11;
const AT_SYMLINK_NOFOLLOW = 1 << 8;
}
}
impl From<Metadata> for Stat { impl From<Metadata> for Stat {
fn from(info: Metadata) -> Self { fn from(info: Metadata) -> Self {
Stat { Stat {
@ -126,7 +134,7 @@ impl From<Metadata> for Stat {
} }
} }
pub fn do_stat(path: &str) -> Result<Stat> { fn do_stat(path: &str) -> Result<Stat> {
warn!("stat is partial implemented as lstat"); warn!("stat is partial implemented as lstat");
do_lstat(path) do_lstat(path)
} }
@ -149,3 +157,31 @@ pub fn do_lstat(path: &str) -> Result<Stat> {
let stat = Stat::from(inode.metadata()?); let stat = Stat::from(inode.metadata()?);
Ok(stat) Ok(stat)
} }
pub fn do_fstatat(dirfd: DirFd, path: &str, flags: StatFlags) -> Result<Stat> {
info!(
"fstatat: dirfd: {:?}, path: {:?}, flags: {:?}",
dirfd, path, flags
);
if path.len() == 0 && !flags.contains(StatFlags::AT_EMPTY_PATH) {
return_errno!(ENOENT, "path is an empty string");
}
if Path::new(path).is_absolute() {
// Path is absolute, so dirfd is ignored
return Ok(do_stat(path)?);
}
match dirfd {
DirFd::Fd(dirfd) => {
if path.len() == 0 {
// Path is an empty string, and the flags contiains AT_EMPTY_PATH,
// so the behavior of fstatat() is similar to that of fstat().
do_fstat(dirfd)
} else {
let dir_path = get_dir_path(dirfd)?;
let path = dir_path + "/" + path;
do_stat(&path)
}
}
DirFd::Cwd => do_stat(path),
}
}

@ -7,6 +7,7 @@ use std::any::Any;
use std::fmt; use std::fmt;
use std::io::{Read, Seek, SeekFrom, Write}; use std::io::{Read, Seek, SeekFrom, Write};
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::path::Path;
pub use self::dev_fs::AsDevRandom; pub use self::dev_fs::AsDevRandom;
pub use self::file::{File, FileRef}; pub use self::file::{File, FileRef};

@ -1,5 +1,7 @@
use super::file_ops; use super::file_ops;
use super::file_ops::{AccessibilityCheckFlags, AccessibilityCheckMode, FcntlCmd, AT_FDCWD}; use super::file_ops::{
AccessibilityCheckFlags, AccessibilityCheckMode, DirFd, FcntlCmd, StatFlags,
};
use super::fs_ops; use super::fs_ops;
use super::*; use super::*;
use util::mem_util::from_user; use util::mem_util::from_user;
@ -14,7 +16,16 @@ pub fn do_open(path: *const i8, flags: u32, mode: u32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)? let path = from_user::clone_cstring_safely(path)?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
let fd = file_ops::do_open(&path, flags, mode)?; let fd = file_ops::do_openat(DirFd::Cwd, &path, flags, mode)?;
Ok(fd as isize)
}
pub fn do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u32) -> Result<isize> {
let dirfd = DirFd::from_i32(dirfd)?;
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
let fd = file_ops::do_openat(dirfd, &path, flags, mode)?;
Ok(fd as isize) Ok(fd as isize)
} }
@ -115,7 +126,7 @@ pub fn do_stat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
.into_owned(); .into_owned();
from_user::check_mut_ptr(stat_buf)?; from_user::check_mut_ptr(stat_buf)?;
let stat = file_ops::do_stat(&path)?; let stat = file_ops::do_fstatat(DirFd::Cwd, &path, StatFlags::empty())?;
unsafe { unsafe {
stat_buf.write(stat); stat_buf.write(stat);
} }
@ -145,6 +156,20 @@ pub fn do_lstat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
Ok(0) Ok(0)
} }
pub fn do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32) -> Result<isize> {
let dirfd = DirFd::from_i32(dirfd)?;
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
from_user::check_mut_ptr(stat_buf)?;
let flags = StatFlags::from_bits_truncate(flags);
let stat = file_ops::do_fstatat(dirfd, &path, flags)?;
unsafe {
stat_buf.write(stat);
}
Ok(0)
}
pub fn do_access(path: *const i8, mode: u32) -> Result<isize> { pub fn do_access(path: *const i8, mode: u32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)? let path = from_user::clone_cstring_safely(path)?
.to_string_lossy() .to_string_lossy()
@ -154,13 +179,7 @@ pub fn do_access(path: *const i8, mode: u32) -> Result<isize> {
} }
pub fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result<isize> { pub fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result<isize> {
let dirfd = if dirfd >= 0 { let dirfd = DirFd::from_i32(dirfd)?;
Some(dirfd as FileDesc)
} else if dirfd == AT_FDCWD {
None
} else {
return_errno!(EINVAL, "invalid dirfd");
};
let path = from_user::clone_cstring_safely(path)? let path = from_user::clone_cstring_safely(path)?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();

@ -60,6 +60,7 @@ pub extern "C" fn dispatch_syscall(
let ret = match syscall_num { let ret = match syscall_num {
// file // file
SysOpen => fs::do_open(arg0 as *const i8, arg1 as u32, arg2 as u32), SysOpen => fs::do_open(arg0 as *const i8, arg1 as u32, arg2 as u32),
SysOpenat => fs::do_openat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32),
SysClose => fs::do_close(arg0 as FileDesc), SysClose => fs::do_close(arg0 as FileDesc),
SysRead => fs::do_read(arg0 as FileDesc, arg1 as *mut u8, arg2 as usize), SysRead => fs::do_read(arg0 as FileDesc, arg1 as *mut u8, arg2 as usize),
SysWrite => fs::do_write(arg0 as FileDesc, arg1 as *const u8, arg2 as usize), SysWrite => fs::do_write(arg0 as FileDesc, arg1 as *const u8, arg2 as usize),
@ -80,6 +81,12 @@ pub extern "C" fn dispatch_syscall(
SysStat => fs::do_stat(arg0 as *const i8, arg1 as *mut Stat), SysStat => fs::do_stat(arg0 as *const i8, arg1 as *mut Stat),
SysFstat => fs::do_fstat(arg0 as FileDesc, arg1 as *mut Stat), SysFstat => fs::do_fstat(arg0 as FileDesc, arg1 as *mut Stat),
SysLstat => fs::do_lstat(arg0 as *const i8, arg1 as *mut Stat), SysLstat => fs::do_lstat(arg0 as *const i8, arg1 as *mut Stat),
SysNewfstatat => fs::do_fstatat(
arg0 as i32,
arg1 as *const i8,
arg2 as *mut Stat,
arg3 as u32,
),
SysAccess => fs::do_access(arg0 as *const i8, arg1 as u32), SysAccess => fs::do_access(arg0 as *const i8, arg1 as u32),
SysFaccessat => fs::do_faccessat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32), SysFaccessat => fs::do_faccessat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32),
SysLseek => fs::do_lseek(arg0 as FileDesc, arg1 as off_t, arg2 as i32), SysLseek => fs::do_lseek(arg0 as FileDesc, arg1 as off_t, arg2 as i32),

@ -11,8 +11,8 @@ endif
# Dependencies: need to be compiled but not to run by any Makefile target # Dependencies: need to be compiled but not to run by any Makefile target
TEST_DEPS := client data_sink TEST_DEPS := client data_sink
# Tests: need to be compiled and run by test-% target # Tests: need to be compiled and run by test-% target
TESTS := empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \ TESTS ?= empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
truncate readdir mkdir link symlink tls pthread uname rlimit server \ truncate readdir mkdir open stat link symlink tls pthread uname rlimit server \
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \ server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
ioctl fcntl ioctl fcntl
# Benchmarks: need to be compiled and run by bench-% target # Benchmarks: need to be compiled and run by bench-% target

5
test/open/Makefile Normal file

@ -0,0 +1,5 @@
include ../test_common.mk
EXTRA_C_FLAGS :=
EXTRA_LINK_FLAGS :=
BIN_ARGS :=

111
test/open/main.c Normal file

@ -0,0 +1,111 @@
#include <fcntl.h>
#include <libgen.h>
#include <unistd.h>
#include <stdio.h>
#include "test.h"
// ============================================================================
// Helper function
// ============================================================================
static int remove_file(const char *file_path) {
int ret;
ret = unlink(file_path);
if (ret < 0) {
THROW_ERROR("failed to unlink the created file");
}
return 0;
}
// ============================================================================
// Test cases for open
// ============================================================================
static int __test_open(const char *file_path, int flags, int mode) {
int fd = open(file_path, flags, mode);
if (fd < 0) {
THROW_ERROR("failed to open a file");
}
close(fd);
return 0;
}
static int __test_openat_with_abs_path(const char *file_path, int flags, int mode) {
int fd = openat(AT_FDCWD, file_path, flags, mode);
if (fd < 0) {
THROW_ERROR("failed to openat a file with abs path");
}
close(fd);
return 0;
}
static int __test_openat_with_dirfd(const char *file_path, int flags, int mode) {
char dir_buf[128] = { 0 };
char base_buf[128] = { 0 };
char *dir_name, *file_name;
int dirfd, fd, ret;
ret = snprintf(dir_buf, sizeof(dir_buf), "%s", file_path);
if (ret >= sizeof(dir_buf) || ret < 0) {
THROW_ERROR("failed to copy file path to the dir buffer");
}
ret = snprintf(base_buf, sizeof(base_buf), "%s", file_path);
if (ret >= sizeof(base_buf) || ret < 0) {
THROW_ERROR("failed to copy file path to the base buffer");
}
dir_name = dirname(dir_buf);
file_name = basename(base_buf);
dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
fd = openat(dirfd, file_name, flags, mode);
if (fd < 0) {
close(dirfd);
THROW_ERROR("failed to openat a file with dirfd");
}
close(dirfd);
close(fd);
return 0;
}
typedef int(*test_open_func_t)(const char *, int, int);
static int test_open_framework(test_open_func_t fn) {
const char *file_path = "/root/test_filesystem_open.txt";
int flags = O_RDONLY | O_CREAT| O_TRUNC;
int mode = 00666;
if (fn(file_path, flags, mode) < 0)
return -1;
if (remove_file(file_path) < 0)
return -1;
return 0;
}
static int test_open() {
return test_open_framework(__test_open);
}
static int test_openat_with_abs_path() {
return test_open_framework(__test_openat_with_abs_path);
}
static int test_openat_with_dirfd() {
return test_open_framework(__test_openat_with_dirfd);
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_open),
TEST_CASE(test_openat_with_abs_path),
TEST_CASE(test_openat_with_dirfd),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}

5
test/stat/Makefile Normal file

@ -0,0 +1,5 @@
include ../test_common.mk
EXTRA_C_FLAGS :=
EXTRA_LINK_FLAGS :=
BIN_ARGS :=

196
test/stat/main.c Normal file

@ -0,0 +1,196 @@
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <unistd.h>
#include <stdio.h>
#include "test.h"
// ============================================================================
// Helper function
// ============================================================================
static int create_file(const char *file_path) {
int fd;
int flags = O_RDONLY | O_CREAT| O_TRUNC;
int mode = 00666;
fd = open(file_path, flags, mode);
if (fd < 0) {
THROW_ERROR("failed to create a file");
}
close(fd);
return 0;
}
static int remove_file(const char *file_path) {
int ret;
ret = unlink(file_path);
if (ret < 0) {
THROW_ERROR("failed to unlink the created file");
}
return 0;
}
// ============================================================================
// Test cases for stat
// ============================================================================
static int __test_stat(const char *file_path) {
struct stat stat_buf;
int ret;
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
return 0;
}
static int __test_fstat(const char *file_path) {
struct stat stat_buf;
int fd, ret;
int flags = O_RDONLY;
fd = open(file_path, flags);
if (fd < 0) {
THROW_ERROR("failed to open file");
}
ret = fstat(fd, &stat_buf);
if (ret < 0) {
close(fd);
THROW_ERROR("failed to fstat file");
}
close(fd);
return 0;
}
static int __test_lstat(const char *file_path) {
struct stat stat_buf;
int ret;
ret = lstat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to lstat file");
}
return 0;
}
static int __test_fstatat_with_abs_path(const char *file_path) {
struct stat stat_buf;
int ret;
ret = fstatat(AT_FDCWD, file_path, &stat_buf, 0);
if (ret < 0) {
THROW_ERROR("failed to fstatat file with abs path");
}
return 0;
}
static int __test_fstatat_with_empty_path(const char *file_path) {
struct stat stat_buf;
int fd, ret;
ret = fstatat(AT_FDCWD, "", &stat_buf, 0);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("fstatat with empty path should return ENOENT");
}
fd = open(file_path, O_RDONLY);
if (fd < 0) {
THROW_ERROR("failed to open file");
}
ret = fstatat(fd, "", &stat_buf, AT_EMPTY_PATH);
if (ret < 0) {
close(fd);
THROW_ERROR("failed to fstatat empty path with AT_EMPTY_PATH flags");
}
close(fd);
return 0;
}
static int __test_fstatat_with_dirfd(const char *file_path) {
struct stat stat_buf;
char dir_buf[128] = { 0 };
char base_buf[128] = { 0 };
char *dir_name, *file_name;
int dirfd, ret;
ret = snprintf(dir_buf, sizeof(dir_buf), "%s", file_path);
if (ret >= sizeof(dir_buf) || ret < 0) {
THROW_ERROR("failed to copy file path to the dir buffer");
}
ret = snprintf(base_buf, sizeof(base_buf), "%s", file_path);
if (ret >= sizeof(base_buf) || ret < 0) {
THROW_ERROR("failed to copy file path to the base buffer");
}
dir_name = dirname(dir_buf);
file_name = basename(base_buf);
dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
ret = fstatat(dirfd, file_name, &stat_buf, 0);
if (ret < 0) {
close(dirfd);
THROW_ERROR("failed to fstatat file with dirfd");
}
close(dirfd);
return 0;
}
typedef int(*test_stat_func_t)(const char *);
static int test_stat_framework(test_stat_func_t fn) {
const char *file_path = "/root/test_filesystem_stat.txt";
if (create_file(file_path) < 0)
return -1;
if (fn(file_path) < 0)
return -1;
if (remove_file(file_path) < 0)
return -1;
return 0;
}
static int test_stat() {
return test_stat_framework(__test_stat);
}
static int test_fstat() {
return test_stat_framework(__test_fstat);
}
static int test_lstat() {
return test_stat_framework(__test_lstat);
}
static int test_fstatat_with_abs_path() {
return test_stat_framework(__test_fstatat_with_abs_path);
}
static int test_fstatat_with_empty_path() {
return test_stat_framework(__test_fstatat_with_empty_path);
}
static int test_fstatat_with_dirfd() {
return test_stat_framework(__test_fstatat_with_dirfd);
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_stat),
TEST_CASE(test_fstat),
TEST_CASE(test_lstat),
TEST_CASE(test_fstatat_with_abs_path),
TEST_CASE(test_fstatat_with_empty_path),
TEST_CASE(test_fstatat_with_dirfd),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}