diff options
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/FileSystem/Inode.cpp | 56 | ||||
-rw-r--r-- | Kernel/FileSystem/Inode.h | 13 | ||||
-rw-r--r-- | Kernel/FileSystem/OpenFileDescription.cpp | 4 | ||||
-rw-r--r-- | Kernel/FileSystem/OpenFileDescription.h | 2 | ||||
-rw-r--r-- | Kernel/Syscalls/fcntl.cpp | 5 | ||||
-rw-r--r-- | Kernel/Thread.h | 38 | ||||
-rw-r--r-- | Kernel/ThreadBlockers.cpp | 34 |
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; +} + } |