Add support for FLOCK
This commit is contained in:
parent
91f025f1c8
commit
f52bf0b514
19
src/libos/src/fs/file_ops/flock.rs
Normal file
19
src/libos/src/fs/file_ops/flock.rs
Normal file
@ -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 {
|
||||
|
199
src/libos/src/fs/locks/flock.rs
Normal file
199
src/libos/src/fs/locks/flock.rs
Normal file
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user