mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:02:43 +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) 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 | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
|     { | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Idan Horowitz
						Idan Horowitz