From 87c1c9a8b3463f0b15a98b4e31807c231e226c67 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Fri, 6 Aug 2021 11:58:03 +0800 Subject: [PATCH] Add support for umask --- deps/sefs | 2 +- src/libos/src/fs/file_ops/chmod.rs | 11 ++ src/libos/src/fs/file_ops/mkdir.rs | 9 +- src/libos/src/fs/file_ops/open.rs | 9 +- src/libos/src/fs/fs_view.rs | 6 +- src/libos/src/fs/hostfs.rs | 2 +- src/libos/src/fs/syscalls.rs | 18 +++- .../src/net/socket/unix/stream/stream.rs | 9 +- src/libos/src/process/do_spawn/mod.rs | 14 +-- src/libos/src/process/process/builder.rs | 10 ++ src/libos/src/process/process/mod.rs | 15 +++ src/libos/src/syscall/mod.rs | 17 +-- src/libos/src/util/resolv_conf_util.rs | 4 +- test/Makefile | 2 +- test/umask/Makefile | 5 + test/umask/main.c | 100 ++++++++++++++++++ 16 files changed, 195 insertions(+), 38 deletions(-) create mode 100644 test/umask/Makefile create mode 100644 test/umask/main.c diff --git a/deps/sefs b/deps/sefs index 41484829..8d999ffd 160000 --- a/deps/sefs +++ b/deps/sefs @@ -1 +1 @@ -Subproject commit 41484829326a6bcbd92343f7c1bdcf4acf5a56f4 +Subproject commit 8d999ffdf066cc0601c5a726ccfcca75437ced0b diff --git a/src/libos/src/fs/file_ops/chmod.rs b/src/libos/src/fs/file_ops/chmod.rs index d25a45a0..b19ca9ac 100644 --- a/src/libos/src/fs/file_ops/chmod.rs +++ b/src/libos/src/fs/file_ops/chmod.rs @@ -53,6 +53,17 @@ impl FileMode { pub fn has_set_gid(&self) -> bool { self.contains(FileMode::S_ISGID) } + + /// Umask is FileMode & 0o777, only the file permission bits are used + pub fn to_umask(mut self) -> Self { + self.remove(Self::S_ISUID | Self::S_ISGID | Self::S_ISVTX); + self + } + + /// Default umask is 0o022 + pub fn default_umask() -> Self { + Self::S_IWGRP | Self::S_IWOTH + } } pub fn do_fchmodat(fs_path: &FsPath, mode: FileMode) -> Result<()> { diff --git a/src/libos/src/fs/file_ops/mkdir.rs b/src/libos/src/fs/file_ops/mkdir.rs index cb0bd255..4e7e663d 100644 --- a/src/libos/src/fs/file_ops/mkdir.rs +++ b/src/libos/src/fs/file_ops/mkdir.rs @@ -1,12 +1,12 @@ use super::*; -pub fn do_mkdirat(fs_path: &FsPath, mode: usize) -> Result<()> { - debug!("mkdirat: fs_path: {:?}, mode: {:#o}", fs_path, mode); +pub fn do_mkdirat(fs_path: &FsPath, mode: FileMode) -> Result<()> { + debug!("mkdirat: fs_path: {:?}, mode: {:#o}", fs_path, mode.bits()); let path = fs_path.to_abs_path()?; let (dir_path, file_name) = split_path(&path); + let current = current!(); let inode = { - let current = current!(); let fs = current.fs().read().unwrap(); fs.lookup_inode(dir_path)? }; @@ -16,6 +16,7 @@ pub fn do_mkdirat(fs_path: &FsPath, mode: usize) -> Result<()> { if !inode.allow_write()? { return_errno!(EPERM, "dir cannot be written"); } - inode.create(file_name, FileType::Dir, mode as u32)?; + let masked_mode = mode & !current.process().umask(); + inode.create(file_name, FileType::Dir, masked_mode.bits())?; Ok(()) } diff --git a/src/libos/src/fs/file_ops/open.rs b/src/libos/src/fs/file_ops/open.rs index 98a79a89..91156b14 100644 --- a/src/libos/src/fs/file_ops/open.rs +++ b/src/libos/src/fs/file_ops/open.rs @@ -1,16 +1,19 @@ use super::*; -pub fn do_openat(fs_path: &FsPath, flags: u32, mode: u32) -> Result { +pub fn do_openat(fs_path: &FsPath, flags: u32, mode: FileMode) -> Result { debug!( "openat: fs_path: {:?}, flags: {:#o}, mode: {:#o}", - fs_path, flags, mode + fs_path, + flags, + mode.bits() ); let path = fs_path.to_abs_path()?; let current = current!(); let fs = current.fs().read().unwrap(); + let masked_mode = mode & !current.process().umask(); - let file_ref: Arc = fs.open_file(&path, flags, mode)?; + let file_ref: Arc = fs.open_file(&path, flags, masked_mode)?; let fd = { let creation_flags = CreationFlags::from_bits_truncate(flags); diff --git a/src/libos/src/fs/fs_view.rs b/src/libos/src/fs/fs_view.rs index 248d38b2..ed95c9b5 100644 --- a/src/libos/src/fs/fs_view.rs +++ b/src/libos/src/fs/fs_view.rs @@ -44,7 +44,7 @@ impl FsView { } /// Open a file on the process. But DO NOT add it to file table. - pub fn open_file(&self, path: &str, flags: u32, mode: u32) -> Result> { + pub fn open_file(&self, path: &str, flags: u32, mode: FileMode) -> Result> { let creation_flags = CreationFlags::from_bits_truncate(flags); let inode = if creation_flags.no_follow_symlink() { match self.lookup_inode_no_follow(path) { @@ -73,7 +73,7 @@ impl FsView { if !dir_inode.allow_write()? { return_errno!(EPERM, "file cannot be created"); } - dir_inode.create(file_name, FileType::File, mode)? + dir_inode.create(file_name, FileType::File, mode.bits())? } Err(e) => return Err(e), } @@ -100,7 +100,7 @@ impl FsView { if !dir_inode.allow_write()? { return_errno!(EPERM, "file cannot be created"); } - dir_inode.create(file_name, FileType::File, mode)? + dir_inode.create(file_name, FileType::File, mode.bits())? } Err(e) => return Err(e), } diff --git a/src/libos/src/fs/hostfs.rs b/src/libos/src/fs/hostfs.rs index 21b730f5..ca39a396 100644 --- a/src/libos/src/fs/hostfs.rs +++ b/src/libos/src/fs/hostfs.rs @@ -134,7 +134,7 @@ impl INode for HNode { Ok(()) } - fn create(&self, name: &str, type_: FileType, mode: u32) -> Result> { + fn create(&self, name: &str, type_: FileType, mode: u16) -> Result> { let new_path = self.path.join(name); if new_path.exists() { return Err(FsError::EntryExist); diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index 8328908c..21b81fda 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -97,25 +97,32 @@ pub fn do_timerfd_gettime(fd: FileDesc, curr_value_ptr: *mut itimerspec_t) -> Re Ok(0) } -pub fn do_creat(path: *const i8, mode: u32) -> Result { +pub fn do_creat(path: *const i8, mode: u16) -> Result { 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: u32) -> Result { +pub fn do_open(path: *const i8, flags: u32, mode: u16) -> Result { self::do_openat(AT_FDCWD, path, flags, mode) } -pub fn do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u32) -> Result { +pub fn do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u16) -> Result { 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 { + 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 { file_ops::do_close(fd)?; Ok(0) @@ -414,15 +421,16 @@ pub fn do_renameat( Ok(0) } -pub fn do_mkdir(path: *const i8, mode: usize) -> Result { +pub fn do_mkdir(path: *const i8, mode: u16) -> Result { self::do_mkdirat(AT_FDCWD, path, mode) } -pub fn do_mkdirat(dirfd: i32, path: *const i8, mode: usize) -> Result { +pub fn do_mkdirat(dirfd: i32, path: *const i8, mode: u16) -> Result { 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) } diff --git a/src/libos/src/net/socket/unix/stream/stream.rs b/src/libos/src/net/socket/unix/stream/stream.rs index 8c037c6b..a793e822 100644 --- a/src/libos/src/net/socket/unix/stream/stream.rs +++ b/src/libos/src/net/socket/unix/stream/stream.rs @@ -3,8 +3,8 @@ use super::endpoint::{end_pair, Endpoint, RelayNotifier}; use super::*; use events::{Event, EventFilter, Notifier, Observer}; use fs::channel::Channel; -use fs::CreationFlags; use fs::IoEvents; +use fs::{CreationFlags, FileMode}; use std::fmt; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -75,8 +75,11 @@ impl Stream { let corresponding_inode_num = { let current = current!(); let fs = current.fs().read().unwrap(); - let file_ref = - fs.open_file(path.path_str(), CreationFlags::O_CREAT.bits(), 0o777)?; + let file_ref = fs.open_file( + path.path_str(), + CreationFlags::O_CREAT.bits(), + FileMode::from_bits(0o777).unwrap(), + )?; file_ref.metadata()?.inode }; *inode_num = Some(corresponding_inode_num); diff --git a/src/libos/src/process/do_spawn/mod.rs b/src/libos/src/process/do_spawn/mod.rs index 8cbc5651..5e286c42 100644 --- a/src/libos/src/process/do_spawn/mod.rs +++ b/src/libos/src/process/do_spawn/mod.rs @@ -10,7 +10,7 @@ use super::task::Task; use super::thread::{ThreadId, ThreadName}; use super::{table, task, ProcessRef, ThreadRef}; use crate::fs::{ - CreationFlags, File, FileDesc, FileTable, FsView, HostStdioFds, StdinFile, StdoutFile, + CreationFlags, File, FileDesc, FileMode, FileTable, FsView, HostStdioFds, StdinFile, StdoutFile, }; use crate::prelude::*; use crate::vm::ProcessVM; @@ -285,6 +285,7 @@ fn new_process_common( process_builder .vm(vm_ref) .exec_path(&elf_path) + .umask(parent.umask()) .parent(parent) .task(task) .sched(sched_ref) @@ -339,12 +340,11 @@ fn init_files( oflag, fd, } => { - let file_ref = - current_ref - .fs() - .read() - .unwrap() - .open_file(path.as_str(), oflag, mode)?; + let file_ref = current_ref.fs().read().unwrap().open_file( + path.as_str(), + oflag, + FileMode::from_bits_truncate(mode as u16), + )?; let creation_flags = CreationFlags::from_bits_truncate(oflag); cloned_file_table.put_at(fd, file_ref, creation_flags.must_close_on_spawn()); } diff --git a/src/libos/src/process/process/builder.rs b/src/libos/src/process/process/builder.rs index 34bde53c..26d5a0a2 100644 --- a/src/libos/src/process/process/builder.rs +++ b/src/libos/src/process/process/builder.rs @@ -5,6 +5,7 @@ use super::super::{ SchedAgentRef, }; use super::{Process, ProcessInner}; +use crate::fs::FileMode; use crate::prelude::*; use crate::signal::{SigDispositions, SigQueues, SigSet}; @@ -16,6 +17,7 @@ pub struct ProcessBuilder { vm: Option, // Optional fields, which have reasonable default values exec_path: Option, + umask: Option, parent: Option, no_parent: bool, sig_dispositions: Option, @@ -29,6 +31,7 @@ impl ProcessBuilder { thread_builder: Some(thread_builder), vm: None, exec_path: None, + umask: None, parent: None, no_parent: false, sig_dispositions: None, @@ -45,6 +48,11 @@ impl ProcessBuilder { self } + pub fn umask(mut self, umask: FileMode) -> Self { + self.umask = Some(umask); + self + } + pub fn parent(mut self, parent: ProcessRef) -> Self { self.parent = Some(parent); self @@ -108,6 +116,7 @@ impl ProcessBuilder { // Build a new process let new_process = { let exec_path = self.exec_path.take().unwrap_or_default(); + let umask = RwLock::new(self.umask.unwrap_or(FileMode::default_umask())); let parent = self.parent.take().map(|parent| RwLock::new(parent)); let inner = SgxMutex::new(ProcessInner::new()); let sig_dispositions = RwLock::new(self.sig_dispositions.unwrap_or_default()); @@ -116,6 +125,7 @@ impl ProcessBuilder { Arc::new(Process { pid, exec_path, + umask, parent, inner, sig_dispositions, diff --git a/src/libos/src/process/process/mod.rs b/src/libos/src/process/process/mod.rs index 0a267d69..6292b94c 100644 --- a/src/libos/src/process/process/mod.rs +++ b/src/libos/src/process/process/mod.rs @@ -2,6 +2,7 @@ use std::fmt; use super::wait::WaitQueue; use super::{ForcedExitStatus, ProcessRef, TermStatus, ThreadRef}; +use crate::fs::FileMode; use crate::prelude::*; use crate::signal::{SigDispositions, SigNum, SigQueues}; @@ -18,6 +19,7 @@ pub struct Process { // Mutable info parent: Option>, inner: SgxMutex, + umask: RwLock, // Signal sig_dispositions: RwLock, sig_queues: RwLock, @@ -99,6 +101,19 @@ impl Process { &self.exec_path } + /// Get the file mode creation mask + pub fn umask(&self) -> FileMode { + self.umask.read().unwrap().clone() + } + + /// Set the file mode creation mask, return the previous value + pub fn set_umask(&self, new_mask: FileMode) -> FileMode { + let mut mask = self.umask.write().unwrap(); + let old_mask = mask.clone(); + *mask = new_mask; + old_mask + } + /// Get the signal queues for process-directed signals. pub fn sig_queues(&self) -> &RwLock { &self.sig_queues diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index b1c9f6dd..8e08388f 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -29,8 +29,9 @@ use crate::fs::{ do_lseek, do_lstat, do_mkdir, do_mkdirat, do_mount_rootfs, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink, do_readlinkat, do_readv, do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_statfs, do_symlink, do_symlinkat, do_sync, - do_timerfd_create, do_timerfd_gettime, do_timerfd_settime, do_truncate, do_unlink, do_unlinkat, - do_write, do_writev, iovec_t, AsTimer, File, FileDesc, FileRef, HostStdioFds, Stat, Statfs, + do_timerfd_create, do_timerfd_gettime, do_timerfd_settime, do_truncate, do_umask, do_unlink, + do_unlinkat, do_write, do_writev, iovec_t, AsTimer, File, FileDesc, FileRef, HostStdioFds, + Stat, Statfs, }; use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t}; use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t}; @@ -89,7 +90,7 @@ macro_rules! process_syscall_table_with_callback { // TODO: Unify the use of C types. For example, u8 or i8 or char_c for C string? (Read = 0) => do_read(fd: FileDesc, buf: *mut u8, size: usize), (Write = 1) => do_write(fd: FileDesc, buf: *const u8, size: usize), - (Open = 2) => do_open(path: *const i8, flags: u32, mode: u32), + (Open = 2) => do_open(path: *const i8, flags: u32, mode: u16), (Close = 3) => do_close(fd: FileDesc), (Stat = 4) => do_stat(path: *const i8, stat_buf: *mut Stat), (Fstat = 5) => do_fstat(fd: FileDesc, stat_buf: *mut Stat), @@ -170,9 +171,9 @@ macro_rules! process_syscall_table_with_callback { (Chdir = 80) => do_chdir(path: *const i8), (Fchdir = 81) => do_fchdir(fd: FileDesc), (Rename = 82) => do_rename(oldpath: *const i8, newpath: *const i8), - (Mkdir = 83) => do_mkdir(path: *const i8, mode: usize), + (Mkdir = 83) => do_mkdir(path: *const i8, mode: u16), (Rmdir = 84) => do_rmdir(path: *const i8), - (Creat = 85) => do_creat(path: *const i8, mode: u32), + (Creat = 85) => do_creat(path: *const i8, mode: u16), (Link = 86) => do_link(oldpath: *const i8, newpath: *const i8), (Unlink = 87) => do_unlink(path: *const i8), (Symlink = 88) => do_symlink(target: *const i8, link_path: *const i8), @@ -182,7 +183,7 @@ macro_rules! process_syscall_table_with_callback { (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(), + (Umask = 95) => do_umask(mask: u16), (Gettimeofday = 96) => do_gettimeofday(tv_u: *mut timeval_t), (Getrlimit = 97) => handle_unsupported(), (Getrusage = 98) => handle_unsupported(), @@ -344,8 +345,8 @@ macro_rules! process_syscall_table_with_callback { (InotifyAddWatch = 254) => handle_unsupported(), (InotifyRmWatch = 255) => handle_unsupported(), (MigratePages = 256) => handle_unsupported(), - (Openat = 257) => do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u32), - (Mkdirat = 258) => do_mkdirat(dirfd: i32, path: *const i8, mode: usize), + (Openat = 257) => do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u16), + (Mkdirat = 258) => do_mkdirat(dirfd: i32, path: *const i8, mode: u16), (Mknodat = 259) => handle_unsupported(), (Fchownat = 260) => do_fchownat(dirfd: i32, path: *const i8, uid: u32, gid: u32, flags: i32), (Futimesat = 261) => handle_unsupported(), diff --git a/src/libos/src/util/resolv_conf_util.rs b/src/libos/src/util/resolv_conf_util.rs index 4e6f27fd..999be775 100644 --- a/src/libos/src/util/resolv_conf_util.rs +++ b/src/libos/src/util/resolv_conf_util.rs @@ -1,5 +1,5 @@ use super::*; -use crate::fs::{AccessMode, CreationFlags, FsView}; +use crate::fs::{AccessMode, CreationFlags, FileMode, FsView}; use resolv_conf::*; use std::ffi::CStr; use std::str; @@ -11,7 +11,7 @@ pub fn write_resolv_conf() -> Result<()> { let resolv_conf_file = fs_view.open_file( RESOLV_CONF_PATH, AccessMode::O_RDWR as u32 | CreationFlags::O_CREAT.bits() | CreationFlags::O_TRUNC.bits(), - 0o666, + FileMode::from_bits(0o666).unwrap(), )?; let resolv_conf_str = RESOLV_CONF_STR.read().unwrap(); match &*resolv_conf_str { diff --git a/test/Makefile b/test/Makefile index b083b282..48c2dfb1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -19,7 +19,7 @@ TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info resolv_conf rlimit \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \ ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \ - spawn_attribute exec statfs + spawn_attribute exec statfs umask # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/umask/Makefile b/test/umask/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/umask/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/umask/main.c b/test/umask/main.c new file mode 100644 index 00000000..bb40f621 --- /dev/null +++ b/test/umask/main.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include "test_fs.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; +} + +static int check_create_file_with_umask(const char *file_path, mode_t mask) { + mode_t mode = 00666; + int fd = creat(file_path, mode); + if (fd < 0) { + THROW_ERROR("failed to create file"); + } + + struct stat stat_buf; + if (fstat(fd, &stat_buf) < 0) { + THROW_ERROR("failed to stat file"); + } + mode_t actual_mode = stat_buf.st_mode & 00777; + if (actual_mode != (mode & ~mask)) { + THROW_ERROR("failed to check the mode with umask(%o), actual_mode is: %o", mask, + actual_mode); + } + + return 0; +} + +// ============================================================================ +// Test cases for umask +// ============================================================================ + +#define DEFAULT_UMASK (00022) + +static int __test_create_file_with_default_umask(const char *file_path) { + if (check_create_file_with_umask(file_path, DEFAULT_UMASK) < 0) { + THROW_ERROR("failed to check default umask"); + } + + return 0; +} + +static int __test_umask(const char *file_path) { + mode_t new_mask = 00066; + int old_mask = umask(new_mask); + if (old_mask != DEFAULT_UMASK) { + THROW_ERROR("failed to get correct default mask"); + } + + if (check_create_file_with_umask(file_path, new_mask) < 0) { + THROW_ERROR("failed to check default umask"); + } + + return 0; +} + +typedef int(*test_file_func_t)(const char *); + +static int test_file_framework(test_file_func_t fn) { + const char *file_path = "/root/test_filesystem_umask.txt"; + + if (fn(file_path) < 0) { + return -1; + } + if (remove_file(file_path) < 0) { + return -1; + } + return 0; +} + +static int test_create_file_with_default_umask() { + return test_file_framework(__test_create_file_with_default_umask); +} + +static int test_umask() { + return test_file_framework(__test_umask); +} + +// ============================================================================ +// Test suite main +// ============================================================================ + +static test_case_t test_cases[] = { + TEST_CASE(test_create_file_with_default_umask), + TEST_CASE(test_umask), +}; + +int main(int argc, const char *argv[]) { + return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); +}