Kernel: Support F_SETLKW in fcntl

This commit is contained in:
Idan Horowitz 2022-07-14 02:17:01 +03:00 committed by Andreas Kling
commit 3a80b25ed6
Notes: sideshowbarker 2024-07-17 08:42:29 +09:00
7 changed files with 133 additions and 29 deletions

View file

@ -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,50 +298,71 @@ 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)
{
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));
TRY(normalize_flock(description, new_lock));
return m_flocks.with([&](auto& flocks) -> ErrorOr<void> {
TRY(can_apply_flock(description, new_lock));
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.
while (true) {
auto success = TRY(try_apply_flock(process, description, new_lock));
if (success)
return {};
}
TRY(flocks.try_append(Flock { new_lock.l_start, new_lock.l_len, &description, process.pid().value(), new_lock.l_type }));
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

View file

@ -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:

View file

@ -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

View file

@ -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: