mirror of
https://github.com/RGBCube/serenity
synced 2025-05-23 15:25:08 +00:00
Kernel: Support F_SETLKW in fcntl
This commit is contained in:
parent
9db10887a1
commit
3a80b25ed6
7 changed files with 133 additions and 29 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
|
* Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -297,50 +298,71 @@ static inline ErrorOr<void> normalize_flock(OpenFileDescription const& descripti
|
||||||
return {};
|
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);
|
VERIFY(new_lock.l_whence == SEEK_SET);
|
||||||
|
|
||||||
if (new_lock.l_type == F_UNLCK)
|
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) {
|
for (auto const& lock : flocks) {
|
||||||
if (!range_overlap(lock.start, lock.len, new_lock.l_start, new_lock.l_len))
|
if (!range_overlap(lock.start, lock.len, new_lock.l_start, new_lock.l_len))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (new_lock.l_type == F_RDLCK && lock.type == F_WRLCK)
|
if (new_lock.l_type == F_RDLCK && lock.type == F_WRLCK)
|
||||||
return EAGAIN;
|
return false;
|
||||||
|
|
||||||
if (new_lock.l_type == F_WRLCK)
|
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)
|
||||||
|
{
|
||||||
|
return m_flocks.with([&](auto& flocks) -> ErrorOr<bool> {
|
||||||
|
if (!can_apply_flock(lock))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
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 == 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 true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
auto new_lock = TRY(copy_typed_from_user(input_lock));
|
||||||
TRY(normalize_flock(description, new_lock));
|
TRY(normalize_flock(description, new_lock));
|
||||||
|
|
||||||
return m_flocks.with([&](auto& flocks) -> ErrorOr<void> {
|
while (true) {
|
||||||
TRY(can_apply_flock(description, new_lock));
|
auto success = TRY(try_apply_flock(process, description, new_lock));
|
||||||
|
if (success)
|
||||||
if (new_lock.l_type == F_UNLCK) {
|
|
||||||
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) {
|
|
||||||
flocks.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Judging by the Linux implementation, unlocking a non-existent lock also works.
|
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
TRY(flocks.try_append(Flock { new_lock.l_start, new_lock.l_len, &description, process.pid().value(), new_lock.l_type }));
|
if (should_block == ShouldBlock::No)
|
||||||
return {};
|
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
|
ErrorOr<void> Inode::get_flock(OpenFileDescription const& description, Userspace<flock*> reference_lock) const
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
enum class ShouldBlock {
|
||||||
|
No = 0,
|
||||||
|
Yes = 1
|
||||||
|
};
|
||||||
|
|
||||||
class Inode : public ListedRefCounted<Inode, LockType::Spinlock>
|
class Inode : public ListedRefCounted<Inode, LockType::Spinlock>
|
||||||
, public Weakable<Inode> {
|
, public Weakable<Inode> {
|
||||||
friend class VirtualFileSystem;
|
friend class VirtualFileSystem;
|
||||||
|
@ -94,10 +99,11 @@ public:
|
||||||
|
|
||||||
ErrorOr<NonnullRefPtr<FIFO>> fifo();
|
ErrorOr<NonnullRefPtr<FIFO>> fifo();
|
||||||
|
|
||||||
ErrorOr<void> can_apply_flock(OpenFileDescription const&, flock const&) const;
|
bool can_apply_flock(flock const&) const;
|
||||||
ErrorOr<void> apply_flock(Process const&, OpenFileDescription const&, Userspace<flock const*>);
|
ErrorOr<void> apply_flock(Process const&, OpenFileDescription const&, Userspace<flock const*>, ShouldBlock);
|
||||||
ErrorOr<void> get_flock(OpenFileDescription const&, Userspace<flock*>) const;
|
ErrorOr<void> get_flock(OpenFileDescription const&, Userspace<flock*>) const;
|
||||||
void remove_flocks_for_description(OpenFileDescription const&);
|
void remove_flocks_for_description(OpenFileDescription const&);
|
||||||
|
Thread::FlockBlockerSet& flock_blocker_set() { return m_flock_blocker_set; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Inode(FileSystem&, InodeIndex);
|
Inode(FileSystem&, InodeIndex);
|
||||||
|
@ -112,6 +118,8 @@ protected:
|
||||||
mutable Mutex m_inode_lock { "Inode"sv };
|
mutable Mutex m_inode_lock { "Inode"sv };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ErrorOr<bool> try_apply_flock(Process const&, OpenFileDescription const&, flock const&);
|
||||||
|
|
||||||
FileSystem& m_file_system;
|
FileSystem& m_file_system;
|
||||||
InodeIndex m_index { 0 };
|
InodeIndex m_index { 0 };
|
||||||
WeakPtr<Memory::SharedInodeVMObject> m_shared_vmobject;
|
WeakPtr<Memory::SharedInodeVMObject> m_shared_vmobject;
|
||||||
|
@ -129,6 +137,7 @@ private:
|
||||||
short type;
|
short type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Thread::FlockBlockerSet m_flock_blocker_set;
|
||||||
SpinlockProtected<Vector<Flock>> m_flocks;
|
SpinlockProtected<Vector<Flock>> m_flocks;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -445,12 +445,12 @@ FileBlockerSet& OpenFileDescription::blocker_set()
|
||||||
return m_file->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)
|
if (!m_inode)
|
||||||
return EBADF;
|
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
|
ErrorOr<void> OpenFileDescription::get_flock(Userspace<flock*> lock) const
|
||||||
|
|
|
@ -124,7 +124,7 @@ public:
|
||||||
|
|
||||||
FileBlockerSet& blocker_set();
|
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;
|
ErrorOr<void> get_flock(Userspace<flock*>) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -45,7 +45,10 @@ ErrorOr<FlatPtr> Process::sys$fcntl(int fd, int cmd, uintptr_t arg)
|
||||||
TRY(description->get_flock(Userspace<flock*>(arg)));
|
TRY(description->get_flock(Userspace<flock*>(arg)));
|
||||||
return 0;
|
return 0;
|
||||||
case F_SETLK:
|
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;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
|
@ -283,7 +283,8 @@ public:
|
||||||
Routing,
|
Routing,
|
||||||
Sleep,
|
Sleep,
|
||||||
Signal,
|
Signal,
|
||||||
Wait
|
Wait,
|
||||||
|
Flock
|
||||||
};
|
};
|
||||||
virtual ~Blocker();
|
virtual ~Blocker();
|
||||||
virtual StringView state_string() const = 0;
|
virtual StringView state_string() const = 0;
|
||||||
|
@ -788,6 +789,41 @@ public:
|
||||||
bool m_finalized { false };
|
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>
|
template<typename AddBlockerHandler>
|
||||||
ErrorOr<void> try_join(AddBlockerHandler add_blocker)
|
ErrorOr<void> try_join(AddBlockerHandler add_blocker)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, the SerenityOS developers.
|
* Copyright (c) 2020, the SerenityOS developers.
|
||||||
|
* Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -820,4 +821,37 @@ bool Thread::WaitBlocker::unblock(Process& process, UnblockFlags flags, u8 signa
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue