summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/FileSystem/Inode.cpp56
-rw-r--r--Kernel/FileSystem/Inode.h13
-rw-r--r--Kernel/FileSystem/OpenFileDescription.cpp4
-rw-r--r--Kernel/FileSystem/OpenFileDescription.h2
-rw-r--r--Kernel/Syscalls/fcntl.cpp5
-rw-r--r--Kernel/Thread.h38
-rw-r--r--Kernel/ThreadBlockers.cpp34
7 files changed, 128 insertions, 24 deletions
diff --git a/Kernel/FileSystem/Inode.cpp b/Kernel/FileSystem/Inode.cpp
index 3721ccf777..69f495d101 100644
--- a/Kernel/FileSystem/Inode.cpp
+++ b/Kernel/FileSystem/Inode.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
+ * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -297,52 +298,73 @@ static inline ErrorOr<void> normalize_flock(OpenFileDescription const& descripti
return {};
}
-ErrorOr<void> Inode::can_apply_flock(OpenFileDescription const&, flock const& new_lock) const
+bool Inode::can_apply_flock(flock const& new_lock) const
{
VERIFY(new_lock.l_whence == SEEK_SET);
if (new_lock.l_type == F_UNLCK)
- return {};
+ return true;
- return m_flocks.with([&](auto& flocks) -> ErrorOr<void> {
+ return m_flocks.with([&](auto& flocks) {
for (auto const& lock : flocks) {
if (!range_overlap(lock.start, lock.len, new_lock.l_start, new_lock.l_len))
continue;
if (new_lock.l_type == F_RDLCK && lock.type == F_WRLCK)
- return EAGAIN;
+ return false;
if (new_lock.l_type == F_WRLCK)
- return EAGAIN;
+ return false;
}
- return {};
+ return true;
});
}
-ErrorOr<void> Inode::apply_flock(Process const& process, OpenFileDescription const& description, Userspace<flock const*> input_lock)
+ErrorOr<bool> Inode::try_apply_flock(Process const& process, OpenFileDescription const& description, flock const& lock)
{
- auto new_lock = TRY(copy_typed_from_user(input_lock));
- TRY(normalize_flock(description, new_lock));
+ return m_flocks.with([&](auto& flocks) -> ErrorOr<bool> {
+ if (!can_apply_flock(lock))
+ return false;
- return m_flocks.with([&](auto& flocks) -> ErrorOr<void> {
- TRY(can_apply_flock(description, new_lock));
-
- if (new_lock.l_type == F_UNLCK) {
+ if (lock.l_type == F_UNLCK) {
+ bool any_locks_unlocked = false;
for (size_t i = 0; i < flocks.size(); ++i) {
- if (&description == flocks[i].owner && flocks[i].start == new_lock.l_start && flocks[i].len == new_lock.l_len) {
+ if (&description == flocks[i].owner && flocks[i].start == lock.l_start && flocks[i].len == lock.l_len) {
flocks.remove(i);
+ any_locks_unlocked |= true;
}
}
+ if (any_locks_unlocked)
+ m_flock_blocker_set.unblock_all_blockers_whose_conditions_are_met();
+
// Judging by the Linux implementation, unlocking a non-existent lock also works.
- return {};
+ return true;
}
- TRY(flocks.try_append(Flock { new_lock.l_start, new_lock.l_len, &description, process.pid().value(), new_lock.l_type }));
- return {};
+ TRY(flocks.try_append(Flock { lock.l_start, lock.l_len, &description, process.pid().value(), lock.l_type }));
+ return true;
});
}
+ErrorOr<void> Inode::apply_flock(Process const& process, OpenFileDescription const& description, Userspace<flock const*> input_lock, ShouldBlock should_block)
+{
+ auto new_lock = TRY(copy_typed_from_user(input_lock));
+ TRY(normalize_flock(description, new_lock));
+
+ while (true) {
+ auto success = TRY(try_apply_flock(process, description, new_lock));
+ if (success)
+ return {};
+
+ if (should_block == ShouldBlock::No)
+ return EAGAIN;
+
+ if (Thread::current()->block<Thread::FlockBlocker>({}, *this, new_lock).was_interrupted())
+ return EINTR;
+ }
+}
+
ErrorOr<void> Inode::get_flock(OpenFileDescription const& description, Userspace<flock*> reference_lock) const
{
flock lookup = {};
diff --git a/Kernel/FileSystem/Inode.h b/Kernel/FileSystem/Inode.h
index befb3a42f2..479c86167c 100644
--- a/Kernel/FileSystem/Inode.h
+++ b/Kernel/FileSystem/Inode.h
@@ -22,6 +22,11 @@
namespace Kernel {
+enum class ShouldBlock {
+ No = 0,
+ Yes = 1
+};
+
class Inode : public ListedRefCounted<Inode, LockType::Spinlock>
, public Weakable<Inode> {
friend class VirtualFileSystem;
@@ -94,10 +99,11 @@ public:
ErrorOr<NonnullRefPtr<FIFO>> fifo();
- ErrorOr<void> can_apply_flock(OpenFileDescription const&, flock const&) const;
- ErrorOr<void> apply_flock(Process const&, OpenFileDescription const&, Userspace<flock const*>);
+ bool can_apply_flock(flock const&) const;
+ ErrorOr<void> apply_flock(Process const&, OpenFileDescription const&, Userspace<flock const*>, ShouldBlock);
ErrorOr<void> get_flock(OpenFileDescription const&, Userspace<flock*>) const;
void remove_flocks_for_description(OpenFileDescription const&);
+ Thread::FlockBlockerSet& flock_blocker_set() { return m_flock_blocker_set; };
protected:
Inode(FileSystem&, InodeIndex);
@@ -112,6 +118,8 @@ protected:
mutable Mutex m_inode_lock { "Inode"sv };
private:
+ ErrorOr<bool> try_apply_flock(Process const&, OpenFileDescription const&, flock const&);
+
FileSystem& m_file_system;
InodeIndex m_index { 0 };
WeakPtr<Memory::SharedInodeVMObject> m_shared_vmobject;
@@ -129,6 +137,7 @@ private:
short type;
};
+ Thread::FlockBlockerSet m_flock_blocker_set;
SpinlockProtected<Vector<Flock>> m_flocks;
public:
diff --git a/Kernel/FileSystem/OpenFileDescription.cpp b/Kernel/FileSystem/OpenFileDescription.cpp
index f4381c7dce..4f19e4c502 100644
--- a/Kernel/FileSystem/OpenFileDescription.cpp
+++ b/Kernel/FileSystem/OpenFileDescription.cpp
@@ -445,12 +445,12 @@ FileBlockerSet& OpenFileDescription::blocker_set()
return m_file->blocker_set();
}
-ErrorOr<void> OpenFileDescription::apply_flock(Process const& process, Userspace<flock const*> lock)
+ErrorOr<void> OpenFileDescription::apply_flock(Process const& process, Userspace<flock const*> lock, ShouldBlock should_block)
{
if (!m_inode)
return EBADF;
- return m_inode->apply_flock(process, *this, lock);
+ return m_inode->apply_flock(process, *this, lock, should_block);
}
ErrorOr<void> OpenFileDescription::get_flock(Userspace<flock*> lock) const
diff --git a/Kernel/FileSystem/OpenFileDescription.h b/Kernel/FileSystem/OpenFileDescription.h
index 3d0c7b2c94..829a3a9e9f 100644
--- a/Kernel/FileSystem/OpenFileDescription.h
+++ b/Kernel/FileSystem/OpenFileDescription.h
@@ -124,7 +124,7 @@ public:
FileBlockerSet& blocker_set();
- ErrorOr<void> apply_flock(Process const&, Userspace<flock const*>);
+ ErrorOr<void> apply_flock(Process const&, Userspace<flock const*>, ShouldBlock);
ErrorOr<void> get_flock(Userspace<flock*>) const;
private:
diff --git a/Kernel/Syscalls/fcntl.cpp b/Kernel/Syscalls/fcntl.cpp
index 8b9d9b0a95..1fb961116a 100644
--- a/Kernel/Syscalls/fcntl.cpp
+++ b/Kernel/Syscalls/fcntl.cpp
@@ -45,7 +45,10 @@ ErrorOr<FlatPtr> Process::sys$fcntl(int fd, int cmd, uintptr_t arg)
TRY(description->get_flock(Userspace<flock*>(arg)));
return 0;
case F_SETLK:
- TRY(description->apply_flock(Process::current(), Userspace<flock const*>(arg)));
+ TRY(description->apply_flock(Process::current(), Userspace<flock const*>(arg), ShouldBlock::No));
+ return 0;
+ case F_SETLKW:
+ TRY(description->apply_flock(Process::current(), Userspace<flock const*>(arg), ShouldBlock::Yes));
return 0;
default:
return EINVAL;
diff --git a/Kernel/Thread.h b/Kernel/Thread.h
index b507261a62..e337b52c3f 100644
--- a/Kernel/Thread.h
+++ b/Kernel/Thread.h
@@ -283,7 +283,8 @@ public:
Routing,
Sleep,
Signal,
- Wait
+ Wait,
+ Flock
};
virtual ~Blocker();
virtual StringView state_string() const = 0;
@@ -788,6 +789,41 @@ public:
bool m_finalized { false };
};
+ class FlockBlocker final : public Blocker {
+ public:
+ FlockBlocker(NonnullRefPtr<Inode>, flock const&);
+ virtual StringView state_string() const override { return "Locking File"sv; }
+ virtual Type blocker_type() const override { return Type::Flock; }
+ virtual void will_unblock_immediately_without_blocking(UnblockImmediatelyReason) override;
+ virtual bool setup_blocker() override;
+ bool try_unblock(bool from_add_blocker);
+
+ private:
+ NonnullRefPtr<Inode> m_inode;
+ flock const& m_flock;
+ bool m_did_unblock { false };
+ };
+
+ class FlockBlockerSet final : public BlockerSet {
+ public:
+ void unblock_all_blockers_whose_conditions_are_met()
+ {
+ BlockerSet::unblock_all_blockers_whose_conditions_are_met([&](auto& b, void*, bool&) {
+ VERIFY(b.blocker_type() == Blocker::Type::Flock);
+ auto& blocker = static_cast<Thread::FlockBlocker&>(b);
+ return blocker.try_unblock(false);
+ });
+ }
+
+ private:
+ bool should_add_blocker(Blocker& b, void*) override
+ {
+ VERIFY(b.blocker_type() == Blocker::Type::Flock);
+ auto& blocker = static_cast<Thread::FlockBlocker&>(b);
+ return !blocker.try_unblock(true);
+ }
+ };
+
template<typename AddBlockerHandler>
ErrorOr<void> try_join(AddBlockerHandler add_blocker)
{
diff --git a/Kernel/ThreadBlockers.cpp b/Kernel/ThreadBlockers.cpp
index f510218576..ca3fd8368e 100644
--- a/Kernel/ThreadBlockers.cpp
+++ b/Kernel/ThreadBlockers.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
+ * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -820,4 +821,37 @@ bool Thread::WaitBlocker::unblock(Process& process, UnblockFlags flags, u8 signa
return true;
}
+Thread::FlockBlocker::FlockBlocker(NonnullRefPtr<Inode> inode, flock const& flock)
+ : m_inode(move(inode))
+ , m_flock(flock)
+{
+}
+
+void Thread::FlockBlocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason reason)
+{
+ VERIFY(reason == UnblockImmediatelyReason::UnblockConditionAlreadyMet);
+}
+
+bool Thread::FlockBlocker::setup_blocker()
+{
+ return add_to_blocker_set(m_inode->flock_blocker_set());
+}
+
+bool Thread::FlockBlocker::try_unblock(bool from_add_blocker)
+{
+ if (!m_inode->can_apply_flock(m_flock))
+ return false;
+
+ {
+ SpinlockLocker lock(m_lock);
+ if (m_did_unblock)
+ return false;
+ m_did_unblock = true;
+ }
+
+ if (!from_add_blocker)
+ unblock_from_blocker();
+ return true;
+}
+
}