summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsin-ack <sin-ack@users.noreply.github.com>2022-10-01 19:28:30 +0000
committerAndrew Kaster <andrewdkaster@gmail.com>2022-12-11 19:55:37 -0700
commit3275015786ec0e412d465ece9792f6a128fc3879 (patch)
treedaae765817808acb430d391fe661e480e4ded241
parent9b425b860cde2fd5b94b0191689161652363d272 (diff)
downloadserenity-3275015786ec0e412d465ece9792f6a128fc3879.zip
Kernel: Implement flock downgrading
This commit makes it possible for a process to downgrade a file lock it holds from a write (exclusive) lock to a read (shared) lock. For this, the process must point to the exact range of the flock, and must be the owner of the lock.
-rw-r--r--Kernel/FileSystem/Inode.cpp54
-rw-r--r--Kernel/FileSystem/Inode.h2
2 files changed, 38 insertions, 18 deletions
diff --git a/Kernel/FileSystem/Inode.cpp b/Kernel/FileSystem/Inode.cpp
index 5053864f85..a5581b323d 100644
--- a/Kernel/FileSystem/Inode.cpp
+++ b/Kernel/FileSystem/Inode.cpp
@@ -298,7 +298,7 @@ static inline ErrorOr<void> normalize_flock(OpenFileDescription const& descripti
return {};
}
-bool Inode::can_apply_flock(flock const& new_lock) const
+bool Inode::can_apply_flock(flock const& new_lock, Optional<OpenFileDescription const&> description) const
{
VERIFY(new_lock.l_whence == SEEK_SET);
@@ -310,8 +310,19 @@ bool Inode::can_apply_flock(flock const& new_lock) const
if (!range_overlap(lock.start, lock.len, new_lock.l_start, new_lock.l_len))
continue;
+ // There are two cases where we can attempt downgrade:
+ //
+ // 1) We're the owner of this lock. The downgrade will immediately
+ // succeed.
+ // 2) We're not the owner of this lock. Our downgrade attempt will
+ // fail, and the thread will start blocking on an FlockBlocker.
+ //
+ // For the first case, we get the description from try_apply_flock
+ // below. For the second case, the check below would always be
+ // false, so there is no need to store the description in the
+ // blocker in the first place.
if (new_lock.l_type == F_RDLCK && lock.type == F_WRLCK)
- return false;
+ return description.has_value() && lock.owner == &description.value() && lock.start == new_lock.l_start && lock.len == new_lock.l_len;
if (new_lock.l_type == F_WRLCK)
return false;
@@ -320,29 +331,38 @@ bool Inode::can_apply_flock(flock const& new_lock) const
});
}
-ErrorOr<bool> Inode::try_apply_flock(Process const& process, OpenFileDescription const& description, flock const& lock)
+ErrorOr<bool> Inode::try_apply_flock(Process const& process, OpenFileDescription const& description, flock const& new_lock)
{
return m_flocks.with([&](auto& flocks) -> ErrorOr<bool> {
- if (!can_apply_flock(lock))
+ if (!can_apply_flock(new_lock, description))
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;
- }
- }
+ bool did_manipulate_lock = false;
+ for (size_t i = 0; i < flocks.size(); ++i) {
+ auto const& lock = flocks[i];
+
+ bool is_potential_downgrade = new_lock.l_type == F_RDLCK && lock.type == F_WRLCK;
+ bool is_potential_unlock = new_lock.l_type == F_UNLCK;
- if (any_locks_unlocked)
- m_flock_blocker_set.unblock_all_blockers_whose_conditions_are_met();
+ bool is_lock_owner = &description == lock.owner;
+ bool lock_range_exactly_matches = lock.start == new_lock.l_start && lock.len == new_lock.l_len;
+ bool can_manage_this_lock = is_lock_owner && lock_range_exactly_matches;
- // Judging by the Linux implementation, unlocking a non-existent lock also works.
- return true;
+ if ((is_potential_downgrade || is_potential_unlock) && can_manage_this_lock) {
+ flocks.remove(i);
+ did_manipulate_lock = true;
+ break;
+ }
}
- TRY(flocks.try_append(Flock { lock.l_start, lock.l_len, &description, process.pid().value(), lock.l_type }));
+ if (new_lock.l_type != F_UNLCK)
+ TRY(flocks.try_append(Flock { new_lock.l_start, new_lock.l_len, &description, process.pid().value(), new_lock.l_type }));
+
+ if (did_manipulate_lock)
+ 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;
});
}
diff --git a/Kernel/FileSystem/Inode.h b/Kernel/FileSystem/Inode.h
index 5702e470d5..cafa6630d4 100644
--- a/Kernel/FileSystem/Inode.h
+++ b/Kernel/FileSystem/Inode.h
@@ -104,7 +104,7 @@ public:
ErrorOr<NonnullLockRefPtr<FIFO>> fifo();
- bool can_apply_flock(flock const&) const;
+ bool can_apply_flock(flock const&, Optional<OpenFileDescription 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&);