diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index bfa07c25..c0c27e4a 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -37,6 +37,7 @@ namespace Kernel 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 rename_inode_impl(BAN::RefPtr, BAN::StringView, BAN::StringView) override; virtual BAN::ErrorOr unlink_impl(BAN::StringView) override; virtual BAN::ErrorOr link_target_impl() override; @@ -64,6 +65,7 @@ namespace Kernel BAN::ErrorOr> fs_block_of_data_block_index(uint32_t data_block_index); BAN::ErrorOr link_inode_to_directory(Ext2Inode&, BAN::StringView name); + BAN::ErrorOr remove_inode_from_directory(BAN::StringView name, bool cleanup_directory); BAN::ErrorOr is_directory_empty(); BAN::ErrorOr cleanup_indirect_block(uint32_t block, uint32_t depth); diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index a1f1f2e0..7355f143 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -97,6 +97,7 @@ namespace Kernel 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 rename_inode(BAN::RefPtr, BAN::StringView, BAN::StringView); BAN::ErrorOr unlink(BAN::StringView); // Link API @@ -139,12 +140,13 @@ namespace Kernel protected: // Directory API - virtual BAN::ErrorOr> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } - 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); } + virtual BAN::ErrorOr> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } + 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 rename_inode_impl(BAN::RefPtr, BAN::StringView, BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } + virtual BAN::ErrorOr unlink_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } // Link API virtual BAN::ErrorOr link_target_impl() { return BAN::Error::from_errno(ENOTSUP); } @@ -187,7 +189,7 @@ namespace Kernel friend class Epoll; friend class FileBackedRegion; friend class OpenFileDescriptorSet; - friend class SharedFileData; + friend struct SharedFileData; friend class TTY; }; diff --git a/kernel/include/kernel/FS/TmpFS/Inode.h b/kernel/include/kernel/FS/TmpFS/Inode.h index 606da429..4508f0f3 100644 --- a/kernel/include/kernel/FS/TmpFS/Inode.h +++ b/kernel/include/kernel/FS/TmpFS/Inode.h @@ -157,6 +157,7 @@ namespace Kernel virtual BAN::ErrorOr create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override final; virtual BAN::ErrorOr create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override final; virtual BAN::ErrorOr link_inode_impl(BAN::StringView, BAN::RefPtr) override final; + virtual BAN::ErrorOr rename_inode_impl(BAN::RefPtr, BAN::StringView, BAN::StringView) override final; virtual BAN::ErrorOr unlink_impl(BAN::StringView) override; virtual bool can_read_impl() const override { return false; } @@ -168,6 +169,8 @@ namespace Kernel template void for_each_valid_entry(F callback); + BAN::ErrorOr unlink_inode(BAN::StringView, bool cleanup); + friend class TmpInode; }; diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 576f31d9..f10fabc5 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -109,9 +109,9 @@ namespace Kernel 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_renameat(int oldfd, const char* old, int newfd, const char* _new); BAN::ErrorOr sys_unlinkat(int fd, const char* path, int flag); BAN::ErrorOr sys_readlinkat(int fd, const char* path, char* buffer, size_t bufsize); - BAN::ErrorOr sys_symlinkat(const char* path1, int fd, const char* path2); BAN::ErrorOr sys_flock(int fd, int op); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index f7084c49..67259c01 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -585,6 +586,37 @@ done: return {}; } + BAN::ErrorOr Ext2Inode::rename_inode_impl(BAN::RefPtr old_parent, BAN::StringView old_name, BAN::StringView new_name) + { + ASSERT(this->mode().ifdir()); + ASSERT(old_parent->mode().ifdir()); + ASSERT(&m_fs == old_parent->filesystem()); + + auto* ext2_parent = static_cast(old_parent.ptr()); + + // FIXME: possible deadlock :) + LockGuard _(ext2_parent->m_mutex); + + auto old_inode = TRY(ext2_parent->find_inode_impl(old_name)); + auto* ext2_inode = static_cast(old_inode.ptr()); + + if (auto replace_or_error = find_inode_impl(new_name); replace_or_error.is_error()) + { + if (replace_or_error.error().get_error_code() != ENOENT) + return replace_or_error.release_error(); + } + else + { + TRY(unlink_impl(new_name)); + } + + TRY(link_inode_to_directory(*ext2_inode, new_name)); + + TRY(ext2_parent->remove_inode_from_directory(old_name, false)); + + return {}; + } + BAN::ErrorOr Ext2Inode::link_inode_to_directory(Ext2Inode& inode, BAN::StringView name) { if (!this->mode().ifdir()) @@ -778,7 +810,7 @@ needs_new_block: return {}; } - BAN::ErrorOr Ext2Inode::unlink_impl(BAN::StringView name) + BAN::ErrorOr Ext2Inode::remove_inode_from_directory(BAN::StringView name, bool cleanup_directory) { ASSERT(mode().ifdir()); if (m_inode.flags & Ext2::Enum::INDEX_FL) @@ -802,7 +834,7 @@ needs_new_block: if (entry.inode && name == BAN::StringView(entry.name, entry.name_len)) { auto inode = TRY(Ext2Inode::create(m_fs, entry.inode)); - if (inode->mode().ifdir()) + if (cleanup_directory && inode->mode().ifdir()) { if (!TRY(inode->is_directory_empty())) return BAN::Error::from_errno(ENOTEMPTY); @@ -836,6 +868,12 @@ needs_new_block: return {}; } + BAN::ErrorOr Ext2Inode::unlink_impl(BAN::StringView name) + { + TRY(remove_inode_from_directory(name, true)); + return {}; + } + BAN::ErrorOr Ext2Inode::allocate_new_block_to_indirect_block(uint32_t& block, uint32_t index, uint32_t depth) { const uint32_t inode_blocks_per_fs_block = blksize() / 512; diff --git a/kernel/kernel/FS/Inode.cpp b/kernel/kernel/FS/Inode.cpp index 7a5724c8..db197901 100644 --- a/kernel/kernel/FS/Inode.cpp +++ b/kernel/kernel/FS/Inode.cpp @@ -114,6 +114,20 @@ namespace Kernel return link_inode_impl(name, inode); } + BAN::ErrorOr Inode::rename_inode(BAN::RefPtr old_parent, BAN::StringView old_name, BAN::StringView new_name) + { + LockGuard _(m_mutex); + if (!this->mode().ifdir()) + return BAN::Error::from_errno(ENOTDIR); + if (!old_parent->mode().ifdir()) + return BAN::Error::from_errno(ENOTDIR); + if (this->filesystem() != old_parent->filesystem()) + return BAN::Error::from_errno(EXDEV); + if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY)) + return BAN::Error::from_errno(EROFS); + return rename_inode_impl(old_parent, old_name, new_name); + } + BAN::ErrorOr Inode::unlink(BAN::StringView name) { LockGuard _(m_mutex); diff --git a/kernel/kernel/FS/TmpFS/Inode.cpp b/kernel/kernel/FS/TmpFS/Inode.cpp index 579426cd..a469ce72 100644 --- a/kernel/kernel/FS/TmpFS/Inode.cpp +++ b/kernel/kernel/FS/TmpFS/Inode.cpp @@ -669,7 +669,44 @@ namespace Kernel return {}; } + BAN::ErrorOr TmpDirectoryInode::rename_inode_impl(BAN::RefPtr old_parent, BAN::StringView old_name, BAN::StringView new_name) + { + ASSERT(this->mode().ifdir()); + ASSERT(old_parent->mode().ifdir()); + ASSERT(&m_fs == old_parent->filesystem()); + + auto* tmp_parent = static_cast(old_parent.ptr()); + + // FIXME: possible deadlock :) + LockGuard _(tmp_parent->m_mutex); + + auto old_inode = TRY(tmp_parent->find_inode_impl(old_name)); + auto* tmp_inode = static_cast(old_inode.ptr()); + + if (auto replace_or_error = find_inode_impl(new_name); replace_or_error.is_error()) + { + if (replace_or_error.error().get_error_code() != ENOENT) + return replace_or_error.release_error(); + } + else + { + TRY(unlink_impl(new_name)); + } + + TRY(link_inode(*tmp_inode, new_name)); + + TRY(tmp_parent->unlink_inode(old_name, false)); + + return {}; + } + BAN::ErrorOr TmpDirectoryInode::unlink_impl(BAN::StringView name) + { + TRY(unlink_inode(name, true)); + return {}; + } + + BAN::ErrorOr TmpDirectoryInode::unlink_inode(BAN::StringView name, bool cleanup) { ino_t entry_ino = 0; @@ -687,7 +724,8 @@ namespace Kernel ASSERT(inode->nlink() > 0); - TRY(inode->prepare_unlink()); + if (cleanup) + TRY(inode->prepare_unlink()); inode->m_inode_info.nlink--; if (inode->nlink() == 0) diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index a8cbb9ba..cea1946a 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1302,6 +1302,27 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_renameat(int oldfd, const char* old, int newfd, const char* _new) + { + LockGuard _(m_process_lock); + if (old != nullptr) + TRY(validate_string_access(old)); + if (_new != nullptr) + TRY(validate_string_access(_new)); + + auto [old_parent, old_name] = TRY(find_parent_file(oldfd, old, O_WRONLY)); + if (!old_parent.inode->mode().ifdir()) + return BAN::Error::from_errno(ENOTDIR); + + auto [new_parent, new_name] = TRY(find_parent_file(newfd, _new, O_WRONLY)); + if (!new_parent.inode->mode().ifdir()) + return BAN::Error::from_errno(ENOTDIR); + + TRY(new_parent.inode->rename_inode(old_parent.inode, old_name, new_name)); + + return 0; + } + BAN::ErrorOr Process::sys_unlinkat(int fd, const char* path, int flag) { if (flag && flag != AT_REMOVEDIR) diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index a917dd26..2d0eb4a1 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -14,6 +14,7 @@ __BEGIN_DECLS O(SYS_OPENAT, openat) \ O(SYS_SEEK, seek) \ O(SYS_TELL, tell) \ + O(SYS_RENAMEAT, renameat) \ O(SYS_TCGETATTR, tcgetattr) \ O(SYS_TCSETATTR, tcsetattr) \ O(SYS_FORK, fork) \ diff --git a/userspace/libraries/LibC/stdio.cpp b/userspace/libraries/LibC/stdio.cpp index 0bfa6379..95c1472e 100644 --- a/userspace/libraries/LibC/stdio.cpp +++ b/userspace/libraries/LibC/stdio.cpp @@ -820,56 +820,12 @@ int remove(const char* path) int rename(const char* old, const char* _new) { - struct stat st; - if (lstat(old, &st) == -1) - return -1; + return renameat(AT_FDCWD, old, AT_FDCWD, _new); +} - if (!S_ISREG(st.st_mode)) - { - errno = ENOTSUP; - return -1; - } - - if (unlink(_new) == -1 && errno != ENOENT) - return -1; - - int old_fd = open(old, O_RDWR); - int new_fd = open(_new, O_RDWR | O_CREAT | O_EXCL, st.st_mode); - if (old_fd == -1 || new_fd == -1) - goto error; - - for (;;) - { - char buffer[512]; - ssize_t nread = read(old_fd, buffer, sizeof(buffer)); - if (nread == -1) - { - unlink(_new); - goto error; - } - if (nread == 0) - break; - - if (write(new_fd, buffer, nread) != nread) - { - unlink(_new); - goto error; - } - } - - close(new_fd); - close(old_fd); - - unlink(old); - - return 0; - -error: - if (old_fd != -1) - close(old_fd); - if (new_fd != -1) - close(new_fd); - return -1; +int renameat(int oldfd, const char* old, int newfd, const char* _new) +{ + return syscall(SYS_RENAMEAT, oldfd, old, newfd, _new); } void rewind(FILE* file)