From 978edf8a17144d1adf758ff1df6952029d814c48 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Wed, 1 Apr 2020 03:38:24 +0000 Subject: [PATCH] Add chmod and chown system calls --- deps/sefs | 2 +- src/libos/src/fs/dev_fs/dev_random.rs | 2 +- src/libos/src/fs/file.rs | 4 + src/libos/src/fs/file_ops/chmod.rs | 78 ++++++++++++++ src/libos/src/fs/file_ops/chown.rs | 30 ++++++ src/libos/src/fs/file_ops/mod.rs | 4 + src/libos/src/fs/file_ops/rename.rs | 8 ++ src/libos/src/fs/file_ops/unlink.rs | 7 +- src/libos/src/fs/inode_file.rs | 40 +++---- src/libos/src/fs/mod.rs | 2 +- src/libos/src/fs/syscalls.rs | 36 +++++++ src/libos/src/process/spawn/mod.rs | 25 +++-- src/libos/src/syscall/mod.rs | 22 ++-- test/Makefile | 4 +- test/chmod/Makefile | 5 + test/chmod/main.c | 116 +++++++++++++++++++++ test/chown/Makefile | 5 + test/chown/main.c | 143 ++++++++++++++++++++++++++ 18 files changed, 491 insertions(+), 42 deletions(-) create mode 100644 src/libos/src/fs/file_ops/chmod.rs create mode 100644 src/libos/src/fs/file_ops/chown.rs create mode 100644 test/chmod/Makefile create mode 100644 test/chmod/main.c create mode 100644 test/chown/Makefile create mode 100644 test/chown/main.c diff --git a/deps/sefs b/deps/sefs index 69f265cd..12ddbb4f 160000 --- a/deps/sefs +++ b/deps/sefs @@ -1 +1 @@ -Subproject commit 69f265cdaacd063f87033627c7ef297ae78bd29f +Subproject commit 12ddbb4f6356a1496cf818b4f043bbe9931b8c96 diff --git a/src/libos/src/fs/dev_fs/dev_random.rs b/src/libos/src/fs/dev_fs/dev_random.rs index f37a67b5..c6ada639 100644 --- a/src/libos/src/fs/dev_fs/dev_random.rs +++ b/src/libos/src/fs/dev_fs/dev_random.rs @@ -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, diff --git a/src/libos/src/fs/file.rs b/src/libos/src/fs/file.rs index 29b2e3c5..c835f939 100644 --- a/src/libos/src/fs/file.rs +++ b/src/libos/src/fs/file.rs @@ -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") } diff --git a/src/libos/src/fs/file_ops/chmod.rs b/src/libos/src/fs/file_ops/chmod.rs new file mode 100644 index 00000000..bbaa89d5 --- /dev/null +++ b/src/libos/src/fs/file_ops/chmod.rs @@ -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(()) +} diff --git a/src/libos/src/fs/file_ops/chown.rs b/src/libos/src/fs/file_ops/chown.rs new file mode 100644 index 00000000..53249f2a --- /dev/null +++ b/src/libos/src/fs/file_ops/chown.rs @@ -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(()) +} diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index fbe14111..0b1e2828 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -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; diff --git a/src/libos/src/fs/file_ops/rename.rs b/src/libos/src/fs/file_ops/rename.rs index c412a23d..a76980f6 100644 --- a/src/libos/src/fs/file_ops/rename.rs +++ b/src/libos/src/fs/file_ops/rename.rs @@ -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(()) diff --git a/src/libos/src/fs/file_ops/unlink.rs b/src/libos/src/fs/file_ops/unlink.rs index dd74ba84..7a5b859f 100644 --- a/src/libos/src/fs/file_ops/unlink.rs +++ b/src/libos/src/fs/file_ops/unlink.rs @@ -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(()) } diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 1464fb72..65303a79 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -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, @@ -13,7 +12,7 @@ pub struct INodeFile { impl File for INodeFile { fn read(&self, buf: &mut [u8]) -> Result { 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 { 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 { 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 { 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 { 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 { 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 { 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, abs_path: &str, flags: u32) -> Result { 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 { 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 { 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()) } } diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 85f5798e..05842a18 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -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}; diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index 0b416b53..f0be9810 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -369,6 +369,42 @@ pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result Ok(len as isize) } +pub fn do_chmod(path: *const i8, mode: u16) -> Result { + 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 { + 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 { + 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 { + file_ops::do_fchown(fd, uid, gid)?; + Ok(0) +} + +pub fn do_lchown(path: *const i8, uid: u32, gid: u32) -> Result { + 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, diff --git a/src/libos/src/process/spawn/mod.rs b/src/libos/src/process/spawn/mod.rs index d22f2f71..fa1bca7b 100644 --- a/src/libos/src/process/spawn/mod.rs +++ b/src/libos/src/process/spawn/mod.rs @@ -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,14 +146,27 @@ pub enum FileAction { } fn load_elf_to_vec(elf_path: &str, parent_ref: &ProcessRef) -> Result> { - #[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")) + .map_err(|e| errno!(e.errno(), "failed to read the executable ELF")) } fn init_files( diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 55306eab..e1d49618 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -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(), diff --git a/test/Makefile b/test/Makefile index 2c77ca25..9021f92e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 diff --git a/test/chmod/Makefile b/test/chmod/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/chmod/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/chmod/main.c b/test/chmod/main.c new file mode 100644 index 00000000..7c1063da --- /dev/null +++ b/test/chmod/main.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#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)); +} diff --git a/test/chown/Makefile b/test/chown/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/chown/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/chown/main.c b/test/chown/main.c new file mode 100644 index 00000000..5546881c --- /dev/null +++ b/test/chown/main.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#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)); +}