Add support for FLOCK

This commit is contained in:
LI Qing 2022-03-09 10:04:01 +08:00 committed by Zongmin.Gu
parent 91f025f1c8
commit f52bf0b514
8 changed files with 282 additions and 13 deletions

@ -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(())
}

@ -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;

@ -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::<FlockList>() {
Some(list) => list,
None => ext.get_or_put_default::<FlockList>(),
};
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::<FlockList>() {
Some(list) => list,
None => {
return;
}
};
flock_list.unlock(self);
}
}
impl Drop for INodeFile {
fn drop(&mut self) {
self.unlock_flock()
}
}
impl Debug for INodeFile {

@ -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<dyn File>,
/// Type of lock, SH_LOCK or EX_LOCK
type_: FlockType,
/// Optional waiters that are blocking by the lock
waiters: Option<WaiterQueue>,
}
impl Flock {
pub fn new(owner: &Arc<dyn File>, type_: FlockType) -> Self {
Self {
owner: Arc::downgrade(owner),
type_,
waiters: None,
}
}
pub fn owner(&self) -> Option<Arc<dyn File>> {
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<VecDeque<Flock>>,
}
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<FlockOps> 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<Self> {
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)
}
}

@ -1,3 +1,4 @@
use super::*;
pub mod flock;
pub mod range_lock;

@ -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;

@ -693,3 +693,10 @@ pub fn do_umount(target: *const i8, flags: u32) -> Result<isize> {
fs_ops::do_umount(&target, flags)?;
Ok(0)
}
pub fn do_flock(fd: FileDesc, operation: i32) -> Result<isize> {
let flock_ops = FlockOps::from_i32(operation)?;
file_ops::do_flock(fd, flock_ops)?;
Ok(0)
}

@ -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),