diff options
author | Liav A <liavalb@gmail.com> | 2021-08-10 20:51:28 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-08-12 20:57:32 +0200 |
commit | 7ba991dc371ecd055829699852f7cb821472667f (patch) | |
tree | 1c53f16ae0de005e8f539a15f12329453b5d4f4a | |
parent | bf1adc2d5d383c0421f19ccb36a0358ff2ee0ce5 (diff) | |
download | serenity-7ba991dc371ecd055829699852f7cb821472667f.zip |
Kernel: Steer away from heap allocations for ProcFS process data
Instead, use more static patterns to acquire that sort of data.
-rw-r--r-- | Documentation/Kernel/ProcFSIndexing.md | 71 | ||||
-rw-r--r-- | Kernel/FileSystem/ProcFS.cpp | 493 | ||||
-rw-r--r-- | Kernel/FileSystem/ProcFS.h | 139 | ||||
-rw-r--r-- | Kernel/GlobalProcessExposed.cpp | 34 | ||||
-rw-r--r-- | Kernel/Process.cpp | 15 | ||||
-rw-r--r-- | Kernel/Process.h | 37 | ||||
-rw-r--r-- | Kernel/ProcessExposed.cpp | 147 | ||||
-rw-r--r-- | Kernel/ProcessExposed.h | 128 | ||||
-rw-r--r-- | Kernel/ProcessSpecificExposed.cpp | 717 | ||||
-rw-r--r-- | Kernel/Thread.cpp | 1 | ||||
-rw-r--r-- | Kernel/Thread.h | 6 |
11 files changed, 950 insertions, 838 deletions
diff --git a/Documentation/Kernel/ProcFSIndexing.md b/Documentation/Kernel/ProcFSIndexing.md new file mode 100644 index 0000000000..0855731909 --- /dev/null +++ b/Documentation/Kernel/ProcFSIndexing.md @@ -0,0 +1,71 @@ +# ProcFS Indexing + +## Is a ProcFS index deterministic value? + +Short answer - yes. Long answer - because of the design pattern that was chosen, +each `InodeIndex` actually represent a known object, so it is guaranteed to be +the same always for global ProcFS objects. For process ID directories, once that +process has been killed, its primary segment value is no longer valid and hence +all sub-segments of it are not relevant anymore, but if the process is still alive, +it is guaranteed that accessing the same `InodeIndex` in regard to a object tied to +a process directory will provide the expected object. + +## The goal - zero allocations when creating new process + +The main goal is to have zero allocations happening in ProcFS when a new process is created. +The old ProcFS design followed that principle, but was quite hard to edit and to extend with new +functionality. +The current ProcFS design doesn't follow that principle, but is easier to edit and to extend. +A compromise is needed to ensure we get the advantages from both designs while minimizing the +effects of the disadvantages of each design. + +## The segmeneted index + +### The layout of the segmented index + +Since it was decided that heap allocations for ProcFS are *mostly* bad, the new +design layout tries to achieve most of the principle of "Don't allocate anything +until actually needed". For that to happen, `InodeIndex` (u64 value) is splitted +to 3 Segments: +- The primary segment: value 0 is reserved for all non-PID inodes in the procfs. +All values from 1 to 0xFFFFFFF are valid PID indices, which represents all PIDs from 0 to 0xFFFFFFE + +- The Sub-directory segment: value 0 is reserved for parent PID directory. All other values are +available for usage of subdirectories in the PID directory. + +- The property segment: value 0 is reserved for parent PID directory. All other values are +available for usage of components in the PID directory or in subdirectories of the PID directory. + +So, the final layout of the 64 bit index is: + +``` +| Primary Segment (28 bits) | Sub-directory (16 bits) | Component (20 bits) | +``` + +Example: To find a Thread 0 stack, for PID 1, the following encoding is applied: + +``` +hex(2 << 16 | 2 << (16 + 28)) == 0x200000020000 +``` + +### Two rules for indexing + +We don't want to allocate anything when a process is created, but we still want +to allocate global objects, so it's somewhat a compromise between two conflicting targets. +To do that we need to ensure that: + +1. If the primary segment value equals to 0, then the sub-directory and property segmentation +is not applied, but a sequential indexing is determined instead. This is needed so ProcFS can still +use global components that were pre-allocated beforehand. This means that there might be up to +68719476735 global components (including global sub-directories objects) in the ProcFS. +Otherwise, for every primary segment value > 0, then the sub-directory and property segmentation +is applied. This means that there might be up to 65534 sub-directories in a PID directory, and +up to 1048575 (1048574 for PID directory) properties (objects) in each sub-directory. + +2. If the primary segment value equals to 0, then value 0 in both aritificial sub-directory +and property segments represents the root ProcFS folder. +Otherwise, for every primary segment value > 0, value 0 in both sub-directory and +property segments are reserved to represent the root PID directory. +Please note that if the sub-directory segment > 0, and property segment = 0 is a valid +index, and represents a valid property object in that sub-directory. + diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index bda413bdf7..0f4f098f17 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -13,6 +13,7 @@ #include <Kernel/FileSystem/ProcFS.h> #include <Kernel/FileSystem/VirtualFileSystem.h> #include <Kernel/Heap/kmalloc.h> +#include <Kernel/Process.h> #include <Kernel/Sections.h> #include <LibC/errno_numbers.h> @@ -36,23 +37,14 @@ UNMAP_AFTER_INIT ProcFSComponentRegistry::ProcFSComponentRegistry() { } -void ProcFSComponentRegistry::register_new_process(Process& new_process) -{ - MutexLocker locker(m_lock); - m_root_directory->m_process_directories.append(ProcFSProcessDirectory::create(new_process)); -} - -void ProcFSComponentRegistry::unregister_process(Process& deleted_process) +RefPtr<ProcFS> ProcFS::create() { - auto process_directory = m_root_directory->process_directory_for(deleted_process).release_nonnull(); - process_directory->prepare_for_deletion(); - process_directory->m_list_node.remove(); - dbgln_if(PROCFS_DEBUG, "ProcFSExposedDirectory ref_count now: {}", process_directory->ref_count()); + return adopt_ref_if_nonnull(new (nothrow) ProcFS); } -RefPtr<ProcFS> ProcFS::create() +ProcFS::ProcFS() + : m_root_inode(ProcFSComponentRegistry::the().root_directory().to_inode(*this)) { - return adopt_ref_if_nonnull(new (nothrow) ProcFS); } ProcFS::~ProcFS() @@ -69,112 +61,112 @@ Inode& ProcFS::root_inode() return *m_root_inode; } -NonnullRefPtr<ProcFSInode> ProcFSInode::create(const ProcFS& fs, const ProcFSExposedComponent& component) +ProcFSInode::ProcFSInode(const ProcFS& fs, InodeIndex index) + : Inode(const_cast<ProcFS&>(fs), index) { - return adopt_ref(*new (nothrow) ProcFSInode(fs, component)); } -ProcFSInode::ProcFSInode(const ProcFS& fs, const ProcFSExposedComponent& component) - : Inode(const_cast<ProcFS&>(fs), component.component_index()) - , m_associated_component(component) +ProcFSInode::~ProcFSInode() { } -KResult ProcFSInode::attach(FileDescription& description) +void ProcFSInode::flush_metadata() { - return m_associated_component->refresh_data(description); } -void ProcFSInode::did_seek(FileDescription& description, off_t new_offset) +KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t) { - if (new_offset != 0) - return; - 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()); - } + return EROFS; } -ProcFSInode::~ProcFSInode() +KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(StringView, mode_t, dev_t, uid_t, gid_t) { + return EROFS; } -ProcFS::ProcFS() - : m_root_inode(ProcFSComponentRegistry::the().root_directory().to_inode(*this)) +KResult ProcFSInode::remove_child(const StringView&) { + return EROFS; } -KResultOr<size_t> ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const +KResult ProcFSInode::chmod(mode_t) { - return m_associated_component->read_bytes(offset, count, buffer, fd); + return EPERM; } -StringView ProcFSInode::name() const +KResult ProcFSInode::chown(uid_t, gid_t) { - return m_associated_component->name(); + return EPERM; } -KResult ProcFSInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const +KResult ProcFSInode::truncate(u64) { - VERIFY_NOT_REACHED(); + return EPERM; } -RefPtr<Inode> ProcFSInode::lookup(StringView) +NonnullRefPtr<ProcFSGlobalInode> ProcFSGlobalInode::create(const ProcFS& fs, const ProcFSExposedComponent& component) { - VERIFY_NOT_REACHED(); + return adopt_ref(*new (nothrow) ProcFSGlobalInode(fs, component)); } -InodeMetadata ProcFSInode::metadata() const +ProcFSGlobalInode::ProcFSGlobalInode(const ProcFS& fs, const ProcFSExposedComponent& component) + : ProcFSInode(fs, component.component_index()) + , m_associated_component(component) { - MutexLocker locker(m_inode_lock); - InodeMetadata metadata; - metadata.inode = { fsid(), m_associated_component->component_index() }; - metadata.mode = S_IFREG | m_associated_component->required_mode(); - metadata.uid = m_associated_component->owner_user(); - metadata.gid = m_associated_component->owner_group(); - metadata.size = m_associated_component->size(); - metadata.mtime = m_associated_component->modified_time(); - return metadata; } -void ProcFSInode::flush_metadata() +void ProcFSGlobalInode::did_seek(FileDescription& description, off_t new_offset) { + if (new_offset != 0) + return; + 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()); + } } -KResultOr<size_t> ProcFSInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd) +KResult ProcFSGlobalInode::attach(FileDescription& description) { - return m_associated_component->write_bytes(offset, count, buffer, fd); + return m_associated_component->refresh_data(description); } -KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(StringView, mode_t, dev_t, uid_t, gid_t) +KResultOr<size_t> ProcFSGlobalInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const { - return EROFS; + return m_associated_component->read_bytes(offset, count, buffer, fd); } -KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t) +StringView ProcFSGlobalInode::name() const { - return EROFS; + return m_associated_component->name(); } -KResult ProcFSInode::remove_child(const StringView&) +KResult ProcFSGlobalInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const { - return EROFS; + VERIFY_NOT_REACHED(); } -KResult ProcFSInode::chmod(mode_t) +RefPtr<Inode> ProcFSGlobalInode::lookup(StringView) { - return EPERM; + VERIFY_NOT_REACHED(); } -KResult ProcFSInode::chown(uid_t, gid_t) +InodeMetadata ProcFSGlobalInode::metadata() const { - return EPERM; + MutexLocker locker(m_inode_lock); + InodeMetadata metadata; + metadata.inode = { fsid(), m_associated_component->component_index() }; + metadata.mode = S_IFREG | m_associated_component->required_mode(); + metadata.uid = m_associated_component->owner_user(); + metadata.gid = m_associated_component->owner_group(); + metadata.size = m_associated_component->size(); + metadata.mtime = m_associated_component->modified_time(); + return metadata; } -KResult ProcFSInode::truncate(u64) +KResultOr<size_t> ProcFSGlobalInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd) { - return EPERM; + return m_associated_component->write_bytes(offset, count, buffer, fd); } NonnullRefPtr<ProcFSDirectoryInode> ProcFSDirectoryInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component) @@ -183,7 +175,7 @@ NonnullRefPtr<ProcFSDirectoryInode> ProcFSDirectoryInode::create(const ProcFS& p } ProcFSDirectoryInode::ProcFSDirectoryInode(const ProcFS& fs, const ProcFSExposedComponent& component) - : ProcFSInode(fs, component) + : ProcFSGlobalInode(fs, component) { } @@ -204,17 +196,17 @@ InodeMetadata ProcFSDirectoryInode::metadata() const } KResult ProcFSDirectoryInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)> callback) const { - MutexLocker locker(fs().m_lock); - return m_associated_component->traverse_as_directory(fs().fsid(), move(callback)); + MutexLocker locker(procfs().m_lock); + return m_associated_component->traverse_as_directory(procfs().fsid(), move(callback)); } RefPtr<Inode> ProcFSDirectoryInode::lookup(StringView name) { - MutexLocker locker(fs().m_lock); + MutexLocker locker(procfs().m_lock); auto component = m_associated_component->lookup(name); if (!component) return {}; - return component->to_inode(fs()); + return component->to_inode(procfs()); } NonnullRefPtr<ProcFSLinkInode> ProcFSLinkInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component) @@ -223,9 +215,10 @@ NonnullRefPtr<ProcFSLinkInode> ProcFSLinkInode::create(const ProcFS& procfs, con } ProcFSLinkInode::ProcFSLinkInode(const ProcFS& fs, const ProcFSExposedComponent& component) - : ProcFSInode(fs, component) + : ProcFSGlobalInode(fs, component) { } + InodeMetadata ProcFSLinkInode::metadata() const { MutexLocker locker(m_inode_lock); @@ -239,4 +232,364 @@ InodeMetadata ProcFSLinkInode::metadata() const return metadata; } +ProcFSProcessAssociatedInode::ProcFSProcessAssociatedInode(const ProcFS& fs, ProcessID associated_pid, InodeIndex determined_index) + : ProcFSInode(fs, determined_index) + , m_pid(associated_pid) +{ +} + +KResultOr<size_t> ProcFSProcessAssociatedInode::write_bytes(off_t, size_t, const UserOrKernelBuffer&, FileDescription*) +{ + VERIFY_NOT_REACHED(); +} + +NonnullRefPtr<ProcFSProcessDirectoryInode> ProcFSProcessDirectoryInode::create(const ProcFS& procfs, ProcessID pid) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessDirectoryInode(procfs, pid)).release_nonnull(); +} + +ProcFSProcessDirectoryInode::ProcFSProcessDirectoryInode(const ProcFS& procfs, ProcessID pid) + : ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid)) +{ +} + +KResult ProcFSProcessDirectoryInode::attach(FileDescription&) +{ + return KSuccess; +} + +InodeMetadata ProcFSProcessDirectoryInode::metadata() const +{ + MutexLocker locker(m_inode_lock); + auto process = Process::from_pid(associated_pid()); + if (!process) + return {}; + InodeMetadata metadata; + metadata.inode = { fsid(), process->component_index() }; + metadata.mode = S_IFDIR | process->required_mode(); + metadata.uid = process->owner_user(); + metadata.gid = process->owner_group(); + metadata.size = 0; + metadata.mtime = process->modified_time(); + return metadata; +} + +KResultOr<size_t> ProcFSProcessDirectoryInode::read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const +{ + VERIFY_NOT_REACHED(); +} + +KResult ProcFSProcessDirectoryInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)> callback) const +{ + MutexLocker locker(procfs().m_lock); + auto process = Process::from_pid(associated_pid()); + if (!process) + return EINVAL; + return process->traverse_as_directory(procfs().fsid(), move(callback)); +} + +RefPtr<Inode> ProcFSProcessDirectoryInode::lookup(StringView name) +{ + MutexLocker locker(procfs().m_lock); + auto process = Process::from_pid(associated_pid()); + if (!process) + return nullptr; + if (name == "fd") + return ProcFSProcessSubDirectoryInode::create(procfs(), SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions, associated_pid()); + if (name == "stacks") + return ProcFSProcessSubDirectoryInode::create(procfs(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks, associated_pid()); + if (name == "unveil") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::Unveil, associated_pid()); + if (name == "pledge") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::Pledge, associated_pid()); + if (name == "fds") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::FileDescriptions, associated_pid()); + if (name == "exe") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::BinaryLink, associated_pid()); + if (name == "cwd") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink, associated_pid()); + if (name == "perf_events") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::PerformanceEvents, associated_pid()); + if (name == "vm") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::VirtualMemoryStats, associated_pid()); + if (name == "root") + return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::RootLink, associated_pid()); + return nullptr; +} + +NonnullRefPtr<ProcFSProcessSubDirectoryInode> ProcFSProcessSubDirectoryInode::create(const ProcFS& procfs, SegmentedProcFSIndex::ProcessSubDirectory sub_directory_type, ProcessID pid) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessSubDirectoryInode(procfs, sub_directory_type, pid)).release_nonnull(); +} + +ProcFSProcessSubDirectoryInode::ProcFSProcessSubDirectoryInode(const ProcFS& procfs, SegmentedProcFSIndex::ProcessSubDirectory sub_directory_type, ProcessID pid) + : ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid, sub_directory_type)) + , m_sub_directory_type(sub_directory_type) +{ +} + +KResultOr<size_t> ProcFSProcessSubDirectoryInode::read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const +{ + VERIFY_NOT_REACHED(); +} + +KResult ProcFSProcessSubDirectoryInode::attach(FileDescription&) +{ + return KSuccess; +} + +void ProcFSProcessSubDirectoryInode::did_seek(FileDescription&, off_t) +{ + VERIFY_NOT_REACHED(); +} + +InodeMetadata ProcFSProcessSubDirectoryInode::metadata() const +{ + MutexLocker locker(m_inode_lock); + auto process = Process::from_pid(associated_pid()); + if (!process) + return {}; + InodeMetadata metadata; + metadata.inode = { fsid(), process->component_index() }; + metadata.mode = S_IFDIR | process->required_mode(); + metadata.uid = process->owner_user(); + metadata.gid = process->owner_group(); + metadata.size = 0; + metadata.mtime = process->modified_time(); + return metadata; +} + +KResult ProcFSProcessSubDirectoryInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)> callback) const +{ + MutexLocker locker(procfs().m_lock); + auto process = Process::from_pid(associated_pid()); + if (!process) + return EINVAL; + switch (m_sub_directory_type) { + case SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions: + return process->traverse_file_descriptions_directory(procfs().fsid(), move(callback)); + case SegmentedProcFSIndex::ProcessSubDirectory::Stacks: + return process->traverse_stacks_directory(procfs().fsid(), move(callback)); + default: + VERIFY_NOT_REACHED(); + } + VERIFY_NOT_REACHED(); +} + +RefPtr<Inode> ProcFSProcessSubDirectoryInode::lookup(StringView name) +{ + MutexLocker locker(procfs().m_lock); + auto process = Process::from_pid(associated_pid()); + if (!process) + return {}; + switch (m_sub_directory_type) { + case SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions: + return process->lookup_file_descriptions_directory(procfs(), name); + case SegmentedProcFSIndex::ProcessSubDirectory::Stacks: + return process->lookup_stacks_directory(procfs(), name); + default: + VERIFY_NOT_REACHED(); + } + VERIFY_NOT_REACHED(); +} + +NonnullRefPtr<ProcFSProcessPropertyInode> ProcFSProcessPropertyInode::create_for_file_description_link(const ProcFS& procfs, unsigned file_description_index, ProcessID pid) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessPropertyInode(procfs, file_description_index, pid)).release_nonnull(); +} +NonnullRefPtr<ProcFSProcessPropertyInode> ProcFSProcessPropertyInode::create_for_thread_stack(const ProcFS& procfs, ThreadID stack_thread_index, ProcessID pid) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessPropertyInode(procfs, stack_thread_index, pid)).release_nonnull(); +} +NonnullRefPtr<ProcFSProcessPropertyInode> ProcFSProcessPropertyInode::create_for_pid_property(const ProcFS& procfs, SegmentedProcFSIndex::MainProcessProperty main_property_type, ProcessID pid) +{ + return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessPropertyInode(procfs, main_property_type, pid)).release_nonnull(); +} + +ProcFSProcessPropertyInode::ProcFSProcessPropertyInode(const ProcFS& procfs, SegmentedProcFSIndex::MainProcessProperty main_property_type, ProcessID pid) + : ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid, main_property_type)) + , m_parent_sub_directory_type(SegmentedProcFSIndex::ProcessSubDirectory::Reserved) +{ + m_possible_data.property_type = main_property_type; +} + +ProcFSProcessPropertyInode::ProcFSProcessPropertyInode(const ProcFS& procfs, unsigned file_description_index, ProcessID pid) + : ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_file_description(pid, file_description_index)) + , m_parent_sub_directory_type(SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions) +{ + m_possible_data.property_index = file_description_index; +} + +ProcFSProcessPropertyInode::ProcFSProcessPropertyInode(const ProcFS& procfs, ThreadID thread_stack_index, ProcessID pid) + : ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_thread_stack(pid, thread_stack_index)) + , m_parent_sub_directory_type(SegmentedProcFSIndex::ProcessSubDirectory::Stacks) +{ + m_possible_data.property_index = thread_stack_index.value(); +} + +KResult ProcFSProcessPropertyInode::attach(FileDescription& description) +{ + return refresh_data(description); +} +void ProcFSProcessPropertyInode::did_seek(FileDescription& description, off_t offset) +{ + if (offset != 0) + return; + (void)refresh_data(description); +} + +static mode_t determine_procfs_process_inode_mode(SegmentedProcFSIndex::ProcessSubDirectory parent_sub_directory_type, SegmentedProcFSIndex::MainProcessProperty main_property) +{ + if (parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions) + return S_IFLNK | 0400; + if (parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Stacks) + return S_IFREG | 0400; + VERIFY(parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Reserved); + if (main_property == SegmentedProcFSIndex::MainProcessProperty::BinaryLink) + return S_IFLNK | 0777; + if (main_property == SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink) + return S_IFLNK | 0777; + if (main_property == SegmentedProcFSIndex::MainProcessProperty::RootLink) + return S_IFLNK | 0777; + return S_IFREG | 0400; +} + +InodeMetadata ProcFSProcessPropertyInode::metadata() const +{ + MutexLocker locker(m_inode_lock); + auto process = Process::from_pid(associated_pid()); + if (!process) + return {}; + InodeMetadata metadata; + metadata.inode = { fsid(), process->component_index() }; + metadata.mode = determine_procfs_process_inode_mode(m_parent_sub_directory_type, m_possible_data.property_type); + metadata.uid = process->owner_user(); + metadata.gid = process->owner_group(); + metadata.size = 0; + metadata.mtime = process->modified_time(); + return metadata; +} +KResult ProcFSProcessPropertyInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const +{ + VERIFY_NOT_REACHED(); +} +KResultOr<size_t> ProcFSProcessPropertyInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const +{ + dbgln_if(PROCFS_DEBUG, "ProcFS ProcessInformation: read_bytes offset: {} count: {}", offset, count); + + VERIFY(offset >= 0); + VERIFY(buffer.user_or_kernel_ptr()); + + if (!description) { + KBufferBuilder builder; + auto process = Process::from_pid(associated_pid()); + if (!process) + return KResult(ESRCH); + if (auto result = try_to_acquire_data(*process, builder); result.is_error()) + return result.error(); + auto data_buffer = builder.build(); + if (!data_buffer) + return KResult(EFAULT); + 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; + } + if (!description->data()) { + dbgln("ProcFS Process Information: Do not have cached data!"); + return KResult(EIO); + } + + MutexLocker locker(m_refresh_lock); + + auto& typed_cached_data = static_cast<ProcFSInodeData&>(*description->data()); + auto& data_buffer = typed_cached_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; +} +RefPtr<Inode> ProcFSProcessPropertyInode::lookup(StringView) +{ + VERIFY_NOT_REACHED(); +} + +static KResult build_from_cached_data(KBufferBuilder& builder, ProcFSInodeData& cached_data) +{ + cached_data.buffer = builder.build(); + if (!cached_data.buffer) + return ENOMEM; + return KSuccess; +} + +KResult ProcFSProcessPropertyInode::try_to_acquire_data(Process& process, KBufferBuilder& builder) const +{ + // FIXME: Verify process is already ref-counted + if (m_parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions) { + if (auto result = process.procfs_get_file_description_link(m_possible_data.property_index, builder); result.is_error()) + return result.error(); + return KSuccess; + } + if (m_parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Stacks) { + if (auto result = process.procfs_get_thread_stack(m_possible_data.property_index, builder); result.is_error()) + return result.error(); + return KSuccess; + } + + VERIFY(m_parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Reserved); + switch (m_possible_data.property_type) { + case SegmentedProcFSIndex::MainProcessProperty::Unveil: + return process.procfs_get_fds_stats(builder); + case SegmentedProcFSIndex::MainProcessProperty::Pledge: + return process.procfs_get_pledge_stats(builder); + case SegmentedProcFSIndex::MainProcessProperty::FileDescriptions: + return process.procfs_get_fds_stats(builder); + case SegmentedProcFSIndex::MainProcessProperty::BinaryLink: + return process.procfs_get_binary_link(builder); + case SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink: + return process.procfs_get_current_work_directory_link(builder); + case SegmentedProcFSIndex::MainProcessProperty::PerformanceEvents: + return process.procfs_get_perf_events(builder); + case SegmentedProcFSIndex::MainProcessProperty::VirtualMemoryStats: + return process.procfs_get_virtual_memory_stats(builder); + case SegmentedProcFSIndex::MainProcessProperty::RootLink: + return process.procfs_get_root_link(builder); + default: + VERIFY_NOT_REACHED(); + } +} + +KResult ProcFSProcessPropertyInode::refresh_data(FileDescription& description) +{ + // 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 = Process::from_pid(associated_pid()); + if (!process) + return KResult(ESRCH); + process->ptrace_lock().lock(); + if (!process->is_dumpable()) { + process->ptrace_lock().unlock(); + return EPERM; + } + ScopeGuard guard = [&] { + process->ptrace_lock().unlock(); + }; + MutexLocker locker(m_refresh_lock); + auto& cached_data = description.data(); + if (!cached_data) { + cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); + if (!cached_data) + return ENOMEM; + } + KBufferBuilder builder; + if (auto result = try_to_acquire_data(*process, builder); result.is_error()) + return result; + return build_from_cached_data(builder, static_cast<ProcFSInodeData&>(*cached_data)); +} } diff --git a/Kernel/FileSystem/ProcFS.h b/Kernel/FileSystem/ProcFS.h index 9ffecfedf8..49b4d89b44 100644 --- a/Kernel/FileSystem/ProcFS.h +++ b/Kernel/FileSystem/ProcFS.h @@ -14,12 +14,17 @@ #include <Kernel/KBufferBuilder.h> #include <Kernel/Locking/Mutex.h> #include <Kernel/ProcessExposed.h> +#include <Kernel/UnixTypes.h> namespace Kernel { class ProcFS final : public FileSystem { friend class ProcFSInode; friend class ProcFSDirectoryInode; + friend class ProcFSProcessDirectoryInode; + friend class ProcFSGlobalInode; + friend class ProcFSAssociatedProcessInode; + friend class ProcFSProcessSubDirectoryInode; public: virtual ~ProcFS() override; @@ -40,33 +45,50 @@ class ProcFSInode : public Inode { friend class ProcFS; public: - static NonnullRefPtr<ProcFSInode> create(const ProcFS&, const ProcFSExposedComponent&); virtual ~ProcFSInode() override; + +protected: + ProcFSInode(const ProcFS&, InodeIndex); + + ProcFS& procfs() { return static_cast<ProcFS&>(Inode::fs()); } + ProcFS const& procfs() const { return static_cast<ProcFS const&>(Inode::fs()); } + + // ^Inode + virtual KResult attach(FileDescription& description) = 0; + virtual void did_seek(FileDescription&, off_t) = 0; + virtual void flush_metadata() override final; + virtual KResultOr<NonnullRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, uid_t, gid_t) override final; + virtual KResult add_child(Inode&, const StringView& name, mode_t) override final; + virtual KResult remove_child(const StringView& name) override final; + virtual KResult chmod(mode_t) override final; + virtual KResult chown(uid_t, gid_t) override final; + virtual KResult truncate(u64) override final; +}; + +class ProcFSGlobalInode : public ProcFSInode { + friend class ProcFS; + +public: + static NonnullRefPtr<ProcFSGlobalInode> create(const ProcFS&, const ProcFSExposedComponent&); + virtual ~ProcFSGlobalInode() override {}; StringView name() const; protected: - ProcFSInode(const ProcFS&, const ProcFSExposedComponent&); + ProcFSGlobalInode(const ProcFS&, const ProcFSExposedComponent&); // ^Inode - virtual KResult attach(FileDescription& description) override; - virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override; - virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override; - virtual RefPtr<Inode> lookup(StringView name) override; - virtual void flush_metadata() override; + virtual KResult attach(FileDescription& description) override final; + virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final; + virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override final; + virtual void did_seek(FileDescription&, off_t) override final; 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(StringView 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 KResult chmod(mode_t) override; - virtual KResult chown(uid_t, gid_t) override; - virtual KResult truncate(u64) override; + virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override; + virtual RefPtr<Inode> lookup(StringView) override; NonnullRefPtr<ProcFSExposedComponent> m_associated_component; }; -class ProcFSLinkInode : public ProcFSInode { +class ProcFSLinkInode : public ProcFSGlobalInode { friend class ProcFS; public: @@ -77,16 +99,13 @@ protected: virtual InodeMetadata metadata() const override; }; -class ProcFSDirectoryInode : public ProcFSInode { +class ProcFSDirectoryInode final : public ProcFSGlobalInode { friend class ProcFS; public: static NonnullRefPtr<ProcFSDirectoryInode> create(const ProcFS&, const ProcFSExposedComponent&); virtual ~ProcFSDirectoryInode() override; - ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); } - ProcFS const& fs() const { return static_cast<ProcFS const&>(Inode::fs()); } - protected: ProcFSDirectoryInode(const ProcFS&, const ProcFSExposedComponent&); // ^Inode @@ -95,4 +114,84 @@ protected: virtual RefPtr<Inode> lookup(StringView name) override; }; +class ProcFSProcessAssociatedInode : public ProcFSInode { + friend class ProcFS; + +protected: + ProcFSProcessAssociatedInode(const ProcFS&, ProcessID, InodeIndex); + ProcessID associated_pid() const { return m_pid; } + + // ^Inode + virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override final; + +private: + const ProcessID m_pid; +}; + +class ProcFSProcessDirectoryInode final : public ProcFSProcessAssociatedInode { + friend class ProcFS; + +public: + static NonnullRefPtr<ProcFSProcessDirectoryInode> create(const ProcFS&, ProcessID); + +private: + ProcFSProcessDirectoryInode(const ProcFS&, ProcessID); + // ^Inode + virtual KResult attach(FileDescription& description) override; + virtual void did_seek(FileDescription&, off_t) override { } + virtual InodeMetadata metadata() const override; + virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override; + virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final; + virtual RefPtr<Inode> lookup(StringView name) override; +}; + +class ProcFSProcessSubDirectoryInode final : public ProcFSProcessAssociatedInode { + friend class ProcFS; + +public: + static NonnullRefPtr<ProcFSProcessSubDirectoryInode> create(const ProcFS&, SegmentedProcFSIndex::ProcessSubDirectory, ProcessID); + +private: + ProcFSProcessSubDirectoryInode(const ProcFS&, SegmentedProcFSIndex::ProcessSubDirectory, ProcessID); + // ^Inode + virtual KResult attach(FileDescription& description) override; + virtual void did_seek(FileDescription&, off_t) override; + virtual InodeMetadata metadata() const override; + virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override; + virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final; + virtual RefPtr<Inode> lookup(StringView name) override; + + const SegmentedProcFSIndex::ProcessSubDirectory m_sub_directory_type; +}; + +class ProcFSProcessPropertyInode final : public ProcFSProcessAssociatedInode { + friend class ProcFS; + +public: + static NonnullRefPtr<ProcFSProcessPropertyInode> create_for_file_description_link(const ProcFS&, unsigned, ProcessID); + static NonnullRefPtr<ProcFSProcessPropertyInode> create_for_thread_stack(const ProcFS&, ThreadID, ProcessID); + static NonnullRefPtr<ProcFSProcessPropertyInode> create_for_pid_property(const ProcFS&, SegmentedProcFSIndex::MainProcessProperty, ProcessID); + +private: + ProcFSProcessPropertyInode(const ProcFS&, SegmentedProcFSIndex::MainProcessProperty, ProcessID); + ProcFSProcessPropertyInode(const ProcFS&, ThreadID, ProcessID); + ProcFSProcessPropertyInode(const ProcFS&, unsigned, ProcessID); + // ^Inode + virtual KResult attach(FileDescription& description) override; + virtual void did_seek(FileDescription&, off_t) override; + virtual InodeMetadata metadata() const override; + virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override; + virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final; + virtual RefPtr<Inode> lookup(StringView name) override final; + + KResult refresh_data(FileDescription& description); + KResult try_to_acquire_data(Process& process, KBufferBuilder& builder) const; + + const SegmentedProcFSIndex::ProcessSubDirectory m_parent_sub_directory_type; + union { + SegmentedProcFSIndex::MainProcessProperty property_type; + unsigned property_index; + } m_possible_data; + mutable Mutex m_refresh_lock; +}; } diff --git a/Kernel/GlobalProcessExposed.cpp b/Kernel/GlobalProcessExposed.cpp index c4c6516c41..8ce519b424 100644 --- a/Kernel/GlobalProcessExposed.cpp +++ b/Kernel/GlobalProcessExposed.cpp @@ -27,6 +27,7 @@ #include <Kernel/Net/Routing.h> #include <Kernel/Net/TCPSocket.h> #include <Kernel/Net/UDPSocket.h> +#include <Kernel/Process.h> #include <Kernel/ProcessExposed.h> #include <Kernel/Sections.h> #include <Kernel/TTY/TTY.h> @@ -905,10 +906,13 @@ KResult ProcFSRootDirectory::traverse_as_directory(unsigned fsid, Function<bool( InodeIdentifier identifier = { fsid, component.component_index() }; callback({ component.name(), identifier, 0 }); } - for (auto& component : m_process_directories) { - InodeIdentifier identifier = { fsid, component.component_index() }; - callback({ component.name(), identifier, 0 }); - } + processes().for_each_shared([&](Process& process) { + VERIFY(!(process.pid() < 0)); + u64 process_id = (u64)process.pid().value(); + InodeIdentifier identifier = { fsid, static_cast<InodeIndex>(process_id << 36) }; + callback({ String::formatted("{:d}", process.pid().value()), identifier, 0 }); + return IterationDecision::Continue; + }); return KSuccess; } @@ -917,12 +921,12 @@ RefPtr<ProcFSExposedComponent> ProcFSRootDirectory::lookup(StringView name) if (auto candidate = ProcFSExposedDirectory::lookup(name); !candidate.is_null()) return candidate; - for (auto& component : m_process_directories) { - if (component.name() == name) { - return component; - } - } - return {}; + String process_directory_name = name; + auto pid = process_directory_name.to_uint<unsigned>(); + if (!pid.has_value()) + return {}; + auto actual_pid = pid.value(); + return Process::from_pid(actual_pid); } UNMAP_AFTER_INIT ProcFSRootDirectory::ProcFSRootDirectory() @@ -934,14 +938,4 @@ UNMAP_AFTER_INIT ProcFSRootDirectory::~ProcFSRootDirectory() { } -RefPtr<ProcFSProcessDirectory> ProcFSRootDirectory::process_directory_for(Process& process) -{ - RefPtr<Process> checked_process = process; - for (auto& directory : m_process_directories) { - if (directory.associated_process().ptr() == checked_process.ptr()) - return directory; - } - return {}; -} - } diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 359b277a35..e3ec84c56c 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -141,7 +141,6 @@ void Process::register_new(Process& process) processes().with_exclusive([&](auto& list) { list.prepend(process); }); - ProcFSComponentRegistry::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) @@ -594,12 +593,6 @@ 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. - ProcFSComponentRegistry::the().unregister_process(*this); - m_state.store(State::Dead, AK::MemoryOrder::memory_order_release); { @@ -752,13 +745,6 @@ 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 = ProcFSComponentRegistry::the().allocate_inode_index(); } void Process::FileDescriptionAndFlags::set(NonnullRefPtr<FileDescription>&& description, u32 flags) @@ -766,7 +752,6 @@ void Process::FileDescriptionAndFlags::set(NonnullRefPtr<FileDescription>&& desc // FIXME: Verify Process::m_fds_lock is locked! m_description = move(description); m_flags = flags; - m_global_procfs_inode_index = ProcFSComponentRegistry::the().allocate_inode_index(); } Custody& Process::root_directory() diff --git a/Kernel/Process.h b/Kernel/Process.h index c8152fd7b0..fce09313ba 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -27,6 +27,7 @@ #include <Kernel/Locking/ProtectedValue.h> #include <Kernel/Memory/AddressSpace.h> #include <Kernel/PerformanceEventBuffer.h> +#include <Kernel/ProcessExposed.h> #include <Kernel/ProcessGroup.h> #include <Kernel/StdLib.h> #include <Kernel/Thread.h> @@ -84,7 +85,7 @@ typedef HashMap<FlatPtr, RefPtr<FutexQueue>> FutexQueues; struct LoadResult; class Process - : public RefCounted<Process> + : public ProcFSExposedComponent , public Weakable<Process> { private: @@ -551,6 +552,31 @@ private: void setup_socket_fd(int fd, NonnullRefPtr<FileDescription> description, int type); +public: + // ^ProcFSExposedComponent stats + virtual InodeIndex component_index() const override; + virtual NonnullRefPtr<Inode> to_inode(const ProcFS& procfs_instance) const override; + virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override; + virtual mode_t required_mode() const override { return 0555; } + virtual uid_t owner_user() const override { return uid(); } + virtual gid_t owner_group() const override { return gid(); } + KResult procfs_get_fds_stats(KBufferBuilder& builder) const; + KResult procfs_get_perf_events(KBufferBuilder& builder) const; + KResult procfs_get_unveil_stats(KBufferBuilder& builder) const; + KResult procfs_get_pledge_stats(KBufferBuilder& builder) const; + KResult procfs_get_virtual_memory_stats(KBufferBuilder& builder) const; + KResult procfs_get_binary_link(KBufferBuilder& builder) const; + KResult procfs_get_root_link(KBufferBuilder& builder) const; + KResult procfs_get_current_work_directory_link(KBufferBuilder& builder) const; + mode_t binary_link_required_mode() const; + KResultOr<size_t> procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const; + KResult traverse_stacks_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const; + RefPtr<Inode> lookup_stacks_directory(const ProcFS&, StringView name) const; + KResultOr<size_t> procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const; + KResult traverse_file_descriptions_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const; + RefPtr<Inode> lookup_file_descriptions_directory(const ProcFS&, StringView name) const; + +private: inline PerformanceEventBuffer* current_perf_events_buffer() { if (g_profiling_all_threads) @@ -595,22 +621,16 @@ public: 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; bool m_is_allocated { false }; 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; }; class ScopedDescriptionAllocation; @@ -626,9 +646,6 @@ public: ScopedSpinLock lock(m_fds_lock); ScopedSpinLock lock_other(other.m_fds_lock); m_fds_metadatas = other.m_fds_metadatas; - for (auto& file_description_metadata : m_fds_metadatas) { - file_description_metadata.refresh_inode_index(); - } return *this; } diff --git a/Kernel/ProcessExposed.cpp b/Kernel/ProcessExposed.cpp index c286476805..ecf85dbc8a 100644 --- a/Kernel/ProcessExposed.cpp +++ b/Kernel/ProcessExposed.cpp @@ -18,32 +18,74 @@ namespace Kernel { static SpinLock<u8> s_index_lock; static InodeIndex s_next_inode_index = 0; -static size_t s_allocate_inode_index() +namespace SegmentedProcFSIndex { +static InodeIndex __build_raw_segmented_index(u32 primary, u16 sub_directory, u32 property) +{ + VERIFY(primary < 0x10000000); + VERIFY(property < 0x100000); + // Note: The sub-directory part is already limited to 0xFFFF, so no need to VERIFY it. + return static_cast<u64>((static_cast<u64>(primary) << 36) | (static_cast<u64>(sub_directory) << 20) | property); +} + +static InodeIndex build_segmented_index_with_known_pid(ProcessID pid, u16 sub_directory, u32 property) +{ + return __build_raw_segmented_index(pid.value() + 1, sub_directory, property); +} + +static InodeIndex build_segmented_index_with_unknown_property(ProcessID pid, ProcessSubDirectory sub_directory, unsigned property) +{ + return build_segmented_index_with_known_pid(pid, to_underlying(sub_directory), static_cast<u32>(property)); +} + +InodeIndex build_segmented_index_for_pid_directory(ProcessID pid) +{ + return build_segmented_index_with_unknown_property(pid, ProcessSubDirectory::Reserved, to_underlying(MainProcessProperty::Reserved)); +} + +InodeIndex build_segmented_index_for_sub_directory(ProcessID pid, ProcessSubDirectory sub_directory) +{ + return build_segmented_index_with_unknown_property(pid, sub_directory, to_underlying(MainProcessProperty::Reserved)); +} + +InodeIndex build_segmented_index_for_main_property(ProcessID pid, ProcessSubDirectory sub_directory, MainProcessProperty property) +{ + return build_segmented_index_with_known_pid(pid, to_underlying(sub_directory), to_underlying(property)); +} + +InodeIndex build_segmented_index_for_main_property_in_pid_directory(ProcessID pid, MainProcessProperty property) +{ + return build_segmented_index_with_known_pid(pid, to_underlying(ProcessSubDirectory::Reserved), to_underlying(property)); +} + +InodeIndex build_segmented_index_for_thread_stack(ProcessID pid, ThreadID thread_id) +{ + return build_segmented_index_with_unknown_property(pid, ProcessSubDirectory::Stacks, thread_id.value()); +} + +InodeIndex build_segmented_index_for_file_description(ProcessID pid, unsigned fd) +{ + return build_segmented_index_with_unknown_property(pid, ProcessSubDirectory::FileDescriptions, fd); +} + +} + +static size_t s_allocate_global_inode_index() { ScopedSpinLock lock(s_index_lock); s_next_inode_index = s_next_inode_index.value() + 1; + // Note: Global ProcFS indices must be above 0 and up to maximum of what 36 bit (2 ^ 36 - 1) can represent. VERIFY(s_next_inode_index > 0); + VERIFY(s_next_inode_index < 0x100000000); return s_next_inode_index.value(); } -InodeIndex ProcFSComponentRegistry::allocate_inode_index() const +ProcFSExposedComponent::ProcFSExposedComponent() { - 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()) + : m_component_index(s_allocate_global_inode_index()) { - VERIFY(preallocated_index.value() != 0); - VERIFY(preallocated_index <= s_next_inode_index); m_name = KString::try_create(name); } @@ -62,16 +104,6 @@ ProcFSExposedLink::ProcFSExposedLink(StringView name) : ProcFSExposedComponent(name) { } - -ProcFSExposedLink::ProcFSExposedLink(StringView name, InodeIndex preallocated_index) - : ProcFSExposedComponent(name, preallocated_index) -{ -} - -struct ProcFSInodeData : public FileDescriptionData { - OwnPtr<KBuffer> 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); @@ -121,71 +153,6 @@ KResult ProcFSGlobalInformation::refresh_data(FileDescription& description) cons return KSuccess; } -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); - } - - MutexLocker locker(m_refresh_lock); - - auto& typed_cached_data = static_cast<ProcFSInodeData&>(*description->data()); - auto& data_buffer = typed_cached_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 parent_directory = const_cast<ProcFSProcessInformation&>(*this).m_parent_directory.strong_ref(); - if (parent_directory.is_null()) - return KResult(EINVAL); - auto process = parent_directory->associated_process(); - if (!process) - return KResult(ESRCH); - process->ptrace_lock().lock(); - if (!process->is_dumpable()) { - process->ptrace_lock().unlock(); - return EPERM; - } - ScopeGuard guard = [&] { - process->ptrace_lock().unlock(); - }; - MutexLocker locker(m_refresh_lock); - auto& cached_data = description.data(); - if (!cached_data) { - cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); - if (!cached_data) - return ENOMEM; - } - KBufferBuilder builder; - if (!const_cast<ProcFSProcessInformation&>(*this).output(builder)) - return ENOENT; - auto& typed_cached_data = static_cast<ProcFSInodeData&>(*cached_data); - typed_cached_data.buffer = builder.build(); - if (!typed_cached_data.buffer) - return ENOMEM; - return KSuccess; -} - KResultOr<size_t> ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const { VERIFY(offset == 0); @@ -210,7 +177,7 @@ NonnullRefPtr<Inode> ProcFSExposedLink::to_inode(const ProcFS& procfs_instance) NonnullRefPtr<Inode> ProcFSExposedComponent::to_inode(const ProcFS& procfs_instance) const { - return ProcFSInode::create(procfs_instance, *this); + return ProcFSGlobalInode::create(procfs_instance, *this); } NonnullRefPtr<Inode> ProcFSExposedDirectory::to_inode(const ProcFS& procfs_instance) const diff --git a/Kernel/ProcessExposed.h b/Kernel/ProcessExposed.h index 527898d72b..85afd01425 100644 --- a/Kernel/ProcessExposed.h +++ b/Kernel/ProcessExposed.h @@ -13,27 +13,49 @@ #include <AK/Types.h> #include <Kernel/Arch/x86/CPU.h> #include <Kernel/FileSystem/File.h> +#include <Kernel/FileSystem/FileDescription.h> #include <Kernel/FileSystem/FileSystem.h> #include <Kernel/KBufferBuilder.h> #include <Kernel/KResult.h> -#include <Kernel/Process.h> #include <Kernel/UserOrKernelBuffer.h> namespace Kernel { +namespace SegmentedProcFSIndex { +enum class MainProcessProperty { + Reserved = 0, + Unveil = 1, + Pledge = 2, + FileDescriptions = 3, + BinaryLink = 4, + CurrentWorkDirectoryLink = 5, + PerformanceEvents = 6, + VirtualMemoryStats = 7, + RootLink = 8, +}; + +enum class ProcessSubDirectory { + Reserved = 0, + FileDescriptions = 1, + Stacks = 2, +}; + +void read_segments(u32& primary, ProcessSubDirectory& sub_directory, MainProcessProperty& property); +InodeIndex build_segmented_index_for_pid_directory(ProcessID); +InodeIndex build_segmented_index_for_sub_directory(ProcessID, ProcessSubDirectory sub_directory); +InodeIndex build_segmented_index_for_main_property(ProcessID, ProcessSubDirectory sub_directory, MainProcessProperty property); +InodeIndex build_segmented_index_for_main_property_in_pid_directory(ProcessID, MainProcessProperty property); +InodeIndex build_segmented_index_for_thread_stack(ProcessID, ThreadID); +InodeIndex build_segmented_index_for_file_description(ProcessID, unsigned); +} + class ProcFSComponentRegistry { public: static ProcFSComponentRegistry& the(); static void initialize(); - - InodeIndex allocate_inode_index() const; - ProcFSComponentRegistry(); - void register_new_process(Process&); - void unregister_process(Process&); - ProcFSRootDirectory& root_directory() { return *m_root_directory; } Mutex& get_lock() { return m_lock; } @@ -64,13 +86,13 @@ public: virtual NonnullRefPtr<Inode> to_inode(const ProcFS& procfs_instance) const; - InodeIndex component_index() const { return m_component_index; }; + virtual InodeIndex component_index() const { return m_component_index; } virtual ~ProcFSExposedComponent() = default; protected: + ProcFSExposedComponent(); explicit ProcFSExposedComponent(StringView name); - ProcFSExposedComponent(StringView name, InodeIndex preallocated_index); private: OwnPtr<KString> m_name; @@ -80,7 +102,6 @@ private: class ProcFSExposedDirectory : public ProcFSExposedComponent , public Weakable<ProcFSExposedDirectory> { - friend class ProcFSProcessDirectory; friend class ProcFSComponentRegistry; public: @@ -114,62 +135,24 @@ public: protected: virtual bool acquire_link(KBufferBuilder& builder) = 0; explicit ProcFSExposedLink(StringView name); - ProcFSExposedLink(StringView name, InodeIndex preallocated_index); mutable Mutex m_lock { "ProcFSLink" }; }; -class ProcFSProcessDirectory final - : public ProcFSExposedDirectory { - friend class ProcFSComponentRegistry; - friend class ProcFSRootDirectory; - friend class ProcFSProcessInformation; - friend class ProcFSProcessPledge; - 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<ProcFSProcessDirectory> create(const Process&); - RefPtr<Process> associated_process() { return m_associated_process; } - - virtual uid_t owner_user() const override { return m_associated_process ? m_associated_process->uid() : 0; } - virtual gid_t owner_group() const override { return m_associated_process ? m_associated_process->gid() : 0; } - virtual KResult refresh_data(FileDescription&) const override; - virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override; - virtual mode_t required_mode() const override { return 0500; } - - virtual void prepare_for_deletion() override; - -private: - void on_attach(); - IntrusiveListNode<ProcFSProcessDirectory, RefPtr<ProcFSProcessDirectory>> m_list_node; - - explicit ProcFSProcessDirectory(const Process&); - RefPtr<Process> m_associated_process; -}; - class ProcFSRootDirectory final : public ProcFSExposedDirectory { friend class ProcFSComponentRegistry; public: virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override; - - RefPtr<ProcFSProcessDirectory> process_directory_for(Process&); static NonnullRefPtr<ProcFSRootDirectory> must_create(); virtual ~ProcFSRootDirectory(); private: virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override; ProcFSRootDirectory(); +}; - IntrusiveList<ProcFSProcessDirectory, RefPtr<ProcFSProcessDirectory>, &ProcFSProcessDirectory::m_list_node> m_process_directories; +struct ProcFSInodeData : public FileDescriptionData { + OwnPtr<KBuffer> buffer; }; class ProcFSGlobalInformation : public ProcFSExposedComponent { @@ -208,47 +191,4 @@ protected: } }; -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 - { - auto parent_directory = m_parent_directory.strong_ref(); - if (!parent_directory) - return false; - auto process = parent_directory->associated_process(); - if (!process) - return false; - return process->uid(); - } - virtual gid_t owner_group() const override - { - auto parent_directory = m_parent_directory.strong_ref(); - if (!parent_directory) - return false; - auto process = parent_directory->associated_process(); - if (!process) - return false; - return process->gid(); - } - - virtual mode_t required_mode() const override { return 0400; } - -protected: - ProcFSProcessInformation(StringView name, const ProcFSProcessDirectory& process_directory) - : ProcFSExposedComponent(name) - , m_parent_directory(process_directory) - { - } - - virtual KResult refresh_data(FileDescription&) const override; - virtual bool output(KBufferBuilder& builder) = 0; - - WeakPtr<ProcFSProcessDirectory> m_parent_directory; - mutable Mutex m_refresh_lock; -}; - } diff --git a/Kernel/ProcessSpecificExposed.cpp b/Kernel/ProcessSpecificExposed.cpp index 3cf220cd7c..6e53a85f1c 100644 --- a/Kernel/ProcessSpecificExposed.cpp +++ b/Kernel/ProcessSpecificExposed.cpp @@ -9,601 +9,294 @@ #include <AK/JsonValue.h> #include <Kernel/Arch/x86/InterruptDisabler.h> #include <Kernel/FileSystem/Custody.h> +#include <Kernel/FileSystem/ProcFS.h> #include <Kernel/KBufferBuilder.h> #include <Kernel/Memory/AnonymousVMObject.h> #include <Kernel/Memory/MemoryManager.h> +#include <Kernel/Process.h> #include <Kernel/ProcessExposed.h> namespace Kernel { -class ProcFSProcessStacks; -class ProcFSThreadStack final : public ProcFSProcessInformation { -public: - // Note: We pass const ProcFSProcessStacks& to enforce creation with this type of directory - static NonnullRefPtr<ProcFSThreadStack> create(const ProcFSProcessDirectory& process_directory, const ProcFSProcessStacks&, const Thread& thread) - { - return adopt_ref(*new (nothrow) ProcFSThreadStack(process_directory, thread)); - } - -private: - explicit ProcFSThreadStack(const ProcFSProcessDirectory& process_directory, const Thread& thread) - : ProcFSProcessInformation(String::formatted("{}", thread.tid()), process_directory) - , 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 && !Memory::is_user_address(VirtualAddress { address })) { - if (kernel_address_added) - continue; - address = 0xdeadc0de; - kernel_address_added = true; - } - array.add(address); +KResultOr<size_t> Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const +{ + JsonArraySerializer array { builder }; + auto thread = Thread::from_tid(thread_id); + if (!thread) + return KResult(ESRCH); + 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 && !Memory::is_user_address(VirtualAddress { address })) { + if (kernel_address_added) + continue; + address = 0xdeadc0de; + kernel_address_added = true; } - - array.finish(); - return true; + array.add(address); } - NonnullRefPtr<Thread> m_associated_thread; -}; - -class ProcFSProcessStacks final : public ProcFSExposedDirectory { - // Note: This directory 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 KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override; - virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override; + array.finish(); + return KSuccess; +} - static NonnullRefPtr<ProcFSProcessStacks> create(const ProcFSProcessDirectory& parent_directory) - { - auto directory = adopt_ref(*new (nothrow) ProcFSProcessStacks(parent_directory)); - return directory; - } +InodeIndex Process::component_index() const +{ + return SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid()); +} - virtual void prepare_for_deletion() override - { - ProcFSExposedDirectory::prepare_for_deletion(); - m_process_directory.clear(); - } +NonnullRefPtr<Inode> Process::to_inode(const ProcFS& procfs_instance) const +{ + return ProcFSProcessDirectoryInode::create(procfs_instance, m_protected_values.pid); +} -private: - ProcFSProcessStacks(const ProcFSProcessDirectory& parent_directory) - : ProcFSExposedDirectory("stacks"sv, parent_directory) - , m_process_directory(parent_directory) - { - } - WeakPtr<ProcFSProcessDirectory> m_process_directory; - mutable Mutex m_lock; -}; +KResult Process::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const +{ + callback({ ".", { fsid, SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid()) }, 0 }); + callback({ "..", { fsid, ProcFSComponentRegistry::the().root_directory().component_index() }, 0 }); + callback({ "fd", { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions) }, 0 }); + callback({ "stacks", { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks) }, 0 }); + callback({ "unveil", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::Unveil) }, 0 }); + callback({ "pledge", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::Pledge) }, 0 }); + callback({ "fds", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::FileDescriptions) }, 0 }); + callback({ "exe", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::BinaryLink) }, 0 }); + callback({ "cwd", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink) }, 0 }); + callback({ "perf_events", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::PerformanceEvents) }, 0 }); + callback({ "vm", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::VirtualMemoryStats) }, 0 }); + callback({ "root", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::RootLink) }, 0 }); + return KSuccess; +} -KResult ProcFSProcessStacks::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const +KResult Process::traverse_stacks_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const { - MutexLocker locker(m_lock); - auto parent_directory = m_process_directory.strong_ref(); - if (parent_directory.is_null()) - return KResult(EINVAL); - callback({ ".", { fsid, component_index() }, 0 }); - callback({ "..", { fsid, parent_directory->component_index() }, 0 }); + callback({ ".", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks, SegmentedProcFSIndex::MainProcessProperty::Reserved) }, 0 }); + callback({ "..", { fsid, component_index() }, 0 }); - auto process = parent_directory->associated_process(); - if (process.is_null()) - return KResult(ESRCH); - process->for_each_thread([&](const Thread& thread) { + for_each_thread([&](const Thread& thread) { int tid = thread.tid().value(); - InodeIdentifier identifier = { fsid, thread.global_procfs_inode_index() }; + InodeIdentifier identifier = { fsid, SegmentedProcFSIndex::build_segmented_index_for_thread_stack(pid(), thread.tid()) }; callback({ String::number(tid), identifier, 0 }); }); return KSuccess; } -RefPtr<ProcFSExposedComponent> ProcFSProcessStacks::lookup(StringView name) +RefPtr<Inode> Process::lookup_stacks_directory(const ProcFS& procfs, StringView name) const { - MutexLocker locker(m_lock); - auto parent_directory = m_process_directory.strong_ref(); - if (parent_directory.is_null()) - return nullptr; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return nullptr; - RefPtr<ProcFSThreadStack> procfd_stack; + RefPtr<ProcFSProcessPropertyInode> thread_stack_inode; // FIXME: Try to exit the loop earlier - process->for_each_thread([&](const Thread& thread) { + for_each_thread([&](const Thread& thread) { int tid = thread.tid().value(); + VERIFY(!(tid < 0)); if (name == String::number(tid)) { - procfd_stack = ProcFSThreadStack::create(*parent_directory, *this, thread); + thread_stack_inode = ProcFSProcessPropertyInode::create_for_thread_stack(procfs, thread.tid(), pid()); } }); - return procfd_stack; + return thread_stack_inode; } -class ProcFSProcessFileDescriptions; -class ProcFSProcessFileDescription final : public ProcFSExposedLink { -public: - // Note: we pass const ProcFSProcessFileDescriptions& just to enforce creation of this in the correct directory. - 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 ProcFSExposedDirectory { - // Note: This directory 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 KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override; - virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override; - - static NonnullRefPtr<ProcFSProcessFileDescriptions> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessFileDescriptions(parent_directory)); - } - - virtual void prepare_for_deletion() override - { - ProcFSExposedDirectory::prepare_for_deletion(); - m_process_directory.clear(); - } - -private: - explicit ProcFSProcessFileDescriptions(const ProcFSProcessDirectory& parent_directory) - : ProcFSExposedDirectory("fd"sv, parent_directory) - , m_process_directory(parent_directory) - { - } - WeakPtr<ProcFSProcessDirectory> m_process_directory; - mutable Mutex m_lock; -}; +KResultOr<size_t> Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const +{ + auto file_description = m_fds.file_description(fd); + if (!file_description) + return EBADF; + auto data = file_description->absolute_path(); + builder.append(data); + return data.length(); +} -KResult ProcFSProcessFileDescriptions::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const +KResult Process::traverse_file_descriptions_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const { - MutexLocker locker(m_lock); - auto parent_directory = m_process_directory.strong_ref(); - if (parent_directory.is_null()) - return KResult(EINVAL); callback({ ".", { fsid, component_index() }, 0 }); - callback({ "..", { fsid, parent_directory->component_index() }, 0 }); - - auto process = parent_directory->associated_process(); - if (process.is_null()) - return KResult(ESRCH); + callback({ "..", { fsid, component_index() }, 0 }); size_t count = 0; - process->fds().enumerate([&](auto& file_description_metadata) { + fds().enumerate([&](auto& file_description_metadata) { if (!file_description_metadata.is_valid()) { count++; return; } - InodeIdentifier identifier = { fsid, file_description_metadata.global_procfs_inode_index() }; + InodeIdentifier identifier = { fsid, SegmentedProcFSIndex::build_segmented_index_for_file_description(pid(), count) }; callback({ String::number(count), identifier, 0 }); count++; }); return KSuccess; } -RefPtr<ProcFSExposedComponent> ProcFSProcessFileDescriptions::lookup(StringView name) + +RefPtr<Inode> Process::lookup_file_descriptions_directory(const ProcFS& procfs, StringView name) const { - MutexLocker locker(m_lock); - auto parent_directory = m_process_directory.strong_ref(); - if (parent_directory.is_null()) - return nullptr; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return nullptr; - RefPtr<ProcFSProcessFileDescription> procfd_fd; + RefPtr<ProcFSProcessPropertyInode> file_description_link; // FIXME: Try to exit the loop earlier size_t count = 0; - process->fds().enumerate([&](auto& file_description_metadata) { + fds().enumerate([&](auto& file_description_metadata) { if (!file_description_metadata.is_valid()) { count++; return; } if (name == String::number(count)) { - procfd_fd = ProcFSProcessFileDescription::create(count, *file_description_metadata.description(), file_description_metadata.global_procfs_inode_index(), *this); + file_description_link = ProcFSProcessPropertyInode::create_for_file_description_link(procfs, static_cast<unsigned>(count), pid()); } count++; }); - return procfd_fd; + return file_description_link; } -class ProcFSProcessPledge final : public ProcFSProcessInformation { -public: - static NonnullRefPtr<ProcFSProcessPledge> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessPledge(parent_directory)); - } - -private: - explicit ProcFSProcessPledge(const ProcFSProcessDirectory& parent_directory) - : ProcFSProcessInformation("pledge"sv, parent_directory) - { - } - virtual bool output(KBufferBuilder& builder) override - { - auto parent_directory = m_parent_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - JsonObjectSerializer obj { builder }; -#define __ENUMERATE_PLEDGE_PROMISE(x) \ - if (process->has_promised(Pledge::x)) { \ - if (!builder.is_empty()) \ - builder.append(' '); \ - builder.append(#x); \ +KResult Process::procfs_get_pledge_stats(KBufferBuilder& builder) const +{ + JsonObjectSerializer obj { builder }; +#define __ENUMERATE_PLEDGE_PROMISE(x) \ + if (has_promised(Pledge::x)) { \ + if (!builder.is_empty()) \ + builder.append(' '); \ + builder.append(#x); \ + } + if (has_promises()) { + StringBuilder builder; + ENUMERATE_PLEDGE_PROMISES + obj.add("promises", builder.build()); } - if (process->has_promises()) { - StringBuilder builder; - ENUMERATE_PLEDGE_PROMISES - obj.add("promises", builder.build()); - } #undef __ENUMERATE_PLEDGE_PROMISE - obj.finish(); - return true; - } -}; - -class ProcFSProcessUnveil final : public ProcFSProcessInformation { -public: - static NonnullRefPtr<ProcFSProcessUnveil> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessUnveil(parent_directory)); - } - -private: - explicit ProcFSProcessUnveil(const ProcFSProcessDirectory& parent_directory) - : ProcFSProcessInformation("unveil"sv, parent_directory) - { - } - virtual bool output(KBufferBuilder& builder) override - { - auto parent_directory = m_parent_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - 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; - } -}; - -class ProcFSProcessPerformanceEvents final : public ProcFSProcessInformation { -public: - static NonnullRefPtr<ProcFSProcessPerformanceEvents> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessPerformanceEvents(parent_directory)); - } - -private: - explicit ProcFSProcessPerformanceEvents(const ProcFSProcessDirectory& parent_directory) - : ProcFSProcessInformation("perf_events"sv, parent_directory) - { - } - virtual bool output(KBufferBuilder& builder) override - { - InterruptDisabler disabler; - auto parent_directory = m_parent_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - if (!process->perf_events()) { - dbgln("ProcFS: No perf events for {}", process->pid()); - return false; - } - return process->perf_events()->to_json(builder); - } -}; + obj.finish(); + return KSuccess; +} -class ProcFSProcessOverallFileDescriptions final : public ProcFSProcessInformation { -public: - static NonnullRefPtr<ProcFSProcessOverallFileDescriptions> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessOverallFileDescriptions(parent_directory)); - } +KResult Process::procfs_get_unveil_stats(KBufferBuilder& builder) const +{ + JsonArraySerializer array { builder }; + for (auto& unveiled_path : 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 KSuccess; +} -private: - explicit ProcFSProcessOverallFileDescriptions(const ProcFSProcessDirectory& parent_directory) - : ProcFSProcessInformation("fds"sv, parent_directory) - { +KResult Process::procfs_get_perf_events(KBufferBuilder& builder) const +{ + InterruptDisabler disabler; + if (!const_cast<Process&>(*this).perf_events()) { + dbgln("ProcFS: No perf events for {}", pid()); + return KResult(ENOBUFS); } - virtual bool output(KBufferBuilder& builder) override - { - auto parent_directory = m_parent_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - JsonArraySerializer array { builder }; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - if (process->fds().open_count() == 0) { - array.finish(); - return true; - } - - size_t count = 0; - process->fds().enumerate([&](auto& file_description_metadata) { - if (!file_description_metadata.is_valid()) { - count++; - return; - } - bool cloexec = file_description_metadata.flags() & FD_CLOEXEC; - RefPtr<FileDescription> description = file_description_metadata.description(); - auto description_object = array.add_object(); - description_object.add("fd", count); - 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()); - count++; - }); + return const_cast<Process&>(*this).perf_events()->to_json(builder) ? KSuccess : KResult(EINVAL); +} +KResult Process::procfs_get_fds_stats(KBufferBuilder& builder) const +{ + JsonArraySerializer array { builder }; + if (fds().open_count() == 0) { array.finish(); - return true; - } -}; - -class ProcFSProcessRoot final : public ProcFSExposedLink { -public: - static NonnullRefPtr<ProcFSProcessRoot> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessRoot(parent_directory)); - } - -private: - explicit ProcFSProcessRoot(const ProcFSProcessDirectory& parent_directory) - : ProcFSExposedLink("root"sv) - , m_parent_process_directory(parent_directory) - { - } - virtual bool acquire_link(KBufferBuilder& builder) override - { - auto parent_directory = m_parent_process_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - builder.append_bytes(process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer()); - return true; - } - WeakPtr<ProcFSProcessDirectory> m_parent_process_directory; -}; - -class ProcFSProcessVirtualMemory final : public ProcFSProcessInformation { -public: - static NonnullRefPtr<ProcFSProcessRoot> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessVirtualMemory(parent_directory)); + return KSuccess; } -private: - explicit ProcFSProcessVirtualMemory(const ProcFSProcessDirectory& parent_directory) - : ProcFSProcessInformation("vm"sv, parent_directory) - { - } - virtual bool output(KBufferBuilder& builder) override - { - auto parent_directory = m_parent_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - JsonArraySerializer array { builder }; - { - ScopedSpinLock lock(process->address_space().get_lock()); - for (auto& region : process->address_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<Memory::AnonymousVMObject const&>(region->vmobject()).is_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()); - } + size_t count = 0; + fds().enumerate([&](auto& file_description_metadata) { + if (!file_description_metadata.is_valid()) { + count++; + return; } - array.finish(); - return true; - } -}; - -class ProcFSProcessCurrentWorkDirectory final : public ProcFSExposedLink { -public: - static NonnullRefPtr<ProcFSProcessCurrentWorkDirectory> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessCurrentWorkDirectory(parent_directory)); - } - -private: - explicit ProcFSProcessCurrentWorkDirectory(const ProcFSProcessDirectory& parent_directory) - : ProcFSExposedLink("cwd"sv) - , m_parent_process_directory(parent_directory) - { - } - virtual bool acquire_link(KBufferBuilder& builder) override - { - auto parent_directory = m_parent_process_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - builder.append_bytes(process->current_directory().absolute_path().bytes()); - return true; - } - - WeakPtr<ProcFSProcessDirectory> m_parent_process_directory; -}; - -class ProcFSProcessBinary final : public ProcFSExposedLink { -public: - static NonnullRefPtr<ProcFSProcessBinary> create(const ProcFSProcessDirectory& parent_directory) - { - return adopt_ref(*new (nothrow) ProcFSProcessBinary(parent_directory)); - } - - virtual mode_t required_mode() const override - { - auto parent_directory = m_parent_process_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - if (!process->executable()) - return 0; - return ProcFSExposedComponent::required_mode(); - } - -private: - explicit ProcFSProcessBinary(const ProcFSProcessDirectory& parent_directory) - : ProcFSExposedLink("exe"sv) - , m_parent_process_directory(parent_directory) - { - } - virtual bool acquire_link(KBufferBuilder& builder) override - { - auto parent_directory = m_parent_process_directory.strong_ref(); - if (parent_directory.is_null()) - return false; - auto process = parent_directory->associated_process(); - if (process.is_null()) - return false; - auto* custody = process->executable(); - if (!custody) - return false; - builder.append(custody->absolute_path().bytes()); - return true; - } - - WeakPtr<ProcFSProcessDirectory> m_parent_process_directory; -}; + bool cloexec = file_description_metadata.flags() & FD_CLOEXEC; + RefPtr<FileDescription> description = file_description_metadata.description(); + auto description_object = array.add_object(); + description_object.add("fd", count); + 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()); + count++; + }); -void ProcFSProcessDirectory::on_attach() -{ - VERIFY(m_components.size() == 0); - m_components.append(ProcFSProcessPledge::create(*this)); - m_components.append(ProcFSProcessUnveil::create(*this)); - m_components.append(ProcFSProcessPerformanceEvents::create(*this)); - m_components.append(ProcFSProcessFileDescriptions::create(*this)); - m_components.append(ProcFSProcessOverallFileDescriptions::create(*this)); - m_components.append(ProcFSProcessRoot::create(*this)); - m_components.append(ProcFSProcessVirtualMemory::create(*this)); - m_components.append(ProcFSProcessCurrentWorkDirectory::create(*this)); - m_components.append(ProcFSProcessBinary::create(*this)); - m_components.append(ProcFSProcessStacks::create(*this)); + array.finish(); + return KSuccess; } -RefPtr<ProcFSExposedComponent> ProcFSProcessDirectory::lookup(StringView name) +KResult Process::procfs_get_root_link(KBufferBuilder& builder) const { - // Note: we need to allocate all sub components when doing a lookup, because - // for some reason, the caller may not call ProcFSInode::attach method before calling this. - if (m_components.size() == 0) - on_attach(); - return ProcFSExposedDirectory::lookup(name); + builder.append_bytes(const_cast<Process&>(*this).root_directory_relative_to_global_root().absolute_path().to_byte_buffer()); + return KSuccess; } -KResult ProcFSProcessDirectory::refresh_data(FileDescription&) const +KResult Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const { - if (m_components.size() != 0) - return KSuccess; - const_cast<ProcFSProcessDirectory&>(*this).on_attach(); + JsonArraySerializer array { builder }; + { + ScopedSpinLock lock(address_space().get_lock()); + for (auto& region : address_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<Memory::AnonymousVMObject const&>(region->vmobject()).is_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 KSuccess; } -NonnullRefPtr<ProcFSProcessDirectory> ProcFSProcessDirectory::create(const Process& process) +KResult Process::procfs_get_current_work_directory_link(KBufferBuilder& builder) const { - return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessDirectory(process)).release_nonnull(); + builder.append_bytes(const_cast<Process&>(*this).current_directory().absolute_path().bytes()); + return KSuccess; } -void ProcFSProcessDirectory::prepare_for_deletion() +mode_t Process::binary_link_required_mode() const { - ProcFSExposedDirectory::prepare_for_deletion(); - m_associated_process.clear(); + if (!executable()) + return 0; + return ProcFSExposedComponent::required_mode(); } -ProcFSProcessDirectory::ProcFSProcessDirectory(const Process& process) - : ProcFSExposedDirectory(String::formatted("{:d}", process.pid().value()), ProcFSComponentRegistry::the().root_directory()) - , m_associated_process(process) +KResult Process::procfs_get_binary_link(KBufferBuilder& builder) const { + auto* custody = executable(); + if (!custody) + return KResult(ENOEXEC); + builder.append(custody->absolute_path().bytes()); + return KSuccess; } } diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index b580e6445e..f4f6c88fac 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -62,7 +62,6 @@ Thread::Thread(NonnullRefPtr<Process> process, NonnullOwnPtr<Memory::Region> ker , m_kernel_stack_region(move(kernel_stack_region)) , m_name(move(name)) , m_block_timer(block_timer) - , m_global_procfs_inode_index(ProcFSComponentRegistry::the().allocate_inode_index()) { bool is_first_thread = m_process->add_thread(*this); if (is_first_thread) { diff --git a/Kernel/Thread.h b/Kernel/Thread.h index b7431d0b40..24441292ac 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -1213,8 +1213,6 @@ public: bool is_profiling_suppressed() const { return m_is_profiling_suppressed; } void set_profiling_suppressed() { m_is_profiling_suppressed = true; } - InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; } - String backtrace(); private: @@ -1364,10 +1362,6 @@ 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_and_release_relock_big_lock(); |