Add chmod and chown system calls

This commit is contained in:
LI Qing 2020-04-01 03:38:24 +00:00
parent e1648fc870
commit 978edf8a17
18 changed files with 491 additions and 42 deletions

2
deps/sefs vendored

@ -1 +1 @@
Subproject commit 69f265cdaacd063f87033627c7ef297ae78bd29f
Subproject commit 12ddbb4f6356a1496cf818b4f043bbe9931b8c96

@ -52,7 +52,7 @@ impl File for DevRandom {
mtime: Timespec { sec: 0, nsec: 0 },
ctime: Timespec { sec: 0, nsec: 0 },
type_: FileType::CharDevice,
mode: 0444,
mode: (FileMode::S_IRUSR | FileMode::S_IRGRP | FileMode::S_IROTH).bits(),
nlinks: 0,
uid: 0,
gid: 0,

@ -47,6 +47,10 @@ pub trait File: Debug + Sync + Send + Any {
return_op_unsupported_error!("metadata")
}
fn set_metadata(&self, metadata: &Metadata) -> Result<()> {
return_op_unsupported_error!("set_metadata")
}
fn set_len(&self, len: u64) -> Result<()> {
return_op_unsupported_error!("set_len")
}

@ -0,0 +1,78 @@
use super::*;
bitflags! {
pub struct FileMode: u16 {
/// set-user-ID
const S_ISUID = 0o4000;
/// set-group-ID
const S_ISGID = 0o2000;
/// sticky bit
const S_ISVTX = 0o1000;
/// read by owner
const S_IRUSR = 0o0400;
/// write by owner
const S_IWUSR = 0o0200;
/// execute/search by owner
const S_IXUSR = 0o0100;
/// read by group
const S_IRGRP = 0o0040;
/// write by group
const S_IWGRP = 0o0020;
/// execute/search by group
const S_IXGRP = 0o0010;
/// read by others
const S_IROTH = 0o0004;
/// write by others
const S_IWOTH = 0o0002;
/// execute/search by others
const S_IXOTH = 0o0001;
}
}
impl FileMode {
pub fn is_readable(&self) -> bool {
self.contains(FileMode::S_IRUSR)
}
pub fn is_writable(&self) -> bool {
self.contains(FileMode::S_IWUSR)
}
pub fn is_executable(&self) -> bool {
self.contains(FileMode::S_IXUSR)
}
pub fn has_sticky_bit(&self) -> bool {
self.contains(FileMode::S_ISVTX)
}
pub fn has_set_uid(&self) -> bool {
self.contains(FileMode::S_ISUID)
}
pub fn has_set_gid(&self) -> bool {
self.contains(FileMode::S_ISGID)
}
}
pub fn do_chmod(path: &str, mode: FileMode) -> Result<()> {
debug!("chmod: path: {:?}, mode: {:?}", path, mode);
let inode = {
let current_ref = process::get_current();
let mut current = current_ref.lock().unwrap();
current.lookup_inode(path)?
};
let mut info = inode.metadata()?;
info.mode = mode.bits();
inode.set_metadata(&info)?;
Ok(())
}
pub fn do_fchmod(fd: FileDesc, mode: FileMode) -> Result<()> {
debug!("fchmod: fd: {}, mode: {:?}", fd, mode);
let file_ref = process::get_file(fd)?;
let mut info = file_ref.metadata()?;
info.mode = mode.bits();
file_ref.set_metadata(&info)?;
Ok(())
}

@ -0,0 +1,30 @@
use super::*;
pub fn do_chown(path: &str, uid: u32, gid: u32) -> Result<()> {
warn!("chown is partial implemented as lchown");
do_lchown(path, uid, gid)
}
pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<()> {
debug!("fchown: fd: {}, uid: {}, gid: {}", fd, uid, gid);
let file_ref = process::get_file(fd)?;
let mut info = file_ref.metadata()?;
info.uid = uid as usize;
info.gid = gid as usize;
file_ref.set_metadata(&info)?;
Ok(())
}
pub fn do_lchown(path: &str, uid: u32, gid: u32) -> Result<()> {
debug!("lchown: path: {:?}, uid: {}, gid: {}", path, uid, gid);
let inode = {
let current_ref = process::get_current();
let mut current = current_ref.lock().unwrap();
current.lookup_inode(path)?
};
let mut info = inode.metadata()?;
info.uid = uid as usize;
info.gid = gid as usize;
inode.set_metadata(&info)?;
Ok(())
}

@ -4,6 +4,8 @@ use process::Process;
pub use self::access::{do_access, do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode};
pub use self::chdir::do_chdir;
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;
pub use self::dirent::do_getdents64;
pub use self::dirfd::{get_dir_path, DirFd};
@ -29,6 +31,8 @@ pub use self::write::{do_pwrite, do_write, do_writev};
mod access;
mod chdir;
mod chmod;
mod chown;
mod close;
mod dirent;
mod dirfd;

@ -9,6 +9,14 @@ pub fn do_rename(oldpath: &str, newpath: &str) -> Result<()> {
let (new_dir_path, new_file_name) = split_path(&newpath);
let old_dir_inode = current_process.lookup_inode(old_dir_path)?;
let new_dir_inode = current_process.lookup_inode(new_dir_path)?;
let old_file_mode = {
let old_file_inode = old_dir_inode.find(old_file_name)?;
let metadata = old_file_inode.metadata()?;
FileMode::from_bits_truncate(metadata.mode)
};
if old_file_mode.has_sticky_bit() {
warn!("ignoring the sticky bit");
}
// TODO: support to modify file's absolute path
old_dir_inode.move_(old_file_name, &new_dir_inode, new_file_name)?;
Ok(())

@ -10,9 +10,14 @@ pub fn do_unlink(path: &str) -> Result<()> {
current_process.lookup_inode(dir_path)?
};
let file_inode = dir_inode.find(file_name)?;
if file_inode.metadata()?.type_ == FileType::Dir {
let metadata = file_inode.metadata()?;
if metadata.type_ == FileType::Dir {
return_errno!(EISDIR, "unlink on directory");
}
let file_mode = FileMode::from_bits_truncate(metadata.mode);
if file_mode.has_sticky_bit() {
warn!("ignoring the sticky bit");
}
dir_inode.unlink(file_name)?;
Ok(())
}

@ -1,6 +1,5 @@
use super::*;
use rcore_fs_sefs::dev::SefsMac;
use sgx_trts::libc::{S_IRUSR, S_IWUSR};
pub struct INodeFile {
inode: Arc<dyn INode>,
@ -13,7 +12,7 @@ pub struct INodeFile {
impl File for INodeFile {
fn read(&self, buf: &mut [u8]) -> Result<usize> {
if !self.access_mode.readable() {
return_errno!(EBADF, "File not readable");
return_errno!(EACCES, "File not readable");
}
let mut offset = self.offset.lock().unwrap();
let len = self.inode.read_at(*offset, buf).map_err(|e| errno!(e))?;
@ -23,7 +22,7 @@ impl File for INodeFile {
fn write(&self, buf: &[u8]) -> Result<usize> {
if !self.access_mode.writable() {
return_errno!(EBADF, "File not writable");
return_errno!(EACCES, "File not writable");
}
let mut offset = self.offset.lock().unwrap();
if self.status_flags.read().unwrap().always_append() {
@ -37,7 +36,7 @@ impl File for INodeFile {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
if !self.access_mode.readable() {
return_errno!(EBADF, "File not readable");
return_errno!(EACCES, "File not readable");
}
let len = self.inode.read_at(offset, buf)?;
Ok(len)
@ -45,7 +44,7 @@ impl File for INodeFile {
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
if !self.access_mode.writable() {
return_errno!(EBADF, "File not writable");
return_errno!(EACCES, "File not writable");
}
let len = self.inode.write_at(offset, buf)?;
Ok(len)
@ -53,7 +52,7 @@ impl File for INodeFile {
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
if !self.access_mode.readable() {
return_errno!(EBADF, "File not readable");
return_errno!(EACCES, "File not readable");
}
let mut offset = self.offset.lock().unwrap();
let mut total_len = 0;
@ -72,7 +71,7 @@ impl File for INodeFile {
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
if !self.access_mode.writable() {
return_errno!(EBADF, "File not writable");
return_errno!(EACCES, "File not writable");
}
let mut offset = self.offset.lock().unwrap();
if self.status_flags.read().unwrap().always_append() {
@ -116,9 +115,14 @@ impl File for INodeFile {
Ok(metadata)
}
fn set_metadata(&self, metadata: &Metadata) -> Result<()> {
self.inode.set_metadata(metadata)?;
Ok(())
}
fn set_len(&self, len: u64) -> Result<()> {
if !self.access_mode.writable() {
return_errno!(EBADF, "File not writable. Can't set len.");
return_errno!(EACCES, "File not writable. Can't set len.");
}
self.inode.resize(len as usize)?;
Ok(())
@ -136,7 +140,7 @@ impl File for INodeFile {
fn read_entry(&self) -> Result<String> {
if !self.access_mode.readable() {
return_errno!(EBADF, "File not readable. Can't read entry.");
return_errno!(EACCES, "File not readable. Can't read entry.");
}
let mut offset = self.offset.lock().unwrap();
let name = self.inode.get_entry(*offset)?;
@ -177,12 +181,12 @@ impl File for INodeFile {
match lock.l_type {
FlockType::F_RDLCK => {
if !self.access_mode.readable() {
return_errno!(EBADF, "File not readable");
return_errno!(EACCES, "File not readable");
}
}
FlockType::F_WRLCK => {
if !self.access_mode.writable() {
return_errno!(EBADF, "File not writable");
return_errno!(EACCES, "File not writable");
}
}
_ => (),
@ -201,10 +205,10 @@ impl INodeFile {
pub fn open(inode: Arc<dyn INode>, abs_path: &str, flags: u32) -> Result<Self> {
let access_mode = AccessMode::from_u32(flags)?;
if (access_mode.readable() && !inode.allow_read()?) {
return_errno!(EBADF, "File not readable");
return_errno!(EACCES, "File not readable");
}
if (access_mode.writable() && !inode.allow_write()?) {
return_errno!(EBADF, "File not writable");
return_errno!(EACCES, "File not writable");
}
let status_flags = StatusFlags::from_bits_truncate(flags);
Ok(INodeFile {
@ -253,16 +257,14 @@ impl INodeExt for dyn INode {
fn allow_write(&self) -> Result<bool> {
let info = self.metadata()?;
let perms = info.mode as u32;
let writable = (perms & S_IWUSR) == S_IWUSR;
Ok(writable)
let file_mode = FileMode::from_bits_truncate(info.mode);
Ok(file_mode.is_writable())
}
fn allow_read(&self) -> Result<bool> {
let info = self.metadata()?;
let perms = info.mode as u32;
let readable = (perms & S_IRUSR) == S_IRUSR;
Ok(readable)
let file_mode = FileMode::from_bits_truncate(info.mode);
Ok(file_mode.is_readable())
}
}

@ -12,7 +12,7 @@ use std::path::Path;
pub use self::dev_fs::AsDevRandom;
pub use self::event_file::{AsEvent, EventFile};
pub use self::file::{File, FileRef};
pub use self::file_ops::{AccessMode, CreationFlags, Stat, StatusFlags};
pub use self::file_ops::{AccessMode, CreationFlags, FileMode, Stat, StatusFlags};
pub use self::file_ops::{Flock, FlockType};
pub use self::file_ops::{IoctlCmd, StructuredIoctlArgType, StructuredIoctlNum};
pub use self::file_table::{FileDesc, FileTable};

@ -369,6 +369,42 @@ pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize>
Ok(len as isize)
}
pub fn do_chmod(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);
file_ops::do_chmod(&path, mode)?;
Ok(0)
}
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_chown(path: *const i8, uid: u32, gid: u32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
file_ops::do_chown(&path, uid, gid)?;
Ok(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_lchown(path: *const i8, uid: u32, gid: u32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
file_ops::do_lchown(&path, uid, gid)?;
Ok(0)
}
pub fn do_sendfile(
out_fd: FileDesc,
in_fd: FileDesc,

@ -5,8 +5,8 @@ use std::path::Path;
use std::sgxfs::SgxFile;
use super::fs::{
CreationFlags, File, FileDesc, FileTable, HostStdioFds, INodeExt, StdinFile, StdoutFile,
ROOT_INODE,
CreationFlags, File, FileDesc, FileMode, FileTable, HostStdioFds, INodeExt, StdinFile,
StdoutFile, ROOT_INODE,
};
use super::misc::ResourceLimitsRef;
use super::vm::{ProcessVM, ProcessVMBuilder};
@ -146,12 +146,25 @@ pub enum FileAction {
}
fn load_elf_to_vec(elf_path: &str, parent_ref: &ProcessRef) -> Result<Vec<u8>> {
#[rustfmt::skip]
parent_ref
let inode = parent_ref
.lock()
.unwrap()
.lookup_inode(elf_path)
.map_err(|e| errno!(e.errno(), "cannot find the ELF"))?
.map_err(|e| errno!(e.errno(), "cannot find the ELF"))?;
let file_mode = {
let info = inode.metadata()?;
FileMode::from_bits_truncate(info.mode)
};
if !file_mode.is_executable() {
return_errno!(EACCES, "elf file is not executable");
}
if file_mode.has_set_uid() || file_mode.has_set_gid() {
warn!(
"set-user-ID and set-group-ID are not supportted, FileMode:{:?}",
file_mode
);
}
inode
.read_as_vec()
.map_err(|e| errno!(e.errno(), "failed to read the executable ELF"))
}

@ -8,12 +8,12 @@
//! 4. Call `do_*` to process the system call (in other modules)
use fs::{
do_access, do_chdir, do_close, do_dup, do_dup2, do_dup3, do_eventfd, do_eventfd2, do_faccessat,
do_fcntl, do_fdatasync, do_fstat, do_fstatat, do_fsync, do_ftruncate, do_getdents64, do_ioctl,
do_link, do_lseek, do_lstat, do_mkdir, do_open, do_openat, do_pipe, do_pipe2, do_pread,
do_pwrite, do_read, do_readlink, do_readv, do_rename, do_rmdir, do_sendfile, do_stat, do_sync,
do_truncate, do_unlink, do_write, do_writev, iovec_t, File, FileDesc, FileRef, HostStdioFds,
Stat,
do_access, do_chdir, do_chmod, do_chown, do_close, do_dup, do_dup2, do_dup3, do_eventfd,
do_eventfd2, do_faccessat, do_fchmod, do_fchown, do_fcntl, do_fdatasync, do_fstat, do_fstatat,
do_fsync, do_ftruncate, do_getdents64, do_ioctl, do_lchown, do_link, do_lseek, do_lstat,
do_mkdir, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink,
do_readv, do_rename, do_rmdir, do_sendfile, do_stat, do_sync, do_truncate, do_unlink, do_write,
do_writev, iovec_t, File, FileDesc, FileRef, HostStdioFds, Stat,
};
use misc::{resource_t, rlimit_t, utsname_t};
use net::{
@ -154,11 +154,11 @@ macro_rules! process_syscall_table_with_callback {
(Unlink = 87) => do_unlink(path: *const i8),
(Symlink = 88) => handle_unsupported(),
(Readlink = 89) => do_readlink(path: *const i8, buf: *mut u8, size: usize),
(Chmod = 90) => handle_unsupported(),
(Fchmod = 91) => handle_unsupported(),
(Chown = 92) => handle_unsupported(),
(Fchown = 93) => handle_unsupported(),
(Lchown = 94) => handle_unsupported(),
(Chmod = 90) => do_chmod(path: *const i8, mode: u16),
(Fchmod = 91) => do_fchmod(fd: FileDesc, mode: u16),
(Chown = 92) => do_chown(path: *const i8, uid: u32, gid: u32),
(Fchown = 93) => do_fchown(fd: FileDesc, uid: u32, gid: u32),
(Lchown = 94) => do_lchown(path: *const i8, uid: u32, gid: u32),
(Umask = 95) => handle_unsupported(),
(Gettimeofday = 96) => do_gettimeofday(tv_u: *mut timeval_t),
(Getrlimit = 97) => handle_unsupported(),

@ -12,8 +12,8 @@ endif
TEST_DEPS := client data_sink
# 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 \
truncate readdir mkdir open stat link symlink tls pthread uname rlimit server \
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
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
# Benchmarks: need to be compiled and run by bench-% target
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput

5
test/chmod/Makefile Normal file

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

116
test/chmod/main.c Normal file

@ -0,0 +1,116 @@
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.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 = 00444;
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_chmod(const char *file_path) {
struct stat stat_buf;
mode_t mode = 00664;
int ret;
ret = chmod(file_path, mode);
if (ret < 0) {
THROW_ERROR("failed to chmod file");
}
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
if ((stat_buf.st_mode & 07777) != mode) {
THROW_ERROR("check chmod result failed");
}
return 0;
}
static int __test_fchmod(const char *file_path) {
struct stat stat_buf;
mode_t mode = 00664;
int fd, ret;
fd = open(file_path, O_RDONLY);
if (fd < 0) {
THROW_ERROR("failed to open file");
}
ret = fchmod(fd, mode);
if (ret < 0) {
close(fd);
THROW_ERROR("failed to fchmod file");
}
close(fd);
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
if ((stat_buf.st_mode & 07777) != mode) {
THROW_ERROR("check fchmod result failed");
}
return 0;
}
typedef int(*test_chmod_func_t)(const char *);
static int test_chmod_framework(test_chmod_func_t fn) {
const char *file_path = "/root/test_filesystem_chmod.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_chmod() {
return test_chmod_framework(__test_chmod);
}
static int test_fchmod() {
return test_chmod_framework(__test_fchmod);
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_chmod),
TEST_CASE(test_fchmod),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}

5
test/chown/Makefile Normal file

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

143
test/chown/main.c Normal file

@ -0,0 +1,143 @@
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.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 = 00444;
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_chown(const char *file_path) {
struct stat stat_buf;
uid_t uid = 100;
gid_t gid = 1000;
int ret;
ret = chown(file_path, uid, gid);
if (ret < 0) {
THROW_ERROR("failed to chown file");
}
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
THROW_ERROR("check chown result failed");
}
return 0;
}
static int __test_lchown(const char *file_path) {
struct stat stat_buf;
uid_t uid = 100;
gid_t gid = 1000;
int ret;
ret = lchown(file_path, uid, gid);
if (ret < 0) {
THROW_ERROR("failed to lchown file");
}
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
THROW_ERROR("check lchown result failed");
}
return 0;
}
static int __test_fchown(const char *file_path) {
struct stat stat_buf;
uid_t uid = 100;
gid_t gid = 1000;
int fd, ret;
fd = open(file_path, O_RDONLY);
if (fd < 0) {
THROW_ERROR("failed to open file");
}
ret = fchown(fd, uid, gid);
if (ret < 0) {
close(fd);
THROW_ERROR("failed to fchown file");
}
close(fd);
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
THROW_ERROR("check fchown result failed");
}
return 0;
}
typedef int(*test_chown_func_t)(const char *);
static int test_chown_framework(test_chown_func_t fn) {
const char *file_path = "/root/test_filesystem_chown.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_chown() {
return test_chown_framework(__test_chown);
}
static int test_lchown() {
return test_chown_framework(__test_lchown);
}
static int test_fchown() {
return test_chown_framework(__test_fchown);
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_chown),
TEST_CASE(test_lchown),
TEST_CASE(test_fchown),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}