mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
Kernel: Steer away from heap allocations for ProcFS process data
Instead, use more static patterns to acquire that sort of data.
This commit is contained in:
parent
bf1adc2d5d
commit
7ba991dc37
Notes:
sideshowbarker
2024-07-18 07:04:26 +09:00
Author: https://github.com/supercomputer7 Commit: https://github.com/SerenityOS/serenity/commit/7ba991dc371 Pull-request: https://github.com/SerenityOS/serenity/pull/9328 Issue: https://github.com/SerenityOS/serenity/issues/9241 Reviewed-by: https://github.com/awesomekling
11 changed files with 991 additions and 879 deletions
71
Documentation/Kernel/ProcFSIndexing.md
Normal file
71
Documentation/Kernel/ProcFSIndexing.md
Normal file
|
@ -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.
|
||||
|
|
@ -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,25 +37,16 @@ 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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
RefPtr<ProcFS> ProcFS::create()
|
||||
{
|
||||
return adopt_ref_if_nonnull(new (nothrow) ProcFS);
|
||||
}
|
||||
|
||||
ProcFS::ProcFS()
|
||||
: m_root_inode(ProcFSComponentRegistry::the().root_directory().to_inode(*this))
|
||||
{
|
||||
}
|
||||
|
||||
ProcFS::~ProcFS()
|
||||
{
|
||||
}
|
||||
|
@ -69,90 +61,25 @@ 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)
|
||||
{
|
||||
}
|
||||
|
||||
KResult ProcFSInode::attach(FileDescription& 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 = m_associated_component->refresh_data(description);
|
||||
if (result.is_error()) {
|
||||
// Subsequent calls to read will return EIO!
|
||||
dbgln("ProcFS: Could not refresh contents: {}", result.error());
|
||||
}
|
||||
}
|
||||
|
||||
ProcFSInode::~ProcFSInode()
|
||||
{
|
||||
}
|
||||
|
||||
ProcFS::ProcFS()
|
||||
: m_root_inode(ProcFSComponentRegistry::the().root_directory().to_inode(*this))
|
||||
{
|
||||
}
|
||||
|
||||
KResultOr<size_t> ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const
|
||||
{
|
||||
return m_associated_component->read_bytes(offset, count, buffer, fd);
|
||||
}
|
||||
|
||||
StringView ProcFSInode::name() const
|
||||
{
|
||||
return m_associated_component->name();
|
||||
}
|
||||
|
||||
KResult ProcFSInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const
|
||||
{
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
RefPtr<Inode> ProcFSInode::lookup(StringView)
|
||||
{
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
InodeMetadata ProcFSInode::metadata() const
|
||||
{
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
KResultOr<size_t> ProcFSInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd)
|
||||
{
|
||||
return m_associated_component->write_bytes(offset, count, buffer, fd);
|
||||
}
|
||||
|
||||
KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(StringView, mode_t, dev_t, uid_t, gid_t)
|
||||
KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t)
|
||||
{
|
||||
return EROFS;
|
||||
}
|
||||
|
||||
KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t)
|
||||
KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(StringView, mode_t, dev_t, uid_t, gid_t)
|
||||
{
|
||||
return EROFS;
|
||||
}
|
||||
|
@ -177,13 +104,78 @@ KResult ProcFSInode::truncate(u64)
|
|||
return EPERM;
|
||||
}
|
||||
|
||||
NonnullRefPtr<ProcFSGlobalInode> ProcFSGlobalInode::create(const ProcFS& fs, const ProcFSExposedComponent& component)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSGlobalInode(fs, component));
|
||||
}
|
||||
|
||||
ProcFSGlobalInode::ProcFSGlobalInode(const ProcFS& fs, const ProcFSExposedComponent& component)
|
||||
: ProcFSInode(fs, component.component_index())
|
||||
, m_associated_component(component)
|
||||
{
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
KResult ProcFSGlobalInode::attach(FileDescription& description)
|
||||
{
|
||||
return m_associated_component->refresh_data(description);
|
||||
}
|
||||
|
||||
KResultOr<size_t> ProcFSGlobalInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const
|
||||
{
|
||||
return m_associated_component->read_bytes(offset, count, buffer, fd);
|
||||
}
|
||||
|
||||
StringView ProcFSGlobalInode::name() const
|
||||
{
|
||||
return m_associated_component->name();
|
||||
}
|
||||
|
||||
KResult ProcFSGlobalInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const
|
||||
{
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
RefPtr<Inode> ProcFSGlobalInode::lookup(StringView)
|
||||
{
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
InodeMetadata ProcFSGlobalInode::metadata() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
KResultOr<size_t> ProcFSGlobalInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd)
|
||||
{
|
||||
return m_associated_component->write_bytes(offset, count, buffer, fd);
|
||||
}
|
||||
|
||||
NonnullRefPtr<ProcFSDirectoryInode> ProcFSDirectoryInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSDirectoryInode(procfs, component));
|
||||
}
|
||||
|
||||
ProcFSDirectoryInode::ProcFSDirectoryInode(const ProcFS& fs, const ProcFSExposedComponent& component)
|
||||
: ProcFSInode(fs, component)
|
||||
: 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,35 +18,77 @@ 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_component_index(s_allocate_global_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);
|
||||
}
|
||||
|
||||
ProcFSExposedDirectory::ProcFSExposedDirectory(StringView name)
|
||||
: ProcFSExposedComponent(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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
array.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
static NonnullRefPtr<ProcFSProcessStacks> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
auto directory = adopt_ref(*new (nothrow) ProcFSProcessStacks(parent_directory));
|
||||
return directory;
|
||||
}
|
||||
|
||||
virtual void prepare_for_deletion() override
|
||||
{
|
||||
ProcFSExposedDirectory::prepare_for_deletion();
|
||||
m_process_directory.clear();
|
||||
}
|
||||
|
||||
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 ProcFSProcessStacks::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
KResultOr<size_t> Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) 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())
|
||||
JsonArraySerializer array { builder };
|
||||
auto thread = Thread::from_tid(thread_id);
|
||||
if (!thread)
|
||||
return KResult(ESRCH);
|
||||
process->for_each_thread([&](const Thread& thread) {
|
||||
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.add(address);
|
||||
}
|
||||
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
InodeIndex Process::component_index() const
|
||||
{
|
||||
return SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid());
|
||||
}
|
||||
|
||||
NonnullRefPtr<Inode> Process::to_inode(const ProcFS& procfs_instance) const
|
||||
{
|
||||
return ProcFSProcessDirectoryInode::create(procfs_instance, m_protected_values.pid);
|
||||
}
|
||||
|
||||
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 Process::traverse_stacks_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
callback({ ".", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks, SegmentedProcFSIndex::MainProcessProperty::Reserved) }, 0 });
|
||||
callback({ "..", { fsid, component_index() }, 0 });
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
KResult ProcFSProcessFileDescriptions::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
KResultOr<size_t> Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) 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 file_description = m_fds.file_description(fd);
|
||||
if (!file_description)
|
||||
return EBADF;
|
||||
auto data = file_description->absolute_path();
|
||||
builder.append(data);
|
||||
return data.length();
|
||||
}
|
||||
|
||||
auto process = parent_directory->associated_process();
|
||||
if (process.is_null())
|
||||
return KResult(ESRCH);
|
||||
KResult Process::traverse_file_descriptions_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
callback({ ".", { fsid, component_index() }, 0 });
|
||||
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));
|
||||
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); \
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessPledge(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSProcessInformation("pledge"sv, parent_directory)
|
||||
{
|
||||
if (has_promises()) {
|
||||
StringBuilder builder;
|
||||
ENUMERATE_PLEDGE_PROMISES
|
||||
obj.add("promises", builder.build());
|
||||
}
|
||||
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); \
|
||||
}
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
class ProcFSProcessOverallFileDescriptions final : public ProcFSProcessInformation {
|
||||
public:
|
||||
static NonnullRefPtr<ProcFSProcessOverallFileDescriptions> create(const ProcFSProcessDirectory& parent_directory)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ProcFSProcessOverallFileDescriptions(parent_directory));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ProcFSProcessOverallFileDescriptions(const ProcFSProcessDirectory& parent_directory)
|
||||
: ProcFSProcessInformation("fds"sv, parent_directory)
|
||||
{
|
||||
}
|
||||
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++;
|
||||
});
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
RefPtr<ProcFSExposedComponent> ProcFSProcessDirectory::lookup(StringView name)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
KResult ProcFSProcessDirectory::refresh_data(FileDescription&) const
|
||||
{
|
||||
if (m_components.size() != 0)
|
||||
return KSuccess;
|
||||
const_cast<ProcFSProcessDirectory&>(*this).on_attach();
|
||||
obj.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
NonnullRefPtr<ProcFSProcessDirectory> ProcFSProcessDirectory::create(const Process& process)
|
||||
KResult Process::procfs_get_unveil_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessDirectory(process)).release_nonnull();
|
||||
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;
|
||||
}
|
||||
|
||||
void ProcFSProcessDirectory::prepare_for_deletion()
|
||||
KResult Process::procfs_get_perf_events(KBufferBuilder& builder) const
|
||||
{
|
||||
ProcFSExposedDirectory::prepare_for_deletion();
|
||||
m_associated_process.clear();
|
||||
InterruptDisabler disabler;
|
||||
if (!const_cast<Process&>(*this).perf_events()) {
|
||||
dbgln("ProcFS: No perf events for {}", pid());
|
||||
return KResult(ENOBUFS);
|
||||
}
|
||||
return const_cast<Process&>(*this).perf_events()->to_json(builder) ? KSuccess : KResult(EINVAL);
|
||||
}
|
||||
|
||||
ProcFSProcessDirectory::ProcFSProcessDirectory(const Process& process)
|
||||
: ProcFSExposedDirectory(String::formatted("{:d}", process.pid().value()), ProcFSComponentRegistry::the().root_directory())
|
||||
, m_associated_process(process)
|
||||
KResult Process::procfs_get_fds_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
JsonArraySerializer array { builder };
|
||||
if (fds().open_count() == 0) {
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
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++;
|
||||
});
|
||||
|
||||
array.finish();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Process::procfs_get_root_link(KBufferBuilder& builder) const
|
||||
{
|
||||
builder.append_bytes(const_cast<Process&>(*this).root_directory_relative_to_global_root().absolute_path().to_byte_buffer());
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
KResult Process::procfs_get_current_work_directory_link(KBufferBuilder& builder) const
|
||||
{
|
||||
builder.append_bytes(const_cast<Process&>(*this).current_directory().absolute_path().bytes());
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
mode_t Process::binary_link_required_mode() const
|
||||
{
|
||||
if (!executable())
|
||||
return 0;
|
||||
return ProcFSExposedComponent::required_mode();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue