diff --git a/kernel/include/kernel/FS/TmpFS/Inode.h b/kernel/include/kernel/FS/TmpFS/Inode.h index d8e25f6d..6bc8427d 100644 --- a/kernel/include/kernel/FS/TmpFS/Inode.h +++ b/kernel/include/kernel/FS/TmpFS/Inode.h @@ -83,10 +83,16 @@ namespace Kernel class TmpSymlinkInode : public TmpInode { public: + static BAN::ErrorOr> create_new(TmpFileSystem&, mode_t, uid_t, gid_t, BAN::StringView target); ~TmpSymlinkInode(); + BAN::ErrorOr set_link_target(BAN::StringView); + + protected: + virtual BAN::ErrorOr link_target_impl() override; + private: - TmpSymlinkInode(TmpFileSystem&, ino_t, const TmpInodeInfo&, BAN::StringView target); + TmpSymlinkInode(TmpFileSystem&, ino_t, const TmpInodeInfo&); }; class TmpDirectoryInode : public TmpInode diff --git a/kernel/kernel/FS/TmpFS/Inode.cpp b/kernel/kernel/FS/TmpFS/Inode.cpp index 0c2afc61..402dd4e4 100644 --- a/kernel/kernel/FS/TmpFS/Inode.cpp +++ b/kernel/kernel/FS/TmpFS/Inode.cpp @@ -215,6 +215,86 @@ namespace Kernel return {}; } + /* SYMLINK INODE */ + + BAN::ErrorOr> TmpSymlinkInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid, BAN::StringView target) + { + auto info = create_inode_info(Mode::IFLNK | mode, uid, gid); + ino_t ino = TRY(fs.allocate_inode(info)); + + auto* inode_ptr = new TmpSymlinkInode(fs, ino, info); + if (inode_ptr == nullptr) + return BAN::Error::from_errno(ENOMEM); + auto inode = BAN::RefPtr::adopt(inode_ptr); + + TRY(inode->set_link_target(target)); + + return inode; + } + + TmpSymlinkInode::TmpSymlinkInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info) + : TmpInode(fs, ino, info) + { + ASSERT(mode().iflnk()); + } + + TmpSymlinkInode::~TmpSymlinkInode() + { + } + + BAN::ErrorOr TmpSymlinkInode::set_link_target(BAN::StringView new_target) + { + free_all_blocks(); + m_inode_info.size = 0; + + if (new_target.size() <= sizeof(TmpInodeInfo::block)) + { + memcpy(m_inode_info.block.data(), new_target.data(), new_target.size()); + m_inode_info.size = new_target.size(); + return {}; + } + + const size_t blocks_needed = BAN::Math::div_round_up(new_target.size(), blksize()); + for (size_t i = 0; i < blocks_needed; i++) + { + const size_t block_index = TRY(block_index_with_allocation(i)); + const size_t byte_count = BAN::Math::min(new_target.size() - i * blksize(), blksize()); + + m_fs.with_block_buffer(block_index, [&](BAN::ByteSpan bytespan) { + memcpy(bytespan.data(), new_target.data() + i * blksize(), byte_count); + }); + + m_inode_info.size += byte_count; + } + + return {}; + } + + BAN::ErrorOr TmpSymlinkInode::link_target_impl() + { + BAN::String result; + TRY(result.resize(size())); + + if ((size_t)size() <= sizeof(TmpInodeInfo::block)) + { + memcpy(result.data(), m_inode_info.block.data(), size()); + return result; + } + + const size_t data_block_count = BAN::Math::div_round_up(size(), blksize()); + for (size_t i = 0; i < data_block_count; i++) + { + const size_t block_index = TRY(block_index_with_allocation(i)); + const size_t byte_count = BAN::Math::min(size() - i * blksize(), blksize()); + + m_fs.with_block_buffer(block_index, [&](BAN::ByteSpan bytespan) { + memcpy(result.data() + i * blksize(), bytespan.data(), byte_count); + }); + } + + return result; + } + /* DIRECTORY INODE */ BAN::ErrorOr> TmpDirectoryInode::create_root(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)