From 12b6e6915034a0c0d1981caf419a35c0a8cd247e Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 12 Jun 2021 04:23:58 +0300 Subject: [PATCH] Kernel: Introduce the new ProcFS design The new ProcFS design consists of two main parts: 1. The representative ProcFS class, which is derived from the FS class. The ProcFS and its inodes are much more lean - merely 3 classes to represent the common type of inodes - regular files, symbolic links and directories. They're backed by a ProcFSExposedComponent object, which is responsible for the functional operation behind the scenes. 2. The backend of the ProcFS - the ProcFSComponentsRegistrar class and all derived classes from the ProcFSExposedComponent class. These together form the entire backend and handle all the functions you can expect from the ProcFS. The ProcFSExposedComponent derived classes split to 3 types in the manner of lifetime in the kernel: 1. Persistent objects - this category includes all basic objects, like the root folder, /proc/bus folder, main blob files in the root folders, etc. These objects are persistent and cannot die ever. 2. Semi-persistent objects - this category includes all PID folders, and subdirectories to the PID folders. It also includes exposed objects like the unveil JSON'ed blob. These object are persistent as long as the the responsible process they represent is still alive. 3. Dynamic objects - this category includes files in the subdirectories of a PID folder, like /proc/PID/fd/* or /proc/PID/stacks/*. Essentially, these objects are always created dynamically and when no longer in need after being used, they're deallocated. Nevertheless, the new allocated backend objects and inodes try to use the same InodeIndex if possible - this might change only when a thread dies and a new thread is born with a new thread stack, or when a file descriptor is closed and a new one within the same file descriptor number is opened. This is needed to actually be able to do something useful with these objects. The new design assures that many ProcFS instances can be used at once, with one backend for usage for all instances. --- Kernel/CMakeLists.txt | 1 + Kernel/Devices/USB/UHCIController.cpp | 159 +++ Kernel/FileSystem/ProcFS.cpp | 1830 ++----------------------- Kernel/FileSystem/ProcFS.h | 115 +- Kernel/Process.cpp | 44 +- Kernel/Process.h | 24 +- Kernel/ProcessExposed.cpp | 1626 ++++++++++++++++++++++ Kernel/ProcessExposed.h | 254 ++++ Kernel/Syscalls/fork.cpp | 3 +- Kernel/Thread.cpp | 2 + Kernel/Thread.h | 6 + Kernel/init.cpp | 4 +- 12 files changed, 2294 insertions(+), 1774 deletions(-) create mode 100644 Kernel/ProcessExposed.cpp create mode 100644 Kernel/ProcessExposed.h diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 9652fbace8..d073b6c8de 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -148,6 +148,7 @@ set(KERNEL_SOURCES Panic.cpp PerformanceEventBuffer.cpp Process.cpp + ProcessExposed.cpp ProcessGroup.cpp RTC.cpp Random.cpp diff --git a/Kernel/Devices/USB/UHCIController.cpp b/Kernel/Devices/USB/UHCIController.cpp index 153c825b58..9c68018c92 100644 --- a/Kernel/Devices/USB/UHCIController.cpp +++ b/Kernel/Devices/USB/UHCIController.cpp @@ -5,12 +5,17 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -65,6 +70,144 @@ static constexpr u16 UHCI_PORTSC_SUSPEND = 0x1000; static constexpr u8 UHCI_NUMBER_OF_ISOCHRONOUS_TDS = 128; static constexpr u16 UHCI_NUMBER_OF_FRAMES = 1024; +class ProcFSUSBBusFolder; +static ProcFSUSBBusFolder* s_procfs_usb_bus_folder; + +class ProcFSUSBDeviceInformation : public ProcFSGlobalInformation { + friend class ProcFSUSBBusFolder; + +public: + virtual ~ProcFSUSBDeviceInformation() override {}; + + static NonnullRefPtr create(USB::Device&); + + RefPtr device() const { return m_device; } + +protected: + explicit ProcFSUSBDeviceInformation(USB::Device& device) + : ProcFSGlobalInformation(String::formatted("{}", device.address())) + , m_device(device) + { + } + virtual bool output(KBufferBuilder& builder) override + { + VERIFY(m_device); // Something has gone very wrong if this isn't true + + JsonArraySerializer array { builder }; + + auto obj = array.add_object(); + obj.add("usb_spec_compliance_bcd", m_device->device_descriptor().usb_spec_compliance_bcd); + obj.add("device_class", m_device->device_descriptor().device_class); + obj.add("device_sub_class", m_device->device_descriptor().device_sub_class); + obj.add("device_protocol", m_device->device_descriptor().device_protocol); + obj.add("max_packet_size", m_device->device_descriptor().max_packet_size); + obj.add("vendor_id", m_device->device_descriptor().vendor_id); + obj.add("product_id", m_device->device_descriptor().product_id); + obj.add("device_release_bcd", m_device->device_descriptor().device_release_bcd); + obj.add("manufacturer_id_descriptor_index", m_device->device_descriptor().manufacturer_id_descriptor_index); + obj.add("product_string_descriptor_index", m_device->device_descriptor().product_string_descriptor_index); + obj.add("serial_number_descriptor_index", m_device->device_descriptor().serial_number_descriptor_index); + obj.add("num_configurations", m_device->device_descriptor().num_configurations); + obj.finish(); + array.finish(); + return true; + } + IntrusiveListNode> m_list_node; + RefPtr m_device; +}; + +class ProcFSUSBBusFolder final : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + +public: + static void initialize(); + void plug(USB::Device&); + void unplug(USB::Device&); + + virtual KResultOr entries_count() const override; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + +private: + ProcFSUSBBusFolder(const ProcFSBusDirectory&); + + RefPtr device_node_for(USB::Device& device); + + IntrusiveList, &ProcFSUSBDeviceInformation::m_list_node> m_device_nodes; + mutable SpinLock m_lock; +}; + +KResultOr ProcFSUSBBusFolder::entries_count() const +{ + ScopedSpinLock lock(m_lock); + return m_device_nodes.size_slow(); +} +KResult ProcFSUSBBusFolder::traverse_as_directory(unsigned fsid, Function callback) const +{ + ScopedSpinLock lock(m_lock); + VERIFY(m_parent_folder); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + for (auto& device_node : m_device_nodes) { + InodeIdentifier identifier = { fsid, device_node.component_index() }; + callback({ device_node.name(), identifier, 0 }); + } + return KSuccess; +} +RefPtr ProcFSUSBBusFolder::lookup(StringView name) +{ + ScopedSpinLock lock(m_lock); + for (auto& device_node : m_device_nodes) { + if (device_node.name() == name) { + return device_node; + } + } + return {}; +} + +RefPtr ProcFSUSBBusFolder::device_node_for(USB::Device& device) +{ + RefPtr checked_device = device; + for (auto& device_node : m_device_nodes) { + if (device_node.device().ptr() == checked_device.ptr()) + return device_node; + } + return {}; +} + +void ProcFSUSBBusFolder::plug(USB::Device& new_device) +{ + ScopedSpinLock lock(m_lock); + auto device_node = device_node_for(new_device); + VERIFY(!device_node); + m_device_nodes.append(ProcFSUSBDeviceInformation::create(new_device)); +} +void ProcFSUSBBusFolder::unplug(USB::Device& deleted_device) +{ + ScopedSpinLock lock(m_lock); + auto device_node = device_node_for(deleted_device); + VERIFY(device_node); + device_node->m_list_node.remove(); +} + +UNMAP_AFTER_INIT ProcFSUSBBusFolder::ProcFSUSBBusFolder(const ProcFSBusDirectory& buses_folder) + : ProcFSExposedFolder("usb"sv, buses_folder) +{ +} + +UNMAP_AFTER_INIT void ProcFSUSBBusFolder::initialize() +{ + auto folder = adopt_ref(*new ProcFSUSBBusFolder(ProcFSComponentsRegistrar::the().buses_folder())); + ProcFSComponentsRegistrar::the().register_new_bus_folder(folder); + s_procfs_usb_bus_folder = folder; +} + +NonnullRefPtr ProcFSUSBDeviceInformation::create(USB::Device& device) +{ + return adopt_ref(*new ProcFSUSBDeviceInformation(device)); +} + UHCIController& UHCIController::the() { return *s_the; @@ -75,6 +218,10 @@ UNMAP_AFTER_INIT void UHCIController::detect() if (kernel_command_line().disable_uhci_controller()) return; + // FIXME: We create the /proc/bus/usb representation here, but it should really be handled + // in a more broad singleton than this once we refactor things in USB subsystem. + ProcFSUSBBusFolder::initialize(); + PCI::enumerate([&](const PCI::Address& address, PCI::ID id) { if (address.is_null()) return; @@ -569,8 +716,14 @@ void UHCIController::spawn_port_proc() dmesgln("UHCI: Device creation failed on port 1 ({})", device.error()); m_devices.at(0) = device.value(); + VERIFY(s_procfs_usb_bus_folder); + s_procfs_usb_bus_folder->plug(device.value()); } else { + // FIXME: Clean up (and properly) the RefPtr to the device in m_devices + VERIFY(s_procfs_usb_bus_folder); + VERIFY(m_devices.at(0)); dmesgln("UHCI: Device detach detected on Root Port 1"); + s_procfs_usb_bus_folder->unplug(*m_devices.at(0)); } } } else { @@ -601,8 +754,14 @@ void UHCIController::spawn_port_proc() dmesgln("UHCI: Device creation failed on port 2 ({})", device.error()); m_devices.at(1) = device.value(); + VERIFY(s_procfs_usb_bus_folder); + s_procfs_usb_bus_folder->plug(device.value()); } else { + // FIXME: Clean up (and properly) the RefPtr to the device in m_devices + VERIFY(s_procfs_usb_bus_folder); + VERIFY(m_devices.at(1)); dmesgln("UHCI: Device detach detected on Root Port 2"); + s_procfs_usb_bus_folder->unplug(*m_devices.at(1)); } } } diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 3bf3d79d7d..aae7bbfbe4 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -1,264 +1,66 @@ /* * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2021, Spencer Dixon + * Copyright (c) 2021, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include #include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include namespace Kernel { -enum ProcParentDirectory { - PDI_AbstractRoot = 0, - PDI_Root, - PDI_Root_bus, - PDI_Root_bus_usb, - PDI_Root_sys, - PDI_Root_net, - PDI_PID, - PDI_PID_fd, - PDI_PID_stacks, -}; -static_assert(PDI_PID_stacks < 16, "Too many directories for identifier scheme"); +static AK::Singleton s_the; -enum ProcFileType { - FI_Invalid = 0, - - FI_Root = 1, // directory - - __FI_Root_Start, - FI_Root_df, - FI_Root_all, - FI_Root_memstat, - FI_Root_cpuinfo, - FI_Root_dmesg, - FI_Root_interrupts, - FI_Root_keymap, - FI_Root_pci, - FI_Root_devices, - FI_Root_uptime, - FI_Root_cmdline, - FI_Root_modules, - FI_Root_profile, - FI_Root_self, // symlink - FI_Root_sys, // directory - FI_Root_net, // directory - FI_Root_bus, // directory - __FI_Root_End, - - FI_Root_sys_variable, - - FI_Root_net_adapters, - FI_Root_net_arp, - FI_Root_net_tcp, - FI_Root_net_udp, - FI_Root_net_local, - - FI_Root_bus_usb, - FI_Root_bus_usb_device, - - FI_PID, - - __FI_PID_Start, - FI_PID_perf_events, - FI_PID_vm, - FI_PID_stacks, // directory - FI_PID_fds, - FI_PID_unveil, - FI_PID_exe, // symlink - FI_PID_cwd, // symlink - FI_PID_root, // symlink - FI_PID_fd, // directory - __FI_PID_End, - - FI_MaxStaticFileIndex, -}; - -static inline ProcessID to_pid(const InodeIdentifier& identifier) +ProcFSComponentsRegistrar& ProcFSComponentsRegistrar::the() { - return identifier.index().value() >> 16u; + return *s_the; } -static inline ThreadID to_tid(const InodeIdentifier& identifier) +UNMAP_AFTER_INIT void ProcFSComponentsRegistrar::initialize() { - // Sneakily, use the exact same mechanism. - return to_pid(identifier).value(); + VERIFY(!s_the.is_initialized()); + s_the.ensure_instance(); } -static inline ProcParentDirectory to_proc_parent_directory(const InodeIdentifier& identifier) +UNMAP_AFTER_INIT ProcFSComponentsRegistrar::ProcFSComponentsRegistrar() + : m_root_folder(ProcFSRootFolder::must_create()) { - return (ProcParentDirectory)((identifier.index().value() >> 12) & 0xf); } -static inline ProcFileType to_proc_file_type(const InodeIdentifier& identifier) +const ProcFSBusDirectory& ProcFSComponentsRegistrar::buses_folder() const { - return (ProcFileType)(identifier.index().value() & 0xfff); + return *m_root_folder->m_buses_folder; } -static inline int to_fd(const InodeIdentifier& identifier) +void ProcFSComponentsRegistrar::register_new_bus_folder(ProcFSExposedFolder& new_bus_folder) { - VERIFY(to_proc_parent_directory(identifier) == PDI_PID_fd); - return (identifier.index().value() & 0xfff) - FI_MaxStaticFileIndex; + VERIFY(!m_root_folder->m_buses_folder.is_null()); + m_root_folder->m_buses_folder->m_components.append(new_bus_folder); } -static inline size_t to_sys_index(const InodeIdentifier& identifier) +void ProcFSComponentsRegistrar::register_new_process(Process& new_process) { - VERIFY(to_proc_parent_directory(identifier) == PDI_Root_sys); - VERIFY(to_proc_file_type(identifier) == FI_Root_sys_variable); - return identifier.index().value() >> 16u; + Locker locker(m_lock); + m_root_folder->m_process_folders.append(ProcFSProcessFolder::create(new_process)); } -static inline u8 to_usb_device_address(const InodeIdentifier& identifier) +void ProcFSComponentsRegistrar::unregister_process(Process& deleted_process) { - VERIFY(to_proc_parent_directory(identifier) == PDI_Root_bus_usb); - VERIFY(to_proc_file_type(identifier) == FI_Root_bus_usb_device); - return (identifier.index().value() >> 16u) & 0xff; + auto process_folder = m_root_folder->process_folder_for(deleted_process); + VERIFY(process_folder); + process_folder->m_list_node.remove(); } -static inline InodeIdentifier to_identifier(unsigned fsid, ProcParentDirectory parent, ProcessID pid, ProcFileType proc_file_type) -{ - return { fsid, ((unsigned)parent << 12u) | ((unsigned)pid.value() << 16u) | (unsigned)proc_file_type }; -} - -static inline InodeIdentifier to_identifier_with_fd(unsigned fsid, ProcessID pid, int fd) -{ - return { fsid, (PDI_PID_fd << 12u) | ((unsigned)pid.value() << 16u) | (FI_MaxStaticFileIndex + fd) }; -} - -static inline InodeIdentifier to_identifier_with_stack(unsigned fsid, ThreadID tid) -{ - return { fsid, (PDI_PID_stacks << 12u) | ((unsigned)tid.value() << 16u) | FI_MaxStaticFileIndex }; -} - -static inline InodeIdentifier sys_var_to_identifier(unsigned fsid, unsigned index) -{ - VERIFY(index < 256); - return { fsid, (PDI_Root_sys << 12u) | (index << 16u) | FI_Root_sys_variable }; -} - -static inline InodeIdentifier usb_device_address_to_identifier(unsigned fsid, unsigned device_address) -{ - VERIFY(device_address < 127); - return { fsid, (PDI_Root_bus_usb << 12u) | (device_address << 16u) | FI_Root_bus_usb_device }; -} - -static inline InodeIdentifier to_parent_id(const InodeIdentifier& identifier) -{ - switch (to_proc_parent_directory(identifier)) { - case PDI_AbstractRoot: - case PDI_Root: - return { identifier.fsid(), FI_Root }; - case PDI_Root_sys: - return { identifier.fsid(), FI_Root_sys }; - case PDI_Root_net: - return { identifier.fsid(), FI_Root_net }; - case PDI_Root_bus: - return { identifier.fsid(), FI_Root_bus }; - case PDI_Root_bus_usb: - return to_identifier(identifier.fsid(), PDI_Root_bus, to_pid(identifier), FI_Root_bus_usb); - case PDI_PID: - return to_identifier(identifier.fsid(), PDI_Root, to_pid(identifier), FI_PID); - case PDI_PID_fd: - return to_identifier(identifier.fsid(), PDI_PID, to_pid(identifier), FI_PID_fd); - case PDI_PID_stacks: - return to_identifier(identifier.fsid(), PDI_PID, to_pid(identifier), FI_PID_stacks); - } - VERIFY_NOT_REACHED(); -} - -#if 0 -static inline u8 to_unused_metadata(const InodeIdentifier& identifier) -{ - return (identifier.index() >> 8) & 0xf; -} -#endif - -static inline bool is_process_related_file(const InodeIdentifier& identifier) -{ - if (to_proc_file_type(identifier) == FI_PID) - return true; - auto proc_parent_directory = to_proc_parent_directory(identifier); - switch (proc_parent_directory) { - case PDI_PID: - case PDI_PID_fd: - return true; - default: - return false; - } -} - -static inline bool is_thread_related_file(const InodeIdentifier& identifier) -{ - auto proc_parent_directory = to_proc_parent_directory(identifier); - return proc_parent_directory == PDI_PID_stacks; -} - -static inline bool is_directory(const InodeIdentifier& identifier) -{ - auto proc_file_type = to_proc_file_type(identifier); - switch (proc_file_type) { - case FI_Root: - case FI_Root_sys: - case FI_Root_net: - case FI_Root_bus: - case FI_Root_bus_usb: - case FI_PID: - case FI_PID_fd: - case FI_PID_stacks: - return true; - default: - return false; - } -} - -static inline bool is_persistent_inode(const InodeIdentifier& identifier) -{ - return to_proc_parent_directory(identifier) == PDI_Root_sys; -} - -struct ProcFSInodeData : public FileDescriptionData { - RefPtr buffer; -}; - RefPtr ProcFS::create() { return adopt_ref_if_nonnull(new (nothrow) ProcFS); @@ -268,1478 +70,112 @@ ProcFS::~ProcFS() { } -static bool procfs$pid_fds(InodeIdentifier identifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - - auto process = Process::from_pid(to_pid(identifier)); - if (!process) { - array.finish(); - return true; - } - if (process->number_of_open_file_descriptors() == 0) { - array.finish(); - return true; - } - - for (int i = 0; i < process->max_open_file_descriptors(); ++i) { - auto description = process->file_description(i); - if (!description) - continue; - bool cloexec = process->fd_flags(i) & FD_CLOEXEC; - - auto description_object = array.add_object(); - description_object.add("fd", i); - description_object.add("absolute_path", description->absolute_path()); - description_object.add("seekable", description->file().is_seekable()); - description_object.add("class", description->file().class_name()); - description_object.add("offset", description->offset()); - description_object.add("cloexec", cloexec); - description_object.add("blocking", description->is_blocking()); - description_object.add("can_read", description->can_read()); - description_object.add("can_write", description->can_write()); - } - array.finish(); - return true; -} - -static bool procfs$pid_fd_entry(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - int fd = to_fd(identifier); - auto description = process->file_description(fd); - if (!description) - return false; - builder.append_bytes(description->absolute_path().bytes()); - return true; -} - -static bool procfs$pid_vm(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - JsonArraySerializer array { builder }; - { - ScopedSpinLock lock(process->space().get_lock()); - for (auto& region : process->space().regions()) { - if (!region->is_user() && !Process::current()->is_superuser()) - continue; - auto region_object = array.add_object(); - region_object.add("readable", region->is_readable()); - region_object.add("writable", region->is_writable()); - region_object.add("executable", region->is_executable()); - region_object.add("stack", region->is_stack()); - region_object.add("shared", region->is_shared()); - region_object.add("syscall", region->is_syscall_region()); - region_object.add("purgeable", region->vmobject().is_anonymous()); - if (region->vmobject().is_anonymous()) { - region_object.add("volatile", static_cast(region->vmobject()).is_any_volatile()); - } - region_object.add("cacheable", region->is_cacheable()); - region_object.add("address", region->vaddr().get()); - region_object.add("size", region->size()); - region_object.add("amount_resident", region->amount_resident()); - region_object.add("amount_dirty", region->amount_dirty()); - region_object.add("cow_pages", region->cow_pages()); - region_object.add("name", region->name()); - region_object.add("vmobject", region->vmobject().class_name()); - - StringBuilder pagemap_builder; - for (size_t i = 0; i < region->page_count(); ++i) { - auto* page = region->physical_page(i); - if (!page) - pagemap_builder.append('N'); - else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) - pagemap_builder.append('Z'); - else - pagemap_builder.append('P'); - } - region_object.add("pagemap", pagemap_builder.to_string()); - } - } - array.finish(); - return true; -} - -static bool procfs$pci(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - PCI::enumerate([&array](PCI::Address address, PCI::ID id) { - auto obj = array.add_object(); - obj.add("seg", address.seg()); - obj.add("bus", address.bus()); - obj.add("device", address.device()); - obj.add("function", address.function()); - obj.add("vendor_id", id.vendor_id); - obj.add("device_id", id.device_id); - obj.add("revision_id", PCI::get_revision_id(address)); - obj.add("subclass", PCI::get_subclass(address)); - obj.add("class", PCI::get_class(address)); - obj.add("subsystem_id", PCI::get_subsystem_id(address)); - obj.add("subsystem_vendor_id", PCI::get_subsystem_vendor_id(address)); - }); - array.finish(); - return true; -} - -static bool procfs$interrupts(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - InterruptManagement::the().enumerate_interrupt_handlers([&array](GenericInterruptHandler& handler) { - auto obj = array.add_object(); - obj.add("purpose", handler.purpose()); - obj.add("interrupt_line", handler.interrupt_number()); - obj.add("controller", handler.controller()); - obj.add("cpu_handler", 0); // FIXME: Determine the responsible CPU for each interrupt handler. - obj.add("device_sharing", (unsigned)handler.sharing_devices_count()); - obj.add("call_count", (unsigned)handler.get_invoking_count()); - }); - array.finish(); - return true; -} - -static bool procfs$keymap(InodeIdentifier, KBufferBuilder& builder) -{ - JsonObjectSerializer json { builder }; - json.add("keymap", HIDManagement::the().keymap_name()); - json.finish(); - return true; -} - -static bool procfs$devices(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - Device::for_each([&array](auto& device) { - auto obj = array.add_object(); - obj.add("major", device.major()); - obj.add("minor", device.minor()); - obj.add("class_name", device.class_name()); - - if (device.is_block_device()) - obj.add("type", "block"); - else if (device.is_character_device()) - obj.add("type", "character"); - else - VERIFY_NOT_REACHED(); - }); - array.finish(); - return true; -} - -static bool procfs$uptime(InodeIdentifier, KBufferBuilder& builder) -{ - builder.appendff("{}\n", TimeManagement::the().uptime_ms() / 1000); - return true; -} - -static bool procfs$cmdline(InodeIdentifier, KBufferBuilder& builder) -{ - builder.append(kernel_command_line().string()); - builder.append('\n'); - return true; -} - -static bool procfs$modules(InodeIdentifier, KBufferBuilder& builder) -{ - extern HashMap>* g_modules; - JsonArraySerializer array { builder }; - for (auto& it : *g_modules) { - auto obj = array.add_object(); - obj.add("name", it.value->name); - obj.add("module_init", it.value->module_init); - obj.add("module_fini", it.value->module_fini); - u32 size = 0; - for (auto& section : it.value->sections) { - size += section.capacity(); - } - obj.add("size", size); - } - array.finish(); - return true; -} - -static bool procfs$profile(InodeIdentifier, KBufferBuilder& builder) -{ - extern PerformanceEventBuffer* g_global_perf_events; - if (!g_global_perf_events) - return false; - - return g_global_perf_events->to_json(builder); -} - -static bool procfs$pid_perf_events(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - InterruptDisabler disabler; - if (!process->perf_events()) - return false; - return process->perf_events()->to_json(builder); -} - -static bool procfs$net_adapters(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - NetworkingManagement::the().for_each([&array](auto& adapter) { - auto obj = array.add_object(); - obj.add("name", adapter.name()); - obj.add("class_name", adapter.class_name()); - obj.add("mac_address", adapter.mac_address().to_string()); - if (!adapter.ipv4_address().is_zero()) { - obj.add("ipv4_address", adapter.ipv4_address().to_string()); - obj.add("ipv4_netmask", adapter.ipv4_netmask().to_string()); - } - if (!adapter.ipv4_gateway().is_zero()) - obj.add("ipv4_gateway", adapter.ipv4_gateway().to_string()); - obj.add("packets_in", adapter.packets_in()); - obj.add("bytes_in", adapter.bytes_in()); - obj.add("packets_out", adapter.packets_out()); - obj.add("bytes_out", adapter.bytes_out()); - obj.add("link_up", adapter.link_up()); - obj.add("mtu", adapter.mtu()); - }); - array.finish(); - return true; -} - -static bool procfs$net_arp(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - Locker locker(arp_table().lock(), Lock::Mode::Shared); - for (auto& it : arp_table().resource()) { - auto obj = array.add_object(); - obj.add("mac_address", it.value.to_string()); - obj.add("ip_address", it.key.to_string()); - } - array.finish(); - return true; -} - -static bool procfs$net_tcp(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - TCPSocket::for_each([&array](auto& socket) { - auto obj = array.add_object(); - obj.add("local_address", socket.local_address().to_string()); - obj.add("local_port", socket.local_port()); - obj.add("peer_address", socket.peer_address().to_string()); - obj.add("peer_port", socket.peer_port()); - obj.add("state", TCPSocket::to_string(socket.state())); - obj.add("ack_number", socket.ack_number()); - obj.add("sequence_number", socket.sequence_number()); - obj.add("packets_in", socket.packets_in()); - obj.add("bytes_in", socket.bytes_in()); - obj.add("packets_out", socket.packets_out()); - obj.add("bytes_out", socket.bytes_out()); - }); - array.finish(); - return true; -} - -static bool procfs$usb_entry(InodeIdentifier identifier, KBufferBuilder& builder) -{ - u8 dev_id = to_usb_device_address(identifier); - auto const& device = USB::UHCIController::the().get_device_from_address(dev_id); - VERIFY(device); // Something has gone very wrong if this isn't true - - JsonArraySerializer array { builder }; - - auto obj = array.add_object(); - obj.add("usb_spec_compliance_bcd", device->device_descriptor().usb_spec_compliance_bcd); - obj.add("device_class", device->device_descriptor().device_class); - obj.add("device_sub_class", device->device_descriptor().device_sub_class); - obj.add("device_protocol", device->device_descriptor().device_protocol); - obj.add("max_packet_size", device->device_descriptor().max_packet_size); - obj.add("vendor_id", device->device_descriptor().vendor_id); - obj.add("product_id", device->device_descriptor().product_id); - obj.add("device_release_bcd", device->device_descriptor().device_release_bcd); - obj.add("manufacturer_id_descriptor_index", device->device_descriptor().manufacturer_id_descriptor_index); - obj.add("product_string_descriptor_index", device->device_descriptor().product_string_descriptor_index); - obj.add("serial_number_descriptor_index", device->device_descriptor().serial_number_descriptor_index); - obj.add("num_configurations", device->device_descriptor().num_configurations); - obj.finish(); - array.finish(); - - return true; -} - -static bool procfs$net_udp(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - UDPSocket::for_each([&array](auto& socket) { - auto obj = array.add_object(); - obj.add("local_address", socket.local_address().to_string()); - obj.add("local_port", socket.local_port()); - obj.add("peer_address", socket.peer_address().to_string()); - obj.add("peer_port", socket.peer_port()); - }); - array.finish(); - return true; -} - -static bool procfs$net_local(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - LocalSocket::for_each([&array](auto& socket) { - auto obj = array.add_object(); - obj.add("path", String(socket.socket_path())); - obj.add("origin_pid", socket.origin_pid()); - obj.add("origin_uid", socket.origin_uid()); - obj.add("origin_gid", socket.origin_gid()); - obj.add("acceptor_pid", socket.acceptor_pid()); - obj.add("acceptor_uid", socket.acceptor_uid()); - obj.add("acceptor_gid", socket.acceptor_gid()); - }); - array.finish(); - return true; -} - -static bool procfs$pid_unveil(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - JsonArraySerializer array { builder }; - for (auto& unveiled_path : process->unveiled_paths()) { - if (!unveiled_path.was_explicitly_unveiled()) - continue; - auto obj = array.add_object(); - obj.add("path", unveiled_path.path()); - StringBuilder permissions_builder; - if (unveiled_path.permissions() & UnveilAccess::Read) - permissions_builder.append('r'); - if (unveiled_path.permissions() & UnveilAccess::Write) - permissions_builder.append('w'); - if (unveiled_path.permissions() & UnveilAccess::Execute) - permissions_builder.append('x'); - if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove) - permissions_builder.append('c'); - if (unveiled_path.permissions() & UnveilAccess::Browse) - permissions_builder.append('b'); - obj.add("permissions", permissions_builder.to_string()); - } - array.finish(); - return true; -} - -static bool procfs$tid_stack(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto thread = Thread::from_tid(to_tid(identifier)); - if (!thread) - return false; - - JsonArraySerializer array { builder }; - bool show_kernel_addresses = Process::current()->is_superuser(); - bool kernel_address_added = false; - for (auto address : Processor::capture_stack_trace(*thread, 1024)) { - if (!show_kernel_addresses && !is_user_address(VirtualAddress { address })) { - if (kernel_address_added) - continue; - address = 0xdeadc0de; - kernel_address_added = true; - } - array.add(JsonValue(address)); - } - - array.finish(); - return true; -} - -static bool procfs$pid_exe(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - auto* custody = process->executable(); - VERIFY(custody); - builder.append(custody->absolute_path().bytes()); - return true; -} - -static bool procfs$pid_cwd(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - builder.append_bytes(process->current_directory().absolute_path().bytes()); - return true; -} - -static bool procfs$pid_root(InodeIdentifier identifier, KBufferBuilder& builder) -{ - auto process = Process::from_pid(to_pid(identifier)); - if (!process) - return false; - builder.append_bytes(process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer()); - return true; -} - -static bool procfs$self(InodeIdentifier, KBufferBuilder& builder) -{ - builder.appendff("{}", Process::current()->pid().value()); - return true; -} - -static bool procfs$dmesg(InodeIdentifier, KBufferBuilder& builder) -{ - InterruptDisabler disabler; - for (char ch : ConsoleDevice::the().logbuffer()) - builder.append(ch); - return true; -} - -static bool procfs$df(InodeIdentifier, KBufferBuilder& builder) -{ - // FIXME: This is obviously racy against the VFS mounts changing. - JsonArraySerializer array { builder }; - VFS::the().for_each_mount([&array](auto& mount) { - auto& fs = mount.guest_fs(); - auto fs_object = array.add_object(); - fs_object.add("class_name", fs.class_name()); - fs_object.add("total_block_count", fs.total_block_count()); - fs_object.add("free_block_count", fs.free_block_count()); - fs_object.add("total_inode_count", fs.total_inode_count()); - fs_object.add("free_inode_count", fs.free_inode_count()); - fs_object.add("mount_point", mount.absolute_path()); - fs_object.add("block_size", static_cast(fs.block_size())); - fs_object.add("readonly", fs.is_readonly()); - fs_object.add("mount_flags", mount.flags()); - - if (fs.is_file_backed()) - fs_object.add("source", static_cast(fs).file_description().absolute_path()); - else - fs_object.add("source", "none"); - }); - array.finish(); - return true; -} - -static bool procfs$cpuinfo(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - Processor::for_each( - [&](Processor& proc) { - auto& info = proc.info(); - auto obj = array.add_object(); - JsonArray features; - for (auto& feature : info.features().split(' ')) - features.append(feature); - obj.add("processor", proc.get_id()); - obj.add("cpuid", info.cpuid()); - obj.add("family", info.display_family()); - obj.add("features", features); - obj.add("model", info.display_model()); - obj.add("stepping", info.stepping()); - obj.add("type", info.type()); - obj.add("brandstr", info.brandstr()); - }); - array.finish(); - return true; -} - -static bool procfs$memstat(InodeIdentifier, KBufferBuilder& builder) -{ - InterruptDisabler disabler; - - kmalloc_stats stats; - get_kmalloc_stats(stats); - - ScopedSpinLock mm_lock(s_mm_lock); - auto user_physical_pages_total = MM.user_physical_pages(); - auto user_physical_pages_used = MM.user_physical_pages_used(); - auto user_physical_pages_committed = MM.user_physical_pages_committed(); - auto user_physical_pages_uncommitted = MM.user_physical_pages_uncommitted(); - - auto super_physical_total = MM.super_physical_pages(); - auto super_physical_used = MM.super_physical_pages_used(); - mm_lock.unlock(); - - JsonObjectSerializer json { builder }; - json.add("kmalloc_allocated", stats.bytes_allocated); - json.add("kmalloc_available", stats.bytes_free); - json.add("kmalloc_eternal_allocated", stats.bytes_eternal); - json.add("user_physical_allocated", user_physical_pages_used); - json.add("user_physical_available", user_physical_pages_total - user_physical_pages_used); - json.add("user_physical_committed", user_physical_pages_committed); - json.add("user_physical_uncommitted", user_physical_pages_uncommitted); - json.add("super_physical_allocated", super_physical_used); - json.add("super_physical_available", super_physical_total - super_physical_used); - json.add("kmalloc_call_count", stats.kmalloc_call_count); - json.add("kfree_call_count", stats.kfree_call_count); - slab_alloc_stats([&json](size_t slab_size, size_t num_allocated, size_t num_free) { - auto prefix = String::formatted("slab_{}", slab_size); - json.add(String::formatted("{}_num_allocated", prefix), num_allocated); - json.add(String::formatted("{}_num_free", prefix), num_free); - }); - json.finish(); - return true; -} - -static bool procfs$all(InodeIdentifier, KBufferBuilder& builder) -{ - JsonArraySerializer array { builder }; - - // Keep this in sync with CProcessStatistics. - auto build_process = [&](const Process& process) { - auto process_object = array.add_object(); - - if (process.is_user_process()) { - StringBuilder pledge_builder; - -#define __ENUMERATE_PLEDGE_PROMISE(promise) \ - if (process.has_promised(Pledge::promise)) { \ - pledge_builder.append(#promise " "); \ - } - ENUMERATE_PLEDGE_PROMISES -#undef __ENUMERATE_PLEDGE_PROMISE - - process_object.add("pledge", pledge_builder.to_string()); - - switch (process.veil_state()) { - case VeilState::None: - process_object.add("veil", "None"); - break; - case VeilState::Dropped: - process_object.add("veil", "Dropped"); - break; - case VeilState::Locked: - process_object.add("veil", "Locked"); - break; - } - } else { - process_object.add("pledge", String()); - process_object.add("veil", String()); - } - - process_object.add("pid", process.pid().value()); - process_object.add("pgid", process.tty() ? process.tty()->pgid().value() : 0); - process_object.add("pgp", process.pgid().value()); - process_object.add("sid", process.sid().value()); - process_object.add("uid", process.uid()); - process_object.add("gid", process.gid()); - process_object.add("ppid", process.ppid().value()); - process_object.add("nfds", process.number_of_open_file_descriptors()); - process_object.add("name", process.name()); - process_object.add("executable", process.executable() ? process.executable()->absolute_path() : ""); - process_object.add("tty", process.tty() ? process.tty()->tty_name() : "notty"); - process_object.add("amount_virtual", process.space().amount_virtual()); - process_object.add("amount_resident", process.space().amount_resident()); - process_object.add("amount_dirty_private", process.space().amount_dirty_private()); - process_object.add("amount_clean_inode", process.space().amount_clean_inode()); - process_object.add("amount_shared", process.space().amount_shared()); - process_object.add("amount_purgeable_volatile", process.space().amount_purgeable_volatile()); - process_object.add("amount_purgeable_nonvolatile", process.space().amount_purgeable_nonvolatile()); - process_object.add("dumpable", process.is_dumpable()); - process_object.add("kernel", process.is_kernel_process()); - auto thread_array = process_object.add_array("threads"); - process.for_each_thread([&](const Thread& thread) { - auto thread_object = thread_array.add_object(); -#if LOCK_DEBUG - thread_object.add("lock_count", thread.lock_count()); -#endif - thread_object.add("tid", thread.tid().value()); - thread_object.add("name", thread.name()); - thread_object.add("times_scheduled", thread.times_scheduled()); - thread_object.add("ticks_user", thread.ticks_in_user()); - thread_object.add("ticks_kernel", thread.ticks_in_kernel()); - thread_object.add("state", thread.state_string()); - thread_object.add("cpu", thread.cpu()); - thread_object.add("priority", thread.priority()); - thread_object.add("syscall_count", thread.syscall_count()); - thread_object.add("inode_faults", thread.inode_faults()); - thread_object.add("zero_faults", thread.zero_faults()); - thread_object.add("cow_faults", thread.cow_faults()); - thread_object.add("file_read_bytes", thread.file_read_bytes()); - thread_object.add("file_write_bytes", thread.file_write_bytes()); - thread_object.add("unix_socket_read_bytes", thread.unix_socket_read_bytes()); - thread_object.add("unix_socket_write_bytes", thread.unix_socket_write_bytes()); - thread_object.add("ipv4_socket_read_bytes", thread.ipv4_socket_read_bytes()); - thread_object.add("ipv4_socket_write_bytes", thread.ipv4_socket_write_bytes()); - }); - }; - - ScopedSpinLock lock(g_scheduler_lock); - auto processes = Process::all_processes(); - build_process(*Scheduler::colonel()); - for (auto& process : processes) - build_process(process); - array.finish(); - return true; -} - -struct SysVariable { - String name; - enum class Type : u8 { - Invalid, - Boolean, - String, - }; - Type type { Type::Invalid }; - Function notify_callback; - void* address { nullptr }; - - static SysVariable& for_inode(InodeIdentifier); - - void notify() - { - if (notify_callback) - notify_callback(); - } -}; - -static Vector* s_sys_variables; - -static inline Vector& sys_variables() -{ - if (s_sys_variables == nullptr) { - s_sys_variables = new Vector; - s_sys_variables->append({ "", SysVariable::Type::Invalid, nullptr, nullptr }); - } - return *s_sys_variables; -} - -SysVariable& SysVariable::for_inode(InodeIdentifier id) -{ - auto index = to_sys_index(id); - if (index >= sys_variables().size()) - return sys_variables()[0]; - auto& variable = sys_variables()[index]; - VERIFY(variable.address); - return variable; -} - -static bool read_sys_bool(InodeIdentifier inode_id, KBufferBuilder& builder) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::Boolean); - - u8 buffer[2]; - auto* lockable_bool = reinterpret_cast*>(variable.address); - { - Locker locker(lockable_bool->lock(), Lock::Mode::Shared); - buffer[0] = lockable_bool->resource() ? '1' : '0'; - } - buffer[1] = '\n'; - builder.append_bytes(ReadonlyBytes { buffer, sizeof(buffer) }); - return true; -} - -static KResultOr write_sys_bool(InodeIdentifier inode_id, const UserOrKernelBuffer& buffer, size_t size) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::Boolean); - - char value = 0; - bool did_read = false; - auto result = buffer.read_buffered<1>(1, [&](u8 const* data, size_t) { - if (did_read) - return 0; - value = (char)data[0]; - did_read = true; - return 1; - }); - if (result.is_error()) - return result.error(); - VERIFY(result.value() == 0 || (result.value() == 1 && did_read)); - if (result.value() == 0 || !(value == '0' || value == '1')) - return size; - - auto* lockable_bool = reinterpret_cast*>(variable.address); - { - Locker locker(lockable_bool->lock()); - lockable_bool->resource() = value == '1'; - } - variable.notify(); - return size; -} - -static bool read_sys_string(InodeIdentifier inode_id, KBufferBuilder& builder) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::String); - - auto* lockable_string = reinterpret_cast*>(variable.address); - Locker locker(lockable_string->lock(), Lock::Mode::Shared); - builder.append_bytes(lockable_string->resource().bytes()); - return true; -} - -static KResultOr write_sys_string(InodeIdentifier inode_id, const UserOrKernelBuffer& buffer, size_t size) -{ - auto& variable = SysVariable::for_inode(inode_id); - VERIFY(variable.type == SysVariable::Type::String); - - auto string_copy = buffer.copy_into_string(size); - if (string_copy.is_null()) - return EFAULT; - - { - auto* lockable_string = reinterpret_cast*>(variable.address); - Locker locker(lockable_string->lock()); - lockable_string->resource() = move(string_copy); - } - variable.notify(); - return size; -} - -void ProcFS::add_sys_bool(String&& name, Lockable& var, Function&& notify_callback) -{ - InterruptDisabler disabler; - - SysVariable variable; - variable.name = move(name); - variable.type = SysVariable::Type::Boolean; - variable.notify_callback = move(notify_callback); - variable.address = &var; - - sys_variables().append(move(variable)); -} - bool ProcFS::initialize() { - static Lockable* kmalloc_stack_helper; - static Lockable* ubsan_deadly_helper; - static Lockable* caps_lock_to_ctrl_helper; - - if (kmalloc_stack_helper == nullptr) { - kmalloc_stack_helper = new Lockable(); - kmalloc_stack_helper->resource() = g_dump_kmalloc_stacks; - ProcFS::add_sys_bool("kmalloc_stacks", *kmalloc_stack_helper, [] { - g_dump_kmalloc_stacks = kmalloc_stack_helper->resource(); - }); - ubsan_deadly_helper = new Lockable(); - ubsan_deadly_helper->resource() = AK::UBSanitizer::g_ubsan_is_deadly; - ProcFS::add_sys_bool("ubsan_is_deadly", *ubsan_deadly_helper, [] { - AK::UBSanitizer::g_ubsan_is_deadly = ubsan_deadly_helper->resource(); - }); - caps_lock_to_ctrl_helper = new Lockable(); - ProcFS::add_sys_bool("caps_lock_to_ctrl", *caps_lock_to_ctrl_helper, [] { - Kernel::g_caps_lock_remapped_to_ctrl.exchange(caps_lock_to_ctrl_helper->resource()); - }); - } return true; } -const char* ProcFS::class_name() const -{ - return "ProcFS"; -} - NonnullRefPtr ProcFS::root_inode() const { return *m_root_inode; } -RefPtr ProcFS::get_inode(InodeIdentifier inode_id) const +NonnullRefPtr ProcFSInode::create(const ProcFS& fs, const ProcFSExposedComponent& component) { - dbgln_if(PROCFS_DEBUG, "ProcFS::get_inode({})", inode_id.index()); - if (inode_id == root_inode()->identifier()) - return m_root_inode; - - Locker locker(m_inodes_lock); - auto it = m_inodes.find(inode_id.index().value()); - if (it != m_inodes.end()) { - // It's possible that the ProcFSInode ref count was dropped to 0 or - // the ~ProcFSInode destructor is even running already, but blocked - // from removing it from this map. So we need to *try* to ref it, - // and if that fails we cannot return this instance anymore and just - // create a new one. - if (it->value->try_ref()) - return adopt_ref_if_nonnull(it->value); - // We couldn't ref it, so just create a new one and replace the entry - } - auto inode = adopt_ref_if_nonnull(new (nothrow) ProcFSInode(const_cast(*this), inode_id.index())); - if (!inode) - return {}; - auto result = m_inodes.set(inode_id.index().value(), inode.ptr()); - VERIFY(result == ((it == m_inodes.end()) ? AK::HashSetResult::InsertedNewEntry : AK::HashSetResult::ReplacedExistingEntry)); - return inode; + return adopt_ref(*new (nothrow) ProcFSInode(fs, component)); } -ProcFSInode::ProcFSInode(ProcFS& fs, InodeIndex index) - : Inode(fs, index) +ProcFSInode::ProcFSInode(const ProcFS& fs, const ProcFSExposedComponent& component) + : Inode(const_cast(fs), component.component_index()) + , m_associated_component(component) { } -ProcFSInode::~ProcFSInode() -{ - Locker locker(fs().m_inodes_lock); - auto it = fs().m_inodes.find(index().value()); - if (it != fs().m_inodes.end() && it->value == this) - fs().m_inodes.remove(it); -} - -RefPtr ProcFSInode::process() const -{ - auto parent = to_proc_parent_directory(identifier()); - if (parent == PDI_PID || parent == PDI_PID_fd || parent == PDI_PID_stacks) - return Process::from_pid(to_pid(identifier())); - - return nullptr; -} - -KResult ProcFSInode::refresh_data(FileDescription& description) const -{ - if (Kernel::is_directory(identifier())) - return KSuccess; - - // For process-specific inodes, hold the process's ptrace lock across refresh - // and refuse to load data if the process is not dumpable. - // Without this, files opened before a process went non-dumpable could still be used for dumping. - auto process = this->process(); - if (process) { - process->ptrace_lock().lock(); - if (!process->is_dumpable()) { - process->ptrace_lock().unlock(); - return EPERM; - } - } - ScopeGuard guard = [&] { - if (process) - process->ptrace_lock().unlock(); - }; - - auto& cached_data = description.data(); - auto* directory_entry = fs().get_directory_entry(identifier()); - - bool (*read_callback)(InodeIdentifier, KBufferBuilder&) = nullptr; - - if (directory_entry) { - read_callback = directory_entry->read_callback; - VERIFY(read_callback); - } else { - switch (to_proc_parent_directory(identifier())) { - case PDI_PID_fd: - read_callback = procfs$pid_fd_entry; - break; - case PDI_PID_stacks: - read_callback = procfs$tid_stack; - break; - case PDI_Root_sys: - switch (SysVariable::for_inode(identifier()).type) { - case SysVariable::Type::Invalid: - VERIFY_NOT_REACHED(); - case SysVariable::Type::Boolean: - read_callback = read_sys_bool; - break; - case SysVariable::Type::String: - read_callback = read_sys_string; - break; - } - break; - case PDI_Root_bus_usb: - read_callback = procfs$usb_entry; - break; - default: - VERIFY_NOT_REACHED(); - } - - VERIFY(read_callback); - } - - if (!cached_data) - cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); - auto& buffer = static_cast(*cached_data).buffer; - if (buffer) { - // If we're reusing the buffer, reset the size to 0 first. This - // ensures we don't accidentally leak previously written data. - buffer->set_size(0); - } - KBufferBuilder builder(buffer, true); - if (!read_callback(identifier(), builder)) - return ENOENT; - // We don't use builder.build() here, which would steal our buffer - // and turn it into an OwnPtr. Instead, just flush to the buffer so - // that we can read all the data that was written. - if (!builder.flush()) - return ENOMEM; - if (!buffer) - return ENOMEM; - return KSuccess; -} - KResult ProcFSInode::attach(FileDescription& description) { - return refresh_data(description); + return m_associated_component->refresh_data(description); } void ProcFSInode::did_seek(FileDescription& description, off_t new_offset) { if (new_offset != 0) return; - auto result = refresh_data(description); + auto result = m_associated_component->refresh_data(description); if (result.is_error()) { // Subsequent calls to read will return EIO! dbgln("ProcFS: Could not refresh contents: {}", result.error()); } } +ProcFSInode::~ProcFSInode() +{ +} + +ProcFS::ProcFS() + : m_root_inode(ProcFSComponentsRegistrar::the().m_root_folder->to_inode(*this)) +{ +} + +KResultOr ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const +{ + return m_associated_component->read_bytes(offset, count, buffer, fd); +} + +StringView ProcFSInode::name() const +{ + return m_associated_component->name(); +} + +KResult ProcFSInode::traverse_as_directory(Function) const +{ + VERIFY_NOT_REACHED(); +} + +RefPtr ProcFSInode::lookup(StringView) +{ + VERIFY_NOT_REACHED(); +} + InodeMetadata ProcFSInode::metadata() const { - dbgln_if(PROCFS_DEBUG, "ProcFSInode::metadata({})", index()); + Locker locker(m_lock); InodeMetadata metadata; - metadata.inode = identifier(); - metadata.ctime = mepoch; - metadata.atime = mepoch; + metadata.inode = { fsid(), m_associated_component->component_index() }; + metadata.mode = m_associated_component->required_mode(); + metadata.uid = 0; + metadata.gid = 0; + metadata.size = m_associated_component->size(); metadata.mtime = mepoch; - auto proc_parent_directory = to_proc_parent_directory(identifier()); - auto proc_file_type = to_proc_file_type(identifier()); - - dbgln_if(PROCFS_DEBUG, " -> pid={}, fi={}, pdi={}", to_pid(identifier()).value(), (int)proc_file_type, (int)proc_parent_directory); - - if (is_process_related_file(identifier())) { - ProcessID pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (process && process->is_dumpable()) { - metadata.uid = process->euid(); - metadata.gid = process->egid(); - } else { - metadata.uid = 0; - metadata.gid = 0; - } - } else if (is_thread_related_file(identifier())) { - ThreadID tid = to_tid(identifier()); - auto thread = Thread::from_tid(tid); - if (thread && thread->process().is_dumpable()) { - metadata.uid = thread->process().euid(); - metadata.gid = thread->process().egid(); - } else { - metadata.uid = 0; - metadata.gid = 0; - } - } - - if (proc_parent_directory == PDI_PID_fd) { - metadata.mode = S_IFLNK | S_IRUSR | S_IWUSR | S_IXUSR; - return metadata; - } - - if (proc_parent_directory == PDI_Root_bus_usb) { - metadata.mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - return metadata; - } - - switch (proc_file_type) { - case FI_Root_self: - metadata.mode = S_IFLNK | S_IRUSR | S_IRGRP | S_IROTH; - break; - case FI_PID_cwd: - case FI_PID_exe: - case FI_PID_root: - metadata.mode = S_IFLNK | S_IRUSR; - break; - case FI_Root: - case FI_Root_sys: - case FI_Root_net: - case FI_Root_bus: - case FI_Root_bus_usb: - metadata.mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - break; - case FI_PID: - case FI_PID_fd: - case FI_PID_stacks: - metadata.mode = S_IFDIR | S_IRUSR | S_IXUSR; - break; - default: - metadata.mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - break; - } - - if (proc_file_type > FI_Invalid && proc_file_type < FI_MaxStaticFileIndex) { - if (fs().m_entries[proc_file_type].supervisor_only) { - metadata.uid = 0; - metadata.gid = 0; - metadata.mode &= ~077; - } - } return metadata; } -KResultOr ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const -{ - dbgln_if(PROCFS_DEBUG, "ProcFS: read_bytes offset: {} count: {}", offset, count); - VERIFY(offset >= 0); - VERIFY(buffer.user_or_kernel_ptr()); - - if (!description) - return EIO; - if (!description->data()) { - dbgln_if(PROCFS_DEBUG, "ProcFS: Do not have cached data!"); - return EIO; - } - - // Be sure to keep a reference to data_buffer while we use it! - RefPtr data_buffer = static_cast(*description->data()).buffer; - - if (!data_buffer || (size_t)offset >= data_buffer->size()) - return 0; - - size_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); - if (!buffer.write(data_buffer->data() + offset, nread)) - return EFAULT; - - return nread; -} - -InodeIdentifier ProcFS::ProcFSDirectoryEntry::identifier(unsigned fsid) const -{ - return to_identifier(fsid, PDI_Root, 0, (ProcFileType)proc_file_type); -} - -KResult ProcFSInode::traverse_as_directory(Function callback) const -{ - dbgln_if(PROCFS_DEBUG, "ProcFS: traverse_as_directory {}", index()); - - if (!Kernel::is_directory(identifier())) - return ENOTDIR; - - auto proc_file_type = to_proc_file_type(identifier()); - auto parent_id = to_parent_id(identifier()); - - callback({ ".", identifier(), 2 }); - callback({ "..", parent_id, 2 }); - - switch (proc_file_type) { - case FI_Root: - for (auto& entry : fs().m_entries) { - // FIXME: strlen() here is sad. - if (!entry.name) - continue; - if (entry.proc_file_type > __FI_Root_Start && entry.proc_file_type < __FI_Root_End) - callback({ { entry.name, strlen(entry.name) }, to_identifier(fsid(), PDI_Root, 0, (ProcFileType)entry.proc_file_type), 0 }); - } - for (auto pid_child : Process::all_pids()) { - callback({ String::number(pid_child.value()), to_identifier(fsid(), PDI_Root, pid_child, FI_PID), 0 }); - } - break; - - case FI_Root_sys: - for (size_t i = 1; i < sys_variables().size(); ++i) { - auto& variable = sys_variables()[i]; - callback({ variable.name, sys_var_to_identifier(fsid(), i), 0 }); - } - break; - - case FI_Root_net: - callback({ "adapters", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_adapters), 0 }); - callback({ "arp", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_arp), 0 }); - callback({ "tcp", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_tcp), 0 }); - callback({ "udp", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_udp), 0 }); - callback({ "local", to_identifier(fsid(), PDI_Root_net, 0, FI_Root_net_local), 0 }); - break; - - case FI_PID: { - auto pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - for (auto& entry : fs().m_entries) { - if (entry.proc_file_type > __FI_PID_Start && entry.proc_file_type < __FI_PID_End) { - if (entry.proc_file_type == FI_PID_exe && !process->executable()) - continue; - // FIXME: strlen() here is sad. - callback({ { entry.name, strlen(entry.name) }, to_identifier(fsid(), PDI_PID, pid, (ProcFileType)entry.proc_file_type), 0 }); - } - } - } break; - - case FI_PID_fd: { - auto pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - for (int i = 0; i < process->max_open_file_descriptors(); ++i) { - auto description = process->file_description(i); - if (!description) - continue; - callback({ String::number(i), to_identifier_with_fd(fsid(), pid, i), 0 }); - } - } break; - - case FI_PID_stacks: { - auto pid = to_pid(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - process->for_each_thread([&](const Thread& thread) { - int tid = thread.tid().value(); - callback({ String::number(tid), to_identifier_with_stack(fsid(), tid), 0 }); - }); - } break; - - case FI_Root_bus: { - callback({ "usb", to_identifier(fsid(), PDI_Root_bus, 0, FI_Root_bus_usb), 0 }); - break; - } - - case FI_Root_bus_usb: { - // FIXME: We currently only support 2 USB devices due to UHCI only... The controller stack should be modified - // to support any number of devices (this is a gross hack....) - for (auto port = 0; port < 2; port++) { - auto const& device = USB::UHCIController::the().get_device_at_port(static_cast(port)); - if (device == nullptr) - continue; - - callback({ String::number(device->address()), usb_device_address_to_identifier(fsid(), device->address()), 0 }); - } - break; - } - - default: - return KSuccess; - } - - return KSuccess; -} - -RefPtr ProcFSInode::lookup(StringView name) -{ - VERIFY(is_directory()); - if (name == ".") - return this; - if (name == "..") - return fs().get_inode(to_parent_id(identifier())); - - auto proc_file_type = to_proc_file_type(identifier()); - - if (proc_file_type == FI_Root) { - for (auto& entry : fs().m_entries) { - if (entry.name == nullptr) - continue; - if (entry.proc_file_type > __FI_Root_Start && entry.proc_file_type < __FI_Root_End) { - if (name == entry.name) { - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, (ProcFileType)entry.proc_file_type)); - } - } - } - auto name_as_number = name.to_uint(); - if (!name_as_number.has_value()) - return {}; - bool process_exists = false; - { - InterruptDisabler disabler; - process_exists = Process::from_pid(name_as_number.value()); - } - if (process_exists) - return fs().get_inode(to_identifier(fsid(), PDI_Root, name_as_number.value(), FI_PID)); - return {}; - } - - if (proc_file_type == FI_Root_sys) { - for (size_t i = 1; i < sys_variables().size(); ++i) { - auto& variable = sys_variables()[i]; - if (name == variable.name) - return fs().get_inode(sys_var_to_identifier(fsid(), i)); - } - return {}; - } - - if (proc_file_type == FI_Root_net) { - if (name == "adapters") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_adapters)); - if (name == "arp") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_arp)); - if (name == "tcp") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_tcp)); - if (name == "udp") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_udp)); - if (name == "local") - return fs().get_inode(to_identifier(fsid(), PDI_Root, 0, FI_Root_net_local)); - return {}; - } - - if (proc_file_type == FI_PID) { - auto process = Process::from_pid(to_pid(identifier())); - if (!process) - return {}; - for (auto& entry : fs().m_entries) { - if (entry.proc_file_type > __FI_PID_Start && entry.proc_file_type < __FI_PID_End) { - if (entry.proc_file_type == FI_PID_exe && !process->executable()) - continue; - if (entry.name == nullptr) - continue; - if (name == entry.name) { - return fs().get_inode(to_identifier(fsid(), PDI_PID, to_pid(identifier()), (ProcFileType)entry.proc_file_type)); - } - } - } - return {}; - } - - if (proc_file_type == FI_Root_bus) { - if (name == "usb") - return fs().get_inode(to_identifier(fsid(), PDI_Root_bus, 0, FI_Root_bus_usb)); - } - - if (proc_file_type == FI_Root_bus_usb) { - u8 device_address = name.to_uint().value(); - return fs().get_inode(usb_device_address_to_identifier(fsid(), device_address)); - } - - if (proc_file_type == FI_PID_fd) { - auto name_as_number = name.to_uint(); - if (!name_as_number.has_value()) - return {}; - bool fd_exists = false; - { - if (auto process = Process::from_pid(to_pid(identifier()))) - fd_exists = process->file_description(name_as_number.value()); - } - if (fd_exists) - return fs().get_inode(to_identifier_with_fd(fsid(), to_pid(identifier()), name_as_number.value())); - } - - if (proc_file_type == FI_PID_stacks) { - auto name_as_number = name.to_int(); - if (!name_as_number.has_value()) - return {}; - int tid = name_as_number.value(); - if (tid <= 0) { - return {}; - } - bool thread_exists = false; - { - auto process = Process::from_pid(to_pid(identifier())); - auto thread = Thread::from_tid(tid); - thread_exists = process && thread && process->pid() == thread->pid(); - } - if (thread_exists) - return fs().get_inode(to_identifier_with_stack(fsid(), tid)); - } - - return {}; -} - void ProcFSInode::flush_metadata() { } -KResultOr ProcFSInode::write_bytes(off_t offset, size_t size, const UserOrKernelBuffer& buffer, FileDescription*) +KResultOr ProcFSInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd) { - // For process-specific inodes, hold the process's ptrace lock across the write - // and refuse to write at all data if the process is not dumpable. - // Without this, files opened before a process went non-dumpable could still be used for dumping. - auto process = this->process(); - if (process) { - process->ptrace_lock().lock(); - if (!process->is_dumpable()) { - process->ptrace_lock().unlock(); - return EPERM; - } - } - ScopeGuard guard = [&] { - if (process) - process->ptrace_lock().unlock(); - }; - - auto result = prepare_to_write_data(); - if (result.is_error()) - return result; - - auto* directory_entry = fs().get_directory_entry(identifier()); - - KResultOr (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t) = nullptr; - - if (directory_entry == nullptr) { - if (to_proc_parent_directory(identifier()) == PDI_Root_sys) { - switch (SysVariable::for_inode(identifier()).type) { - case SysVariable::Type::Invalid: - VERIFY_NOT_REACHED(); - case SysVariable::Type::Boolean: - write_callback = write_sys_bool; - break; - case SysVariable::Type::String: - write_callback = write_sys_string; - break; - } - } else - return EPERM; - } else { - if (!directory_entry->write_callback) - return EPERM; - write_callback = directory_entry->write_callback; - } - - VERIFY(is_persistent_inode(identifier())); - // FIXME: Being able to write into ProcFS at a non-zero offset seems like something we should maybe support.. - VERIFY(offset == 0); - auto nwritten_or_error = write_callback(identifier(), buffer, size); - if (nwritten_or_error.is_error()) - dbgln("ProcFS: Writing {} bytes failed: {}", size, nwritten_or_error.error()); - return nwritten_or_error; -} - -KResultOr> ProcFSInode::resolve_as_link(Custody& base, RefPtr* out_parent, int options, int symlink_recursion_level) const -{ - if (FI_Root_self == to_proc_file_type(identifier())) { - return VFS::the().resolve_path(String::number(Process::current()->pid().value()), base, out_parent, options, symlink_recursion_level); - } - - // The only other links are in pid directories, so it's safe to ignore - // unrelated files and the thread-specific stacks/ directory. - if (!is_process_related_file(identifier())) - return Inode::resolve_as_link(base, out_parent, options, symlink_recursion_level); - - // FIXME: We should return a custody for FI_PID or FI_PID_fd here - // for correctness. It's impossible to create files in ProcFS, - // so returning null shouldn't break much. - if (out_parent) - *out_parent = nullptr; - - auto pid = to_pid(identifier()); - auto proc_file_type = to_proc_file_type(identifier()); - auto process = Process::from_pid(pid); - if (!process) - return ENOENT; - - if (to_proc_parent_directory(identifier()) == PDI_PID_fd) { - if (out_parent) - *out_parent = base; - int fd = to_fd(identifier()); - auto description = process->file_description(fd); - if (!description) - return ENOENT; - auto proxy_inode = ProcFSProxyInode::create(const_cast(fs()), *description); - return Custody::try_create(&base, "", proxy_inode, base.mount_flags()); - } - - Custody* res = nullptr; - - switch (proc_file_type) { - case FI_PID_cwd: - res = &process->current_directory(); - break; - case FI_PID_exe: - res = process->executable(); - break; - case FI_PID_root: - // Note: we open root_directory() here, not - // root_directory_relative_to_global_root(). - // This seems more useful. - res = &process->root_directory(); - break; - default: - VERIFY_NOT_REACHED(); - } - - if (!res) - return ENOENT; - - return *res; -} - -KResult ProcFSInode::set_mtime(time_t) -{ - return KSuccess; -} - -ProcFSProxyInode::ProcFSProxyInode(ProcFS& fs, FileDescription& fd) - : Inode(fs, 0) - , m_fd(fd) -{ -} - -ProcFSProxyInode::~ProcFSProxyInode() -{ -} - -KResult ProcFSProxyInode::attach(FileDescription& fd) -{ - return m_fd->inode()->attach(fd); -} - -void ProcFSProxyInode::did_seek(FileDescription& fd, off_t new_offset) -{ - return m_fd->inode()->did_seek(fd, new_offset); -} - -InodeMetadata ProcFSProxyInode::metadata() const -{ - InodeMetadata metadata = m_fd->metadata(); - - if (m_fd->is_readable()) - metadata.mode |= 0444; - else - metadata.mode &= ~0444; - - if (m_fd->is_writable()) - metadata.mode |= 0222; - else - metadata.mode &= ~0222; - - if (!metadata.is_directory()) - metadata.mode &= ~0111; - - return metadata; -} - -KResultOr> ProcFSProxyInode::create_child(const String& name, mode_t mode, dev_t dev, uid_t uid, gid_t gid) -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->create_child(name, mode, dev, uid, gid); -} - -KResult ProcFSProxyInode::add_child(Inode& child, const StringView& name, mode_t mode) -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->add_child(child, name, mode); -} - -KResult ProcFSProxyInode::remove_child(const StringView& name) -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->remove_child(name); -} - -RefPtr ProcFSProxyInode::lookup(StringView name) -{ - if (!m_fd->inode()) - return {}; - return m_fd->inode()->lookup(name); -} - -KResultOr ProcFSProxyInode::directory_entry_count() const -{ - if (!m_fd->inode()) - return EINVAL; - return m_fd->inode()->directory_entry_count(); + return m_associated_component->write_bytes(offset, count, buffer, fd); } KResultOr> ProcFSInode::create_child(const String&, mode_t, dev_t, uid_t, gid_t) { - return EPERM; + return EROFS; } KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t) { - return EPERM; + return EROFS; } -KResult ProcFSInode::remove_child([[maybe_unused]] const StringView& name) +KResult ProcFSInode::remove_child(const StringView&) { - return EPERM; + return EROFS; } KResultOr ProcFSInode::directory_entry_count() const { - VERIFY(is_directory()); - size_t count = 0; - KResult result = traverse_as_directory([&count](auto&) { - ++count; - return true; - }); - - if (result.is_error()) - return result; - - return count; + VERIFY_NOT_REACHED(); } KResult ProcFSInode::chmod(mode_t) @@ -1747,55 +183,83 @@ KResult ProcFSInode::chmod(mode_t) return EPERM; } -ProcFS::ProcFS() -{ - m_root_inode = adopt_ref(*new ProcFSInode(*this, 1)); - m_entries.resize(FI_MaxStaticFileIndex); - m_entries[FI_Root_df] = { "df", FI_Root_df, false, procfs$df }; - m_entries[FI_Root_all] = { "all", FI_Root_all, false, procfs$all }; - m_entries[FI_Root_memstat] = { "memstat", FI_Root_memstat, false, procfs$memstat }; - m_entries[FI_Root_cpuinfo] = { "cpuinfo", FI_Root_cpuinfo, false, procfs$cpuinfo }; - m_entries[FI_Root_dmesg] = { "dmesg", FI_Root_dmesg, true, procfs$dmesg }; - m_entries[FI_Root_self] = { "self", FI_Root_self, false, procfs$self }; - m_entries[FI_Root_pci] = { "pci", FI_Root_pci, false, procfs$pci }; - m_entries[FI_Root_interrupts] = { "interrupts", FI_Root_interrupts, false, procfs$interrupts }; - m_entries[FI_Root_keymap] = { "keymap", FI_Root_keymap, false, procfs$keymap }; - m_entries[FI_Root_devices] = { "devices", FI_Root_devices, false, procfs$devices }; - m_entries[FI_Root_uptime] = { "uptime", FI_Root_uptime, false, procfs$uptime }; - m_entries[FI_Root_cmdline] = { "cmdline", FI_Root_cmdline, true, procfs$cmdline }; - m_entries[FI_Root_modules] = { "modules", FI_Root_modules, true, procfs$modules }; - m_entries[FI_Root_profile] = { "profile", FI_Root_profile, true, procfs$profile }; - m_entries[FI_Root_sys] = { "sys", FI_Root_sys, true }; - m_entries[FI_Root_net] = { "net", FI_Root_net, false }; - m_entries[FI_Root_bus] = { "bus", FI_Root_bus, false }; - - m_entries[FI_Root_net_adapters] = { "adapters", FI_Root_net_adapters, false, procfs$net_adapters }; - m_entries[FI_Root_net_arp] = { "arp", FI_Root_net_arp, true, procfs$net_arp }; - m_entries[FI_Root_net_tcp] = { "tcp", FI_Root_net_tcp, false, procfs$net_tcp }; - m_entries[FI_Root_net_udp] = { "udp", FI_Root_net_udp, false, procfs$net_udp }; - m_entries[FI_Root_net_local] = { "local", FI_Root_net_local, false, procfs$net_local }; - - m_entries[FI_PID_vm] = { "vm", FI_PID_vm, false, procfs$pid_vm }; - m_entries[FI_PID_stacks] = { "stacks", FI_PID_stacks, false }; - m_entries[FI_PID_fds] = { "fds", FI_PID_fds, false, procfs$pid_fds }; - m_entries[FI_PID_exe] = { "exe", FI_PID_exe, false, procfs$pid_exe }; - m_entries[FI_PID_cwd] = { "cwd", FI_PID_cwd, false, procfs$pid_cwd }; - m_entries[FI_PID_unveil] = { "unveil", FI_PID_unveil, false, procfs$pid_unveil }; - m_entries[FI_PID_root] = { "root", FI_PID_root, false, procfs$pid_root }; - m_entries[FI_PID_perf_events] = { "perf_events", FI_PID_perf_events, false, procfs$pid_perf_events }; - m_entries[FI_PID_fd] = { "fd", FI_PID_fd, false }; -} - -ProcFS::ProcFSDirectoryEntry* ProcFS::get_directory_entry(InodeIdentifier identifier) const -{ - auto proc_file_type = to_proc_file_type(identifier); - if (proc_file_type != FI_Invalid && proc_file_type != FI_Root_sys_variable && proc_file_type != FI_Root_bus_usb_device && proc_file_type < FI_MaxStaticFileIndex) - return const_cast(&m_entries[proc_file_type]); - return nullptr; -} - KResult ProcFSInode::chown(uid_t, gid_t) { return EPERM; } + +KResult ProcFSInode::truncate(u64) +{ + return EPERM; +} + +NonnullRefPtr ProcFSDirectoryInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component) +{ + return adopt_ref(*new (nothrow) ProcFSDirectoryInode(procfs, component)); +} + +ProcFSDirectoryInode::ProcFSDirectoryInode(const ProcFS& fs, const ProcFSExposedComponent& component) + : ProcFSInode(fs, component) + , m_parent_fs(const_cast(fs)) +{ +} + +ProcFSDirectoryInode::~ProcFSDirectoryInode() +{ +} +InodeMetadata ProcFSDirectoryInode::metadata() const +{ + Locker locker(m_lock); + InodeMetadata metadata; + metadata.inode = { fsid(), m_associated_component->component_index() }; + metadata.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH | S_IXOTH; + metadata.uid = 0; + metadata.gid = 0; + metadata.size = 0; + metadata.mtime = mepoch; + return metadata; +} +KResult ProcFSDirectoryInode::traverse_as_directory(Function callback) const +{ + Locker locker(m_parent_fs.m_lock); + return m_associated_component->traverse_as_directory(m_parent_fs.fsid(), move(callback)); +} + +RefPtr ProcFSDirectoryInode::lookup(StringView name) +{ + Locker locker(m_parent_fs.m_lock); + auto component = m_associated_component->lookup(name); + if (!component) + return {}; + return component->to_inode(m_parent_fs); +} + +KResultOr ProcFSDirectoryInode::directory_entry_count() const +{ + Locker locker(m_lock); + return m_associated_component->entries_count(); +} + +NonnullRefPtr ProcFSLinkInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component) +{ + return adopt_ref(*new (nothrow) ProcFSLinkInode(procfs, component)); +} + +ProcFSLinkInode::ProcFSLinkInode(const ProcFS& fs, const ProcFSExposedComponent& component) + : ProcFSInode(fs, component) +{ +} +InodeMetadata ProcFSLinkInode::metadata() const +{ + Locker locker(m_lock); + InodeMetadata metadata; + metadata.inode = { fsid(), m_associated_component->component_index() }; + metadata.mode = S_IFLNK | m_associated_component->required_mode(); + metadata.uid = 0; + metadata.gid = 0; + metadata.size = 0; + metadata.mtime = mepoch; + return metadata; +} + } diff --git a/Kernel/FileSystem/ProcFS.h b/Kernel/FileSystem/ProcFS.h index a93b8c5414..71d32a5384 100644 --- a/Kernel/FileSystem/ProcFS.h +++ b/Kernel/FileSystem/ProcFS.h @@ -12,130 +12,91 @@ #include #include #include +#include namespace Kernel { class Process; class ProcFSInode; +class ProcFSDirectoryInode; class ProcFS final : public FS { friend class ProcFSInode; + friend class ProcFSDirectoryInode; public: virtual ~ProcFS() override; static RefPtr create(); virtual bool initialize() override; - virtual const char* class_name() const override; + virtual const char* class_name() const override { return "ProcFS"; } virtual NonnullRefPtr root_inode() const override; - static void add_sys_bool(String&&, Lockable&, Function&& notify_callback = nullptr); - private: ProcFS(); - struct ProcFSDirectoryEntry { - ProcFSDirectoryEntry() = default; - ProcFSDirectoryEntry(const char* a_name, unsigned a_proc_file_type, bool a_supervisor_only, bool (*read_callback)(InodeIdentifier, KBufferBuilder&) = nullptr, KResultOr (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t) = nullptr, RefPtr&& a_inode = nullptr) - : name(a_name) - , proc_file_type(a_proc_file_type) - , supervisor_only(a_supervisor_only) - , read_callback(read_callback) - , write_callback(write_callback) - , inode(move(a_inode)) - { - } - - const char* name { nullptr }; - unsigned proc_file_type { 0 }; - bool supervisor_only { false }; - bool (*read_callback)(InodeIdentifier, KBufferBuilder&); - KResultOr (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t); - RefPtr inode; - InodeIdentifier identifier(unsigned fsid) const; - }; - - RefPtr get_inode(InodeIdentifier) const; - ProcFSDirectoryEntry* get_directory_entry(InodeIdentifier) const; - - Vector m_entries; - - mutable Lock m_inodes_lock; - mutable HashMap m_inodes; - RefPtr m_root_inode; + NonnullRefPtr m_root_inode; }; -class ProcFSInode final : public Inode { +class ProcFSInode : public Inode { friend class ProcFS; public: + static NonnullRefPtr create(const ProcFS&, const ProcFSExposedComponent&); virtual ~ProcFSInode() override; + StringView name() const; + +protected: + ProcFSInode(const ProcFS&, const ProcFSExposedComponent&); -private: // ^Inode - virtual KResult attach(FileDescription&) override; - virtual void did_seek(FileDescription&, off_t) override; + virtual KResult attach(FileDescription& description) override; virtual KResultOr read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override; - virtual InodeMetadata metadata() const override; virtual KResult traverse_as_directory(Function) const override; virtual RefPtr lookup(StringView name) override; virtual void flush_metadata() override; + virtual InodeMetadata metadata() const override; virtual KResultOr write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) 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 void did_seek(FileDescription&, off_t) override; virtual KResultOr directory_entry_count() const override; virtual KResult chmod(mode_t) override; virtual KResult chown(uid_t, gid_t) override; - virtual KResultOr> resolve_as_link(Custody& base, RefPtr* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0) const override; - virtual KResult set_mtime(time_t) override; + virtual KResult truncate(u64) override; - KResult refresh_data(FileDescription&) const; - - RefPtr process() const; - - ProcFS& fs() { return static_cast(Inode::fs()); } - const ProcFS& fs() const { return static_cast(Inode::fs()); } - ProcFSInode(ProcFS&, InodeIndex); + NonnullRefPtr m_associated_component; }; -class ProcFSProxyInode final : public Inode { - friend class ProcFSInode; +class ProcFSLinkInode : public ProcFSInode { + friend class ProcFS; public: - virtual ~ProcFSProxyInode() override; + static NonnullRefPtr create(const ProcFS&, const ProcFSExposedComponent&); -private: - // ^Inode - virtual KResult attach(FileDescription&) override; - virtual void did_seek(FileDescription&, off_t) override; - virtual KResultOr read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const override { VERIFY_NOT_REACHED(); } +protected: + ProcFSLinkInode(const ProcFS&, const ProcFSExposedComponent&); virtual InodeMetadata metadata() const override; - virtual KResult traverse_as_directory(Function) const override { VERIFY_NOT_REACHED(); } - virtual RefPtr lookup(StringView name) override; - virtual void flush_metadata() override {}; - virtual KResultOr write_bytes(off_t, size_t, const UserOrKernelBuffer&, FileDescription*) override { VERIFY_NOT_REACHED(); } - 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 { return EINVAL; } - virtual KResult chown(uid_t, gid_t) override { return EINVAL; } - virtual KResultOr> resolve_as_link(Custody&, RefPtr*, int, int) const override { VERIFY_NOT_REACHED(); } - virtual FileDescription* preopen_fd() override { return m_fd; } - - ProcFS& fs() { return static_cast(Inode::fs()); } - const ProcFS& fs() const { return static_cast(Inode::fs()); } - - ProcFSProxyInode(ProcFS&, FileDescription&); - static NonnullRefPtr create(ProcFS& fs, FileDescription& fd) - { - return adopt_ref(*new ProcFSProxyInode(fs, fd)); - } - - NonnullRefPtr m_fd; }; +class ProcFSDirectoryInode : public ProcFSInode { + friend class ProcFS; + +public: + static NonnullRefPtr create(const ProcFS&, const ProcFSExposedComponent&); + virtual ~ProcFSDirectoryInode() override; + +protected: + ProcFSDirectoryInode(const ProcFS&, const ProcFSExposedComponent&); + // ^Inode + virtual InodeMetadata metadata() const override; + virtual KResult traverse_as_directory(Function) const override; + virtual RefPtr lookup(StringView name) override; + virtual KResultOr directory_entry_count() const override; + + ProcFS& m_parent_fs; +}; } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index a96383d4c6..1ea0850b53 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,15 @@ void Process::kill_all_threads() }); } +void Process::register_new(Process& process) +{ + // Note: this is essentially the same like process->ref() + RefPtr new_process = process; + ScopedSpinLock lock(g_processes_lock); + g_processes->prepend(process); + ProcFSComponentsRegistrar::the().register_new_process(process); +} + RefPtr Process::create_user_process(RefPtr& first_thread, const String& path, uid_t uid, gid_t gid, ProcessID parent_pid, int& error, Vector&& arguments, Vector&& environment, TTY* tty) { auto parts = path.split('/'); @@ -166,11 +176,7 @@ RefPtr Process::create_user_process(RefPtr& first_thread, const return {}; } - { - process->ref(); - ScopedSpinLock lock(g_processes_lock); - g_processes->prepend(*process); - } + register_new(*process); error = 0; return process; } @@ -189,9 +195,7 @@ RefPtr Process::create_kernel_process(RefPtr& first_thread, Str #endif if (process->pid() != 0) { - process->ref(); - ScopedSpinLock lock(g_processes_lock); - g_processes->prepend(*process); + register_new(*process); } ScopedSpinLock lock(g_scheduler_lock); @@ -391,6 +395,7 @@ RefPtr Process::from_pid(ProcessID pid) RefPtr Process::file_description(int fd) const { + ScopedSpinLock lock(m_fds_lock); if (fd < 0) return nullptr; if (static_cast(fd) < m_fds.size()) @@ -400,6 +405,7 @@ RefPtr Process::file_description(int fd) const int Process::fd_flags(int fd) const { + ScopedSpinLock lock(m_fds_lock); if (fd < 0) return -1; if (static_cast(fd) < m_fds.size()) @@ -409,9 +415,10 @@ int Process::fd_flags(int fd) const int Process::number_of_open_file_descriptors() const { + ScopedSpinLock lock(m_fds_lock); int count = 0; - for (auto& description : m_fds) { - if (description) + for (size_t index = 0; index < m_fds.size(); index++) { + if (m_fds[index].is_valid()) ++count; } return count; @@ -419,6 +426,7 @@ int Process::number_of_open_file_descriptors() const int Process::alloc_fd(int first_candidate_fd) { + ScopedSpinLock lock(m_fds_lock); for (int i = first_candidate_fd; i < (int)m_max_open_file_descriptors; ++i) { if (!m_fds[i]) return i; @@ -535,6 +543,12 @@ void Process::finalize() m_arguments.clear(); m_environment.clear(); + // Note: We need to remove the references from the ProcFS registrar + // If we don't do it here, we can't drop the object later, and we can't + // do this from the destructor because the state of the object doesn't + // allow us to take references anymore. + ProcFSComponentsRegistrar::the().unregister_process(*this); + m_dead = true; { @@ -676,14 +690,24 @@ RefPtr Process::create_kernel_thread(void (*entry)(void*), void* entry_d void Process::FileDescriptionAndFlags::clear() { + // FIXME: Verify Process::m_fds_lock is locked! m_description = nullptr; m_flags = 0; + m_global_procfs_inode_index = 0; +} + +void Process::FileDescriptionAndFlags::refresh_inode_index() +{ + // FIXME: Verify Process::m_fds_lock is locked! + m_global_procfs_inode_index = ProcFSComponentsRegistrar::the().allocate_inode_index(); } void Process::FileDescriptionAndFlags::set(NonnullRefPtr&& description, u32 flags) { + // FIXME: Verify Process::m_fds_lock is locked! m_description = move(description); m_flags = flags; + m_global_procfs_inode_index = ProcFSComponentsRegistrar::the().allocate_inode_index(); } Custody& Process::root_directory() diff --git a/Kernel/Process.h b/Kernel/Process.h index 8359ed0171..5b1bc7bd65 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,7 @@ class Process friend class Thread; friend class CoreDump; + friend class ProcFSProcessFileDescriptions; // Helper class to temporarily unprotect a process's protected data so you can write to it. class ProtectedDataMutationScope { @@ -169,6 +171,7 @@ public: static RefPtr create_kernel_process(RefPtr& first_thread, String&& name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT); static RefPtr create_user_process(RefPtr& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector&& arguments = Vector(), Vector&& environment = Vector(), TTY* = nullptr); + static void register_new(Process&); ~Process(); static Vector all_pids(); @@ -585,23 +588,41 @@ private: static constexpr int m_max_open_file_descriptors { FD_SETSIZE }; +public: class FileDescriptionAndFlags { + friend class FileDescriptionRegistrar; + public: operator bool() const { return !!m_description; } + bool is_valid() const { return !m_description.is_null(); } + FileDescription* description() { return m_description; } const FileDescription* description() const { return m_description; } - + InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; } u32 flags() const { return m_flags; } void set_flags(u32 flags) { m_flags = flags; } void clear(); void set(NonnullRefPtr&&, u32 flags = 0); + void refresh_inode_index(); private: RefPtr m_description; u32 m_flags { 0 }; + + // Note: This is needed so when we generate inodes for ProcFS, we know that + // we assigned a global Inode index to it so we can use it later + InodeIndex m_global_procfs_inode_index; }; + + // FIXME: We create a copy when trying to iterate the Vector because + // we don't want to put lots of locking when accessing this Vector. + // Maybe try to encapsulate the Vector inside a protective class with locking + // mechanism. + Vector fds() const { return m_fds; } + +private: Vector m_fds; mutable RecursiveSpinLock m_thread_list_lock; @@ -638,6 +659,7 @@ private: FutexQueues m_futex_queues; SpinLock m_futex_lock; + mutable SpinLock m_fds_lock; // This member is used in the implementation of ptrace's PT_TRACEME flag. // If it is set to true, the process will stop at the next execve syscall diff --git a/Kernel/ProcessExposed.cpp b/Kernel/ProcessExposed.cpp new file mode 100644 index 0000000000..e9f81aa91e --- /dev/null +++ b/Kernel/ProcessExposed.cpp @@ -0,0 +1,1626 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +static SpinLock s_index_lock; +static InodeIndex s_next_inode_index = 0; + +static size_t s_allocate_inode_index() +{ + ScopedSpinLock lock(s_index_lock); + s_next_inode_index = s_next_inode_index.value() + 1; + VERIFY(s_next_inode_index > 0); + return s_next_inode_index.value(); +} + +InodeIndex ProcFSComponentsRegistrar::allocate_inode_index() const +{ + return s_allocate_inode_index(); +} + +ProcFSExposedComponent::ProcFSExposedComponent(StringView name) + : m_component_index(s_allocate_inode_index()) +{ + m_name = KString::try_create(name); +} + +// Note: This constructor is intended to be used in /proc/pid/fd/* symlinks +// so we preallocated inode index for them so we just need to set it here. +ProcFSExposedComponent::ProcFSExposedComponent(StringView name, InodeIndex preallocated_index) + : m_component_index(preallocated_index.value()) +{ + VERIFY(preallocated_index.value() != 0); + VERIFY(preallocated_index <= s_next_inode_index); + m_name = KString::try_create(name); +} + +ProcFSExposedFolder::ProcFSExposedFolder(StringView name) + : ProcFSExposedComponent(name) +{ +} + +ProcFSExposedFolder::ProcFSExposedFolder(StringView name, const ProcFSExposedFolder& parent_folder) + : ProcFSExposedComponent(name) + , m_parent_folder(parent_folder) +{ +} + +ProcFSExposedLink::ProcFSExposedLink(StringView name) + : ProcFSExposedComponent(name) +{ +} + +ProcFSExposedLink::ProcFSExposedLink(StringView name, InodeIndex preallocated_index) + : ProcFSExposedComponent(name, preallocated_index) +{ +} + +struct ProcFSInodeData : public FileDescriptionData { + RefPtr buffer; +}; + +KResultOr ProcFSGlobalInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const +{ + dbgln_if(PROCFS_DEBUG, "ProcFSGlobalInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count); + + VERIFY(offset >= 0); + VERIFY(buffer.user_or_kernel_ptr()); + + if (!description) + return KResult(EIO); + if (!description->data()) { + dbgln("ProcFSGlobalInformation: Do not have cached data!"); + return KResult(EIO); + } + + // Be sure to keep a reference to data_buffer while we use it! + RefPtr data_buffer = static_cast(*description->data()).buffer; + + if (!data_buffer || (size_t)offset >= data_buffer->size()) + return 0; + + ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); + if (!buffer.write(data_buffer->data() + offset, nread)) + return KResult(EFAULT); + + return nread; +} + +KResult ProcFSGlobalInformation::refresh_data(FileDescription& description) const +{ + ScopedSpinLock lock(m_refresh_lock); + auto& cached_data = description.data(); + if (!cached_data) + cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); + VERIFY(description.data()); + auto& buffer = static_cast(*cached_data).buffer; + if (buffer) { + // If we're reusing the buffer, reset the size to 0 first. This + // ensures we don't accidentally leak previously written data. + buffer->set_size(0); + } + KBufferBuilder builder(buffer, true); + if (!const_cast(*this).output(builder)) + return ENOENT; + // We don't use builder.build() here, which would steal our buffer + // and turn it into an OwnPtr. Instead, just flush to the buffer so + // that we can read all the data that was written. + if (!builder.flush()) + return ENOMEM; + if (!buffer) + return ENOMEM; + return KSuccess; +} + +RefPtr ProcFSRootFolder::process_folder_for(Process& process) +{ + RefPtr checked_process = process; + for (auto& folder : m_process_folders) { + if (folder.associated_process().ptr() == checked_process.ptr()) + return folder; + } + return {}; +} + +KResultOr ProcFSProcessInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const +{ + dbgln_if(PROCFS_DEBUG, "ProcFSProcessInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count); + + VERIFY(offset >= 0); + VERIFY(buffer.user_or_kernel_ptr()); + + if (!description) + return KResult(EIO); + if (!description->data()) { + dbgln("ProcFSGlobalInformation: Do not have cached data!"); + return KResult(EIO); + } + + // Be sure to keep a reference to data_buffer while we use it! + RefPtr data_buffer = static_cast(*description->data()).buffer; + + if (!data_buffer || (size_t)offset >= data_buffer->size()) + return 0; + + ssize_t nread = min(static_cast(data_buffer->size() - offset), static_cast(count)); + if (!buffer.write(data_buffer->data() + offset, nread)) + return KResult(EFAULT); + + return nread; +} + +KResult ProcFSProcessInformation::refresh_data(FileDescription& description) const +{ + // For process-specific inodes, hold the process's ptrace lock across refresh + // and refuse to load data if the process is not dumpable. + // Without this, files opened before a process went non-dumpable could still be used for dumping. + auto process = const_cast(*this).m_parent_folder->m_associated_process; + process->ptrace_lock().lock(); + if (!process->is_dumpable()) { + process->ptrace_lock().unlock(); + return EPERM; + } + ScopeGuard guard = [&] { + process->ptrace_lock().unlock(); + }; + ScopedSpinLock lock(m_refresh_lock); + auto& cached_data = description.data(); + if (!cached_data) + cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); + VERIFY(description.data()); + auto& buffer = static_cast(*cached_data).buffer; + if (buffer) { + // If we're reusing the buffer, reset the size to 0 first. This + // ensures we don't accidentally leak previously written data. + buffer->set_size(0); + } + KBufferBuilder builder(buffer, true); + if (!const_cast(*this).output(builder)) + return ENOENT; + // We don't use builder.build() here, which would steal our buffer + // and turn it into an OwnPtr. Instead, just flush to the buffer so + // that we can read all the data that was written. + if (!builder.flush()) + return ENOMEM; + if (!buffer) + return ENOMEM; + return KSuccess; +} + +class ProcFSSystemDirectory; + +class ProcFSDumpKmallocStacks : public ProcFSSystemBoolean { +public: + static NonnullRefPtr must_create(const ProcFSSystemDirectory&); + virtual bool value() const override + { + Locker locker(m_lock); + return g_dump_kmalloc_stacks; + } + virtual void set_value(bool new_value) override + { + Locker locker(m_lock); + g_dump_kmalloc_stacks = new_value; + } + +private: + ProcFSDumpKmallocStacks(); + mutable Lock m_lock; +}; + +class ProcFSUBSanDeadly : public ProcFSSystemBoolean { +public: + static NonnullRefPtr must_create(const ProcFSSystemDirectory&); + virtual bool value() const override + { + Locker locker(m_lock); + return AK::UBSanitizer::g_ubsan_is_deadly; + } + virtual void set_value(bool new_value) override + { + Locker locker(m_lock); + AK::UBSanitizer::g_ubsan_is_deadly = new_value; + } + +private: + ProcFSUBSanDeadly(); + mutable Lock m_lock; +}; + +class ProcFSCapsLockRemap : public ProcFSSystemBoolean { +public: + static NonnullRefPtr must_create(const ProcFSSystemDirectory&); + virtual bool value() const override + { + Locker locker(m_lock); + return g_caps_lock_remapped_to_ctrl.load(); + } + virtual void set_value(bool new_value) override + { + Locker locker(m_lock); + g_caps_lock_remapped_to_ctrl.exchange(new_value); + } + +private: + ProcFSCapsLockRemap(); + mutable Lock m_lock; +}; + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDumpKmallocStacks::must_create(const ProcFSSystemDirectory&) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDumpKmallocStacks).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSUBSanDeadly::must_create(const ProcFSSystemDirectory&) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSUBSanDeadly).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSCapsLockRemap::must_create(const ProcFSSystemDirectory&) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSCapsLockRemap).release_nonnull(); +} + +UNMAP_AFTER_INIT ProcFSDumpKmallocStacks::ProcFSDumpKmallocStacks() + : ProcFSSystemBoolean("kmalloc_stacks"sv) +{ +} + +UNMAP_AFTER_INIT ProcFSUBSanDeadly::ProcFSUBSanDeadly() + : ProcFSSystemBoolean("ubsan_is_deadly"sv) +{ +} + +UNMAP_AFTER_INIT ProcFSCapsLockRemap::ProcFSCapsLockRemap() + : ProcFSSystemBoolean("caps_lock_to_ctrl"sv) +{ +} + +class ProcFSSelfProcessFolder final : public ProcFSExposedLink { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSSelfProcessFolder(); + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.appendff("{}", Process::current()->pid().value()); + return true; + } +}; + +class ProcFSDiskUsage final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSDiskUsage(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + VFS::the().for_each_mount([&array](auto& mount) { + auto& fs = mount.guest_fs(); + auto fs_object = array.add_object(); + fs_object.add("class_name", fs.class_name()); + fs_object.add("total_block_count", fs.total_block_count()); + fs_object.add("free_block_count", fs.free_block_count()); + fs_object.add("total_inode_count", fs.total_inode_count()); + fs_object.add("free_inode_count", fs.free_inode_count()); + fs_object.add("mount_point", mount.absolute_path()); + fs_object.add("block_size", static_cast(fs.block_size())); + fs_object.add("readonly", fs.is_readonly()); + fs_object.add("mount_flags", mount.flags()); + + if (fs.is_file_backed()) + fs_object.add("source", static_cast(fs).file_description().absolute_path()); + else + fs_object.add("source", "none"); + }); + array.finish(); + return true; + } +}; + +class ProcFSMemoryStatus final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSMemoryStatus(); + virtual bool output(KBufferBuilder& builder) override + { + InterruptDisabler disabler; + + kmalloc_stats stats; + get_kmalloc_stats(stats); + + ScopedSpinLock mm_lock(s_mm_lock); + auto user_physical_pages_total = MM.user_physical_pages(); + auto user_physical_pages_used = MM.user_physical_pages_used(); + auto user_physical_pages_committed = MM.user_physical_pages_committed(); + auto user_physical_pages_uncommitted = MM.user_physical_pages_uncommitted(); + + auto super_physical_total = MM.super_physical_pages(); + auto super_physical_used = MM.super_physical_pages_used(); + mm_lock.unlock(); + + JsonObjectSerializer json { builder }; + json.add("kmalloc_allocated", stats.bytes_allocated); + json.add("kmalloc_available", stats.bytes_free); + json.add("kmalloc_eternal_allocated", stats.bytes_eternal); + json.add("user_physical_allocated", user_physical_pages_used); + json.add("user_physical_available", user_physical_pages_total - user_physical_pages_used); + json.add("user_physical_committed", user_physical_pages_committed); + json.add("user_physical_uncommitted", user_physical_pages_uncommitted); + json.add("super_physical_allocated", super_physical_used); + json.add("super_physical_available", super_physical_total - super_physical_used); + json.add("kmalloc_call_count", stats.kmalloc_call_count); + json.add("kfree_call_count", stats.kfree_call_count); + slab_alloc_stats([&json](size_t slab_size, size_t num_allocated, size_t num_free) { + auto prefix = String::formatted("slab_{}", slab_size); + json.add(String::formatted("{}_num_allocated", prefix), num_allocated); + json.add(String::formatted("{}_num_free", prefix), num_free); + }); + json.finish(); + return true; + } +}; + +class ProcFSOverallProcesses final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSOverallProcesses(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + + // Keep this in sync with CProcessStatistics. + auto build_process = [&](const Process& process) { + auto process_object = array.add_object(); + + if (process.is_user_process()) { + StringBuilder pledge_builder; + +#define __ENUMERATE_PLEDGE_PROMISE(promise) \ + if (process.has_promised(Pledge::promise)) { \ + pledge_builder.append(#promise " "); \ + } + ENUMERATE_PLEDGE_PROMISES +#undef __ENUMERATE_PLEDGE_PROMISE + + process_object.add("pledge", pledge_builder.to_string()); + + switch (process.veil_state()) { + case VeilState::None: + process_object.add("veil", "None"); + break; + case VeilState::Dropped: + process_object.add("veil", "Dropped"); + break; + case VeilState::Locked: + process_object.add("veil", "Locked"); + break; + } + } else { + process_object.add("pledge", String()); + process_object.add("veil", String()); + } + + process_object.add("pid", process.pid().value()); + process_object.add("pgid", process.tty() ? process.tty()->pgid().value() : 0); + process_object.add("pgp", process.pgid().value()); + process_object.add("sid", process.sid().value()); + process_object.add("uid", process.uid()); + process_object.add("gid", process.gid()); + process_object.add("ppid", process.ppid().value()); + process_object.add("nfds", process.number_of_open_file_descriptors()); + process_object.add("name", process.name()); + process_object.add("executable", process.executable() ? process.executable()->absolute_path() : ""); + process_object.add("tty", process.tty() ? process.tty()->tty_name() : "notty"); + process_object.add("amount_virtual", process.space().amount_virtual()); + process_object.add("amount_resident", process.space().amount_resident()); + process_object.add("amount_dirty_private", process.space().amount_dirty_private()); + process_object.add("amount_clean_inode", process.space().amount_clean_inode()); + process_object.add("amount_shared", process.space().amount_shared()); + process_object.add("amount_purgeable_volatile", process.space().amount_purgeable_volatile()); + process_object.add("amount_purgeable_nonvolatile", process.space().amount_purgeable_nonvolatile()); + process_object.add("dumpable", process.is_dumpable()); + process_object.add("kernel", process.is_kernel_process()); + auto thread_array = process_object.add_array("threads"); + process.for_each_thread([&](const Thread& thread) { + auto thread_object = thread_array.add_object(); +#if LOCK_DEBUG + thread_object.add("lock_count", thread.lock_count()); +#endif + thread_object.add("tid", thread.tid().value()); + thread_object.add("name", thread.name()); + thread_object.add("times_scheduled", thread.times_scheduled()); + thread_object.add("ticks_user", thread.ticks_in_user()); + thread_object.add("ticks_kernel", thread.ticks_in_kernel()); + thread_object.add("state", thread.state_string()); + thread_object.add("cpu", thread.cpu()); + thread_object.add("priority", thread.priority()); + thread_object.add("syscall_count", thread.syscall_count()); + thread_object.add("inode_faults", thread.inode_faults()); + thread_object.add("zero_faults", thread.zero_faults()); + thread_object.add("cow_faults", thread.cow_faults()); + thread_object.add("file_read_bytes", thread.file_read_bytes()); + thread_object.add("file_write_bytes", thread.file_write_bytes()); + thread_object.add("unix_socket_read_bytes", thread.unix_socket_read_bytes()); + thread_object.add("unix_socket_write_bytes", thread.unix_socket_write_bytes()); + thread_object.add("ipv4_socket_read_bytes", thread.ipv4_socket_read_bytes()); + thread_object.add("ipv4_socket_write_bytes", thread.ipv4_socket_write_bytes()); + }); + }; + + ScopedSpinLock lock(g_scheduler_lock); + auto processes = Process::all_processes(); + build_process(*Scheduler::colonel()); + for (auto& process : processes) + build_process(process); + array.finish(); + return true; + } +}; +class ProcFSCPUInformation final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSCPUInformation(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + Processor::for_each( + [&](Processor& proc) { + auto& info = proc.info(); + auto obj = array.add_object(); + JsonArray features; + for (auto& feature : info.features().split(' ')) + features.append(feature); + obj.add("processor", proc.get_id()); + obj.add("cpuid", info.cpuid()); + obj.add("family", info.display_family()); + obj.add("features", features); + obj.add("model", info.display_model()); + obj.add("stepping", info.stepping()); + obj.add("type", info.type()); + obj.add("brandstr", info.brandstr()); + }); + array.finish(); + return true; + } +}; +class ProcFSDmesg final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSDmesg(); + virtual bool output(KBufferBuilder& builder) override + { + InterruptDisabler disabler; + for (char ch : ConsoleDevice::the().logbuffer()) + builder.append(ch); + return true; + } +}; +class ProcFSInterrupts final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSInterrupts(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + InterruptManagement::the().enumerate_interrupt_handlers([&array](GenericInterruptHandler& handler) { + auto obj = array.add_object(); + obj.add("purpose", handler.purpose()); + obj.add("interrupt_line", handler.interrupt_number()); + obj.add("controller", handler.controller()); + obj.add("cpu_handler", 0); // FIXME: Determine the responsible CPU for each interrupt handler. + obj.add("device_sharing", (unsigned)handler.sharing_devices_count()); + obj.add("call_count", (unsigned)handler.get_invoking_count()); + }); + array.finish(); + return true; + } +}; +class ProcFSKeymap final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSKeymap(); + virtual bool output(KBufferBuilder& builder) override + { + JsonObjectSerializer json { builder }; + json.add("keymap", HIDManagement::the().keymap_name()); + json.finish(); + return true; + } +}; + +// FIXME: Remove this after we enumerate the SysFS from lspci and SystemMonitor +class ProcFSPCI final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSPCI(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + PCI::enumerate([&array](PCI::Address address, PCI::ID id) { + auto obj = array.add_object(); + obj.add("seg", address.seg()); + obj.add("bus", address.bus()); + obj.add("device", address.device()); + obj.add("function", address.function()); + obj.add("vendor_id", id.vendor_id); + obj.add("device_id", id.device_id); + obj.add("revision_id", PCI::get_revision_id(address)); + obj.add("subclass", PCI::get_subclass(address)); + obj.add("class", PCI::get_class(address)); + obj.add("subsystem_id", PCI::get_subsystem_id(address)); + obj.add("subsystem_vendor_id", PCI::get_subsystem_vendor_id(address)); + }); + array.finish(); + return true; + } +}; + +class ProcFSDevices final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSDevices(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + Device::for_each([&array](auto& device) { + auto obj = array.add_object(); + obj.add("major", device.major()); + obj.add("minor", device.minor()); + obj.add("class_name", device.class_name()); + + if (device.is_block_device()) + obj.add("type", "block"); + else if (device.is_character_device()) + obj.add("type", "character"); + else + VERIFY_NOT_REACHED(); + }); + array.finish(); + return true; + } +}; +class ProcFSUptime final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSUptime(); + virtual bool output(KBufferBuilder& builder) override + { + builder.appendff("{}\n", TimeManagement::the().uptime_ms() / 1000); + return true; + } +}; +class ProcFSCommandLine final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSCommandLine(); + virtual bool output(KBufferBuilder& builder) override + { + builder.append(kernel_command_line().string()); + builder.append('\n'); + return true; + } +}; +class ProcFSModules final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSModules(); + virtual bool output(KBufferBuilder& builder) override + { + extern HashMap>* g_modules; + JsonArraySerializer array { builder }; + for (auto& it : *g_modules) { + auto obj = array.add_object(); + obj.add("name", it.value->name); + obj.add("module_init", it.value->module_init); + obj.add("module_fini", it.value->module_fini); + u32 size = 0; + for (auto& section : it.value->sections) { + size += section.capacity(); + } + obj.add("size", size); + } + array.finish(); + return true; + } +}; +class ProcFSProfile final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSProfile(); + virtual bool output(KBufferBuilder& builder) override + { + extern PerformanceEventBuffer* g_global_perf_events; + if (!g_global_perf_events) + return false; + + return g_global_perf_events->to_json(builder); + } +}; + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSSelfProcessFolder::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSSelfProcessFolder()).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDiskUsage::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDiskUsage).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSMemoryStatus::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSMemoryStatus).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSOverallProcesses::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSOverallProcesses).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSCPUInformation::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSCPUInformation).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDmesg::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDmesg).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSInterrupts::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSInterrupts).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSKeymap::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSKeymap).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSPCI::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSPCI).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSDevices::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSDevices).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSUptime::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSUptime).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSCommandLine::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSCommandLine).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSModules::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSModules).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSProfile::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSProfile).release_nonnull(); +} + +UNMAP_AFTER_INIT ProcFSSelfProcessFolder::ProcFSSelfProcessFolder() + : ProcFSExposedLink("self"sv) +{ +} +UNMAP_AFTER_INIT ProcFSDiskUsage::ProcFSDiskUsage() + : ProcFSGlobalInformation("df"sv) +{ +} +UNMAP_AFTER_INIT ProcFSMemoryStatus::ProcFSMemoryStatus() + : ProcFSGlobalInformation("memstat"sv) +{ +} +UNMAP_AFTER_INIT ProcFSOverallProcesses::ProcFSOverallProcesses() + : ProcFSGlobalInformation("all"sv) +{ +} +UNMAP_AFTER_INIT ProcFSCPUInformation::ProcFSCPUInformation() + : ProcFSGlobalInformation("cpuinfo"sv) +{ +} +UNMAP_AFTER_INIT ProcFSDmesg::ProcFSDmesg() + : ProcFSGlobalInformation("dmesg"sv) +{ +} +UNMAP_AFTER_INIT ProcFSInterrupts::ProcFSInterrupts() + : ProcFSGlobalInformation("interrupts"sv) +{ +} +UNMAP_AFTER_INIT ProcFSKeymap::ProcFSKeymap() + : ProcFSGlobalInformation("keymap"sv) +{ +} +UNMAP_AFTER_INIT ProcFSPCI::ProcFSPCI() + : ProcFSGlobalInformation("pci"sv) +{ +} +UNMAP_AFTER_INIT ProcFSDevices::ProcFSDevices() + : ProcFSGlobalInformation("devices"sv) +{ +} +UNMAP_AFTER_INIT ProcFSUptime::ProcFSUptime() + : ProcFSGlobalInformation("uptime"sv) +{ +} +UNMAP_AFTER_INIT ProcFSCommandLine::ProcFSCommandLine() + : ProcFSGlobalInformation("cmdline"sv) +{ +} +UNMAP_AFTER_INIT ProcFSModules::ProcFSModules() + : ProcFSGlobalInformation("modules"sv) +{ +} +UNMAP_AFTER_INIT ProcFSProfile::ProcFSProfile() + : ProcFSGlobalInformation("profile"sv) +{ +} + +class ProcFSAdapters final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSAdapters(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + NetworkingManagement::the().for_each([&array](auto& adapter) { + auto obj = array.add_object(); + obj.add("name", adapter.name()); + obj.add("class_name", adapter.class_name()); + obj.add("mac_address", adapter.mac_address().to_string()); + if (!adapter.ipv4_address().is_zero()) { + obj.add("ipv4_address", adapter.ipv4_address().to_string()); + obj.add("ipv4_netmask", adapter.ipv4_netmask().to_string()); + } + if (!adapter.ipv4_gateway().is_zero()) + obj.add("ipv4_gateway", adapter.ipv4_gateway().to_string()); + obj.add("packets_in", adapter.packets_in()); + obj.add("bytes_in", adapter.bytes_in()); + obj.add("packets_out", adapter.packets_out()); + obj.add("bytes_out", adapter.bytes_out()); + obj.add("link_up", adapter.link_up()); + obj.add("mtu", adapter.mtu()); + }); + array.finish(); + return true; + } +}; + +class ProcFSARP final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSARP(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + Locker locker(arp_table().lock(), Lock::Mode::Shared); + for (auto& it : arp_table().resource()) { + auto obj = array.add_object(); + obj.add("mac_address", it.value.to_string()); + obj.add("ip_address", it.key.to_string()); + } + array.finish(); + return true; + } +}; + +class ProcFSTCP final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSTCP(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + TCPSocket::for_each([&array](auto& socket) { + auto obj = array.add_object(); + obj.add("local_address", socket.local_address().to_string()); + obj.add("local_port", socket.local_port()); + obj.add("peer_address", socket.peer_address().to_string()); + obj.add("peer_port", socket.peer_port()); + obj.add("state", TCPSocket::to_string(socket.state())); + obj.add("ack_number", socket.ack_number()); + obj.add("sequence_number", socket.sequence_number()); + obj.add("packets_in", socket.packets_in()); + obj.add("bytes_in", socket.bytes_in()); + obj.add("packets_out", socket.packets_out()); + obj.add("bytes_out", socket.bytes_out()); + }); + array.finish(); + return true; + } +}; + +class ProcFSLocalNet final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSLocalNet(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + LocalSocket::for_each([&array](auto& socket) { + auto obj = array.add_object(); + obj.add("path", String(socket.socket_path())); + obj.add("origin_pid", socket.origin_pid()); + obj.add("origin_uid", socket.origin_uid()); + obj.add("origin_gid", socket.origin_gid()); + obj.add("acceptor_pid", socket.acceptor_pid()); + obj.add("acceptor_uid", socket.acceptor_uid()); + obj.add("acceptor_gid", socket.acceptor_gid()); + }); + array.finish(); + return true; + } +}; + +class ProcFSUDP final : public ProcFSGlobalInformation { +public: + static NonnullRefPtr must_create(); + +private: + ProcFSUDP(); + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + UDPSocket::for_each([&array](auto& socket) { + auto obj = array.add_object(); + obj.add("local_address", socket.local_address().to_string()); + obj.add("local_port", socket.local_port()); + obj.add("peer_address", socket.peer_address().to_string()); + obj.add("peer_port", socket.peer_port()); + }); + array.finish(); + return true; + } +}; + +class ProcFSNetworkDirectory : public ProcFSExposedFolder { +public: + static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); + +private: + ProcFSNetworkDirectory(const ProcFSRootFolder& parent_folder); +}; + +class ProcFSSystemDirectory : public ProcFSExposedFolder { +public: + static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); + +private: + ProcFSSystemDirectory(const ProcFSRootFolder& parent_folder); +}; + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSAdapters::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSAdapters).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSARP::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSARP).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSTCP::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSTCP).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSLocalNet::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSLocalNet).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSUDP::must_create() +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSUDP).release_nonnull(); +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSNetworkDirectory::must_create(const ProcFSRootFolder& parent_folder) +{ + auto folder = adopt_ref(*new (nothrow) ProcFSNetworkDirectory(parent_folder)); + folder->m_components.append(ProcFSAdapters::must_create()); + folder->m_components.append(ProcFSARP::must_create()); + folder->m_components.append(ProcFSTCP::must_create()); + folder->m_components.append(ProcFSLocalNet::must_create()); + folder->m_components.append(ProcFSUDP::must_create()); + return folder; +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSBusDirectory::must_create(const ProcFSRootFolder& parent_folder) +{ + auto folder = adopt_ref(*new (nothrow) ProcFSBusDirectory(parent_folder)); + return folder; +} +UNMAP_AFTER_INIT NonnullRefPtr ProcFSSystemDirectory::must_create(const ProcFSRootFolder& parent_folder) +{ + auto folder = adopt_ref(*new (nothrow) ProcFSSystemDirectory(parent_folder)); + folder->m_components.append(ProcFSDumpKmallocStacks::must_create(folder)); + folder->m_components.append(ProcFSUBSanDeadly::must_create(folder)); + folder->m_components.append(ProcFSCapsLockRemap::must_create(folder)); + return folder; +} + +UNMAP_AFTER_INIT ProcFSAdapters::ProcFSAdapters() + : ProcFSGlobalInformation("adapters"sv) +{ +} +UNMAP_AFTER_INIT ProcFSARP::ProcFSARP() + : ProcFSGlobalInformation("arp"sv) +{ +} +UNMAP_AFTER_INIT ProcFSTCP::ProcFSTCP() + : ProcFSGlobalInformation("tcp"sv) +{ +} +UNMAP_AFTER_INIT ProcFSLocalNet::ProcFSLocalNet() + : ProcFSGlobalInformation("local"sv) +{ +} +UNMAP_AFTER_INIT ProcFSUDP::ProcFSUDP() + : ProcFSGlobalInformation("udp"sv) +{ +} +UNMAP_AFTER_INIT ProcFSNetworkDirectory::ProcFSNetworkDirectory(const ProcFSRootFolder& parent_folder) + : ProcFSExposedFolder("net"sv, parent_folder) +{ +} +UNMAP_AFTER_INIT ProcFSBusDirectory::ProcFSBusDirectory(const ProcFSRootFolder& parent_folder) + : ProcFSExposedFolder("bus"sv, parent_folder) +{ +} +UNMAP_AFTER_INIT ProcFSSystemDirectory::ProcFSSystemDirectory(const ProcFSRootFolder& parent_folder) + : ProcFSExposedFolder("sys"sv, parent_folder) +{ +} + +UNMAP_AFTER_INIT NonnullRefPtr ProcFSRootFolder::must_create() +{ + auto folder = adopt_ref(*new (nothrow) ProcFSRootFolder); + folder->m_components.append(ProcFSSelfProcessFolder::must_create()); + folder->m_components.append(ProcFSDiskUsage::must_create()); + folder->m_components.append(ProcFSMemoryStatus::must_create()); + folder->m_components.append(ProcFSOverallProcesses::must_create()); + folder->m_components.append(ProcFSCPUInformation::must_create()); + folder->m_components.append(ProcFSDmesg::must_create()); + folder->m_components.append(ProcFSInterrupts::must_create()); + folder->m_components.append(ProcFSKeymap::must_create()); + folder->m_components.append(ProcFSPCI::must_create()); + folder->m_components.append(ProcFSDevices::must_create()); + folder->m_components.append(ProcFSUptime::must_create()); + folder->m_components.append(ProcFSCommandLine::must_create()); + folder->m_components.append(ProcFSModules::must_create()); + folder->m_components.append(ProcFSProfile::must_create()); + + folder->m_components.append(ProcFSNetworkDirectory::must_create(*folder)); + auto buses_folder = ProcFSBusDirectory::must_create(*folder); + folder->m_components.append(buses_folder); + folder->m_buses_folder = buses_folder; + folder->m_components.append(ProcFSSystemDirectory::must_create(*folder)); + return folder; +} + +UNMAP_AFTER_INIT ProcFSRootFolder::ProcFSRootFolder() + : ProcFSExposedFolder("."sv) +{ +} + +UNMAP_AFTER_INIT ProcFSRootFolder::~ProcFSRootFolder() +{ +} + +class ProcFSProcessStacks; +class ProcFSThreadStack final : public ProcFSProcessInformation { +public: + // Note: We pass const ProcFSProcessStacks& to enforce creation with this type of folder + static NonnullRefPtr create(const ProcFSProcessFolder& process_folder, const ProcFSProcessStacks&, const Thread& thread) + { + return adopt_ref(*new (nothrow) ProcFSThreadStack(process_folder, thread)); + } + +private: + explicit ProcFSThreadStack(const ProcFSProcessFolder& process_folder, const Thread& thread) + : ProcFSProcessInformation(String::formatted("{}", thread.tid()), process_folder) + , m_associated_thread(thread) + { + } + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + bool show_kernel_addresses = Process::current()->is_superuser(); + bool kernel_address_added = false; + for (auto address : Processor::capture_stack_trace(*m_associated_thread, 1024)) { + if (!show_kernel_addresses && !is_user_address(VirtualAddress { address })) { + if (kernel_address_added) + continue; + address = 0xdeadc0de; + kernel_address_added = true; + } + array.add(JsonValue(address)); + } + + array.finish(); + return true; + } + + NonnullRefPtr m_associated_thread; +}; + +class ProcFSProcessStacks final : public ProcFSExposedFolder { + // Note: This folder is special, because everything that is created here is dynamic! + // This means we don't register anything in the m_components Vector, and every inode + // is created in runtime when called to get it + // Every ProcFSThreadStack (that represents a thread stack) is created only as a temporary object + // therefore, we don't use m_components so when we are done with the ProcFSThreadStack object, + // It should be deleted (as soon as possible) +public: + virtual KResultOr entries_count() const override; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + auto folder = adopt_ref(*new (nothrow) ProcFSProcessStacks(parent_folder)); + return folder; + } + + virtual void prepare_for_deletion() override + { + ProcFSExposedFolder::prepare_for_deletion(); + m_process_folder.clear(); + } + +private: + ProcFSProcessStacks(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedFolder("stacks"sv, parent_folder) + , m_process_folder(parent_folder) + { + } + RefPtr m_process_folder; + mutable Lock m_lock; +}; + +KResultOr ProcFSProcessStacks::entries_count() const +{ + Locker locker(m_lock); + auto process = m_process_folder->m_associated_process; + return process->thread_count(); +} + +KResult ProcFSProcessStacks::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(m_lock); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + auto process = m_process_folder->m_associated_process; + process->for_each_thread([&](const Thread& thread) { + int tid = thread.tid().value(); + InodeIdentifier identifier = { fsid, thread.global_procfs_inode_index() }; + callback({ String::number(tid), identifier, 0 }); + }); + return KSuccess; +} + +RefPtr ProcFSProcessStacks::lookup(StringView name) +{ + Locker locker(m_lock); + auto process = m_process_folder->m_associated_process; + RefPtr procfd_stack; + // FIXME: Try to exit the loop earlier + process->for_each_thread([&](const Thread& thread) { + int tid = thread.tid().value(); + if (name == String::number(tid)) { + procfd_stack = ProcFSThreadStack::create(*m_process_folder, *this, thread); + } + }); + return procfd_stack; +} + +class ProcFSProcessFileDescriptions; +class ProcFSProcessFileDescription final : public ProcFSExposedLink { +public: + // Note: we pass const ProcFSProcessFileDescriptions& just to enforce creation of this in the correct folder. + static NonnullRefPtr create(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index, const ProcFSProcessFileDescriptions&) + { + return adopt_ref(*new (nothrow) ProcFSProcessFileDescription(fd_number, fd, preallocated_index)); + } + +private: + explicit ProcFSProcessFileDescription(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index) + : ProcFSExposedLink(String::formatted("{}", fd_number), preallocated_index) + , m_associated_file_description(fd) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.append_bytes(m_associated_file_description->absolute_path().bytes()); + return true; + } + + NonnullRefPtr m_associated_file_description; +}; + +class ProcFSProcessFileDescriptions final : public ProcFSExposedFolder { + // Note: This folder is special, because everything that is created here is dynamic! + // This means we don't register anything in the m_components Vector, and every inode + // is created in runtime when called to get it + // Every ProcFSProcessFileDescription (that represents a file descriptor) is created only as a temporary object + // therefore, we don't use m_components so when we are done with the ProcFSProcessFileDescription object, + // It should be deleted (as soon as possible) +public: + virtual KResultOr entries_count() const override; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessFileDescriptions(parent_folder)); + } + + virtual void prepare_for_deletion() override + { + ProcFSExposedFolder::prepare_for_deletion(); + m_process_folder.clear(); + } + +private: + explicit ProcFSProcessFileDescriptions(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedFolder("fd"sv, parent_folder) + , m_process_folder(parent_folder) + { + } + RefPtr m_process_folder; + mutable Lock m_lock; +}; + +KResultOr ProcFSProcessFileDescriptions::entries_count() const +{ + Locker locker(m_lock); + return m_process_folder->m_associated_process->number_of_open_file_descriptors(); +} +KResult ProcFSProcessFileDescriptions::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(m_lock); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + auto process = m_process_folder->m_associated_process; + for (int i = 0; i < process->max_open_file_descriptors(); ++i) { + auto description_metadata = process->fds()[i]; + if (!description_metadata.is_valid()) + continue; + InodeIdentifier identifier = { fsid, description_metadata.global_procfs_inode_index() }; + callback({ String::number(i), identifier, 0 }); + } + return KSuccess; +} +RefPtr ProcFSProcessFileDescriptions::lookup(StringView name) +{ + Locker locker(m_lock); + auto process = m_process_folder->m_associated_process; + ScopedSpinLock lock(process->m_fds_lock); + for (int i = 0; i < process->max_open_file_descriptors(); ++i) { + auto description_metadata = process->fds()[i]; + if (!description_metadata.is_valid()) + continue; + if (name == String::number(i)) { + return ProcFSProcessFileDescription::create(i, *description_metadata.description(), description_metadata.global_procfs_inode_index(), *this); + } + } + return nullptr; +} + +class ProcFSProcessUnveil final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessUnveil(parent_folder)); + } + +private: + explicit ProcFSProcessUnveil(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("unveil"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + for (auto& unveiled_path : m_parent_folder->m_associated_process->unveiled_paths()) { + if (!unveiled_path.was_explicitly_unveiled()) + continue; + auto obj = array.add_object(); + obj.add("path", unveiled_path.path()); + StringBuilder permissions_builder; + if (unveiled_path.permissions() & UnveilAccess::Read) + permissions_builder.append('r'); + if (unveiled_path.permissions() & UnveilAccess::Write) + permissions_builder.append('w'); + if (unveiled_path.permissions() & UnveilAccess::Execute) + permissions_builder.append('x'); + if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove) + permissions_builder.append('c'); + if (unveiled_path.permissions() & UnveilAccess::Browse) + permissions_builder.append('b'); + obj.add("permissions", permissions_builder.to_string()); + } + array.finish(); + return true; + } +}; + +class ProcFSProcessPerformanceEvents final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessPerformanceEvents(parent_folder)); + } + +private: + explicit ProcFSProcessPerformanceEvents(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("perf_events"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + InterruptDisabler disabler; + if (!m_parent_folder->m_associated_process->perf_events()) { + dbgln("ProcFS: No perf events for {}", m_parent_folder->m_associated_process->pid()); + return false; + } + return m_parent_folder->m_associated_process->perf_events()->to_json(builder); + } +}; + +class ProcFSProcessOverallFileDescriptions final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessOverallFileDescriptions(parent_folder)); + } + +private: + explicit ProcFSProcessOverallFileDescriptions(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("fds"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + JsonArraySerializer array { builder }; + auto process = m_parent_folder->m_associated_process; + if (process->number_of_open_file_descriptors() == 0) { + array.finish(); + return true; + } + + for (int i = 0; i < process->max_open_file_descriptors(); ++i) { + auto description = process->file_description(i); + if (!description) + continue; + bool cloexec = process->fd_flags(i) & FD_CLOEXEC; + + auto description_object = array.add_object(); + description_object.add("fd", i); + description_object.add("absolute_path", description->absolute_path()); + description_object.add("seekable", description->file().is_seekable()); + description_object.add("class", description->file().class_name()); + description_object.add("offset", description->offset()); + description_object.add("cloexec", cloexec); + description_object.add("blocking", description->is_blocking()); + description_object.add("can_read", description->can_read()); + description_object.add("can_write", description->can_write()); + } + array.finish(); + return true; + } +}; + +class ProcFSProcessRoot final : public ProcFSExposedLink { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessRoot(parent_folder)); + } + +private: + explicit ProcFSProcessRoot(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedLink("root"sv) + , m_parent_process_directory(parent_folder) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.append_bytes(m_parent_process_directory->m_associated_process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer()); + return true; + } + NonnullRefPtr m_parent_process_directory; +}; + +class ProcFSProcessVirtualMemory final : public ProcFSProcessInformation { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessVirtualMemory(parent_folder)); + } + +private: + explicit ProcFSProcessVirtualMemory(const ProcFSProcessFolder& parent_folder) + : ProcFSProcessInformation("vm"sv, parent_folder) + { + } + virtual bool output(KBufferBuilder& builder) override + { + auto process = m_parent_folder->m_associated_process; + JsonArraySerializer array { builder }; + { + ScopedSpinLock lock(process->space().get_lock()); + for (auto& region : process->space().regions()) { + if (!region->is_user() && !Process::current()->is_superuser()) + continue; + auto region_object = array.add_object(); + region_object.add("readable", region->is_readable()); + region_object.add("writable", region->is_writable()); + region_object.add("executable", region->is_executable()); + region_object.add("stack", region->is_stack()); + region_object.add("shared", region->is_shared()); + region_object.add("syscall", region->is_syscall_region()); + region_object.add("purgeable", region->vmobject().is_anonymous()); + if (region->vmobject().is_anonymous()) { + region_object.add("volatile", static_cast(region->vmobject()).is_any_volatile()); + } + region_object.add("cacheable", region->is_cacheable()); + region_object.add("address", region->vaddr().get()); + region_object.add("size", region->size()); + region_object.add("amount_resident", region->amount_resident()); + region_object.add("amount_dirty", region->amount_dirty()); + region_object.add("cow_pages", region->cow_pages()); + region_object.add("name", region->name()); + region_object.add("vmobject", region->vmobject().class_name()); + + StringBuilder pagemap_builder; + for (size_t i = 0; i < region->page_count(); ++i) { + auto* page = region->physical_page(i); + if (!page) + pagemap_builder.append('N'); + else if (page->is_shared_zero_page() || page->is_lazy_committed_page()) + pagemap_builder.append('Z'); + else + pagemap_builder.append('P'); + } + region_object.add("pagemap", pagemap_builder.to_string()); + } + } + array.finish(); + return true; + } +}; + +class ProcFSProcessCurrentWorkDirectory final : public ProcFSExposedLink { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessCurrentWorkDirectory(parent_folder)); + } + +private: + explicit ProcFSProcessCurrentWorkDirectory(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedLink("cwd"sv) + , m_parent_process_directory(parent_folder) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + builder.append_bytes(m_parent_process_directory->m_associated_process->current_directory().absolute_path().bytes()); + return true; + } + + NonnullRefPtr m_parent_process_directory; +}; + +class ProcFSProcessBinary final : public ProcFSExposedLink { +public: + static NonnullRefPtr create(const ProcFSProcessFolder& parent_folder) + { + return adopt_ref(*new (nothrow) ProcFSProcessBinary(parent_folder)); + } + + virtual mode_t required_mode() const override + { + if (!m_parent_process_directory->m_associated_process->executable()) + return 0; + return ProcFSExposedComponent::required_mode(); + } + +private: + explicit ProcFSProcessBinary(const ProcFSProcessFolder& parent_folder) + : ProcFSExposedLink("exe"sv) + , m_parent_process_directory(parent_folder) + { + } + virtual bool acquire_link(KBufferBuilder& builder) override + { + auto* custody = m_parent_process_directory->m_associated_process->executable(); + if (!custody) + return false; + builder.append(custody->absolute_path().bytes()); + return true; + } + + NonnullRefPtr m_parent_process_directory; +}; + +NonnullRefPtr ProcFSProcessFolder::create(const Process& process) +{ + auto folder = adopt_ref_if_nonnull(new (nothrow) ProcFSProcessFolder(process)).release_nonnull(); + folder->m_components.append(ProcFSProcessUnveil::create(folder)); + folder->m_components.append(ProcFSProcessPerformanceEvents::create(folder)); + folder->m_components.append(ProcFSProcessFileDescriptions::create(folder)); + folder->m_components.append(ProcFSProcessOverallFileDescriptions::create(folder)); + folder->m_components.append(ProcFSProcessRoot::create(folder)); + folder->m_components.append(ProcFSProcessVirtualMemory::create(folder)); + folder->m_components.append(ProcFSProcessCurrentWorkDirectory::create(folder)); + folder->m_components.append(ProcFSProcessBinary::create(folder)); + folder->m_components.append(ProcFSProcessStacks::create(folder)); + return folder; +} + +ProcFSProcessFolder::ProcFSProcessFolder(const Process& process) + : ProcFSExposedFolder(String::formatted("{:d}", process.pid().value()), ProcFSComponentsRegistrar::the().root_folder()) + , m_associated_process(process) +{ +} + +KResultOr ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const +{ + VERIFY(offset == 0); + Locker locker(m_lock); + KBufferBuilder builder; + if (!const_cast(*this).acquire_link(builder)) + return KResult(EFAULT); + auto blob = builder.build(); + if (!blob) + return KResult(EFAULT); + + ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); + if (!buffer.write(blob->data() + offset, nread)) + return KResult(EFAULT); + return nread; +} + +NonnullRefPtr ProcFSExposedLink::to_inode(const ProcFS& procfs_instance) const +{ + return ProcFSLinkInode::create(procfs_instance, *this); +} + +NonnullRefPtr ProcFSExposedComponent::to_inode(const ProcFS& procfs_instance) const +{ + return ProcFSInode::create(procfs_instance, *this); +} + +NonnullRefPtr ProcFSExposedFolder::to_inode(const ProcFS& procfs_instance) const +{ + return ProcFSDirectoryInode::create(procfs_instance, *this); +} + +void ProcFSExposedFolder::add_component(const ProcFSExposedComponent&) +{ + TODO(); +} + +RefPtr ProcFSExposedFolder::lookup(StringView name) +{ + for (auto& component : m_components) { + if (component.name() == name) { + return component; + } + } + return {}; +} + +KResult ProcFSExposedFolder::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(ProcFSComponentsRegistrar::the().m_lock); + VERIFY(m_parent_folder); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + for (auto& component : m_components) { + InodeIdentifier identifier = { fsid, component.component_index() }; + callback({ component.name(), identifier, 0 }); + } + return KSuccess; +} + +RefPtr ProcFSRootFolder::lookup(StringView name) +{ + if (auto candidate = ProcFSExposedFolder::lookup(name); !candidate.is_null()) + return candidate; + + for (auto& component : m_process_folders) { + if (component.name() == name) { + return component; + } + } + return {}; +} + +KResult ProcFSRootFolder::traverse_as_directory(unsigned fsid, Function callback) const +{ + Locker locker(ProcFSComponentsRegistrar::the().m_lock); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, 0 }, 0 }); + + for (auto& component : m_components) { + InodeIdentifier identifier = { fsid, component.component_index() }; + callback({ component.name(), identifier, 0 }); + } + for (auto& component : m_process_folders) { + InodeIdentifier identifier = { fsid, component.component_index() }; + callback({ component.name(), identifier, 0 }); + } + return KSuccess; +} + +} diff --git a/Kernel/ProcessExposed.h b/Kernel/ProcessExposed.h new file mode 100644 index 0000000000..105af7c24d --- /dev/null +++ b/Kernel/ProcessExposed.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class ProcFS; +class ProcFSExposedComponent; +class ProcFSExposedFolder; +class ProcFSRootFolder; +class ProcFSBusDirectory; +class ProcFSSystemBoolean; + +class ProcFSComponentsRegistrar { + friend class ProcFS; + friend class ProcFSExposedComponent; + friend class ProcFSExposedFolder; + friend class ProcFSRootFolder; + +public: + static ProcFSComponentsRegistrar& the(); + + static void initialize(); + + InodeIndex allocate_inode_index() const; + + ProcFSComponentsRegistrar(); + void register_new_bus_folder(ProcFSExposedFolder&); + + const ProcFSBusDirectory& buses_folder() const; + + void register_new_process(Process&); + void unregister_process(Process&); + + ProcFSRootFolder& root_folder() { return *m_root_folder; } + +private: + Lock m_lock; + NonnullRefPtr m_root_folder; +}; + +class ProcFSExposedComponent : public RefCounted { +public: + virtual KResultOr entries_count() const { VERIFY_NOT_REACHED(); }; + StringView name() const { return m_name->view(); } + virtual KResultOr read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const { VERIFY_NOT_REACHED(); } + virtual KResult traverse_as_directory(unsigned, Function) const { VERIFY_NOT_REACHED(); } + virtual RefPtr lookup(StringView) { VERIFY_NOT_REACHED(); }; + virtual KResultOr write_bytes(off_t, size_t, const UserOrKernelBuffer&, FileDescription*) { return KResult(EROFS); } + virtual size_t size() const { return 0; } + + virtual mode_t required_mode() const { return 0444; } + virtual uid_t owner_user() const { return 0; } + virtual gid_t owner_group() const { return 0; } + + virtual void prepare_for_deletion() { } + virtual KResult refresh_data(FileDescription&) const + { + return KSuccess; + } + + virtual NonnullRefPtr to_inode(const ProcFS& procfs_instance) const; + + size_t component_index() const { return m_component_index; }; + + virtual ~ProcFSExposedComponent() = default; + +protected: + explicit ProcFSExposedComponent(StringView name); + ProcFSExposedComponent(StringView name, InodeIndex preallocated_index); + +private: + OwnPtr m_name; + size_t m_component_index; +}; + +class ProcFSExposedFolder : public ProcFSExposedComponent { + friend class ProcFSProcessFolder; + friend class ProcFSComponentsRegistrar; + +public: + virtual KResultOr entries_count() const override { return m_components.size(); }; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + void add_component(const ProcFSExposedComponent&); + + virtual void prepare_for_deletion() override + { + m_components.clear(); + m_parent_folder.clear(); + } + virtual mode_t required_mode() const override { return 0555; } + + virtual NonnullRefPtr to_inode(const ProcFS& procfs_instance) const override final; + +protected: + explicit ProcFSExposedFolder(StringView name); + ProcFSExposedFolder(StringView name, const ProcFSExposedFolder& parent_folder); + NonnullRefPtrVector m_components; + RefPtr m_parent_folder; +}; + +class ProcFSExposedLink : public ProcFSExposedComponent { +public: + virtual NonnullRefPtr to_inode(const ProcFS& procfs_instance) const override final; + + virtual KResultOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const override; + +protected: + virtual bool acquire_link(KBufferBuilder& builder) = 0; + explicit ProcFSExposedLink(StringView name); + ProcFSExposedLink(StringView name, InodeIndex preallocated_index); + mutable Lock m_lock { "ProcFSLink" }; +}; + +class ProcFSRootFolder; +class ProcFSProcessInformation; + +class ProcFSProcessFolder final : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + friend class ProcFSRootFolder; + friend class ProcFSProcessInformation; + friend class ProcFSProcessUnveil; + friend class ProcFSProcessPerformanceEvents; + friend class ProcFSProcessFileDescription; + friend class ProcFSProcessFileDescriptions; + friend class ProcFSProcessOverallFileDescriptions; + friend class ProcFSProcessRoot; + friend class ProcFSProcessVirtualMemory; + friend class ProcFSProcessCurrentWorkDirectory; + friend class ProcFSProcessBinary; + friend class ProcFSProcessStacks; + +public: + static NonnullRefPtr create(const Process&); + NonnullRefPtr associated_process() { return m_associated_process; } + + virtual uid_t owner_user() const override { return m_associated_process->uid(); } + virtual gid_t owner_group() const override { return m_associated_process->gid(); } + +private: + IntrusiveListNode> m_list_node; + + explicit ProcFSProcessFolder(const Process&); + NonnullRefPtr m_associated_process; +}; + +class ProcFSRootFolder; + +class ProcFSBusDirectory : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + +public: + static NonnullRefPtr must_create(const ProcFSRootFolder& parent_folder); + +private: + ProcFSBusDirectory(const ProcFSRootFolder& parent_folder); +}; + +class ProcFSRootFolder final : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + +public: + virtual RefPtr lookup(StringView name) override; + + RefPtr process_folder_for(Process&); + static NonnullRefPtr must_create(); + virtual ~ProcFSRootFolder(); + +private: + virtual KResult traverse_as_directory(unsigned, Function) const override; + ProcFSRootFolder(); + + RefPtr m_buses_folder; + IntrusiveList, &ProcFSProcessFolder::m_list_node> m_process_folders; +}; + +class ProcFSGlobalInformation : public ProcFSExposedComponent { +public: + virtual ~ProcFSGlobalInformation() override {}; + + virtual KResultOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const override; + + virtual mode_t required_mode() const override { return 0444; } + +protected: + explicit ProcFSGlobalInformation(StringView name) + : ProcFSExposedComponent(name) + { + } + virtual KResult refresh_data(FileDescription&) const override; + virtual bool output(KBufferBuilder& builder) = 0; + + mutable SpinLock m_refresh_lock; +}; + +class ProcFSSystemBoolean : public ProcFSGlobalInformation { +public: + virtual bool value() const = 0; + virtual void set_value(bool new_value) = 0; + +protected: + explicit ProcFSSystemBoolean(StringView name) + : ProcFSGlobalInformation(name) + { + } + virtual bool output(KBufferBuilder& builder) override + { + builder.appendff("{}\n", value()); + return true; + } +}; + +class ProcFSProcessInformation : public ProcFSExposedComponent { +public: + virtual ~ProcFSProcessInformation() override {}; + + virtual KResultOr read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const override; + + virtual uid_t owner_user() const override { return m_parent_folder->m_associated_process->uid(); } + virtual gid_t owner_group() const override { return m_parent_folder->m_associated_process->gid(); } + +protected: + ProcFSProcessInformation(StringView name, const ProcFSProcessFolder& process_folder) + : ProcFSExposedComponent(name) + , m_parent_folder(process_folder) + { + } + + virtual KResult refresh_data(FileDescription&) const override; + virtual bool output(KBufferBuilder& builder) = 0; + + NonnullRefPtr m_parent_folder; + mutable SpinLock m_refresh_lock; +}; + +} diff --git a/Kernel/Syscalls/fork.cpp b/Kernel/Syscalls/fork.cpp index 1021233f03..4619424fc6 100644 --- a/Kernel/Syscalls/fork.cpp +++ b/Kernel/Syscalls/fork.cpp @@ -109,8 +109,7 @@ KResultOr Process::sys$fork(RegisterState& regs) child->m_master_tls_region = child_region; } - ScopedSpinLock processes_lock(g_processes_lock); - g_processes->prepend(*child); + Process::register_new(*child); } PerformanceManager::add_process_created_event(*child); diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index e18dd2c782..3eca547e4b 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ Thread::Thread(NonnullRefPtr process, NonnullOwnPtr kernel_stac , m_kernel_stack_region(move(kernel_stack_region)) , m_name(m_process->name()) , m_block_timer(block_timer) + , m_global_procfs_inode_index(ProcFSComponentsRegistrar::the().allocate_inode_index()) { bool is_first_thread = m_process->add_thread(*this); if (is_first_thread) { diff --git a/Kernel/Thread.h b/Kernel/Thread.h index f71d51d22f..ad758aa5e7 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1184,6 +1185,7 @@ public: bool may_die_immediately() const { return m_may_die_immediately; } void set_may_die_immediately(bool flag) { m_may_die_immediately = flag; } + InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; } private: Thread(NonnullRefPtr, NonnullOwnPtr, NonnullRefPtr); @@ -1328,6 +1330,10 @@ private: RefPtr m_block_timer; + // Note: This is needed so when we generate thread stack inodes for ProcFS, we know that + // we assigned a global Inode index to it so we can use it later + InodeIndex m_global_procfs_inode_index; + bool m_is_profiling_suppressed { false }; void yield_without_holding_big_lock(); diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 10e4e8b1eb..908ef0f248 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -148,6 +149,7 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init() // Initialize the PCI Bus as early as possible, for early boot (PCI based) serial logging SystemRegistrar::initialize(); + ProcFSComponentsRegistrar::initialize(); PCI::initialize(); PCISerialDevice::detect();