From 1dcabb09cdf2f07c6df9e95eb8b971e3dc6a3250 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Wed, 29 Apr 2020 05:18:01 +0000 Subject: [PATCH] Fix the access and faccessat system calls --- src/libos/src/fs/file_ops/access.rs | 45 +++++++-- src/libos/src/fs/file_ops/mod.rs | 2 +- src/libos/src/fs/syscalls.rs | 3 +- test/Makefile | 2 +- test/access/Makefile | 5 + test/access/main.c | 148 ++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 13 deletions(-) create mode 100644 test/access/Makefile create mode 100644 test/access/main.c diff --git a/src/libos/src/fs/file_ops/access.rs b/src/libos/src/fs/file_ops/access.rs index c73847a9..4bd6e5f6 100644 --- a/src/libos/src/fs/file_ops/access.rs +++ b/src/libos/src/fs/file_ops/access.rs @@ -2,27 +2,37 @@ use super::*; bitflags! { pub struct AccessibilityCheckMode : u32 { + /// F_OK = 0, test for the existence of the file + /// X_OK, test for execute permission const X_OK = 1; + /// W_OK, test for write permission const W_OK = 2; + /// R_OK, test for read permission const R_OK = 4; } } impl AccessibilityCheckMode { - pub fn from_u32(bits: u32) -> Result { + pub fn from_u32(bits: u32) -> Result { AccessibilityCheckMode::from_bits(bits).ok_or_else(|| errno!(EINVAL, "invalid mode")) } + + pub fn test_for_exist(&self) -> bool { + self.bits == 0 + } } bitflags! { pub struct AccessibilityCheckFlags : u32 { + /// If path is a symbolic link, do not dereference it const AT_SYMLINK_NOFOLLOW = 0x100; + /// Perform access checks using the effective user and group IDs const AT_EACCESS = 0x200; } } impl AccessibilityCheckFlags { - pub fn from_u32(bits: u32) -> Result { + pub fn from_u32(bits: u32) -> Result { AccessibilityCheckFlags::from_bits(bits).ok_or_else(|| errno!(EINVAL, "invalid flags")) } } @@ -37,21 +47,36 @@ pub fn do_faccessat( "faccessat: dirfd: {:?}, path: {:?}, mode: {:?}, flags: {:?}", dirfd, path, mode, flags ); - match dirfd { - // TODO: handle dirfd - DirFd::Fd(dirfd) => return_errno!(ENOSYS, "cannot accept dirfd"), - DirFd::Cwd => do_access(path, mode), + if Path::new(path).is_absolute() { + // Path is absolute, so dirfd is ignored + return Ok(do_access(path, mode)?); } + let path = match dirfd { + DirFd::Fd(dirfd) => { + let dir_path = get_dir_path(dirfd)?; + dir_path + "/" + path + } + DirFd::Cwd => path.to_owned(), + }; + do_access(&path, mode) } -pub fn do_access(path: &str, mode: AccessibilityCheckMode) -> Result<()> { - debug!("access: path: {:?}, mode: {:?}", path, mode); +fn do_access(path: &str, mode: AccessibilityCheckMode) -> Result<()> { let inode = { let current = current!(); let fs = current.fs().lock().unwrap(); fs.lookup_inode(path)? }; - //let metadata = inode.get_metadata(); - // TODO: check metadata.mode with mode + if mode.test_for_exist() { + return Ok(()); + } + // Check the permissions of file owner + let owner_file_mode = { + let metadata = inode.metadata()?; + AccessibilityCheckMode::from_u32((metadata.mode >> 6) as u32 & 0b111)? + }; + if !owner_file_mode.contains(mode) { + return_errno!(EACCES, "the requested access is denied"); + } Ok(()) } diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index f538bea6..f810020e 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -2,7 +2,7 @@ use super::dev_fs::{DevNull, DevRandom, DevSgx, DevZero}; use super::*; use process::Process; -pub use self::access::{do_access, do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode}; +pub use self::access::{do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode}; pub use self::chmod::{do_chmod, do_fchmod, FileMode}; pub use self::chown::{do_chown, do_fchown, do_lchown}; pub use self::close::do_close; diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index da1debc2..fe2c4228 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -197,7 +197,8 @@ pub fn do_access(path: *const i8, mode: u32) -> Result { .to_string_lossy() .into_owned(); let mode = AccessibilityCheckMode::from_u32(mode)?; - file_ops::do_access(&path, mode).map(|_| 0) + let flags = AccessibilityCheckFlags::empty(); + file_ops::do_faccessat(DirFd::Cwd, &path, mode, flags).map(|_| 0) } pub fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result { diff --git a/test/Makefile b/test/Makefile index ba4f6e37..9a1f10f1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -14,7 +14,7 @@ TEST_DEPS := client data_sink TESTS ?= empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \ truncate readdir mkdir open stat link symlink chmod chown tls pthread uname rlimit \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \ - ioctl fcntl eventfd emulate_syscall + ioctl fcntl eventfd emulate_syscall access # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/access/Makefile b/test/access/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/access/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/access/main.c b/test/access/main.c new file mode 100644 index 00000000..35e1d529 --- /dev/null +++ b/test/access/main.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include "test.h" + +// ============================================================================ +// Helper function +// ============================================================================ + +static int create_file(const char *file_path, mode_t mode) { + int flags = O_RDONLY | O_CREAT| O_TRUNC; + int fd; + + 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 access +// ============================================================================ + +static int __test_access(const char *file_path) { + if (access(file_path, F_OK) < 0) { + THROW_ERROR("failed to access file with F_OK"); + } + if (access(file_path, R_OK | W_OK) < 0) { + THROW_ERROR("failed to access file"); + } + if (access(file_path, R_OK | W_OK | X_OK) >= 0 || errno != EACCES) { + THROW_ERROR("failed to access file with X_OK"); + } + if (access(file_path, 0xF) >= 0 || errno != EINVAL) { + THROW_ERROR("failed to access file with invalid mode"); + } + if (remove_file(file_path) < 0) { + return -1; + } + if (access(file_path, F_OK) >= 0 || errno != ENOENT) { + THROW_ERROR("failed to access file after unlink"); + } + return 0; +} + +static int __test_faccessat_with_abs_path(const char *file_path) { + if (faccessat(AT_FDCWD, file_path, F_OK, 0) < 0) { + THROW_ERROR("failed to faccessat file with abs path"); + } + if (remove_file(file_path) < 0) { + return -1; + } + if (faccessat(AT_FDCWD, file_path, F_OK, 0) >= 0 || errno != ENOENT) { + THROW_ERROR("failed to faccessat file after unlink"); + } + return 0; +} + +static int __test_faccessat_with_dirfd(const char *file_path) { + 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"); + } + if (faccessat(dirfd, file_name, F_OK, 0) < 0) { + close(dirfd); + THROW_ERROR("failed to faccessat file with dirfd"); + } + if (remove_file(file_path) < 0) { + close(dirfd); + return -1; + } + if (faccessat(dirfd, file_name, F_OK, 0) >= 0 || errno != ENOENT) { + close(dirfd); + THROW_ERROR("failed to faccessat file after unlink"); + } + close(dirfd); + return 0; +} + +typedef int(*test_access_func_t)(const char *); + +static int test_access_framework(test_access_func_t fn) { + const char *file_path = "/root/test_filesystem_access.txt"; + mode_t mode = 00666; + + if (create_file(file_path, mode) < 0) { + return -1; + } + if (fn(file_path) < 0) + return -1; + return 0; +} + +static int test_access() { + return test_access_framework(__test_access); +} + +static int test_faccessat_with_abs_path() { + return test_access_framework(__test_faccessat_with_abs_path); +} + +static int test_faccessat_with_dirfd() { + return test_access_framework(__test_faccessat_with_dirfd); +} + +// ============================================================================ +// Test suite main +// ============================================================================ + +static test_case_t test_cases[] = { + TEST_CASE(test_access), + TEST_CASE(test_faccessat_with_abs_path), + TEST_CASE(test_faccessat_with_dirfd), +}; + +int main(int argc, const char *argv[]) { + return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); +}