diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index b8824ca223..8ca3712589 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -36,6 +36,7 @@ namespace Kernel virtual BAN::ErrorOr list_next_inodes_impl(off_t, struct dirent*, size_t) override; virtual BAN::ErrorOr create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override; virtual BAN::ErrorOr create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override; + virtual BAN::ErrorOr link_inode_impl(BAN::StringView, BAN::RefPtr) override; virtual BAN::ErrorOr unlink_impl(BAN::StringView) override; virtual BAN::ErrorOr link_target_impl() override; diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 540e56cf24..abfd1a1967 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -95,6 +95,7 @@ namespace Kernel BAN::ErrorOr list_next_inodes(off_t, struct dirent* list, size_t list_size); BAN::ErrorOr create_file(BAN::StringView, mode_t, uid_t, gid_t); BAN::ErrorOr create_directory(BAN::StringView, mode_t, uid_t, gid_t); + BAN::ErrorOr link_inode(BAN::StringView, BAN::RefPtr); BAN::ErrorOr unlink(BAN::StringView); // Link API @@ -131,6 +132,7 @@ namespace Kernel virtual BAN::ErrorOr list_next_inodes_impl(off_t, struct dirent*, size_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } + virtual BAN::ErrorOr link_inode_impl(BAN::StringView, BAN::RefPtr) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr unlink_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } // Link API diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 46d253dd56..221f4f13e1 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -110,6 +110,7 @@ namespace Kernel BAN::ErrorOr sys_write(int fd, const void* buffer, size_t count); BAN::ErrorOr sys_access(const char* path, int amode); BAN::ErrorOr sys_create_dir(const char*, mode_t); + BAN::ErrorOr sys_hardlinkat(int fd1, const char* path1, int fd2, const char* path2, int flag); BAN::ErrorOr sys_unlink(const char*); BAN::ErrorOr sys_readlinkat(int fd, const char* path, char* buffer, size_t bufsize); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index 598dc0ae39..87f42133b4 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -518,6 +518,23 @@ done: return {}; } + BAN::ErrorOr Ext2Inode::link_inode_impl(BAN::StringView name, BAN::RefPtr inode) + { + ASSERT(this->mode().ifdir()); + ASSERT(!inode->mode().ifdir()); + + if (&m_fs != inode->filesystem()) + return BAN::Error::from_errno(EXDEV); + + if (!find_inode_impl(name).is_error()) + return BAN::Error::from_errno(EEXIST); + + auto ext2_inode = static_cast(inode.ptr()); + TRY(link_inode_to_directory(*ext2_inode, name)); + + return {}; + } + BAN::ErrorOr Ext2Inode::link_inode_to_directory(Ext2Inode& inode, BAN::StringView name) { if (!this->mode().ifdir()) diff --git a/kernel/kernel/FS/Inode.cpp b/kernel/kernel/FS/Inode.cpp index 9feac253ec..68de32e69d 100644 --- a/kernel/kernel/FS/Inode.cpp +++ b/kernel/kernel/FS/Inode.cpp @@ -93,6 +93,16 @@ namespace Kernel return create_directory_impl(name, mode, uid, gid); } + BAN::ErrorOr Inode::link_inode(BAN::StringView name, BAN::RefPtr inode) + { + LockGuard _(m_mutex); + if (!this->mode().ifdir()) + return BAN::Error::from_errno(ENOTDIR); + if (inode->mode().ifdir()) + return BAN::Error::from_errno(EINVAL); + return link_inode_impl(name, inode); + } + BAN::ErrorOr Inode::unlink(BAN::StringView name) { LockGuard _(m_mutex); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index af8d823ad1..ceecefa6fa 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1042,6 +1042,40 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_hardlinkat(int fd1, const char* path1, int fd2, const char* path2, int flag) + { + LockGuard _(m_process_lock); + TRY(validate_string_access(path1)); + TRY(validate_string_access(path2)); + + auto inode = TRY(find_file(fd1, path1, flag)).inode; + if (inode->mode().ifdir()) + return BAN::Error::from_errno(EISDIR); + + auto parent = TRY(find_parent(fd2, path2)); + + BAN::RefPtr parent_inode; + BAN::StringView file_name; + + BAN::StringView path2_sv = path2; + if (auto index = path2_sv.rfind('/'); index.has_value()) + { + parent_inode = TRY(VirtualFileSystem::get().file_from_relative_path(parent, m_credentials, path2_sv.substring(0, index.value()), O_EXEC | O_WRONLY)).inode; + file_name = path2_sv.substring(index.value() + 1); + } + else + { + parent_inode = parent.inode; + file_name = path2_sv; + if (!parent_inode->can_access(m_credentials, O_WRONLY)) + return BAN::Error::from_errno(EACCES); + } + + ASSERT(parent_inode->mode().ifdir()); + TRY(parent_inode->link_inode(file_name, inode)); + return 0; + } + BAN::ErrorOr Process::sys_unlink(const char* path) { LockGuard _(m_process_lock); diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index 7806f4014a..365f926e0a 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -90,6 +90,7 @@ __BEGIN_DECLS O(SYS_PTSNAME, ptsname) \ O(SYS_FSYNC, fsync) \ O(SYS_SYMLINKAT, symlinkat) \ + O(SYS_HARDLINKAT, hardlinkat) \ enum Syscall { diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index 2eb57bb619..aad4367e5b 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -630,3 +630,13 @@ int symlinkat(const char* path1, int fd, const char* path2) { return syscall(SYS_SYMLINKAT, path1, fd, path2); } + +int link(const char* path1, const char* path2) +{ + return linkat(AT_FDCWD, path1, AT_FDCWD, path2, 0); +} + +int linkat(int fd1, const char *path1, int fd2, const char *path2, int flag) +{ + return syscall(SYS_HARDLINKAT, fd1, path1, fd2, path2, flag); +}