浏览代码

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.
Liav A 4 年之前
父节点
当前提交
12b6e69150

+ 1 - 0
Kernel/CMakeLists.txt

@@ -148,6 +148,7 @@ set(KERNEL_SOURCES
     Panic.cpp
     PerformanceEventBuffer.cpp
     Process.cpp
+    ProcessExposed.cpp
     ProcessGroup.cpp
     RTC.cpp
     Random.cpp

+ 159 - 0
Kernel/Devices/USB/UHCIController.cpp

@@ -5,12 +5,17 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <AK/JsonArray.h>
+#include <AK/JsonArraySerializer.h>
+#include <AK/JsonObjectSerializer.h>
 #include <AK/Platform.h>
 #include <Kernel/CommandLine.h>
 #include <Kernel/Debug.h>
 #include <Kernel/Devices/USB/UHCIController.h>
 #include <Kernel/Devices/USB/USBRequest.h>
+#include <Kernel/KBufferBuilder.h>
 #include <Kernel/Process.h>
+#include <Kernel/ProcessExposed.h>
 #include <Kernel/Sections.h>
 #include <Kernel/StdLib.h>
 #include <Kernel/Time/TimeManagement.h>
@@ -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<ProcFSUSBDeviceInformation> create(USB::Device&);
+
+    RefPtr<USB::Device> 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<ProcFSUSBDeviceInformation, RefPtr<ProcFSUSBDeviceInformation>> m_list_node;
+    RefPtr<USB::Device> m_device;
+};
+
+class ProcFSUSBBusFolder final : public ProcFSExposedFolder {
+    friend class ProcFSComponentsRegistrar;
+
+public:
+    static void initialize();
+    void plug(USB::Device&);
+    void unplug(USB::Device&);
+
+    virtual KResultOr<size_t> entries_count() const override;
+    virtual KResult traverse_as_directory(unsigned, Function<bool(const FS::DirectoryEntryView&)>) const override;
+    virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
+
+private:
+    ProcFSUSBBusFolder(const ProcFSBusDirectory&);
+
+    RefPtr<ProcFSUSBDeviceInformation> device_node_for(USB::Device& device);
+
+    IntrusiveList<ProcFSUSBDeviceInformation, RefPtr<ProcFSUSBDeviceInformation>, &ProcFSUSBDeviceInformation::m_list_node> m_device_nodes;
+    mutable SpinLock<u8> m_lock;
+};
+
+KResultOr<size_t> ProcFSUSBBusFolder::entries_count() const
+{
+    ScopedSpinLock lock(m_lock);
+    return m_device_nodes.size_slow();
+}
+KResult ProcFSUSBBusFolder::traverse_as_directory(unsigned fsid, Function<bool(const FS::DirectoryEntryView&)> 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<ProcFSExposedComponent> 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<ProcFSUSBDeviceInformation> ProcFSUSBBusFolder::device_node_for(USB::Device& device)
+{
+    RefPtr<USB::Device> 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> 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));
                         }
                     }
                 }

+ 110 - 1646
Kernel/FileSystem/ProcFS.cpp

@@ -1,264 +1,66 @@
 /*
  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2021, Spencer Dixon <spencercdixon@gmail.com>
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
-#include <AK/JsonObject.h>
-#include <AK/JsonObjectSerializer.h>
-#include <AK/JsonValue.h>
-#include <AK/ScopeGuard.h>
-#include <AK/UBSanitizer.h>
-#include <Kernel/Arch/x86/InterruptDisabler.h>
-#include <Kernel/Arch/x86/ProcessorInfo.h>
-#include <Kernel/CommandLine.h>
-#include <Kernel/ConsoleDevice.h>
+#include <AK/Singleton.h>
 #include <Kernel/Debug.h>
-#include <Kernel/Devices/BlockDevice.h>
-#include <Kernel/Devices/HID/HIDManagement.h>
-#include <Kernel/Devices/USB/UHCIController.h>
-#include <Kernel/Devices/USB/USBDevice.h>
 #include <Kernel/FileSystem/Custody.h>
 #include <Kernel/FileSystem/FileBackedFileSystem.h>
 #include <Kernel/FileSystem/FileDescription.h>
 #include <Kernel/FileSystem/ProcFS.h>
 #include <Kernel/FileSystem/VirtualFileSystem.h>
 #include <Kernel/Heap/kmalloc.h>
-#include <Kernel/Interrupts/GenericInterruptHandler.h>
-#include <Kernel/Interrupts/InterruptManagement.h>
-#include <Kernel/KBufferBuilder.h>
-#include <Kernel/Module.h>
-#include <Kernel/Net/LocalSocket.h>
-#include <Kernel/Net/NetworkAdapter.h>
-#include <Kernel/Net/NetworkingManagement.h>
-#include <Kernel/Net/Routing.h>
-#include <Kernel/Net/TCPSocket.h>
-#include <Kernel/Net/UDPSocket.h>
-#include <Kernel/PCI/Access.h>
-#include <Kernel/PerformanceEventBuffer.h>
-#include <Kernel/Process.h>
-#include <Kernel/Scheduler.h>
-#include <Kernel/StdLib.h>
-#include <Kernel/TTY/TTY.h>
-#include <Kernel/VM/AnonymousVMObject.h>
-#include <Kernel/VM/MemoryManager.h>
+#include <Kernel/Sections.h>
 #include <LibC/errno_numbers.h>
 
 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<ProcFSComponentsRegistrar> 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)
-{
-    return identifier.index().value() >> 16u;
-}
-
-static inline ThreadID to_tid(const InodeIdentifier& identifier)
+ProcFSComponentsRegistrar& ProcFSComponentsRegistrar::the()
 {
-    // Sneakily, use the exact same mechanism.
-    return to_pid(identifier).value();
+    return *s_the;
 }
 
-static inline ProcParentDirectory to_proc_parent_directory(const InodeIdentifier& identifier)
+UNMAP_AFTER_INIT void ProcFSComponentsRegistrar::initialize()
 {
-    return (ProcParentDirectory)((identifier.index().value() >> 12) & 0xf);
+    VERIFY(!s_the.is_initialized());
+    s_the.ensure_instance();
 }
 
-static inline ProcFileType to_proc_file_type(const InodeIdentifier& identifier)
+UNMAP_AFTER_INIT ProcFSComponentsRegistrar::ProcFSComponentsRegistrar()
+    : m_root_folder(ProcFSRootFolder::must_create())
 {
-    return (ProcFileType)(identifier.index().value() & 0xfff);
 }
 
-static inline int to_fd(const InodeIdentifier& identifier)
+const ProcFSBusDirectory& ProcFSComponentsRegistrar::buses_folder() const
 {
-    VERIFY(to_proc_parent_directory(identifier) == PDI_PID_fd);
-    return (identifier.index().value() & 0xfff) - FI_MaxStaticFileIndex;
+    return *m_root_folder->m_buses_folder;
 }
 
-static inline size_t to_sys_index(const InodeIdentifier& identifier)
+void ProcFSComponentsRegistrar::register_new_bus_folder(ProcFSExposedFolder& new_bus_folder)
 {
-    VERIFY(to_proc_parent_directory(identifier) == PDI_Root_sys);
-    VERIFY(to_proc_file_type(identifier) == FI_Root_sys_variable);
-    return identifier.index().value() >> 16u;
+    VERIFY(!m_root_folder->m_buses_folder.is_null());
+    m_root_folder->m_buses_folder->m_components.append(new_bus_folder);
 }
 
-static inline u8 to_usb_device_address(const InodeIdentifier& identifier)
+void ProcFSComponentsRegistrar::register_new_process(Process& new_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;
+    Locker locker(m_lock);
+    m_root_folder->m_process_folders.append(ProcFSProcessFolder::create(new_process));
 }
 
-static inline InodeIdentifier to_identifier(unsigned fsid, ProcParentDirectory parent, ProcessID pid, ProcFileType proc_file_type)
+void ProcFSComponentsRegistrar::unregister_process(Process& deleted_process)
 {
-    return { fsid, ((unsigned)parent << 12u) | ((unsigned)pid.value() << 16u) | (unsigned)proc_file_type };
+    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_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<KBufferImpl> buffer;
-};
-
 RefPtr<ProcFS> ProcFS::create()
 {
     return adopt_ref_if_nonnull(new (nothrow) ProcFS);
@@ -268,1534 +70,196 @@ 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<const AnonymousVMObject&>(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<KBufferBuilder> 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<String, OwnPtr<Module>>* 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<u64>(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<const FileBackedFS&>(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<KBufferBuilder> 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<void()> notify_callback;
-    void* address { nullptr };
-
-    static SysVariable& for_inode(InodeIdentifier);
-
-    void notify()
-    {
-        if (notify_callback)
-            notify_callback();
-    }
-};
-
-static Vector<SysVariable, 16>* s_sys_variables;
-
-static inline Vector<SysVariable, 16>& sys_variables()
-{
-    if (s_sys_variables == nullptr) {
-        s_sys_variables = new Vector<SysVariable, 16>;
-        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<Lockable<bool>*>(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<size_t> 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<Lockable<bool>*>(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<Lockable<String>*>(variable.address);
-    Locker locker(lockable_string->lock(), Lock::Mode::Shared);
-    builder.append_bytes(lockable_string->resource().bytes());
-    return true;
-}
-
-static KResultOr<size_t> 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<Lockable<String>*>(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<bool>& var, Function<void()>&& 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<bool>* kmalloc_stack_helper;
-    static Lockable<bool>* ubsan_deadly_helper;
-    static Lockable<bool>* caps_lock_to_ctrl_helper;
-
-    if (kmalloc_stack_helper == nullptr) {
-        kmalloc_stack_helper = new Lockable<bool>();
-        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<bool>();
-        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<bool>();
-        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<Inode> ProcFS::root_inode() const
 {
     return *m_root_inode;
 }
 
-RefPtr<Inode> ProcFS::get_inode(InodeIdentifier inode_id) const
-{
-    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<ProcFS&>(*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;
-}
-
-ProcFSInode::ProcFSInode(ProcFS& fs, InodeIndex index)
-    : Inode(fs, index)
-{
-}
-
-ProcFSInode::~ProcFSInode()
+NonnullRefPtr<ProcFSInode> ProcFSInode::create(const ProcFS& fs, const ProcFSExposedComponent& component)
 {
-    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);
+    return adopt_ref(*new (nothrow) ProcFSInode(fs, component));
 }
 
-RefPtr<Process> ProcFSInode::process() const
+ProcFSInode::ProcFSInode(const ProcFS& fs, const ProcFSExposedComponent& component)
+    : Inode(const_cast<ProcFS&>(fs), component.component_index())
+    , m_associated_component(component)
 {
-    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<ProcFSInodeData&>(*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());
     }
 }
 
-InodeMetadata ProcFSInode::metadata() const
+ProcFSInode::~ProcFSInode()
 {
-    dbgln_if(PROCFS_DEBUG, "ProcFSInode::metadata({})", index());
-    InodeMetadata metadata;
-    metadata.inode = identifier();
-    metadata.ctime = mepoch;
-    metadata.atime = mepoch;
-    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<size_t> ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const
+ProcFS::ProcFS()
+    : m_root_inode(ProcFSComponentsRegistrar::the().m_root_folder->to_inode(*this))
 {
-    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<KBufferImpl> data_buffer = static_cast<ProcFSInodeData&>(*description->data()).buffer;
-
-    if (!data_buffer || (size_t)offset >= data_buffer->size())
-        return 0;
-
-    size_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(count));
-    if (!buffer.write(data_buffer->data() + offset, nread))
-        return EFAULT;
-
-    return nread;
 }
 
-InodeIdentifier ProcFS::ProcFSDirectoryEntry::identifier(unsigned fsid) const
+KResultOr<size_t> ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const
 {
-    return to_identifier(fsid, PDI_Root, 0, (ProcFileType)proc_file_type);
+    return m_associated_component->read_bytes(offset, count, buffer, fd);
 }
 
-KResult ProcFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntryView&)> callback) const
+StringView ProcFSInode::name() 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<USB::Device::PortNumber>(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;
+    return m_associated_component->name();
 }
 
-RefPtr<Inode> ProcFSInode::lookup(StringView name)
+KResult ProcFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntryView&)>) const
 {
-    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 {};
+    VERIFY_NOT_REACHED();
 }
 
-void ProcFSInode::flush_metadata()
+RefPtr<Inode> ProcFSInode::lookup(StringView)
 {
+    VERIFY_NOT_REACHED();
 }
 
-KResultOr<size_t> ProcFSInode::write_bytes(off_t offset, size_t size, const UserOrKernelBuffer& buffer, FileDescription*)
+InodeMetadata ProcFSInode::metadata() const
 {
-    // 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<size_t> (*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;
+    Locker locker(m_lock);
+    InodeMetadata metadata;
+    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;
+    return metadata;
 }
 
-KResultOr<NonnullRefPtr<Custody>> ProcFSInode::resolve_as_link(Custody& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) const
+void ProcFSInode::flush_metadata()
 {
-    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<ProcFS&>(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)
+KResultOr<size_t> ProcFSInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd)
 {
-    return KSuccess;
+    return m_associated_component->write_bytes(offset, count, buffer, fd);
 }
 
-ProcFSProxyInode::ProcFSProxyInode(ProcFS& fs, FileDescription& fd)
-    : Inode(fs, 0)
-    , m_fd(fd)
+KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(const String&, mode_t, dev_t, uid_t, gid_t)
 {
+    return EROFS;
 }
 
-ProcFSProxyInode::~ProcFSProxyInode()
+KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t)
 {
+    return EROFS;
 }
 
-KResult ProcFSProxyInode::attach(FileDescription& fd)
+KResult ProcFSInode::remove_child(const StringView&)
 {
-    return m_fd->inode()->attach(fd);
+    return EROFS;
 }
 
-void ProcFSProxyInode::did_seek(FileDescription& fd, off_t new_offset)
+KResultOr<size_t> ProcFSInode::directory_entry_count() const
 {
-    return m_fd->inode()->did_seek(fd, new_offset);
+    VERIFY_NOT_REACHED();
 }
 
-InodeMetadata ProcFSProxyInode::metadata() const
+KResult ProcFSInode::chmod(mode_t)
 {
-    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;
+    return EPERM;
 }
 
-KResultOr<NonnullRefPtr<Inode>> ProcFSProxyInode::create_child(const String& name, mode_t mode, dev_t dev, uid_t uid, gid_t gid)
+KResult ProcFSInode::chown(uid_t, gid_t)
 {
-    if (!m_fd->inode())
-        return EINVAL;
-    return m_fd->inode()->create_child(name, mode, dev, uid, gid);
+    return EPERM;
 }
 
-KResult ProcFSProxyInode::add_child(Inode& child, const StringView& name, mode_t mode)
+KResult ProcFSInode::truncate(u64)
 {
-    if (!m_fd->inode())
-        return EINVAL;
-    return m_fd->inode()->add_child(child, name, mode);
+    return EPERM;
 }
 
-KResult ProcFSProxyInode::remove_child(const StringView& name)
+NonnullRefPtr<ProcFSDirectoryInode> ProcFSDirectoryInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component)
 {
-    if (!m_fd->inode())
-        return EINVAL;
-    return m_fd->inode()->remove_child(name);
+    return adopt_ref(*new (nothrow) ProcFSDirectoryInode(procfs, component));
 }
 
-RefPtr<Inode> ProcFSProxyInode::lookup(StringView name)
+ProcFSDirectoryInode::ProcFSDirectoryInode(const ProcFS& fs, const ProcFSExposedComponent& component)
+    : ProcFSInode(fs, component)
+    , m_parent_fs(const_cast<ProcFS&>(fs))
 {
-    if (!m_fd->inode())
-        return {};
-    return m_fd->inode()->lookup(name);
 }
 
-KResultOr<size_t> ProcFSProxyInode::directory_entry_count() const
+ProcFSDirectoryInode::~ProcFSDirectoryInode()
 {
-    if (!m_fd->inode())
-        return EINVAL;
-    return m_fd->inode()->directory_entry_count();
 }
-
-KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(const String&, mode_t, dev_t, uid_t, gid_t)
+InodeMetadata ProcFSDirectoryInode::metadata() const
 {
-    return EPERM;
+    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 ProcFSInode::add_child(Inode&, const StringView&, mode_t)
+KResult ProcFSDirectoryInode::traverse_as_directory(Function<bool(const FS::DirectoryEntryView&)> callback) const
 {
-    return EPERM;
+    Locker locker(m_parent_fs.m_lock);
+    return m_associated_component->traverse_as_directory(m_parent_fs.fsid(), move(callback));
 }
 
-KResult ProcFSInode::remove_child([[maybe_unused]] const StringView& name)
+RefPtr<Inode> ProcFSDirectoryInode::lookup(StringView name)
 {
-    return EPERM;
+    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<size_t> ProcFSInode::directory_entry_count() const
+KResultOr<size_t> ProcFSDirectoryInode::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;
+    Locker locker(m_lock);
+    return m_associated_component->entries_count();
 }
 
-KResult ProcFSInode::chmod(mode_t)
+NonnullRefPtr<ProcFSLinkInode> ProcFSLinkInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component)
 {
-    return EPERM;
+    return adopt_ref(*new (nothrow) ProcFSLinkInode(procfs, component));
 }
 
-ProcFS::ProcFS()
+ProcFSLinkInode::ProcFSLinkInode(const ProcFS& fs, const ProcFSExposedComponent& component)
+    : ProcFSInode(fs, component)
 {
-    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
+InodeMetadata ProcFSLinkInode::metadata() 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<ProcFSDirectoryEntry*>(&m_entries[proc_file_type]);
-    return nullptr;
+    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;
 }
 
-KResult ProcFSInode::chown(uid_t, gid_t)
-{
-    return EPERM;
-}
 }

+ 33 - 72
Kernel/FileSystem/ProcFS.h

@@ -12,130 +12,91 @@
 #include <Kernel/FileSystem/Inode.h>
 #include <Kernel/KBufferBuilder.h>
 #include <Kernel/Lock.h>
+#include <Kernel/ProcessExposed.h>
 
 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<ProcFS> create();
 
     virtual bool initialize() override;
-    virtual const char* class_name() const override;
+    virtual const char* class_name() const override { return "ProcFS"; }
 
     virtual NonnullRefPtr<Inode> root_inode() const override;
 
-    static void add_sys_bool(String&&, Lockable<bool>&, Function<void()>&& 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<size_t> (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t) = nullptr, RefPtr<ProcFSInode>&& 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<size_t> (*write_callback)(InodeIdentifier, const UserOrKernelBuffer&, size_t);
-        RefPtr<ProcFSInode> inode;
-        InodeIdentifier identifier(unsigned fsid) const;
-    };
-
-    RefPtr<Inode> get_inode(InodeIdentifier) const;
-    ProcFSDirectoryEntry* get_directory_entry(InodeIdentifier) const;
-
-    Vector<ProcFSDirectoryEntry> m_entries;
-
-    mutable Lock m_inodes_lock;
-    mutable HashMap<unsigned, ProcFSInode*> m_inodes;
-    RefPtr<ProcFSInode> m_root_inode;
+    NonnullRefPtr<ProcFSDirectoryInode> m_root_inode;
 };
 
-class ProcFSInode final : public Inode {
+class ProcFSInode : public Inode {
     friend class ProcFS;
 
 public:
+    static NonnullRefPtr<ProcFSInode> 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<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override;
-    virtual InodeMetadata metadata() const override;
     virtual KResult traverse_as_directory(Function<bool(const FS::DirectoryEntryView&)>) const override;
     virtual RefPtr<Inode> lookup(StringView name) override;
     virtual void flush_metadata() override;
+    virtual InodeMetadata metadata() const override;
     virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override;
     virtual KResultOr<NonnullRefPtr<Inode>> 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<size_t> directory_entry_count() const override;
     virtual KResult chmod(mode_t) override;
     virtual KResult chown(uid_t, gid_t) override;
-    virtual KResultOr<NonnullRefPtr<Custody>> resolve_as_link(Custody& base, RefPtr<Custody>* 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;
+
+    NonnullRefPtr<ProcFSExposedComponent> m_associated_component;
+};
 
-    KResult refresh_data(FileDescription&) const;
+class ProcFSLinkInode : public ProcFSInode {
+    friend class ProcFS;
 
-    RefPtr<Process> process() const;
+public:
+    static NonnullRefPtr<ProcFSLinkInode> create(const ProcFS&, const ProcFSExposedComponent&);
 
-    ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); }
-    const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); }
-    ProcFSInode(ProcFS&, InodeIndex);
+protected:
+    ProcFSLinkInode(const ProcFS&, const ProcFSExposedComponent&);
+    virtual InodeMetadata metadata() const override;
 };
 
-class ProcFSProxyInode final : public Inode {
-    friend class ProcFSInode;
+class ProcFSDirectoryInode : public ProcFSInode {
+    friend class ProcFS;
 
 public:
-    virtual ~ProcFSProxyInode() override;
+    static NonnullRefPtr<ProcFSDirectoryInode> create(const ProcFS&, const ProcFSExposedComponent&);
+    virtual ~ProcFSDirectoryInode() override;
 
-private:
+protected:
+    ProcFSDirectoryInode(const ProcFS&, const ProcFSExposedComponent&);
     // ^Inode
-    virtual KResult attach(FileDescription&) override;
-    virtual void did_seek(FileDescription&, off_t) override;
-    virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const override { VERIFY_NOT_REACHED(); }
     virtual InodeMetadata metadata() const override;
-    virtual KResult traverse_as_directory(Function<bool(const FS::DirectoryEntryView&)>) const override { VERIFY_NOT_REACHED(); }
+    virtual KResult traverse_as_directory(Function<bool(const FS::DirectoryEntryView&)>) const override;
     virtual RefPtr<Inode> lookup(StringView name) override;
-    virtual void flush_metadata() override {};
-    virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer&, FileDescription*) override { VERIFY_NOT_REACHED(); }
-    virtual KResultOr<NonnullRefPtr<Inode>> 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<size_t> 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<NonnullRefPtr<Custody>> resolve_as_link(Custody&, RefPtr<Custody>*, int, int) const override { VERIFY_NOT_REACHED(); }
-    virtual FileDescription* preopen_fd() override { return m_fd; }
-
-    ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); }
-    const ProcFS& fs() const { return static_cast<const ProcFS&>(Inode::fs()); }
 
-    ProcFSProxyInode(ProcFS&, FileDescription&);
-    static NonnullRefPtr<ProcFSProxyInode> create(ProcFS& fs, FileDescription& fd)
-    {
-        return adopt_ref(*new ProcFSProxyInode(fs, fd));
-    }
-
-    NonnullRefPtr<FileDescription> m_fd;
+    ProcFS& m_parent_fs;
 };
-
 }

+ 34 - 10
Kernel/Process.cpp

@@ -24,6 +24,7 @@
 #include <Kernel/PerformanceEventBuffer.h>
 #include <Kernel/PerformanceManager.h>
 #include <Kernel/Process.h>
+#include <Kernel/ProcessExposed.h>
 #include <Kernel/RTC.h>
 #include <Kernel/Sections.h>
 #include <Kernel/StdLib.h>
@@ -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<Process> new_process = process;
+    ScopedSpinLock lock(g_processes_lock);
+    g_processes->prepend(process);
+    ProcFSComponentsRegistrar::the().register_new_process(process);
+}
+
 RefPtr<Process> Process::create_user_process(RefPtr<Thread>& first_thread, const String& path, uid_t uid, gid_t gid, ProcessID parent_pid, int& error, Vector<String>&& arguments, Vector<String>&& environment, TTY* tty)
 {
     auto parts = path.split('/');
@@ -166,11 +176,7 @@ RefPtr<Process> Process::create_user_process(RefPtr<Thread>& 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> Process::create_kernel_process(RefPtr<Thread>& 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> Process::from_pid(ProcessID pid)
 
 RefPtr<FileDescription> Process::file_description(int fd) const
 {
+    ScopedSpinLock lock(m_fds_lock);
     if (fd < 0)
         return nullptr;
     if (static_cast<size_t>(fd) < m_fds.size())
@@ -400,6 +405,7 @@ RefPtr<FileDescription> 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<size_t>(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<Thread> 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<FileDescription>&& 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()

+ 23 - 1
Kernel/Process.h

@@ -17,6 +17,7 @@
 #include <AK/WeakPtr.h>
 #include <AK/Weakable.h>
 #include <Kernel/API/Syscall.h>
+#include <Kernel/FileSystem/FileDescription.h>
 #include <Kernel/FileSystem/InodeMetadata.h>
 #include <Kernel/Forward.h>
 #include <Kernel/FutexQueue.h>
@@ -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<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT);
     static RefPtr<Process> create_user_process(RefPtr<Thread>& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector<String>&& arguments = Vector<String>(), Vector<String>&& environment = Vector<String>(), TTY* = nullptr);
+    static void register_new(Process&);
     ~Process();
 
     static Vector<ProcessID> 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<FileDescription>&&, u32 flags = 0);
+        void refresh_inode_index();
 
     private:
         RefPtr<FileDescription> 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<FileDescriptionAndFlags> fds() const { return m_fds; }
+
+private:
     Vector<FileDescriptionAndFlags> m_fds;
 
     mutable RecursiveSpinLock m_thread_list_lock;
@@ -638,6 +659,7 @@ private:
 
     FutexQueues m_futex_queues;
     SpinLock<u8> m_futex_lock;
+    mutable SpinLock<u8> 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

+ 1626 - 0
Kernel/ProcessExposed.cpp

@@ -0,0 +1,1626 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/JsonArraySerializer.h>
+#include <AK/JsonObject.h>
+#include <AK/JsonObjectSerializer.h>
+#include <AK/JsonValue.h>
+#include <AK/UBSanitizer.h>
+#include <Kernel/Arch/x86/CPU.h>
+#include <Kernel/Arch/x86/InterruptDisabler.h>
+#include <Kernel/Arch/x86/ProcessorInfo.h>
+#include <Kernel/CommandLine.h>
+#include <Kernel/ConsoleDevice.h>
+#include <Kernel/Debug.h>
+#include <Kernel/Devices/BlockDevice.h>
+#include <Kernel/Devices/HID/HIDManagement.h>
+#include <Kernel/FileSystem/Custody.h>
+#include <Kernel/FileSystem/FileBackedFileSystem.h>
+#include <Kernel/FileSystem/FileDescription.h>
+#include <Kernel/FileSystem/ProcFS.h>
+#include <Kernel/FileSystem/VirtualFileSystem.h>
+#include <Kernel/Heap/kmalloc.h>
+#include <Kernel/Interrupts/GenericInterruptHandler.h>
+#include <Kernel/Interrupts/InterruptManagement.h>
+#include <Kernel/KBufferBuilder.h>
+#include <Kernel/Module.h>
+#include <Kernel/Net/LocalSocket.h>
+#include <Kernel/Net/NetworkAdapter.h>
+#include <Kernel/Net/NetworkingManagement.h>
+#include <Kernel/Net/Routing.h>
+#include <Kernel/Net/TCPSocket.h>
+#include <Kernel/Net/UDPSocket.h>
+#include <Kernel/PCI/Access.h>
+#include <Kernel/PerformanceEventBuffer.h>
+#include <Kernel/Process.h>
+#include <Kernel/ProcessExposed.h>
+#include <Kernel/Sections.h>
+#include <Kernel/VM/AnonymousVMObject.h>
+#include <Kernel/VM/MemoryManager.h>
+
+namespace Kernel {
+
+static SpinLock<u8> 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<KBufferImpl> buffer;
+};
+
+KResultOr<size_t> 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<KBufferImpl> data_buffer = static_cast<ProcFSInodeData&>(*description->data()).buffer;
+
+    if (!data_buffer || (size_t)offset >= data_buffer->size())
+        return 0;
+
+    ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(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<ProcFSInodeData&>(*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<ProcFSGlobalInformation&>(*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<ProcFSProcessFolder> ProcFSRootFolder::process_folder_for(Process& process)
+{
+    RefPtr<Process> checked_process = process;
+    for (auto& folder : m_process_folders) {
+        if (folder.associated_process().ptr() == checked_process.ptr())
+            return folder;
+    }
+    return {};
+}
+
+KResultOr<size_t> 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<KBufferImpl> data_buffer = static_cast<ProcFSInodeData&>(*description->data()).buffer;
+
+    if (!data_buffer || (size_t)offset >= data_buffer->size())
+        return 0;
+
+    ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(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<ProcFSProcessInformation&>(*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<ProcFSInodeData&>(*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<ProcFSProcessInformation&>(*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<ProcFSDumpKmallocStacks> 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<ProcFSUBSanDeadly> 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<ProcFSCapsLockRemap> 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> ProcFSDumpKmallocStacks::must_create(const ProcFSSystemDirectory&)
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSDumpKmallocStacks).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSUBSanDeadly> ProcFSUBSanDeadly::must_create(const ProcFSSystemDirectory&)
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSUBSanDeadly).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSCapsLockRemap> 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<ProcFSSelfProcessFolder> 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<ProcFSDiskUsage> 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<u64>(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<const FileBackedFS&>(fs).file_description().absolute_path());
+            else
+                fs_object.add("source", "none");
+        });
+        array.finish();
+        return true;
+    }
+};
+
+class ProcFSMemoryStatus final : public ProcFSGlobalInformation {
+public:
+    static NonnullRefPtr<ProcFSMemoryStatus> 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<KBufferBuilder> 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<ProcFSOverallProcesses> 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<ProcFSCPUInformation> 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<ProcFSDmesg> 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<ProcFSInterrupts> 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<ProcFSKeymap> must_create();
+
+private:
+    ProcFSKeymap();
+    virtual bool output(KBufferBuilder& builder) override
+    {
+        JsonObjectSerializer<KBufferBuilder> 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<ProcFSPCI> 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<ProcFSDevices> 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<ProcFSUptime> 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<ProcFSCommandLine> 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<ProcFSModules> must_create();
+
+private:
+    ProcFSModules();
+    virtual bool output(KBufferBuilder& builder) override
+    {
+        extern HashMap<String, OwnPtr<Module>>* 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<ProcFSProfile> 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> ProcFSSelfProcessFolder::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSSelfProcessFolder()).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSDiskUsage> ProcFSDiskUsage::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSDiskUsage).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSMemoryStatus> ProcFSMemoryStatus::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSMemoryStatus).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSOverallProcesses> ProcFSOverallProcesses::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSOverallProcesses).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSCPUInformation> ProcFSCPUInformation::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSCPUInformation).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSDmesg> ProcFSDmesg::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSDmesg).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSInterrupts> ProcFSInterrupts::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSInterrupts).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSKeymap> ProcFSKeymap::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSKeymap).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSPCI> ProcFSPCI::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSPCI).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSDevices> ProcFSDevices::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSDevices).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSUptime> ProcFSUptime::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSUptime).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSCommandLine> ProcFSCommandLine::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSCommandLine).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSModules> ProcFSModules::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSModules).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSProfile> 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<ProcFSAdapters> 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<ProcFSARP> 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<ProcFSTCP> 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<ProcFSLocalNet> 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<ProcFSUDP> 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<ProcFSNetworkDirectory> must_create(const ProcFSRootFolder& parent_folder);
+
+private:
+    ProcFSNetworkDirectory(const ProcFSRootFolder& parent_folder);
+};
+
+class ProcFSSystemDirectory : public ProcFSExposedFolder {
+public:
+    static NonnullRefPtr<ProcFSSystemDirectory> must_create(const ProcFSRootFolder& parent_folder);
+
+private:
+    ProcFSSystemDirectory(const ProcFSRootFolder& parent_folder);
+};
+
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSAdapters> ProcFSAdapters::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSAdapters).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSARP> ProcFSARP::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSARP).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSTCP> ProcFSTCP::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSTCP).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSLocalNet> ProcFSLocalNet::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSLocalNet).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSUDP> ProcFSUDP::must_create()
+{
+    return adopt_ref_if_nonnull(new (nothrow) ProcFSUDP).release_nonnull();
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSNetworkDirectory> 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> ProcFSBusDirectory::must_create(const ProcFSRootFolder& parent_folder)
+{
+    auto folder = adopt_ref(*new (nothrow) ProcFSBusDirectory(parent_folder));
+    return folder;
+}
+UNMAP_AFTER_INIT NonnullRefPtr<ProcFSSystemDirectory> 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> 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<ProcFSThreadStack> 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<Thread> 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<size_t> entries_count() const override;
+    virtual KResult traverse_as_directory(unsigned, Function<bool(const FS::DirectoryEntryView&)>) const override;
+    virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
+
+    static NonnullRefPtr<ProcFSProcessStacks> 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<ProcFSProcessFolder> m_process_folder;
+    mutable Lock m_lock;
+};
+
+KResultOr<size_t> 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<bool(const FS::DirectoryEntryView&)> 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<ProcFSExposedComponent> ProcFSProcessStacks::lookup(StringView name)
+{
+    Locker locker(m_lock);
+    auto process = m_process_folder->m_associated_process;
+    RefPtr<ProcFSThreadStack> 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<ProcFSProcessFileDescription> 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<FileDescription> 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<size_t> entries_count() const override;
+    virtual KResult traverse_as_directory(unsigned, Function<bool(const FS::DirectoryEntryView&)>) const override;
+    virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
+
+    static NonnullRefPtr<ProcFSProcessFileDescriptions> 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<ProcFSProcessFolder> m_process_folder;
+    mutable Lock m_lock;
+};
+
+KResultOr<size_t> 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<bool(const FS::DirectoryEntryView&)> 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<ProcFSExposedComponent> 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<ProcFSProcessUnveil> 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<ProcFSProcessPerformanceEvents> 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<ProcFSProcessOverallFileDescriptions> 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<ProcFSProcessRoot> 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<ProcFSProcessFolder> m_parent_process_directory;
+};
+
+class ProcFSProcessVirtualMemory final : public ProcFSProcessInformation {
+public:
+    static NonnullRefPtr<ProcFSProcessRoot> 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<const AnonymousVMObject&>(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<ProcFSProcessCurrentWorkDirectory> 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<ProcFSProcessFolder> m_parent_process_directory;
+};
+
+class ProcFSProcessBinary final : public ProcFSExposedLink {
+public:
+    static NonnullRefPtr<ProcFSProcessBinary> 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<ProcFSProcessFolder> m_parent_process_directory;
+};
+
+NonnullRefPtr<ProcFSProcessFolder> 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<size_t> 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<ProcFSExposedLink&>(*this).acquire_link(builder))
+        return KResult(EFAULT);
+    auto blob = builder.build();
+    if (!blob)
+        return KResult(EFAULT);
+
+    ssize_t nread = min(static_cast<off_t>(blob->size() - offset), static_cast<off_t>(count));
+    if (!buffer.write(blob->data() + offset, nread))
+        return KResult(EFAULT);
+    return nread;
+}
+
+NonnullRefPtr<Inode> ProcFSExposedLink::to_inode(const ProcFS& procfs_instance) const
+{
+    return ProcFSLinkInode::create(procfs_instance, *this);
+}
+
+NonnullRefPtr<Inode> ProcFSExposedComponent::to_inode(const ProcFS& procfs_instance) const
+{
+    return ProcFSInode::create(procfs_instance, *this);
+}
+
+NonnullRefPtr<Inode> ProcFSExposedFolder::to_inode(const ProcFS& procfs_instance) const
+{
+    return ProcFSDirectoryInode::create(procfs_instance, *this);
+}
+
+void ProcFSExposedFolder::add_component(const ProcFSExposedComponent&)
+{
+    TODO();
+}
+
+RefPtr<ProcFSExposedComponent> ProcFSExposedFolder::lookup(StringView name)
+{
+    for (auto& component : m_components) {
+        if (component.name() == name) {
+            return component;
+        }
+    }
+    return {};
+}
+
+KResult ProcFSExposedFolder::traverse_as_directory(unsigned fsid, Function<bool(const FS::DirectoryEntryView&)> 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<ProcFSExposedComponent> 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<bool(const FS::DirectoryEntryView&)> 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;
+}
+
+}

+ 254 - 0
Kernel/ProcessExposed.h

@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/RefCounted.h>
+#include <AK/RefPtr.h>
+#include <AK/String.h>
+#include <AK/Types.h>
+#include <Kernel/Arch/x86/CPU.h>
+#include <Kernel/FileSystem/File.h>
+#include <Kernel/FileSystem/FileSystem.h>
+#include <Kernel/KBufferBuilder.h>
+#include <Kernel/KResult.h>
+#include <Kernel/Process.h>
+#include <Kernel/UserOrKernelBuffer.h>
+
+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<ProcFSRootFolder> m_root_folder;
+};
+
+class ProcFSExposedComponent : public RefCounted<ProcFSExposedComponent> {
+public:
+    virtual KResultOr<size_t> entries_count() const { VERIFY_NOT_REACHED(); };
+    StringView name() const { return m_name->view(); }
+    virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const { VERIFY_NOT_REACHED(); }
+    virtual KResult traverse_as_directory(unsigned, Function<bool(const FS::DirectoryEntryView&)>) const { VERIFY_NOT_REACHED(); }
+    virtual RefPtr<ProcFSExposedComponent> lookup(StringView) { VERIFY_NOT_REACHED(); };
+    virtual KResultOr<size_t> 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<Inode> 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<KString> m_name;
+    size_t m_component_index;
+};
+
+class ProcFSExposedFolder : public ProcFSExposedComponent {
+    friend class ProcFSProcessFolder;
+    friend class ProcFSComponentsRegistrar;
+
+public:
+    virtual KResultOr<size_t> entries_count() const override { return m_components.size(); };
+    virtual KResult traverse_as_directory(unsigned, Function<bool(const FS::DirectoryEntryView&)>) const override;
+    virtual RefPtr<ProcFSExposedComponent> 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<Inode> to_inode(const ProcFS& procfs_instance) const override final;
+
+protected:
+    explicit ProcFSExposedFolder(StringView name);
+    ProcFSExposedFolder(StringView name, const ProcFSExposedFolder& parent_folder);
+    NonnullRefPtrVector<ProcFSExposedComponent> m_components;
+    RefPtr<ProcFSExposedFolder> m_parent_folder;
+};
+
+class ProcFSExposedLink : public ProcFSExposedComponent {
+public:
+    virtual NonnullRefPtr<Inode> to_inode(const ProcFS& procfs_instance) const override final;
+
+    virtual KResultOr<size_t> 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<ProcFSProcessFolder> create(const Process&);
+    NonnullRefPtr<Process> 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<ProcFSProcessFolder, RefPtr<ProcFSProcessFolder>> m_list_node;
+
+    explicit ProcFSProcessFolder(const Process&);
+    NonnullRefPtr<Process> m_associated_process;
+};
+
+class ProcFSRootFolder;
+
+class ProcFSBusDirectory : public ProcFSExposedFolder {
+    friend class ProcFSComponentsRegistrar;
+
+public:
+    static NonnullRefPtr<ProcFSBusDirectory> must_create(const ProcFSRootFolder& parent_folder);
+
+private:
+    ProcFSBusDirectory(const ProcFSRootFolder& parent_folder);
+};
+
+class ProcFSRootFolder final : public ProcFSExposedFolder {
+    friend class ProcFSComponentsRegistrar;
+
+public:
+    virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
+
+    RefPtr<ProcFSProcessFolder> process_folder_for(Process&);
+    static NonnullRefPtr<ProcFSRootFolder> must_create();
+    virtual ~ProcFSRootFolder();
+
+private:
+    virtual KResult traverse_as_directory(unsigned, Function<bool(const FS::DirectoryEntryView&)>) const override;
+    ProcFSRootFolder();
+
+    RefPtr<ProcFSBusDirectory> m_buses_folder;
+    IntrusiveList<ProcFSProcessFolder, RefPtr<ProcFSProcessFolder>, &ProcFSProcessFolder::m_list_node> m_process_folders;
+};
+
+class ProcFSGlobalInformation : public ProcFSExposedComponent {
+public:
+    virtual ~ProcFSGlobalInformation() override {};
+
+    virtual KResultOr<size_t> 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<u8> 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<size_t> 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<ProcFSProcessFolder> m_parent_folder;
+    mutable SpinLock<u8> m_refresh_lock;
+};
+
+}

+ 1 - 2
Kernel/Syscalls/fork.cpp

@@ -109,8 +109,7 @@ KResultOr<FlatPtr> 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);

+ 2 - 0
Kernel/Thread.cpp

@@ -16,6 +16,7 @@
 #include <Kernel/Panic.h>
 #include <Kernel/PerformanceEventBuffer.h>
 #include <Kernel/Process.h>
+#include <Kernel/ProcessExposed.h>
 #include <Kernel/Scheduler.h>
 #include <Kernel/Sections.h>
 #include <Kernel/Thread.h>
@@ -59,6 +60,7 @@ Thread::Thread(NonnullRefPtr<Process> process, NonnullOwnPtr<Region> 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) {

+ 6 - 0
Kernel/Thread.h

@@ -22,6 +22,7 @@
 #include <Kernel/Arch/x86/RegisterState.h>
 #include <Kernel/Arch/x86/SafeMem.h>
 #include <Kernel/Debug.h>
+#include <Kernel/FileSystem/InodeIdentifier.h>
 #include <Kernel/Forward.h>
 #include <Kernel/KResult.h>
 #include <Kernel/LockMode.h>
@@ -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<Process>, NonnullOwnPtr<Region>, NonnullRefPtr<Timer>);
@@ -1328,6 +1330,10 @@ private:
 
     RefPtr<Timer> 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();

+ 3 - 1
Kernel/init.cpp

@@ -8,8 +8,8 @@
 #include <Kernel/ACPI/DynamicParser.h>
 #include <Kernel/ACPI/Initialize.h>
 #include <Kernel/ACPI/MultiProcessorParser.h>
-#include <Kernel/Arch/x86/Processor.h>
 #include <Kernel/Arch/PC/BIOS.h>
+#include <Kernel/Arch/x86/Processor.h>
 #include <Kernel/CMOS.h>
 #include <Kernel/CommandLine.h>
 #include <Kernel/Devices/FullDevice.h>
@@ -40,6 +40,7 @@
 #include <Kernel/PCI/Initializer.h>
 #include <Kernel/Panic.h>
 #include <Kernel/Process.h>
+#include <Kernel/ProcessExposed.h>
 #include <Kernel/RTC.h>
 #include <Kernel/Random.h>
 #include <Kernel/Scheduler.h>
@@ -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();