diff --git a/Kernel/FileSystem/File.cpp b/Kernel/FileSystem/File.cpp index 2ca225a0df..cc59666497 100644 --- a/Kernel/FileSystem/File.cpp +++ b/Kernel/FileSystem/File.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Kernel { @@ -53,5 +54,4 @@ void File::detach(FileDescription&) { m_attach_count--; } - } diff --git a/Kernel/FileSystem/FileDescription.cpp b/Kernel/FileSystem/FileDescription.cpp index 179d9c57d8..6432cb26aa 100644 --- a/Kernel/FileSystem/FileDescription.cpp +++ b/Kernel/FileSystem/FileDescription.cpp @@ -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 lock) +{ + if (!m_inode) + return EBADF; + + return m_inode->apply_flock(process, *this, lock); +} + +KResult FileDescription::get_flock(Userspace lock) const +{ + if (!m_inode) + return EBADF; + + return m_inode->get_flock(*this, lock); +} } diff --git a/Kernel/FileSystem/FileDescription.h b/Kernel/FileSystem/FileDescription.h index ae8f9d45b7..c22e8a9d22 100644 --- a/Kernel/FileSystem/FileDescription.h +++ b/Kernel/FileSystem/FileDescription.h @@ -127,6 +127,9 @@ public: FileBlockCondition& block_condition(); + KResult apply_flock(Process const&, Userspace); + KResult get_flock(Userspace) const; + private: friend class VirtualFileSystem; explicit FileDescription(File&); diff --git a/Kernel/FileSystem/Inode.cpp b/Kernel/FileSystem/Inode.cpp index 9663656bd4..d8e31de11c 100644 --- a/Kernel/FileSystem/Inode.cpp +++ b/Kernel/FileSystem/Inode.cpp @@ -11,11 +11,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include namespace Kernel { @@ -272,4 +274,126 @@ RefPtr Inode::shared_vmobject() const return m_shared_vmobject.strong_ref(); } +template +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 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 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--); + } +} } diff --git a/Kernel/FileSystem/Inode.h b/Kernel/FileSystem/Inode.h index 519e4a9db0..c40e530df4 100644 --- a/Kernel/FileSystem/Inode.h +++ b/Kernel/FileSystem/Inode.h @@ -97,6 +97,11 @@ public: NonnullRefPtr fifo(); + KResult can_apply_flock(FileDescription const&, flock const&) const; + KResult apply_flock(Process const&, FileDescription const&, Userspace); + KResult get_flock(FileDescription const&, Userspace) const; + void remove_flocks_for_description(FileDescription const&); + protected: Inode(FileSystem&, InodeIndex); void set_metadata_dirty(bool); @@ -119,6 +124,16 @@ private: RefPtr m_fifo; IntrusiveListNode m_inode_list_node; + struct Flock { + short type; + off_t start; + off_t len; + FileDescription const* owner; + pid_t pid; + }; + + Vector m_flocks; + public: using List = IntrusiveList, &Inode::m_inode_list_node>; }; diff --git a/Kernel/Syscalls/fcntl.cpp b/Kernel/Syscalls/fcntl.cpp index cff72f2d6b..19d978def0 100644 --- a/Kernel/Syscalls/fcntl.cpp +++ b/Kernel/Syscalls/fcntl.cpp @@ -43,6 +43,10 @@ KResultOr Process::sys$fcntl(int fd, int cmd, u32 arg) break; case F_ISTTY: return description->is_tty(); + case F_GETLK: + return description->get_flock(Userspace(arg)); + case F_SETLK: + return description->apply_flock(*Process::current(), Userspace(arg)); default: return EINVAL; } diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index f9429eb529..4ca23090fe 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -108,6 +108,9 @@ enum { #define F_GETFL 3 #define F_SETFL 4 #define F_ISTTY 5 +#define F_GETLK 6 +#define F_SETLK 7 +#define F_SETLKW 8 #define FD_CLOEXEC 1 @@ -746,3 +749,15 @@ struct statvfs { unsigned long f_flag; unsigned long f_namemax; }; + +#define F_RDLCK ((short)0) +#define F_WRLCK ((short)1) +#define F_UNLCK ((short)2) + +struct flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; diff --git a/Userland/Libraries/LibC/fcntl.h b/Userland/Libraries/LibC/fcntl.h index 7e61ec3324..463e3ba060 100644 --- a/Userland/Libraries/LibC/fcntl.h +++ b/Userland/Libraries/LibC/fcntl.h @@ -18,6 +18,9 @@ __BEGIN_DECLS #define F_GETFL 3 #define F_SETFL 4 #define F_ISTTY 5 +#define F_GETLK 6 +#define F_SETLK 7 +#define F_SETLKW 8 #define FD_CLOEXEC 1 @@ -48,12 +51,9 @@ int create_inode_watcher(unsigned flags); int inode_watcher_add_watch(int fd, const char* path, size_t path_length, unsigned event_mask); int inode_watcher_remove_watch(int fd, int wd); -#define F_RDLCK 0 -#define F_WRLCK 1 -#define F_UNLCK 2 -#define F_GETLK 5 -#define F_SETLK 6 -#define F_SETLKW 7 +#define F_RDLCK ((short)0) +#define F_WRLCK ((short)1) +#define F_UNLCK ((short)2) struct flock { short l_type;