/* * Copyright (c) 2020, Sergey Bugaev * * SPDX-License-Identifier: BSD-2-Clause */ #include #include namespace Kernel { Plan9FSInode::Plan9FSInode(Plan9FS& fs, u32 fid) : Inode(fs, fid) { } ErrorOr> Plan9FSInode::try_create(Plan9FS& fs, u32 fid) { return adopt_nonnull_ref_or_enomem(new (nothrow) Plan9FSInode(fs, fid)); } Plan9FSInode::~Plan9FSInode() { Plan9FSMessage clunk_request { fs(), Plan9FSMessage::Type::Tclunk }; clunk_request << fid(); // FIXME: Should we observe this error somehow? [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(clunk_request); } ErrorOr Plan9FSInode::ensure_open_for_mode(int mode) { bool use_lopen = fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L; u32 l_mode = 0; u8 p9_mode = 0; { MutexLocker locker(m_inode_lock); // If it's already open in this mode, we're done. if ((m_open_mode & mode) == mode) return {}; m_open_mode |= mode; if ((m_open_mode & O_RDWR) == O_RDWR) { l_mode |= 2; p9_mode |= 2; } else if (m_open_mode & O_WRONLY) { l_mode |= 1; p9_mode |= 1; } else if (m_open_mode & O_RDONLY) { // Leave the values at 0. } } if (use_lopen) { Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tlopen }; message << fid() << l_mode; return fs().post_message_and_wait_for_a_reply(message); } Plan9FSMessage message { fs(), Plan9FSMessage::Type::Topen }; message << fid() << p9_mode; return fs().post_message_and_wait_for_a_reply(message); } ErrorOr Plan9FSInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const { TRY(const_cast(*this).ensure_open_for_mode(O_RDONLY)); size = fs().adjust_buffer_size(size); Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treadlink }; StringView data; // Try readlink first. bool readlink_succeeded = false; if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L && offset == 0) { message << fid(); if (auto result = fs().post_message_and_wait_for_a_reply(message); !result.is_error()) { readlink_succeeded = true; message >> data; } } if (!readlink_succeeded) { message = Plan9FSMessage { fs(), Plan9FSMessage::Type::Tread }; message << fid() << (u64)offset << (u32)size; TRY(fs().post_message_and_wait_for_a_reply(message)); data = message.read_data(); } // Guard against the server returning more data than requested. size_t nread = min(data.length(), size); TRY(buffer.write(data.characters_without_null_termination(), nread)); return nread; } ErrorOr Plan9FSInode::replace_child(StringView, Inode&) { // TODO return ENOTIMPL; } ErrorOr Plan9FSInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& data, OpenFileDescription*) { TRY(ensure_open_for_mode(O_WRONLY)); size = fs().adjust_buffer_size(size); auto data_copy = TRY(data.try_copy_into_kstring(size)); // FIXME: this seems ugly Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twrite }; message << fid() << (u64)offset; TRY(message.append_data(data_copy->view())); TRY(fs().post_message_and_wait_for_a_reply(message)); u32 nwritten; message >> nwritten; return nwritten; } InodeMetadata Plan9FSInode::metadata() const { InodeMetadata metadata; metadata.inode = identifier(); // 9P2000.L; TODO: 9P2000 & 9P2000.u Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tgetattr }; message << fid() << (u64)GetAttrMask::Basic; auto result = fs().post_message_and_wait_for_a_reply(message); if (result.is_error()) { // Just return blank metadata; hopefully that's enough to result in an // error at some upper layer. Ideally, there would be a way for // Inode::metadata() to return failure. return metadata; } u64 valid; Plan9FSQIdentifier qid; u32 mode; u32 uid; u32 gid; u64 nlink; u64 rdev; u64 size; u64 blksize; u64 blocks; message >> valid >> qid >> mode >> uid >> gid >> nlink >> rdev >> size >> blksize >> blocks; // TODO: times... if (valid & (u64)GetAttrMask::Mode) metadata.mode = mode; if (valid & (u64)GetAttrMask::NLink) metadata.link_count = nlink; #if 0 // FIXME: Map UID/GID somehow? Or what do we do? if (valid & (u64)GetAttrMask::UID) metadata.uid = uid; if (valid & (u64)GetAttrMask::GID) metadata.uid = gid; // FIXME: What about device nodes? if (valid & (u64)GetAttrMask::RDev) metadata.encoded_device = 0; // TODO #endif if (valid & (u64)GetAttrMask::Size) metadata.size = size; if (valid & (u64)GetAttrMask::Blocks) { metadata.block_size = blksize; metadata.block_count = blocks; } return metadata; } ErrorOr Plan9FSInode::flush_metadata() { // Do nothing. return {}; } ErrorOr Plan9FSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const { // TODO: Should we synthesize "." and ".." here? if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) { // Start by cloning the fid and opening it. auto clone_fid = fs().allocate_fid(); { Plan9FSMessage clone_message { fs(), Plan9FSMessage::Type::Twalk }; clone_message << fid() << clone_fid << (u16)0; TRY(fs().post_message_and_wait_for_a_reply(clone_message)); Plan9FSMessage open_message { fs(), Plan9FSMessage::Type::Tlopen }; open_message << clone_fid << (u32)0; auto result = fs().post_message_and_wait_for_a_reply(open_message); if (result.is_error()) { Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk }; close_message << clone_fid; // FIXME: Should we observe this error? [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message); return result; } } u64 offset = 0; u32 count = fs().adjust_buffer_size(8 * MiB); ErrorOr result; while (true) { Plan9FSMessage message { fs(), Plan9FSMessage::Type::Treaddir }; message << clone_fid << offset << count; result = fs().post_message_and_wait_for_a_reply(message); if (result.is_error()) break; StringView data = message.read_data(); if (data.is_empty()) { // We've reached the end. break; } for (Plan9FSMessage::Decoder decoder { data }; decoder.has_more_data();) { Plan9FSQIdentifier qid; u8 type; StringView name; decoder >> qid >> offset >> type >> name; result = callback({ name, { fsid(), fs().allocate_fid() }, 0 }); if (result.is_error()) break; } if (result.is_error()) break; } Plan9FSMessage close_message { fs(), Plan9FSMessage::Type::Tclunk }; close_message << clone_fid; // FIXME: Should we observe this error? [[maybe_unused]] auto rc = fs().post_message_and_explicitly_ignore_reply(close_message); return result; } // TODO return ENOTIMPL; } ErrorOr> Plan9FSInode::lookup(StringView name) { u32 newfid = fs().allocate_fid(); Plan9FSMessage message { fs(), Plan9FSMessage::Type::Twalk }; message << fid() << newfid << (u16)1 << name; TRY(fs().post_message_and_wait_for_a_reply(message)); return TRY(Plan9FSInode::try_create(fs(), newfid)); } ErrorOr> Plan9FSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) { // TODO return ENOTIMPL; } ErrorOr Plan9FSInode::add_child(Inode&, StringView, mode_t) { // TODO return ENOTIMPL; } ErrorOr Plan9FSInode::remove_child(StringView) { // TODO return ENOTIMPL; } ErrorOr Plan9FSInode::chmod(mode_t) { // TODO return ENOTIMPL; } ErrorOr Plan9FSInode::chown(UserID, GroupID) { // TODO return ENOTIMPL; } ErrorOr Plan9FSInode::truncate(u64 new_size) { if (fs().m_remote_protocol_version >= Plan9FS::ProtocolVersion::v9P2000L) { Plan9FSMessage message { fs(), Plan9FSMessage::Type::Tsetattr }; SetAttrMask valid = SetAttrMask::Size; u32 mode = 0; u32 uid = 0; u32 gid = 0; u64 atime_sec = 0; u64 atime_nsec = 0; u64 mtime_sec = 0; u64 mtime_nsec = 0; message << fid() << (u64)valid << mode << uid << gid << new_size << atime_sec << atime_nsec << mtime_sec << mtime_nsec; return fs().post_message_and_wait_for_a_reply(message); } // TODO: wstat version return {}; } }