/* * Copyright (c) 2020, Sergey Bugaev * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace Kernel { class Plan9FSInode; class Plan9FS final : public FileBackedFS { friend class Plan9FSInode; public: virtual ~Plan9FS() override; static NonnullRefPtr create(FileDescription&); virtual bool initialize() override; virtual bool supports_watchers() const override { return false; } virtual NonnullRefPtr root_inode() const override; u16 allocate_tag() { return m_next_tag++; } u32 allocate_fid() { return m_next_fid++; } enum class ProtocolVersion { v9P2000, v9P2000u, v9P2000L }; struct qid { u8 type; u32 version; u64 path; }; class Message; private: Plan9FS(FileDescription&); class Blocker; class Plan9FSBlockCondition : public Thread::BlockCondition { public: Plan9FSBlockCondition(Plan9FS& fs) : m_fs(fs) { } void unblock_completed(u16); void unblock_all(); void try_unblock(Blocker&); protected: virtual bool should_add_blocker(Thread::Blocker&, void*) override; private: Plan9FS& m_fs; mutable SpinLock m_lock; }; struct ReceiveCompletion : public RefCounted { mutable SpinLock lock; bool completed { false }; const u16 tag; OwnPtr message; KResult result { KSuccess }; ReceiveCompletion(u16 tag); ~ReceiveCompletion(); }; class Blocker final : public Thread::Blocker { public: Blocker(Plan9FS& fs, Message& message, NonnullRefPtr completion) : m_fs(fs) , m_message(message) , m_completion(move(completion)) { set_block_condition(fs.m_completion_blocker); } virtual const char* state_string() const override { return "Waiting"; } virtual Type blocker_type() const override { return Type::Plan9FS; } virtual void not_blocking(bool) override; const NonnullRefPtr& completion() const { return m_completion; } u16 tag() const { return m_completion->tag; } bool is_completed() const; bool unblock() { unblock_from_blocker(); return true; } bool unblock(u16 tag); private: Plan9FS& m_fs; Message& m_message; NonnullRefPtr m_completion; bool m_did_unblock { false }; }; friend class Blocker; virtual const char* class_name() const override { return "Plan9FS"; } bool is_complete(const ReceiveCompletion&); KResult post_message(Message&, RefPtr); KResult do_read(u8* buffer, size_t); KResult read_and_dispatch_one_message(); KResult post_message_and_wait_for_a_reply(Message&); KResult post_message_and_explicitly_ignore_reply(Message&); ProtocolVersion parse_protocol_version(const StringView&) const; ssize_t adjust_buffer_size(ssize_t size) const; void thread_main(); void ensure_thread(); RefPtr m_root_inode; Atomic m_next_tag { (u16)-1 }; Atomic m_next_fid { 1 }; ProtocolVersion m_remote_protocol_version { ProtocolVersion::v9P2000 }; size_t m_max_message_size { 4 * KiB }; Lock m_send_lock { "Plan9FS send" }; Plan9FSBlockCondition m_completion_blocker; HashMap> m_completions; SpinLock m_thread_lock; RefPtr m_thread; Atomic m_thread_running { false }; 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 void flush_metadata() override; virtual KResultOr read_bytes(off_t, ssize_t, UserOrKernelBuffer& buffer, FileDescription*) const override; virtual KResultOr write_bytes(off_t, ssize_t, const UserOrKernelBuffer& data, FileDescription*) override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual KResultOr> create_child(const String& name, mode_t, dev_t, uid_t, gid_t) override; virtual KResult add_child(Inode&, const StringView& name, mode_t) override; virtual KResult remove_child(const StringView& name) override; virtual KResultOr directory_entry_count() const override; virtual KResult chmod(mode_t) override; virtual KResult chown(uid_t, gid_t) override; virtual KResult truncate(u64) override; private: Plan9FSInode(Plan9FS&, u32 fid); static NonnullRefPtr 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 }; KResult ensure_open_for_mode(int mode); Plan9FS& fs() { return reinterpret_cast(Inode::fs()); } Plan9FS& fs() const { return const_cast(reinterpret_cast(Inode::fs())); } }; }