From f52bf0b514ff616a21585b05e8532ba8f5ea5d4d Mon Sep 17 00:00:00 2001 From: LI Qing Date: Wed, 9 Mar 2022 10:04:01 +0800 Subject: [PATCH] Add support for FLOCK --- src/libos/src/fs/file_ops/flock.rs | 19 +++ src/libos/src/fs/file_ops/mod.rs | 2 + src/libos/src/fs/inode_file.rs | 40 ++++++ src/libos/src/fs/locks/flock.rs | 199 +++++++++++++++++++++++++++++ src/libos/src/fs/locks/mod.rs | 1 + src/libos/src/fs/mod.rs | 9 +- src/libos/src/fs/syscalls.rs | 7 + src/libos/src/syscall/mod.rs | 18 +-- 8 files changed, 282 insertions(+), 13 deletions(-) create mode 100644 src/libos/src/fs/file_ops/flock.rs create mode 100644 src/libos/src/fs/locks/flock.rs diff --git a/src/libos/src/fs/file_ops/flock.rs b/src/libos/src/fs/file_ops/flock.rs new file mode 100644 index 00000000..7cc9d5be --- /dev/null +++ b/src/libos/src/fs/file_ops/flock.rs @@ -0,0 +1,19 @@ +use super::*; + +pub fn do_flock(fd: FileDesc, ops: FlockOps) -> Result<()> { + debug!("flock: fd: {}, ops: {:?}", fd, ops); + + let file_ref = current!().file(fd)?; + let inode_file = file_ref.as_inode_file()?; + if ops.contains(FlockOps::LOCK_UN) { + inode_file.unlock_flock(); + } else { + let is_nonblocking = ops.contains(FlockOps::LOCK_NB); + let flock = { + let type_ = FlockType::from(ops); + Flock::new(&file_ref, type_) + }; + inode_file.set_flock(flock, is_nonblocking)?; + } + Ok(()) +} diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index e949cee7..e1090b16 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -9,6 +9,7 @@ pub use self::dup::{do_dup, do_dup2, do_dup3}; pub use self::fallocate::{do_fallocate, FallocateFlags}; pub use self::fcntl::{do_fcntl, FcntlCmd}; pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags}; +pub use self::flock::do_flock; pub use self::fspath::{get_abs_path_by_fd, FsPath, AT_FDCWD}; pub use self::fsync::{do_fdatasync, do_fsync}; pub use self::getdents::{do_getdents, do_getdents64}; @@ -38,6 +39,7 @@ mod dup; mod fallocate; mod fcntl; mod file_flags; +mod flock; mod fspath; mod fsync; mod getdents; diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 202b8364..e5c7fd03 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -338,6 +338,46 @@ impl INodeFile { range_lock_list.unlock(lock) } + + pub fn set_flock(&self, lock: Flock, is_nonblocking: bool) -> Result<()> { + let ext = match self.inode.ext() { + Some(ext) => ext, + None => { + warn!("Inode extension is not supported, let the lock could be acquired"); + // TODO: Implement inode extension for FS + return Ok(()); + } + }; + let flock_list = match ext.get::() { + Some(list) => list, + None => ext.get_or_put_default::(), + }; + + flock_list.set_lock(lock, is_nonblocking) + } + + pub fn unlock_flock(&self) { + let ext = match self.inode.ext() { + Some(ext) => ext, + None => { + return; + } + }; + let flock_list = match ext.get::() { + Some(list) => list, + None => { + return; + } + }; + + flock_list.unlock(self); + } +} + +impl Drop for INodeFile { + fn drop(&mut self) { + self.unlock_flock() + } } impl Debug for INodeFile { diff --git a/src/libos/src/fs/locks/flock.rs b/src/libos/src/fs/locks/flock.rs new file mode 100644 index 00000000..7e492e03 --- /dev/null +++ b/src/libos/src/fs/locks/flock.rs @@ -0,0 +1,199 @@ +/// Non-POSIX file advisory lock (FLOCK) +use super::*; +use crate::events::{Waiter, WaiterQueue}; +use rcore_fs::vfs::AnyExt; +use std::ptr; +use std::sync::Weak; + +/// Kernel representation of FLOCK +pub struct Flock { + /// Owner of FLOCK, an opened file descriptor holding the lock + owner: Weak, + /// Type of lock, SH_LOCK or EX_LOCK + type_: FlockType, + /// Optional waiters that are blocking by the lock + waiters: Option, +} + +impl Flock { + pub fn new(owner: &Arc, type_: FlockType) -> Self { + Self { + owner: Arc::downgrade(owner), + type_, + waiters: None, + } + } + + pub fn owner(&self) -> Option> { + Weak::upgrade(&self.owner) + } + + pub fn same_owner_with(&self, other: &Self) -> bool { + self.owner.ptr_eq(&other.owner) + } + + pub fn conflict_with(&self, other: &Self) -> bool { + if self.same_owner_with(other) { + return false; + } + if self.type_ == FlockType::EX_LOCK || other.type_ == FlockType::EX_LOCK { + return true; + } + false + } + + pub fn enqueue_waiter(&mut self, waiter: &Waiter) { + if self.waiters.is_none() { + self.waiters = Some(WaiterQueue::new()); + } + self.waiters.as_ref().unwrap().reset_and_enqueue(waiter) + } + + pub fn dequeue_and_wake_all_waiters(&mut self) -> usize { + if self.waiters.is_some() { + return self.waiters.as_ref().unwrap().dequeue_and_wake_all(); + } + 0 + } +} + +impl Drop for Flock { + fn drop(&mut self) { + self.dequeue_and_wake_all_waiters(); + } +} + +impl Debug for Flock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Flock") + .field("owner", &self.owner.as_ptr()) + .field("type_", &self.type_) + .finish() + } +} + +/// List of Non-POSIX file advisory lock (FLOCK) +pub struct FlockList { + inner: RwLock>, +} + +impl FlockList { + pub fn new() -> Self { + Self { + inner: RwLock::new(VecDeque::new()), + } + } + + pub fn set_lock(&self, mut req_lock: Flock, is_nonblocking: bool) -> Result<()> { + debug!( + "set_lock with Flock: {:?}, is_nonblocking: {}", + req_lock, is_nonblocking + ); + + loop { + let mut list = self.inner.write().unwrap(); + if let Some(mut conflict_lock) = list.iter_mut().find(|l| req_lock.conflict_with(&l)) { + if is_nonblocking { + return_errno!(EAGAIN, "The file is locked"); + } + // Start to wait + let waiter = Waiter::new(); + // FLOCK do not support deadlock detection + conflict_lock.enqueue_waiter(&waiter); + // Ensure that we drop any locks before wait + drop(list); + waiter.wait(None)?; + // Wake up, let's try to set lock again + continue; + } + match list.iter().position(|l| req_lock.same_owner_with(&l)) { + Some(idx) => { + std::mem::swap(&mut req_lock, &mut list[idx]); + } + None => { + list.push_front(req_lock); + } + } + break; + } + Ok(()) + } + + pub fn unlock(&self, req_owner: &INodeFile) { + debug!("unlock with owner: {:?}", req_owner as *const INodeFile); + + let mut list = self.inner.write().unwrap(); + list.retain(|lock| { + if let Some(owner) = lock.owner() { + !ptr::eq( + Arc::as_ptr(&owner) as *const INodeFile, + req_owner as *const INodeFile, + ) + } else { + false + } + }); + } +} + +impl Default for FlockList { + fn default() -> Self { + Self::new() + } +} + +impl AnyExt for FlockList {} + +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, PartialEq)] +#[repr(u16)] +pub enum FlockType { + /// Shared lock + SH_LOCK = 0, + /// Exclusive lock + EX_LOCK = 1, +} + +impl From for FlockType { + fn from(ops: FlockOps) -> Self { + if ops.contains(FlockOps::LOCK_EX) { + Self::EX_LOCK + } else if ops.contains(FlockOps::LOCK_SH) { + Self::SH_LOCK + } else { + panic!("invalid flockops"); + } + } +} + +bitflags! { + pub struct FlockOps: i32 { + /// Shared lock + const LOCK_SH = 1; + /// Exclusive lock + const LOCK_EX = 2; + // Or'd with one of the above to prevent blocking + const LOCK_NB = 4; + // Remove lock + const LOCK_UN = 8; + } +} + +impl FlockOps { + pub fn from_i32(bits: i32) -> Result { + let ops = Self::from_bits(bits).ok_or_else(|| errno!(EINVAL, "invalid operation"))?; + if ops.contains(Self::LOCK_SH) { + if ops.contains(Self::LOCK_EX) || ops.contains(Self::LOCK_UN) { + return_errno!(EINVAL, "invalid operation"); + } + } else if ops.contains(Self::LOCK_EX) { + if ops.contains(Self::LOCK_UN) { + return_errno!(EINVAL, "invalid operation"); + } + } else if !ops.contains(Self::LOCK_UN) { + return_errno!(EINVAL, "invalid operation"); + } + + Ok(ops) + } +} diff --git a/src/libos/src/fs/locks/mod.rs b/src/libos/src/fs/locks/mod.rs index 48b15154..e527fc61 100644 --- a/src/libos/src/fs/locks/mod.rs +++ b/src/libos/src/fs/locks/mod.rs @@ -1,3 +1,4 @@ use super::*; +pub mod flock; pub mod range_lock; diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 43ce9533..bfd75169 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -23,13 +23,14 @@ pub use self::file_ops::{ IfConf, IoctlCmd, Stat, StatusFlags, StructuredIoctlArgType, StructuredIoctlNum, }; pub use self::file_table::{FileDesc, FileTable, FileTableEvent, FileTableNotifier}; -pub use self::locks::range_lock::{ - FileRange, RangeLock, RangeLockBuilder, RangeLockList, RangeLockType, OFFSET_MAX, -}; pub use self::fs_ops::Statfs; pub use self::fs_view::FsView; pub use self::host_fd::HostFd; pub use self::inode_file::{AsINodeFile, INodeExt, INodeFile}; +pub use self::locks::flock::{Flock, FlockList, FlockOps, FlockType}; +pub use self::locks::range_lock::{ + FileRange, RangeLock, RangeLockBuilder, RangeLockList, RangeLockType, OFFSET_MAX, +}; pub use self::pipe::PipeType; pub use self::rootfs::ROOT_FS; pub use self::stdio::{HostStdioFds, StdinFile, StdoutFile}; @@ -43,12 +44,12 @@ mod events; mod file; mod file_ops; mod file_table; -mod locks; mod fs_ops; mod fs_view; mod host_fd; mod hostfs; mod inode_file; +mod locks; mod pipe; mod procfs; mod rootfs; diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index 4e7e5441..dc13ebb6 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -693,3 +693,10 @@ pub fn do_umount(target: *const i8, flags: u32) -> Result { fs_ops::do_umount(&target, flags)?; Ok(0) } + +pub fn do_flock(fd: FileDesc, operation: i32) -> Result { + let flock_ops = FlockOps::from_i32(operation)?; + + file_ops::do_flock(fd, flock_ops)?; + Ok(0) +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 6b8bbe95..cdc24a0d 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -24,14 +24,14 @@ use crate::exception::do_handle_exception; use crate::fs::{ do_access, do_chdir, do_chmod, do_chown, do_close, do_creat, do_dup, do_dup2, do_dup3, do_eventfd, do_eventfd2, do_faccessat, do_fallocate, do_fchdir, do_fchmod, do_fchmodat, - do_fchown, do_fchownat, do_fcntl, do_fdatasync, do_fstat, do_fstatat, do_fstatfs, do_fsync, - do_ftruncate, do_getcwd, do_getdents, do_getdents64, do_ioctl, do_lchown, do_link, do_linkat, - do_lseek, do_lstat, do_mkdir, do_mkdirat, do_mount, 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_umask, - do_umount, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, AsTimer, File, FileDesc, - FileRef, HostStdioFds, Stat, Statfs, + do_fchown, do_fchownat, do_fcntl, do_fdatasync, do_flock, do_fstat, do_fstatat, do_fstatfs, + do_fsync, do_ftruncate, do_getcwd, do_getdents, do_getdents64, do_ioctl, do_lchown, do_link, + do_linkat, do_lseek, do_lstat, do_mkdir, do_mkdirat, do_mount, 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_umask, do_umount, 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, RandFlags}; @@ -161,7 +161,7 @@ macro_rules! process_syscall_table_with_callback { (Msgrcv = 70) => handle_unsupported(), (Msgctl = 71) => handle_unsupported(), (Fcntl = 72) => do_fcntl(fd: FileDesc, cmd: u32, arg: u64), - (Flock = 73) => handle_unsupported(), + (Flock = 73) => do_flock(fd: FileDesc, operation: i32), (Fsync = 74) => do_fsync(fd: FileDesc), (Fdatasync = 75) => do_fdatasync(fd: FileDesc), (Truncate = 76) => do_truncate(path: *const i8, len: usize),