Kernel/LibC: Implement rename{,at}
This commit is contained in:
parent
05d59a05df
commit
4ec8f4a4bf
|
|
@ -37,6 +37,7 @@ namespace Kernel
|
|||
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
||||
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
||||
virtual BAN::ErrorOr<void> link_inode_impl(BAN::StringView, BAN::RefPtr<Inode>) override;
|
||||
virtual BAN::ErrorOr<void> rename_inode_impl(BAN::RefPtr<Inode>, BAN::StringView, BAN::StringView) override;
|
||||
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
|
||||
|
||||
virtual BAN::ErrorOr<BAN::String> link_target_impl() override;
|
||||
|
|
@ -64,6 +65,7 @@ namespace Kernel
|
|||
BAN::ErrorOr<BAN::Optional<uint32_t>> fs_block_of_data_block_index(uint32_t data_block_index);
|
||||
|
||||
BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name);
|
||||
BAN::ErrorOr<void> remove_inode_from_directory(BAN::StringView name, bool cleanup_directory);
|
||||
BAN::ErrorOr<bool> is_directory_empty();
|
||||
|
||||
BAN::ErrorOr<void> cleanup_indirect_block(uint32_t block, uint32_t depth);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ namespace Kernel
|
|||
BAN::ErrorOr<void> create_file(BAN::StringView, mode_t, uid_t, gid_t);
|
||||
BAN::ErrorOr<void> create_directory(BAN::StringView, mode_t, uid_t, gid_t);
|
||||
BAN::ErrorOr<void> link_inode(BAN::StringView, BAN::RefPtr<Inode>);
|
||||
BAN::ErrorOr<void> rename_inode(BAN::RefPtr<Inode>, BAN::StringView, BAN::StringView);
|
||||
BAN::ErrorOr<void> unlink(BAN::StringView);
|
||||
|
||||
// Link API
|
||||
|
|
@ -144,6 +145,7 @@ namespace Kernel
|
|||
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
|
||||
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
|
||||
virtual BAN::ErrorOr<void> link_inode_impl(BAN::StringView, BAN::RefPtr<Inode>) { return BAN::Error::from_errno(ENOTSUP); }
|
||||
virtual BAN::ErrorOr<void> rename_inode_impl(BAN::RefPtr<Inode>, BAN::StringView, BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
|
||||
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
|
||||
|
||||
// Link API
|
||||
|
|
@ -187,7 +189,7 @@ namespace Kernel
|
|||
friend class Epoll;
|
||||
friend class FileBackedRegion;
|
||||
friend class OpenFileDescriptorSet;
|
||||
friend class SharedFileData;
|
||||
friend struct SharedFileData;
|
||||
friend class TTY;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ namespace Kernel
|
|||
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override final;
|
||||
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override final;
|
||||
virtual BAN::ErrorOr<void> link_inode_impl(BAN::StringView, BAN::RefPtr<Inode>) override final;
|
||||
virtual BAN::ErrorOr<void> rename_inode_impl(BAN::RefPtr<Inode>, BAN::StringView, BAN::StringView) override final;
|
||||
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
|
||||
|
||||
virtual bool can_read_impl() const override { return false; }
|
||||
|
|
@ -168,6 +169,8 @@ namespace Kernel
|
|||
template<TmpFuncs::for_each_valid_entry_callback F>
|
||||
void for_each_valid_entry(F callback);
|
||||
|
||||
BAN::ErrorOr<void> unlink_inode(BAN::StringView, bool cleanup);
|
||||
|
||||
friend class TmpInode;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -109,9 +109,9 @@ namespace Kernel
|
|||
BAN::ErrorOr<long> sys_access(const char* path, int amode);
|
||||
BAN::ErrorOr<long> sys_create_dir(const char*, mode_t);
|
||||
BAN::ErrorOr<long> sys_hardlinkat(int fd1, const char* path1, int fd2, const char* path2, int flag);
|
||||
BAN::ErrorOr<long> sys_renameat(int oldfd, const char* old, int newfd, const char* _new);
|
||||
BAN::ErrorOr<long> sys_unlinkat(int fd, const char* path, int flag);
|
||||
BAN::ErrorOr<long> sys_readlinkat(int fd, const char* path, char* buffer, size_t bufsize);
|
||||
|
||||
BAN::ErrorOr<long> sys_symlinkat(const char* path1, int fd, const char* path2);
|
||||
|
||||
BAN::ErrorOr<long> sys_flock(int fd, int op);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/FS/Ext2/FileSystem.h>
|
||||
#include <kernel/FS/Ext2/Inode.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
|
@ -585,6 +586,37 @@ done:
|
|||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Ext2Inode::rename_inode_impl(BAN::RefPtr<Inode> 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<Ext2Inode*>(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<Ext2Inode*>(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<void> Ext2Inode::link_inode_to_directory(Ext2Inode& inode, BAN::StringView name)
|
||||
{
|
||||
if (!this->mode().ifdir())
|
||||
|
|
@ -778,7 +810,7 @@ needs_new_block:
|
|||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Ext2Inode::unlink_impl(BAN::StringView name)
|
||||
BAN::ErrorOr<void> 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<void> Ext2Inode::unlink_impl(BAN::StringView name)
|
||||
{
|
||||
TRY(remove_inode_from_directory(name, true));
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<uint32_t> 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;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,20 @@ namespace Kernel
|
|||
return link_inode_impl(name, inode);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Inode::rename_inode(BAN::RefPtr<Inode> 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<void> Inode::unlink(BAN::StringView name)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
|
|
|||
|
|
@ -669,7 +669,44 @@ namespace Kernel
|
|||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::rename_inode_impl(BAN::RefPtr<Inode> 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<TmpDirectoryInode*>(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<TmpInode*>(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<void> TmpDirectoryInode::unlink_impl(BAN::StringView name)
|
||||
{
|
||||
TRY(unlink_inode(name, true));
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> TmpDirectoryInode::unlink_inode(BAN::StringView name, bool cleanup)
|
||||
{
|
||||
ino_t entry_ino = 0;
|
||||
|
||||
|
|
@ -687,6 +724,7 @@ namespace Kernel
|
|||
|
||||
ASSERT(inode->nlink() > 0);
|
||||
|
||||
if (cleanup)
|
||||
TRY(inode->prepare_unlink());
|
||||
inode->m_inode_info.nlink--;
|
||||
|
||||
|
|
|
|||
|
|
@ -1302,6 +1302,27 @@ namespace Kernel
|
|||
return 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> 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<long> Process::sys_unlinkat(int fd, const char* path, int flag)
|
||||
{
|
||||
if (flag && flag != AT_REMOVEDIR)
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue