mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 21:17:44 +00:00
Kernel+LibC: Implement fcntl(2) advisory locks
Advisory locks don't actually prevent other processes from writing to the file, but they do prevent other processes looking to acquire and advisory lock on the file. This implementation currently only adds non-blocking locks, which are all I need for now.
This commit is contained in:
parent
fbc56461da
commit
3fa2816642
8 changed files with 186 additions and 7 deletions
|
@ -7,6 +7,7 @@
|
|||
#include <AK/StringView.h>
|
||||
#include <Kernel/FileSystem/File.h>
|
||||
#include <Kernel/FileSystem/FileDescription.h>
|
||||
#include <Kernel/Process.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
@ -53,5 +54,4 @@ void File::detach(FileDescription&)
|
|||
{
|
||||
m_attach_count--;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,6 +74,9 @@ FileDescription::~FileDescription()
|
|||
(void)m_file->close();
|
||||
if (m_inode)
|
||||
m_inode->detach(*this);
|
||||
|
||||
if (m_inode)
|
||||
m_inode->remove_flocks_for_description(*this);
|
||||
}
|
||||
|
||||
KResult FileDescription::attach()
|
||||
|
@ -446,4 +449,19 @@ FileBlockCondition& FileDescription::block_condition()
|
|||
return m_file->block_condition();
|
||||
}
|
||||
|
||||
KResult FileDescription::apply_flock(Process const& process, Userspace<flock const*> lock)
|
||||
{
|
||||
if (!m_inode)
|
||||
return EBADF;
|
||||
|
||||
return m_inode->apply_flock(process, *this, lock);
|
||||
}
|
||||
|
||||
KResult FileDescription::get_flock(Userspace<flock*> lock) const
|
||||
{
|
||||
if (!m_inode)
|
||||
return EBADF;
|
||||
|
||||
return m_inode->get_flock(*this, lock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,6 +127,9 @@ public:
|
|||
|
||||
FileBlockCondition& block_condition();
|
||||
|
||||
KResult apply_flock(Process const&, Userspace<flock const*>);
|
||||
KResult get_flock(Userspace<flock*>) const;
|
||||
|
||||
private:
|
||||
friend class VirtualFileSystem;
|
||||
explicit FileDescription(File&);
|
||||
|
|
|
@ -11,11 +11,13 @@
|
|||
#include <AK/StringView.h>
|
||||
#include <Kernel/API/InodeWatcherEvent.h>
|
||||
#include <Kernel/FileSystem/Custody.h>
|
||||
#include <Kernel/FileSystem/FileDescription.h>
|
||||
#include <Kernel/FileSystem/Inode.h>
|
||||
#include <Kernel/FileSystem/InodeWatcher.h>
|
||||
#include <Kernel/FileSystem/VirtualFileSystem.h>
|
||||
#include <Kernel/KBufferBuilder.h>
|
||||
#include <Kernel/Net/LocalSocket.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/VM/SharedInodeVMObject.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
@ -272,4 +274,126 @@ RefPtr<SharedInodeVMObject> Inode::shared_vmobject() const
|
|||
return m_shared_vmobject.strong_ref();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool range_overlap(T start1, T len1, T start2, T len2)
|
||||
{
|
||||
return ((start1 < start2 + len2) || len2 == 0) && ((start2 < start1 + len1) || len1 == 0);
|
||||
}
|
||||
|
||||
static inline KResult normalize_flock(FileDescription const& description, flock& lock)
|
||||
{
|
||||
off_t start;
|
||||
switch (lock.l_whence) {
|
||||
case SEEK_SET:
|
||||
start = lock.l_start;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
start = description.offset() + lock.l_start;
|
||||
break;
|
||||
case SEEK_END:
|
||||
// FIXME: Implement SEEK_END and negative lengths.
|
||||
return ENOTSUP;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
lock = { lock.l_type, SEEK_SET, start, lock.l_len, 0 };
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Inode::can_apply_flock(FileDescription const& description, flock const& new_lock) const
|
||||
{
|
||||
VERIFY(new_lock.l_whence == SEEK_SET);
|
||||
|
||||
MutexLocker locker(m_inode_lock, Mutex::Mode::Shared);
|
||||
|
||||
if (new_lock.l_type == F_UNLCK) {
|
||||
for (auto& lock : m_flocks) {
|
||||
if (&description == lock.owner && lock.start == new_lock.l_start && lock.len == new_lock.l_len)
|
||||
return KSuccess;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (auto& lock : m_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;
|
||||
|
||||
if (new_lock.l_type == F_WRLCK)
|
||||
return EAGAIN;
|
||||
}
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Inode::apply_flock(Process const& process, FileDescription const& description, Userspace<flock const*> input_lock)
|
||||
{
|
||||
flock new_lock;
|
||||
if (!copy_from_user(&new_lock, input_lock))
|
||||
return EFAULT;
|
||||
|
||||
auto rc = normalize_flock(description, new_lock);
|
||||
if (rc.is_error())
|
||||
return rc;
|
||||
|
||||
MutexLocker locker(m_inode_lock);
|
||||
|
||||
rc = can_apply_flock(description, new_lock);
|
||||
if (rc.is_error())
|
||||
return rc;
|
||||
|
||||
if (new_lock.l_type == F_UNLCK) {
|
||||
for (size_t i = 0; i < m_flocks.size(); ++i) {
|
||||
if (&description == m_flocks[i].owner && m_flocks[i].start == new_lock.l_start && m_flocks[i].len == new_lock.l_len) {
|
||||
m_flocks.remove(i);
|
||||
return KSuccess;
|
||||
}
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
m_flocks.append(Flock { new_lock.l_type, new_lock.l_start, new_lock.l_len, &description, process.pid().value() });
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Inode::get_flock(FileDescription const& description, Userspace<flock*> reference_lock) const
|
||||
{
|
||||
flock lookup;
|
||||
if (!copy_from_user(&lookup, reference_lock))
|
||||
return EFAULT;
|
||||
|
||||
auto rc = normalize_flock(description, lookup);
|
||||
if (rc.is_error())
|
||||
return rc;
|
||||
|
||||
MutexLocker locker(m_inode_lock, Mutex::Mode::Shared);
|
||||
|
||||
for (auto& lock : m_flocks) {
|
||||
if (!range_overlap(lock.start, lock.len, lookup.l_start, lookup.l_len))
|
||||
continue;
|
||||
|
||||
if ((lookup.l_type == F_RDLCK && lock.type == F_WRLCK) || lookup.l_type == F_WRLCK) {
|
||||
lookup = { lock.type, SEEK_SET, lock.start, lock.len, lock.pid };
|
||||
if (!copy_to_user(reference_lock, &lookup))
|
||||
return EFAULT;
|
||||
return KSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
lookup.l_type = F_UNLCK;
|
||||
if (!copy_to_user(reference_lock, &lookup))
|
||||
return EFAULT;
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
void Inode::remove_flocks_for_description(FileDescription const& description)
|
||||
{
|
||||
MutexLocker locker(m_inode_lock);
|
||||
|
||||
for (size_t i = 0; i < m_flocks.size(); ++i) {
|
||||
if (&description == m_flocks[i].owner)
|
||||
m_flocks.remove(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,11 @@ public:
|
|||
|
||||
NonnullRefPtr<FIFO> fifo();
|
||||
|
||||
KResult can_apply_flock(FileDescription const&, flock const&) const;
|
||||
KResult apply_flock(Process const&, FileDescription const&, Userspace<flock const*>);
|
||||
KResult get_flock(FileDescription const&, Userspace<flock*>) const;
|
||||
void remove_flocks_for_description(FileDescription const&);
|
||||
|
||||
protected:
|
||||
Inode(FileSystem&, InodeIndex);
|
||||
void set_metadata_dirty(bool);
|
||||
|
@ -119,6 +124,16 @@ private:
|
|||
RefPtr<FIFO> m_fifo;
|
||||
IntrusiveListNode<Inode> m_inode_list_node;
|
||||
|
||||
struct Flock {
|
||||
short type;
|
||||
off_t start;
|
||||
off_t len;
|
||||
FileDescription const* owner;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
Vector<Flock> m_flocks;
|
||||
|
||||
public:
|
||||
using List = IntrusiveList<Inode, RawPtr<Inode>, &Inode::m_inode_list_node>;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue