mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Add flock and sys_flock
This commit is contained in:
parent
cbd8879243
commit
ce2af1eb05
@ -93,7 +93,7 @@ provided by Linux on x86-64 architecture.
|
||||
| 70 | msgrcv | ❌ |
|
||||
| 71 | msgctl | ❌ |
|
||||
| 72 | fcntl | ✅ |
|
||||
| 73 | flock | ❌ |
|
||||
| 73 | flock | ✅ |
|
||||
| 74 | fsync | ✅ |
|
||||
| 75 | fdatasync | ✅ |
|
||||
| 76 | truncate | ✅ |
|
||||
|
193
kernel/aster-nix/src/fs/utils/flock.rs
Normal file
193
kernel/aster-nix/src/fs/utils/flock.rs
Normal file
@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
use alloc::fmt;
|
||||
use core::ptr;
|
||||
|
||||
use ostd::sync::WaitQueue;
|
||||
|
||||
use crate::{
|
||||
fs::{file_handle::FileLike, inode_handle::InodeHandle},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
/// Represents a file lock (FLOCK) with an owner and type.
|
||||
#[derive(Debug, Clone)]
|
||||
struct Flock {
|
||||
/// Owner of the lock, which is an opened file descriptor.
|
||||
owner: Weak<dyn FileLike>,
|
||||
/// Type of the lock, either shared or exclusive.
|
||||
type_: FlockType,
|
||||
}
|
||||
|
||||
/// Represents a Flock item that can be held in a list of file locks.
|
||||
/// Each FlockItem contains a lock and a wait queue for threads that are blocked by the lock.
|
||||
pub struct FlockItem {
|
||||
lock: Flock,
|
||||
/// A wait queue for any threads that are blocked by this lock.
|
||||
waitqueue: Arc<WaitQueue>,
|
||||
}
|
||||
|
||||
impl FlockItem {
|
||||
/// Creates a new FlockItem with the specified owner and lock type.
|
||||
pub fn new(owner: &Arc<dyn FileLike>, type_: FlockType) -> Self {
|
||||
Self {
|
||||
lock: Flock {
|
||||
owner: Arc::downgrade(owner),
|
||||
type_,
|
||||
},
|
||||
waitqueue: Arc::new(WaitQueue::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the owner of the lock if it exists.
|
||||
pub fn owner(&self) -> Option<Arc<dyn FileLike>> {
|
||||
Weak::upgrade(&self.lock.owner)
|
||||
}
|
||||
|
||||
/// Checks if this lock has the same owner as another lock.
|
||||
pub fn same_owner_with(&self, other: &Self) -> bool {
|
||||
self.lock.owner.ptr_eq(&other.lock.owner)
|
||||
}
|
||||
|
||||
/// Returns true if this lock conflicts with another lock.
|
||||
/// Two locks conflict if they have different owners and at least one of them is an exclusive lock.
|
||||
pub fn conflict_with(&self, other: &Self) -> bool {
|
||||
if self.same_owner_with(other) {
|
||||
return false;
|
||||
}
|
||||
if self.lock.type_ == FlockType::ExclusiveLock
|
||||
|| other.lock.type_ == FlockType::ExclusiveLock
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Waits until the lock can be acquired.
|
||||
pub fn wait(&self) {
|
||||
let cond = || None::<()>;
|
||||
self.waitqueue.wait_until(cond);
|
||||
}
|
||||
|
||||
/// Wakes all threads that are waiting for this lock.
|
||||
pub fn wake_all(&self) {
|
||||
self.waitqueue.wake_all();
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FlockItem {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
lock: self.lock.clone(),
|
||||
waitqueue: self.waitqueue.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When a FlockItem is dropped, it wakes all threads that are waiting for the lock.
|
||||
impl Drop for FlockItem {
|
||||
fn drop(&mut self) {
|
||||
self.waitqueue.wake_all();
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FlockItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Flock")
|
||||
.field("owner", &self.lock.owner.as_ptr())
|
||||
.field("type_", &self.lock.type_)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a list of non-POSIX file advisory locks (FLOCK).
|
||||
/// The list is used to manage file locks and resolve conflicts between them.
|
||||
pub struct FlockList {
|
||||
inner: RwMutex<VecDeque<FlockItem>>,
|
||||
}
|
||||
|
||||
impl FlockList {
|
||||
/// Creates a new FlockList.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: RwMutex::new(VecDeque::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to set a lock on the file.
|
||||
/// If no conflicting locks exist, the lock is set and the function returns Ok(()).
|
||||
/// If is_nonblocking is true and a conflicting lock exists, the function returns EAGAIN.
|
||||
/// Otherwise, the function waits until the lock can be acquired.
|
||||
pub fn set_lock(&self, mut req_lock: FlockItem, is_nonblocking: bool) -> Result<()> {
|
||||
debug!(
|
||||
"set_lock with Flock: {:?}, is_nonblocking: {}",
|
||||
req_lock, is_nonblocking
|
||||
);
|
||||
loop {
|
||||
let conflict_lock;
|
||||
{
|
||||
let mut list = self.inner.write();
|
||||
if let Some(existing_lock) = list.iter().find(|l| req_lock.conflict_with(l)) {
|
||||
if is_nonblocking {
|
||||
return_errno_with_message!(Errno::EAGAIN, "the file is locked");
|
||||
}
|
||||
conflict_lock = existing_lock.clone();
|
||||
} else {
|
||||
match list.iter().position(|l| req_lock.same_owner_with(l)) {
|
||||
Some(idx) => {
|
||||
core::mem::swap(&mut req_lock, &mut list[idx]);
|
||||
}
|
||||
None => {
|
||||
list.push_front(req_lock);
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
conflict_lock.wait();
|
||||
}
|
||||
}
|
||||
|
||||
/// Unlocks the specified owner, waking any waiting threads.
|
||||
/// If the owner is no longer valid, the lock is removed from the list.
|
||||
/// If the owner is valid, the lock is removed from the list and all threads waiting for the lock are woken.
|
||||
/// The function does nothing if the owner is not found in the list.
|
||||
/// The function is called when the file is closed or the lock is released.
|
||||
pub fn unlock<R>(&self, req_owner: &InodeHandle<R>) {
|
||||
debug!(
|
||||
"unlock with owner: {:?}",
|
||||
req_owner as *const InodeHandle<R>
|
||||
);
|
||||
let mut list = self.inner.write();
|
||||
list.retain(|lock| {
|
||||
if let Some(owner) = lock.owner() {
|
||||
if ptr::eq(
|
||||
Arc::as_ptr(&owner) as *const InodeHandle<R>,
|
||||
req_owner as *const InodeHandle<R>,
|
||||
) {
|
||||
lock.wake_all(); // Wake all threads waiting for this lock.
|
||||
false // Remove lock from the list.
|
||||
} else {
|
||||
true // Keep lock in the list.
|
||||
}
|
||||
} else {
|
||||
false // Remove lock if the owner is no longer valid.
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FlockList {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the type of a Flock - either shared or exclusive.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[repr(u16)]
|
||||
pub enum FlockType {
|
||||
/// Represents a shared lock.
|
||||
SharedLock = 0,
|
||||
/// Represents an exclusive lock.
|
||||
ExclusiveLock = 1,
|
||||
}
|
@ -25,6 +25,7 @@ use crate::syscall::{
|
||||
exit_group::sys_exit_group,
|
||||
fallocate::sys_fallocate,
|
||||
fcntl::sys_fcntl,
|
||||
flock::sys_flock,
|
||||
fork::sys_fork,
|
||||
fsync::{sys_fdatasync, sys_fsync},
|
||||
futex::sys_futex,
|
||||
@ -190,6 +191,7 @@ impl_syscall_nums_and_dispatch_fn! {
|
||||
SYS_KILL = 62 => sys_kill(args[..2]);
|
||||
SYS_UNAME = 63 => sys_uname(args[..1]);
|
||||
SYS_FCNTL = 72 => sys_fcntl(args[..3]);
|
||||
SYS_FLOCK = 73 => sys_flock(args[..2]);
|
||||
SYS_FSYNC = 74 => sys_fsync(args[..1]);
|
||||
SYS_FDATASYNC = 75 => sys_fdatasync(args[..1]);
|
||||
SYS_TRUNCATE = 76 => sys_truncate(args[..2]);
|
||||
|
82
kernel/aster-nix/src/syscall/flock.rs
Normal file
82
kernel/aster-nix/src/syscall/flock.rs
Normal file
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::SyscallReturn;
|
||||
use crate::{
|
||||
fs::{
|
||||
file_table::FileDesc,
|
||||
inode_handle::InodeHandle,
|
||||
utils::{FlockItem, FlockType},
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub fn sys_flock(fd: FileDesc, ops: i32, ctx: &Context) -> Result<SyscallReturn> {
|
||||
debug!("flock: fd: {}, ops: {:?}", fd, ops);
|
||||
|
||||
let file = {
|
||||
let current = ctx.process;
|
||||
let file_table = current.file_table().lock();
|
||||
file_table.get_file(fd)?.clone()
|
||||
};
|
||||
let inode_file = file
|
||||
.downcast_ref::<InodeHandle>()
|
||||
.ok_or(Error::with_message(Errno::EBADF, "not inode"))?;
|
||||
let ops: FlockOps = FlockOps::from_i32(ops)?;
|
||||
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);
|
||||
FlockItem::new(&file, type_)
|
||||
};
|
||||
inode_file.set_flock(flock, is_nonblocking)?;
|
||||
}
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
impl From<FlockOps> for FlockType {
|
||||
fn from(ops: FlockOps) -> Self {
|
||||
if ops.contains(FlockOps::LOCK_EX) {
|
||||
Self::ExclusiveLock
|
||||
} else if ops.contains(FlockOps::LOCK_SH) {
|
||||
Self::SharedLock
|
||||
} else {
|
||||
panic!("invalid flockops");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
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 {
|
||||
fn from_i32(bits: i32) -> Result<Self> {
|
||||
if let Some(ops) = Self::from_bits(bits) {
|
||||
if ops.contains(Self::LOCK_SH) {
|
||||
if ops.contains(Self::LOCK_EX) || ops.contains(Self::LOCK_UN) {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid operation");
|
||||
}
|
||||
} else if ops.contains(Self::LOCK_EX) {
|
||||
if ops.contains(Self::LOCK_UN) {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid operation");
|
||||
}
|
||||
} else if !ops.contains(Self::LOCK_UN) {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid operation");
|
||||
}
|
||||
Ok(ops)
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid operation");
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ mod exit;
|
||||
mod exit_group;
|
||||
mod fallocate;
|
||||
mod fcntl;
|
||||
mod flock;
|
||||
mod fork;
|
||||
mod fsync;
|
||||
mod futex;
|
||||
|
@ -16,6 +16,7 @@ TESTS ?= \
|
||||
epoll_test \
|
||||
eventfd_test \
|
||||
fcntl_test \
|
||||
flock_test \
|
||||
fsync_test \
|
||||
getdents_test \
|
||||
link_test \
|
||||
|
9
test/syscall_test/blocklists/flock_test
Normal file
9
test/syscall_test/blocklists/flock_test
Normal file
@ -0,0 +1,9 @@
|
||||
FlockTest.TestSharedLockFailExclusiveHolderBlocking_NoRandomSave
|
||||
FlockTest.TestExclusiveLockFailExclusiveHolderBlocking_NoRandomSave
|
||||
FlockTest.BlockingLockFirstSharedSecondExclusive_NoRandomSave
|
||||
FlockTest.BlockingLockFirstExclusiveSecondShared_NoRandomSave
|
||||
FlockTest.BlockingLockFirstExclusiveSecondExclusive_NoRandomSave
|
||||
FlockTestNoFixture.FlockSymlink
|
||||
FlockTestNoFixture.FlockProc
|
||||
FlockTestNoFixture.FlockPipe
|
||||
FlockTestNoFixture.FlockSocket
|
Loading…
x
Reference in New Issue
Block a user