diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index c1adf0c0e5..c6b5413152 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -124,7 +124,9 @@ set(KERNEL_SOURCES FileSystem/ISO9660FileSystem.cpp FileSystem/Mount.cpp FileSystem/OpenFileDescription.cpp - FileSystem/Plan9FileSystem.cpp + FileSystem/Plan9FS/FileSystem.cpp + FileSystem/Plan9FS/Inode.cpp + FileSystem/Plan9FS/Message.cpp FileSystem/ProcFS/ComponentRegistry.cpp FileSystem/ProcFS/DirectoryInode.cpp FileSystem/ProcFS/FileSystem.cpp diff --git a/Kernel/FileSystem/Plan9FS/Definitions.h b/Kernel/FileSystem/Plan9FS/Definitions.h new file mode 100644 index 0000000000..e2220db3fd --- /dev/null +++ b/Kernel/FileSystem/Plan9FS/Definitions.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Kernel { + +struct Plan9FSQIdentifier { + u8 type; + u32 version; + u64 path; +}; + +} diff --git a/Kernel/FileSystem/Plan9FS/FileSystem.cpp b/Kernel/FileSystem/Plan9FS/FileSystem.cpp new file mode 100644 index 0000000000..0bed1411fe --- /dev/null +++ b/Kernel/FileSystem/Plan9FS/FileSystem.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel { + +ErrorOr> Plan9FS::try_create(OpenFileDescription& file_description) +{ + return TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Plan9FS(file_description))); +} + +Plan9FS::Plan9FS(OpenFileDescription& file_description) + : FileBackedFileSystem(file_description) + , m_completion_blocker(*this) +{ +} + +ErrorOr Plan9FS::prepare_to_clear_last_mount() +{ + // FIXME: Do proper cleaning here. + return {}; +} + +Plan9FS::~Plan9FS() +{ + // Make sure to destroy the root inode before the FS gets destroyed. + if (m_root_inode) { + VERIFY(m_root_inode->ref_count() == 1); + m_root_inode = nullptr; + } +} + +bool Plan9FS::is_initialized_while_locked() +{ + VERIFY(m_lock.is_locked()); + return !m_root_inode.is_null(); +} + +ErrorOr Plan9FS::initialize_while_locked() +{ + VERIFY(m_lock.is_locked()); + VERIFY(!is_initialized_while_locked()); + + ensure_thread(); + + Plan9FSMessage version_message { *this, Plan9FSMessage::Type::Tversion }; + version_message << (u32)m_max_message_size << "9P2000.L"sv; + + TRY(post_message_and_wait_for_a_reply(version_message)); + + u32 msize; + StringView remote_protocol_version; + version_message >> msize >> remote_protocol_version; + dbgln("Remote supports msize={} and protocol version {}", msize, remote_protocol_version); + m_remote_protocol_version = parse_protocol_version(remote_protocol_version); + m_max_message_size = min(m_max_message_size, (size_t)msize); + + // TODO: auth + + u32 root_fid = allocate_fid(); + Plan9FSMessage attach_message { *this, Plan9FSMessage::Type::Tattach }; + // FIXME: This needs a user name and an "export" name; but how do we get them? + // Perhaps initialize() should accept a string of FS-specific options... + attach_message << root_fid << (u32)-1 << "sergey"sv + << "/"sv; + if (m_remote_protocol_version >= ProtocolVersion::v9P2000u) + attach_message << (u32)-1; + + TRY(post_message_and_wait_for_a_reply(attach_message)); + m_root_inode = TRY(Plan9FSInode::try_create(*this, root_fid)); + return {}; +} + +Plan9FS::ProtocolVersion Plan9FS::parse_protocol_version(StringView s) const +{ + if (s == "9P2000.L") + return ProtocolVersion::v9P2000L; + if (s == "9P2000.u") + return ProtocolVersion::v9P2000u; + return ProtocolVersion::v9P2000; +} + +Inode& Plan9FS::root_inode() +{ + return *m_root_inode; +} + +Plan9FS::ReceiveCompletion::ReceiveCompletion(u16 tag) + : tag(tag) +{ +} + +Plan9FS::ReceiveCompletion::~ReceiveCompletion() = default; + +bool Plan9FS::Blocker::unblock(u16 tag) +{ + { + SpinlockLocker lock(m_lock); + if (m_did_unblock) + return false; + m_did_unblock = true; + + if (m_completion->tag != tag) + return false; + if (!m_completion->result.is_error()) + m_message = move(*m_completion->message); + } + return unblock(); +} + +bool Plan9FS::Blocker::setup_blocker() +{ + return add_to_blocker_set(m_fs.m_completion_blocker); +} + +void Plan9FS::Blocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason) +{ + { + SpinlockLocker lock(m_lock); + if (m_did_unblock) + return; + } + + m_fs.m_completion_blocker.try_unblock(*this); +} + +bool Plan9FS::Blocker::is_completed() const +{ + SpinlockLocker lock(m_completion->lock); + return m_completion->completed; +} + +bool Plan9FS::Plan9FSBlockerSet::should_add_blocker(Thread::Blocker& b, void*) +{ + // NOTE: m_lock is held already! + auto& blocker = static_cast(b); + return !blocker.is_completed(); +} + +void Plan9FS::Plan9FSBlockerSet::unblock_completed(u16 tag) +{ + unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { + VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); + auto& blocker = static_cast(b); + return blocker.unblock(tag); + }); +} + +void Plan9FS::Plan9FSBlockerSet::unblock_all() +{ + unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { + VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); + auto& blocker = static_cast(b); + return blocker.unblock(); + }); +} + +void Plan9FS::Plan9FSBlockerSet::try_unblock(Plan9FS::Blocker& blocker) +{ + if (m_fs.is_complete(*blocker.completion())) { + SpinlockLocker lock(m_lock); + blocker.unblock(blocker.completion()->tag); + } +} + +bool Plan9FS::is_complete(ReceiveCompletion const& completion) +{ + MutexLocker locker(m_lock); + if (m_completions.contains(completion.tag)) { + // If it's still in the map then it can't be complete + VERIFY(!completion.completed); + return false; + } + + // if it's not in the map anymore, it must be complete. But we MUST + // hold m_lock to be able to check completion.completed! + VERIFY(completion.completed); + return true; +} + +ErrorOr Plan9FS::post_message(Plan9FSMessage& message, LockRefPtr completion) +{ + auto const& buffer = message.build(); + u8 const* data = buffer.data(); + size_t size = buffer.size(); + auto& description = file_description(); + + MutexLocker locker(m_send_lock); + + if (completion) { + // Save the completion record *before* we send the message. This + // ensures that it exists when the thread reads the response + MutexLocker locker(m_lock); + auto tag = completion->tag; + m_completions.set(tag, completion.release_nonnull()); + // TODO: What if there is a collision? Do we need to wait until + // the existing record with the tag completes before queueing + // this one? + } + + while (size > 0) { + if (!description.can_write()) { + auto unblock_flags = Thread::FileBlocker::BlockFlags::None; + if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) + return EINTR; + } + auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast(data)); + auto nwritten = TRY(description.write(data_buffer, size)); + data += nwritten; + size -= nwritten; + } + + return {}; +} + +ErrorOr Plan9FS::do_read(u8* data, size_t size) +{ + auto& description = file_description(); + while (size > 0) { + if (!description.can_read()) { + auto unblock_flags = Thread::FileBlocker::BlockFlags::None; + if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) + return EINTR; + } + auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data); + auto nread = TRY(description.read(data_buffer, size)); + if (nread == 0) + return EIO; + data += nread; + size -= nread; + } + return {}; +} + +ErrorOr Plan9FS::read_and_dispatch_one_message() +{ + struct [[gnu::packed]] Header { + u32 size; + u8 type; + u16 tag; + }; + Header header; + TRY(do_read(reinterpret_cast(&header), sizeof(header))); + + auto buffer = TRY(KBuffer::try_create_with_size("Plan9FS: Plan9FSMessage read buffer"sv, header.size, Memory::Region::Access::ReadWrite)); + // Copy the already read header into the buffer. + memcpy(buffer->data(), &header, sizeof(header)); + TRY(do_read(buffer->data() + sizeof(header), header.size - sizeof(header))); + + MutexLocker locker(m_lock); + + auto optional_completion = m_completions.get(header.tag); + if (optional_completion.has_value()) { + auto* completion = optional_completion.value(); + SpinlockLocker lock(completion->lock); + completion->result = {}; + completion->message = adopt_own_if_nonnull(new (nothrow) Plan9FSMessage { move(buffer) }); + completion->completed = true; + + m_completions.remove(header.tag); + m_completion_blocker.unblock_completed(header.tag); + } else { + dbgln("Received a 9p message of type {} with an unexpected tag {}, dropping", header.type, header.tag); + } + + return {}; +} + +ErrorOr Plan9FS::post_message_and_explicitly_ignore_reply(Plan9FSMessage& message) +{ + return post_message(message, {}); +} + +ErrorOr Plan9FS::post_message_and_wait_for_a_reply(Plan9FSMessage& message) +{ + auto request_type = message.type(); + auto tag = message.tag(); + auto completion = adopt_lock_ref(*new ReceiveCompletion(tag)); + TRY(post_message(message, completion)); + if (Thread::current()->block({}, *this, message, completion).was_interrupted()) + return EINTR; + + if (completion->result.is_error()) { + dbgln("Plan9FS: Plan9FSMessage was aborted with error {}", completion->result.error()); + return EIO; + } + + auto reply_type = message.type(); + + if (reply_type == Plan9FSMessage::Type::Rlerror) { + // Contains a numerical Linux errno; hopefully our errno numbers match. + u32 error_code; + message >> error_code; + return Error::from_errno((ErrnoCode)error_code); + } + if (reply_type == Plan9FSMessage::Type::Rerror) { + // Contains an error message. We could attempt to parse it, but for now + // we simply return EIO instead. In 9P200.u, it can also contain a + // numerical errno in an unspecified encoding; we ignore those too. + StringView error_name; + message >> error_name; + dbgln("Plan9FS: Received error name {}", error_name); + return EIO; + } + if ((u8)reply_type != (u8)request_type + 1) { + // Other than those error messages. we only expect the matching reply + // message type. + dbgln("Plan9FS: Received unexpected message type {} in response to {}", (u8)reply_type, (u8)request_type); + return EIO; + } + + return {}; +} + +size_t Plan9FS::adjust_buffer_size(size_t size) const +{ + size_t max_size = m_max_message_size - Plan9FSMessage::max_header_size; + return min(size, max_size); +} + +void Plan9FS::thread_main() +{ + dbgln("Plan9FS: Thread running"); + do { + auto result = read_and_dispatch_one_message(); + if (result.is_error()) { + // If we fail to read, wake up everyone with an error. + MutexLocker locker(m_lock); + + for (auto& it : m_completions) { + it.value->result = result; + it.value->completed = true; + } + m_completions.clear(); + m_completion_blocker.unblock_all(); + dbgln("Plan9FS: Thread terminating, error reading"); + return; + } + } while (!m_thread_shutdown); + dbgln("Plan9FS: Thread terminating"); +} + +void Plan9FS::ensure_thread() +{ + SpinlockLocker lock(m_thread_lock); + if (!m_thread_running.exchange(true, AK::MemoryOrder::memory_order_acq_rel)) { + auto process_name = KString::try_create("Plan9FS"sv); + if (process_name.is_error()) + TODO(); + (void)Process::create_kernel_process(m_thread, process_name.release_value(), [&]() { + thread_main(); + m_thread_running.store(false, AK::MemoryOrder::memory_order_release); + }); + } +} + +} diff --git a/Kernel/FileSystem/Plan9FileSystem.h b/Kernel/FileSystem/Plan9FS/FileSystem.h similarity index 58% rename from Kernel/FileSystem/Plan9FileSystem.h rename to Kernel/FileSystem/Plan9FS/FileSystem.h index ac3c6ad126..1f5c4af322 100644 --- a/Kernel/FileSystem/Plan9FileSystem.h +++ b/Kernel/FileSystem/Plan9FS/FileSystem.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include namespace Kernel { @@ -35,14 +37,6 @@ public: v9P2000L }; - struct qid { - u8 type; - u32 version; - u64 path; - }; - - class Message; - private: Plan9FS(OpenFileDescription&); @@ -76,7 +70,7 @@ private: mutable Spinlock lock { LockRank::None }; bool completed { false }; const u16 tag; - OwnPtr message; + OwnPtr message; ErrorOr result; ReceiveCompletion(u16 tag); @@ -85,7 +79,7 @@ private: class Blocker final : public Thread::Blocker { public: - Blocker(Plan9FS& fs, Message& message, NonnullLockRefPtr completion) + Blocker(Plan9FS& fs, Plan9FSMessage& message, NonnullLockRefPtr completion) : m_fs(fs) , m_message(message) , m_completion(move(completion)) @@ -110,7 +104,7 @@ private: private: Plan9FS& m_fs; - Message& m_message; + Plan9FSMessage& m_message; NonnullLockRefPtr m_completion; bool m_did_unblock { false }; }; @@ -119,11 +113,11 @@ private: virtual StringView class_name() const override { return "Plan9FS"sv; } bool is_complete(ReceiveCompletion const&); - ErrorOr post_message(Message&, LockRefPtr); + ErrorOr post_message(Plan9FSMessage&, LockRefPtr); ErrorOr do_read(u8* buffer, size_t); ErrorOr read_and_dispatch_one_message(); - ErrorOr post_message_and_wait_for_a_reply(Message&); - ErrorOr post_message_and_explicitly_ignore_reply(Message&); + ErrorOr post_message_and_wait_for_a_reply(Plan9FSMessage&); + ErrorOr post_message_and_explicitly_ignore_reply(Plan9FSMessage&); ProtocolVersion parse_protocol_version(StringView) const; size_t adjust_buffer_size(size_t size) const; @@ -148,76 +142,4 @@ private: Atomic m_thread_shutdown { false }; }; -class Plan9FSInode final : public Inode { - friend class Plan9FS; - -public: - virtual ~Plan9FSInode() override; - - u32 fid() const { return index().value(); } - - // ^Inode - virtual InodeMetadata metadata() const override; - virtual ErrorOr flush_metadata() override; - virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; - virtual ErrorOr> lookup(StringView name) override; - virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; - virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; - virtual ErrorOr remove_child(StringView name) override; - virtual ErrorOr chmod(mode_t) override; - virtual ErrorOr chown(UserID, GroupID) override; - virtual ErrorOr truncate(u64) override; - -private: - // ^Inode - virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; - virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) override; - - Plan9FSInode(Plan9FS&, u32 fid); - static ErrorOr> try_create(Plan9FS&, u32 fid); - - enum class GetAttrMask : u64 { - Mode = 0x1, - NLink = 0x2, - UID = 0x4, - GID = 0x8, - RDev = 0x10, - ATime = 0x20, - MTime = 0x40, - CTime = 0x80, - Ino = 0x100, - Size = 0x200, - Blocks = 0x400, - - BTime = 0x800, - Gen = 0x1000, - DataVersion = 0x2000, - - Basic = 0x7ff, - All = 0x3fff - }; - - enum class SetAttrMask : u64 { - Mode = 0x1, - UID = 0x2, - GID = 0x4, - Size = 0x8, - ATime = 0x10, - MTime = 0x20, - CTime = 0x40, - ATimeSet = 0x80, - MTimeSet = 0x100 - }; - - // Mode in which the file is already open, using SerenityOS constants. - int m_open_mode { 0 }; - ErrorOr ensure_open_for_mode(int mode); - - Plan9FS& fs() { return reinterpret_cast(Inode::fs()); } - Plan9FS& fs() const - { - return const_cast(reinterpret_cast(Inode::fs())); - } -}; - } diff --git a/Kernel/FileSystem/Plan9FS/Inode.cpp b/Kernel/FileSystem/Plan9FS/Inode.cpp new file mode 100644 index 0000000000..4ca3566c2a --- /dev/null +++ b/Kernel/FileSystem/Plan9FS/Inode.cpp @@ -0,0 +1,301 @@ +/* + * 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_lock_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::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; + 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 {}; +} + +} diff --git a/Kernel/FileSystem/Plan9FS/Inode.h b/Kernel/FileSystem/Plan9FS/Inode.h new file mode 100644 index 0000000000..a44a57e143 --- /dev/null +++ b/Kernel/FileSystem/Plan9FS/Inode.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { + +class Plan9FSInode final : public Inode { + friend class Plan9FS; + +public: + virtual ~Plan9FSInode() override; + + u32 fid() const { return index().value(); } + + // ^Inode + virtual InodeMetadata metadata() const override; + virtual ErrorOr flush_metadata() override; + virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; + virtual ErrorOr> lookup(StringView name) override; + virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; + virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; + virtual ErrorOr remove_child(StringView name) override; + virtual ErrorOr chmod(mode_t) override; + virtual ErrorOr chown(UserID, GroupID) override; + virtual ErrorOr truncate(u64) override; + +private: + // ^Inode + virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; + virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) override; + + Plan9FSInode(Plan9FS&, u32 fid); + static ErrorOr> try_create(Plan9FS&, u32 fid); + + enum class GetAttrMask : u64 { + Mode = 0x1, + NLink = 0x2, + UID = 0x4, + GID = 0x8, + RDev = 0x10, + ATime = 0x20, + MTime = 0x40, + CTime = 0x80, + Ino = 0x100, + Size = 0x200, + Blocks = 0x400, + + BTime = 0x800, + Gen = 0x1000, + DataVersion = 0x2000, + + Basic = 0x7ff, + All = 0x3fff + }; + + enum class SetAttrMask : u64 { + Mode = 0x1, + UID = 0x2, + GID = 0x4, + Size = 0x8, + ATime = 0x10, + MTime = 0x20, + CTime = 0x40, + ATimeSet = 0x80, + MTimeSet = 0x100 + }; + + // Mode in which the file is already open, using SerenityOS constants. + int m_open_mode { 0 }; + ErrorOr ensure_open_for_mode(int mode); + + Plan9FS& fs() { return reinterpret_cast(Inode::fs()); } + Plan9FS& fs() const + { + return const_cast(reinterpret_cast(Inode::fs())); + } +}; + +} diff --git a/Kernel/FileSystem/Plan9FS/Message.cpp b/Kernel/FileSystem/Plan9FS/Message.cpp new file mode 100644 index 0000000000..733e6cef5d --- /dev/null +++ b/Kernel/FileSystem/Plan9FS/Message.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel { + +Plan9FSMessage& Plan9FSMessage::operator<<(u8 number) +{ + return append_number(number); +} + +Plan9FSMessage& Plan9FSMessage::operator<<(u16 number) +{ + return append_number(number); +} + +Plan9FSMessage& Plan9FSMessage::operator<<(u32 number) +{ + return append_number(number); +} + +Plan9FSMessage& Plan9FSMessage::operator<<(u64 number) +{ + return append_number(number); +} + +Plan9FSMessage& Plan9FSMessage::operator<<(StringView string) +{ + *this << static_cast(string.length()); + // FIXME: Handle append failure. + (void)m_builder.append(string); + return *this; +} + +void Plan9FSMessage::append_data(StringView data) +{ + *this << static_cast(data.length()); + // FIXME: Handle append failure. + (void)m_builder.append(data); +} + +Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u8& number) +{ + return read_number(number); +} + +Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u16& number) +{ + return read_number(number); +} + +Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u32& number) +{ + return read_number(number); +} + +Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(u64& number) +{ + return read_number(number); +} + +Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(Plan9FSQIdentifier& qid) +{ + return *this >> qid.type >> qid.version >> qid.path; +} + +Plan9FSMessage::Decoder& Plan9FSMessage::Decoder::operator>>(StringView& string) +{ + u16 length; + *this >> length; + VERIFY(length <= m_data.length()); + string = m_data.substring_view(0, length); + m_data = m_data.substring_view_starting_after_substring(string); + return *this; +} + +StringView Plan9FSMessage::Decoder::read_data() +{ + u32 length; + *this >> length; + VERIFY(length <= m_data.length()); + auto data = m_data.substring_view(0, length); + m_data = m_data.substring_view_starting_after_substring(data); + return data; +} + +Plan9FSMessage::Plan9FSMessage(Plan9FS& fs, Type type) + : m_builder(KBufferBuilder::try_create().release_value()) // FIXME: Don't assume KBufferBuilder allocation success. + , m_tag(fs.allocate_tag()) + , m_type(type) + , m_have_been_built(false) +{ + u32 size_placeholder = 0; + *this << size_placeholder << (u8)type << m_tag; +} + +Plan9FSMessage::Plan9FSMessage(NonnullOwnPtr&& buffer) + : m_built { move(buffer), Decoder({ buffer->bytes() }) } + , m_have_been_built(true) +{ + u32 size; + u8 raw_type; + *this >> size >> raw_type >> m_tag; + m_type = (Type)raw_type; +} + +Plan9FSMessage::~Plan9FSMessage() +{ + if (m_have_been_built) { + m_built.buffer.~NonnullOwnPtr(); + m_built.decoder.~Decoder(); + } else { + m_builder.~KBufferBuilder(); + } +} + +Plan9FSMessage& Plan9FSMessage::operator=(Plan9FSMessage&& message) +{ + m_tag = message.m_tag; + m_type = message.m_type; + + if (m_have_been_built) { + m_built.buffer.~NonnullOwnPtr(); + m_built.decoder.~Decoder(); + } else { + m_builder.~KBufferBuilder(); + } + + m_have_been_built = message.m_have_been_built; + if (m_have_been_built) { + new (&m_built.buffer) NonnullOwnPtr(move(message.m_built.buffer)); + new (&m_built.decoder) Decoder(move(message.m_built.decoder)); + } else { + new (&m_builder) KBufferBuilder(move(message.m_builder)); + } + + return *this; +} + +KBuffer const& Plan9FSMessage::build() +{ + VERIFY(!m_have_been_built); + + auto tmp_buffer = m_builder.build(); + + // FIXME: We should not assume success here. + VERIFY(tmp_buffer); + + m_have_been_built = true; + m_builder.~KBufferBuilder(); + + new (&m_built.buffer) NonnullOwnPtr(tmp_buffer.release_nonnull()); + new (&m_built.decoder) Decoder({ m_built.buffer->data(), m_built.buffer->size() }); + u32* size = reinterpret_cast(m_built.buffer->data()); + *size = m_built.buffer->size(); + return *m_built.buffer; +} + +} diff --git a/Kernel/FileSystem/Plan9FS/Message.h b/Kernel/FileSystem/Plan9FS/Message.h new file mode 100644 index 0000000000..d5e178a0d0 --- /dev/null +++ b/Kernel/FileSystem/Plan9FS/Message.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class Plan9FS; +class Plan9FSInode; + +class Plan9FSMessage { + friend class Plan9FS; + friend class Plan9FSInode; + +public: + enum class Type : u8 { + // 9P2000.L + Tlerror = 6, + Rlerror = 7, + Tstatfs = 8, + Rstatfs = 9, + + Tlopen = 12, + Rlopen = 13, + Tlcreate = 14, + Rlcreate = 15, + Tsymlink = 16, + Rsymlink = 17, + Tmknod = 18, + Rmknod = 19, + Trename = 20, + Rrename = 21, + Treadlink = 22, + Rreadlink = 23, + Tgetattr = 24, + Rgetattr = 25, + Tsetattr = 26, + Rsetattr = 27, + + Txattrwalk = 30, + Rxattrwalk = 31, + Txattrcreate = 32, + Rxattrcreate = 33, + + Treaddir = 40, + Rreaddir = 41, + + Tfsync = 50, + Rfsync = 51, + Tlock = 52, + Rlock = 53, + Tgetlock = 54, + Rgetlock = 55, + + Tlink = 70, + Rlink = 71, + Tmkdir = 72, + Rmkdir = 73, + Trenameat = 74, + Rrenameat = 75, + Tunlinkat = 76, + Runlinkat = 77, + + // 9P2000 + Tversion = 100, + Rversion = 101, + Tauth = 102, + Rauth = 103, + Tattach = 104, + Rattach = 105, + Terror = 106, + Rerror = 107, + Tflush = 108, + Rflush = 109, + Twalk = 110, + Rwalk = 111, + Topen = 112, + Ropen = 113, + Tcreate = 114, + Rcreate = 115, + Tread = 116, + Rread = 117, + Twrite = 118, + Rwrite = 119, + Tclunk = 120, + Rclunk = 121, + Tremove = 122, + Rremove = 123, + Tstat = 124, + Rstat = 125, + Twstat = 126, + Rwstat = 127 + }; + + class Decoder { + public: + explicit Decoder(StringView data) + : m_data(data) + { + } + + Decoder& operator>>(u8&); + Decoder& operator>>(u16&); + Decoder& operator>>(u32&); + Decoder& operator>>(u64&); + Decoder& operator>>(StringView&); + Decoder& operator>>(Plan9FSQIdentifier&); + StringView read_data(); + + bool has_more_data() const { return !m_data.is_empty(); } + + private: + StringView m_data; + + template + Decoder& read_number(N& number) + { + VERIFY(sizeof(number) <= m_data.length()); + memcpy(&number, m_data.characters_without_null_termination(), sizeof(number)); + m_data = m_data.substring_view(sizeof(number), m_data.length() - sizeof(number)); + return *this; + } + }; + + Plan9FSMessage& operator<<(u8); + Plan9FSMessage& operator<<(u16); + Plan9FSMessage& operator<<(u32); + Plan9FSMessage& operator<<(u64); + Plan9FSMessage& operator<<(StringView); + void append_data(StringView); + + template + Plan9FSMessage& operator>>(T& t) + { + VERIFY(m_have_been_built); + m_built.decoder >> t; + return *this; + } + + StringView read_data() + { + VERIFY(m_have_been_built); + return m_built.decoder.read_data(); + } + + Type type() const { return m_type; } + u16 tag() const { return m_tag; } + + Plan9FSMessage(Plan9FS&, Type); + Plan9FSMessage(NonnullOwnPtr&&); + ~Plan9FSMessage(); + Plan9FSMessage& operator=(Plan9FSMessage&&); + + KBuffer const& build(); + + static constexpr size_t max_header_size = 24; + +private: + template + Plan9FSMessage& append_number(N number) + { + VERIFY(!m_have_been_built); + // FIXME: Handle append failure. + (void)m_builder.append(reinterpret_cast(&number), sizeof(number)); + return *this; + } + + union { + KBufferBuilder m_builder; + struct { + NonnullOwnPtr buffer; + Decoder decoder; + } m_built; + }; + + u16 m_tag { 0 }; + Type m_type { 0 }; + bool m_have_been_built { false }; +}; + +} diff --git a/Kernel/FileSystem/Plan9FileSystem.cpp b/Kernel/FileSystem/Plan9FileSystem.cpp deleted file mode 100644 index be2f4f18fb..0000000000 --- a/Kernel/FileSystem/Plan9FileSystem.cpp +++ /dev/null @@ -1,970 +0,0 @@ -/* - * Copyright (c) 2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -ErrorOr> Plan9FS::try_create(OpenFileDescription& file_description) -{ - return TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Plan9FS(file_description))); -} - -Plan9FS::Plan9FS(OpenFileDescription& file_description) - : FileBackedFileSystem(file_description) - , m_completion_blocker(*this) -{ -} - -ErrorOr Plan9FS::prepare_to_clear_last_mount() -{ - // FIXME: Do proper cleaning here. - return {}; -} - -Plan9FS::~Plan9FS() -{ - // Make sure to destroy the root inode before the FS gets destroyed. - if (m_root_inode) { - VERIFY(m_root_inode->ref_count() == 1); - m_root_inode = nullptr; - } -} - -class Plan9FS::Message { -public: - enum class Type : u8 { - // 9P2000.L - Tlerror = 6, - Rlerror = 7, - Tstatfs = 8, - Rstatfs = 9, - - Tlopen = 12, - Rlopen = 13, - Tlcreate = 14, - Rlcreate = 15, - Tsymlink = 16, - Rsymlink = 17, - Tmknod = 18, - Rmknod = 19, - Trename = 20, - Rrename = 21, - Treadlink = 22, - Rreadlink = 23, - Tgetattr = 24, - Rgetattr = 25, - Tsetattr = 26, - Rsetattr = 27, - - Txattrwalk = 30, - Rxattrwalk = 31, - Txattrcreate = 32, - Rxattrcreate = 33, - - Treaddir = 40, - Rreaddir = 41, - - Tfsync = 50, - Rfsync = 51, - Tlock = 52, - Rlock = 53, - Tgetlock = 54, - Rgetlock = 55, - - Tlink = 70, - Rlink = 71, - Tmkdir = 72, - Rmkdir = 73, - Trenameat = 74, - Rrenameat = 75, - Tunlinkat = 76, - Runlinkat = 77, - - // 9P2000 - Tversion = 100, - Rversion = 101, - Tauth = 102, - Rauth = 103, - Tattach = 104, - Rattach = 105, - Terror = 106, - Rerror = 107, - Tflush = 108, - Rflush = 109, - Twalk = 110, - Rwalk = 111, - Topen = 112, - Ropen = 113, - Tcreate = 114, - Rcreate = 115, - Tread = 116, - Rread = 117, - Twrite = 118, - Rwrite = 119, - Tclunk = 120, - Rclunk = 121, - Tremove = 122, - Rremove = 123, - Tstat = 124, - Rstat = 125, - Twstat = 126, - Rwstat = 127 - }; - - class Decoder { - public: - explicit Decoder(StringView data) - : m_data(data) - { - } - - Decoder& operator>>(u8&); - Decoder& operator>>(u16&); - Decoder& operator>>(u32&); - Decoder& operator>>(u64&); - Decoder& operator>>(StringView&); - Decoder& operator>>(qid&); - StringView read_data(); - - bool has_more_data() const { return !m_data.is_empty(); } - - private: - StringView m_data; - - template - Decoder& read_number(N& number) - { - VERIFY(sizeof(number) <= m_data.length()); - memcpy(&number, m_data.characters_without_null_termination(), sizeof(number)); - m_data = m_data.substring_view(sizeof(number), m_data.length() - sizeof(number)); - return *this; - } - }; - - Message& operator<<(u8); - Message& operator<<(u16); - Message& operator<<(u32); - Message& operator<<(u64); - Message& operator<<(StringView); - void append_data(StringView); - - template - Message& operator>>(T& t) - { - VERIFY(m_have_been_built); - m_built.decoder >> t; - return *this; - } - - StringView read_data() - { - VERIFY(m_have_been_built); - return m_built.decoder.read_data(); - } - - Type type() const { return m_type; } - u16 tag() const { return m_tag; } - - Message(Plan9FS&, Type); - Message(NonnullOwnPtr&&); - ~Message(); - Message& operator=(Message&&); - - KBuffer const& build(); - - static constexpr size_t max_header_size = 24; - -private: - template - Message& append_number(N number) - { - VERIFY(!m_have_been_built); - // FIXME: Handle append failure. - (void)m_builder.append(reinterpret_cast(&number), sizeof(number)); - return *this; - } - - union { - KBufferBuilder m_builder; - struct { - NonnullOwnPtr buffer; - Decoder decoder; - } m_built; - }; - - u16 m_tag { 0 }; - Type m_type { 0 }; - bool m_have_been_built { false }; -}; - -bool Plan9FS::is_initialized_while_locked() -{ - VERIFY(m_lock.is_locked()); - return !m_root_inode.is_null(); -} - -ErrorOr Plan9FS::initialize_while_locked() -{ - VERIFY(m_lock.is_locked()); - VERIFY(!is_initialized_while_locked()); - - ensure_thread(); - - Message version_message { *this, Message::Type::Tversion }; - version_message << (u32)m_max_message_size << "9P2000.L"sv; - - TRY(post_message_and_wait_for_a_reply(version_message)); - - u32 msize; - StringView remote_protocol_version; - version_message >> msize >> remote_protocol_version; - dbgln("Remote supports msize={} and protocol version {}", msize, remote_protocol_version); - m_remote_protocol_version = parse_protocol_version(remote_protocol_version); - m_max_message_size = min(m_max_message_size, (size_t)msize); - - // TODO: auth - - u32 root_fid = allocate_fid(); - Message attach_message { *this, Message::Type::Tattach }; - // FIXME: This needs a user name and an "export" name; but how do we get them? - // Perhaps initialize() should accept a string of FS-specific options... - attach_message << root_fid << (u32)-1 << "sergey"sv - << "/"sv; - if (m_remote_protocol_version >= ProtocolVersion::v9P2000u) - attach_message << (u32)-1; - - TRY(post_message_and_wait_for_a_reply(attach_message)); - m_root_inode = TRY(Plan9FSInode::try_create(*this, root_fid)); - return {}; -} - -Plan9FS::ProtocolVersion Plan9FS::parse_protocol_version(StringView s) const -{ - if (s == "9P2000.L") - return ProtocolVersion::v9P2000L; - if (s == "9P2000.u") - return ProtocolVersion::v9P2000u; - return ProtocolVersion::v9P2000; -} - -Inode& Plan9FS::root_inode() -{ - return *m_root_inode; -} - -Plan9FS::Message& Plan9FS::Message::operator<<(u8 number) -{ - return append_number(number); -} - -Plan9FS::Message& Plan9FS::Message::operator<<(u16 number) -{ - return append_number(number); -} - -Plan9FS::Message& Plan9FS::Message::operator<<(u32 number) -{ - return append_number(number); -} - -Plan9FS::Message& Plan9FS::Message::operator<<(u64 number) -{ - return append_number(number); -} - -Plan9FS::Message& Plan9FS::Message::operator<<(StringView string) -{ - *this << static_cast(string.length()); - // FIXME: Handle append failure. - (void)m_builder.append(string); - return *this; -} - -void Plan9FS::Message::append_data(StringView data) -{ - *this << static_cast(data.length()); - // FIXME: Handle append failure. - (void)m_builder.append(data); -} - -Plan9FS::Message::Decoder& Plan9FS::Message::Decoder::operator>>(u8& number) -{ - return read_number(number); -} - -Plan9FS::Message::Decoder& Plan9FS::Message::Decoder::operator>>(u16& number) -{ - return read_number(number); -} - -Plan9FS::Message::Decoder& Plan9FS::Message::Decoder::operator>>(u32& number) -{ - return read_number(number); -} - -Plan9FS::Message::Decoder& Plan9FS::Message::Decoder::operator>>(u64& number) -{ - return read_number(number); -} - -Plan9FS::Message::Decoder& Plan9FS::Message::Decoder::operator>>(qid& qid) -{ - return *this >> qid.type >> qid.version >> qid.path; -} - -Plan9FS::Message::Decoder& Plan9FS::Message::Decoder::operator>>(StringView& string) -{ - u16 length; - *this >> length; - VERIFY(length <= m_data.length()); - string = m_data.substring_view(0, length); - m_data = m_data.substring_view_starting_after_substring(string); - return *this; -} - -StringView Plan9FS::Message::Decoder::read_data() -{ - u32 length; - *this >> length; - VERIFY(length <= m_data.length()); - auto data = m_data.substring_view(0, length); - m_data = m_data.substring_view_starting_after_substring(data); - return data; -} - -Plan9FS::Message::Message(Plan9FS& fs, Type type) - : m_builder(KBufferBuilder::try_create().release_value()) // FIXME: Don't assume KBufferBuilder allocation success. - , m_tag(fs.allocate_tag()) - , m_type(type) - , m_have_been_built(false) -{ - u32 size_placeholder = 0; - *this << size_placeholder << (u8)type << m_tag; -} - -Plan9FS::Message::Message(NonnullOwnPtr&& buffer) - : m_built { move(buffer), Decoder({ buffer->bytes() }) } - , m_have_been_built(true) -{ - u32 size; - u8 raw_type; - *this >> size >> raw_type >> m_tag; - m_type = (Type)raw_type; -} - -Plan9FS::Message::~Message() -{ - if (m_have_been_built) { - m_built.buffer.~NonnullOwnPtr(); - m_built.decoder.~Decoder(); - } else { - m_builder.~KBufferBuilder(); - } -} - -Plan9FS::Message& Plan9FS::Message::operator=(Message&& message) -{ - m_tag = message.m_tag; - m_type = message.m_type; - - if (m_have_been_built) { - m_built.buffer.~NonnullOwnPtr(); - m_built.decoder.~Decoder(); - } else { - m_builder.~KBufferBuilder(); - } - - m_have_been_built = message.m_have_been_built; - if (m_have_been_built) { - new (&m_built.buffer) NonnullOwnPtr(move(message.m_built.buffer)); - new (&m_built.decoder) Decoder(move(message.m_built.decoder)); - } else { - new (&m_builder) KBufferBuilder(move(message.m_builder)); - } - - return *this; -} - -KBuffer const& Plan9FS::Message::build() -{ - VERIFY(!m_have_been_built); - - auto tmp_buffer = m_builder.build(); - - // FIXME: We should not assume success here. - VERIFY(tmp_buffer); - - m_have_been_built = true; - m_builder.~KBufferBuilder(); - - new (&m_built.buffer) NonnullOwnPtr(tmp_buffer.release_nonnull()); - new (&m_built.decoder) Decoder({ m_built.buffer->data(), m_built.buffer->size() }); - u32* size = reinterpret_cast(m_built.buffer->data()); - *size = m_built.buffer->size(); - return *m_built.buffer; -} - -Plan9FS::ReceiveCompletion::ReceiveCompletion(u16 tag) - : tag(tag) -{ -} - -Plan9FS::ReceiveCompletion::~ReceiveCompletion() = default; - -bool Plan9FS::Blocker::unblock(u16 tag) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return false; - m_did_unblock = true; - - if (m_completion->tag != tag) - return false; - if (!m_completion->result.is_error()) - m_message = move(*m_completion->message); - } - return unblock(); -} - -bool Plan9FS::Blocker::setup_blocker() -{ - return add_to_blocker_set(m_fs.m_completion_blocker); -} - -void Plan9FS::Blocker::will_unblock_immediately_without_blocking(UnblockImmediatelyReason) -{ - { - SpinlockLocker lock(m_lock); - if (m_did_unblock) - return; - } - - m_fs.m_completion_blocker.try_unblock(*this); -} - -bool Plan9FS::Blocker::is_completed() const -{ - SpinlockLocker lock(m_completion->lock); - return m_completion->completed; -} - -bool Plan9FS::Plan9FSBlockerSet::should_add_blocker(Thread::Blocker& b, void*) -{ - // NOTE: m_lock is held already! - auto& blocker = static_cast(b); - return !blocker.is_completed(); -} - -void Plan9FS::Plan9FSBlockerSet::unblock_completed(u16 tag) -{ - unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); - auto& blocker = static_cast(b); - return blocker.unblock(tag); - }); -} - -void Plan9FS::Plan9FSBlockerSet::unblock_all() -{ - unblock_all_blockers_whose_conditions_are_met([&](Thread::Blocker& b, void*, bool&) { - VERIFY(b.blocker_type() == Thread::Blocker::Type::Plan9FS); - auto& blocker = static_cast(b); - return blocker.unblock(); - }); -} - -void Plan9FS::Plan9FSBlockerSet::try_unblock(Plan9FS::Blocker& blocker) -{ - if (m_fs.is_complete(*blocker.completion())) { - SpinlockLocker lock(m_lock); - blocker.unblock(blocker.completion()->tag); - } -} - -bool Plan9FS::is_complete(ReceiveCompletion const& completion) -{ - MutexLocker locker(m_lock); - if (m_completions.contains(completion.tag)) { - // If it's still in the map then it can't be complete - VERIFY(!completion.completed); - return false; - } - - // if it's not in the map anymore, it must be complete. But we MUST - // hold m_lock to be able to check completion.completed! - VERIFY(completion.completed); - return true; -} - -ErrorOr Plan9FS::post_message(Message& message, LockRefPtr completion) -{ - auto const& buffer = message.build(); - u8 const* data = buffer.data(); - size_t size = buffer.size(); - auto& description = file_description(); - - MutexLocker locker(m_send_lock); - - if (completion) { - // Save the completion record *before* we send the message. This - // ensures that it exists when the thread reads the response - MutexLocker locker(m_lock); - auto tag = completion->tag; - m_completions.set(tag, completion.release_nonnull()); - // TODO: What if there is a collision? Do we need to wait until - // the existing record with the tag completes before queueing - // this one? - } - - while (size > 0) { - if (!description.can_write()) { - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) - return EINTR; - } - auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(const_cast(data)); - auto nwritten = TRY(description.write(data_buffer, size)); - data += nwritten; - size -= nwritten; - } - - return {}; -} - -ErrorOr Plan9FS::do_read(u8* data, size_t size) -{ - auto& description = file_description(); - while (size > 0) { - if (!description.can_read()) { - auto unblock_flags = Thread::FileBlocker::BlockFlags::None; - if (Thread::current()->block({}, description, unblock_flags).was_interrupted()) - return EINTR; - } - auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data); - auto nread = TRY(description.read(data_buffer, size)); - if (nread == 0) - return EIO; - data += nread; - size -= nread; - } - return {}; -} - -ErrorOr Plan9FS::read_and_dispatch_one_message() -{ - struct [[gnu::packed]] Header { - u32 size; - u8 type; - u16 tag; - }; - Header header; - TRY(do_read(reinterpret_cast(&header), sizeof(header))); - - auto buffer = TRY(KBuffer::try_create_with_size("Plan9FS: Message read buffer"sv, header.size, Memory::Region::Access::ReadWrite)); - // Copy the already read header into the buffer. - memcpy(buffer->data(), &header, sizeof(header)); - TRY(do_read(buffer->data() + sizeof(header), header.size - sizeof(header))); - - MutexLocker locker(m_lock); - - auto optional_completion = m_completions.get(header.tag); - if (optional_completion.has_value()) { - auto* completion = optional_completion.value(); - SpinlockLocker lock(completion->lock); - completion->result = {}; - completion->message = adopt_own_if_nonnull(new (nothrow) Message { move(buffer) }); - completion->completed = true; - - m_completions.remove(header.tag); - m_completion_blocker.unblock_completed(header.tag); - } else { - dbgln("Received a 9p message of type {} with an unexpected tag {}, dropping", header.type, header.tag); - } - - return {}; -} - -ErrorOr Plan9FS::post_message_and_explicitly_ignore_reply(Message& message) -{ - return post_message(message, {}); -} - -ErrorOr Plan9FS::post_message_and_wait_for_a_reply(Message& message) -{ - auto request_type = message.type(); - auto tag = message.tag(); - auto completion = adopt_lock_ref(*new ReceiveCompletion(tag)); - TRY(post_message(message, completion)); - if (Thread::current()->block({}, *this, message, completion).was_interrupted()) - return EINTR; - - if (completion->result.is_error()) { - dbgln("Plan9FS: Message was aborted with error {}", completion->result.error()); - return EIO; - } - - auto reply_type = message.type(); - - if (reply_type == Message::Type::Rlerror) { - // Contains a numerical Linux errno; hopefully our errno numbers match. - u32 error_code; - message >> error_code; - return Error::from_errno((ErrnoCode)error_code); - } - if (reply_type == Message::Type::Rerror) { - // Contains an error message. We could attempt to parse it, but for now - // we simply return EIO instead. In 9P200.u, it can also contain a - // numerical errno in an unspecified encoding; we ignore those too. - StringView error_name; - message >> error_name; - dbgln("Plan9FS: Received error name {}", error_name); - return EIO; - } - if ((u8)reply_type != (u8)request_type + 1) { - // Other than those error messages. we only expect the matching reply - // message type. - dbgln("Plan9FS: Received unexpected message type {} in response to {}", (u8)reply_type, (u8)request_type); - return EIO; - } - - return {}; -} - -size_t Plan9FS::adjust_buffer_size(size_t size) const -{ - size_t max_size = m_max_message_size - Message::max_header_size; - return min(size, max_size); -} - -void Plan9FS::thread_main() -{ - dbgln("Plan9FS: Thread running"); - do { - auto result = read_and_dispatch_one_message(); - if (result.is_error()) { - // If we fail to read, wake up everyone with an error. - MutexLocker locker(m_lock); - - for (auto& it : m_completions) { - it.value->result = result; - it.value->completed = true; - } - m_completions.clear(); - m_completion_blocker.unblock_all(); - dbgln("Plan9FS: Thread terminating, error reading"); - return; - } - } while (!m_thread_shutdown); - dbgln("Plan9FS: Thread terminating"); -} - -void Plan9FS::ensure_thread() -{ - SpinlockLocker lock(m_thread_lock); - if (!m_thread_running.exchange(true, AK::MemoryOrder::memory_order_acq_rel)) { - auto process_name = KString::try_create("Plan9FS"sv); - if (process_name.is_error()) - TODO(); - (void)Process::create_kernel_process(m_thread, process_name.release_value(), [&]() { - thread_main(); - m_thread_running.store(false, AK::MemoryOrder::memory_order_release); - }); - } -} - -Plan9FSInode::Plan9FSInode(Plan9FS& fs, u32 fid) - : Inode(fs, fid) -{ -} - -ErrorOr> Plan9FSInode::try_create(Plan9FS& fs, u32 fid) -{ - return adopt_nonnull_lock_ref_or_enomem(new (nothrow) Plan9FSInode(fs, fid)); -} - -Plan9FSInode::~Plan9FSInode() -{ - Plan9FS::Message clunk_request { fs(), Plan9FS::Message::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) { - Plan9FS::Message message { fs(), Plan9FS::Message::Type::Tlopen }; - message << fid() << l_mode; - return fs().post_message_and_wait_for_a_reply(message); - } - - Plan9FS::Message message { fs(), Plan9FS::Message::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); - - Plan9FS::Message message { fs(), Plan9FS::Message::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 = Plan9FS::Message { fs(), Plan9FS::Message::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::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 - - Plan9FS::Message message { fs(), Plan9FS::Message::Type::Twrite }; - message << fid() << (u64)offset; - 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 - Plan9FS::Message message { fs(), Plan9FS::Message::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; - Plan9FS::qid 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(); - { - Plan9FS::Message clone_message { fs(), Plan9FS::Message::Type::Twalk }; - clone_message << fid() << clone_fid << (u16)0; - TRY(fs().post_message_and_wait_for_a_reply(clone_message)); - Plan9FS::Message open_message { fs(), Plan9FS::Message::Type::Tlopen }; - open_message << clone_fid << (u32)0; - auto result = fs().post_message_and_wait_for_a_reply(open_message); - if (result.is_error()) { - Plan9FS::Message close_message { fs(), Plan9FS::Message::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) { - Plan9FS::Message message { fs(), Plan9FS::Message::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 (Plan9FS::Message::Decoder decoder { data }; decoder.has_more_data();) { - Plan9FS::qid 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; - } - - Plan9FS::Message close_message { fs(), Plan9FS::Message::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(); - Plan9FS::Message message { fs(), Plan9FS::Message::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) { - Plan9FS::Message message { fs(), Plan9FS::Message::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 {}; -} - -} diff --git a/Kernel/Syscalls/mount.cpp b/Kernel/Syscalls/mount.cpp index 41828fd178..5f79020eec 100644 --- a/Kernel/Syscalls/mount.cpp +++ b/Kernel/Syscalls/mount.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include