mirror of
https://github.com/RGBCube/serenity
synced 2025-05-20 20:35:07 +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
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/ProcFS.h>
|
||||||
#include <Kernel/FileSystem/VirtualFileSystem.h>
|
#include <Kernel/FileSystem/VirtualFileSystem.h>
|
||||||
#include <Kernel/Heap/kmalloc.h>
|
#include <Kernel/Heap/kmalloc.h>
|
||||||
|
#include <Kernel/Process.h>
|
||||||
#include <Kernel/Sections.h>
|
#include <Kernel/Sections.h>
|
||||||
#include <LibC/errno_numbers.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()
|
RefPtr<ProcFS> ProcFS::create()
|
||||||
{
|
{
|
||||||
return adopt_ref_if_nonnull(new (nothrow) ProcFS);
|
return adopt_ref_if_nonnull(new (nothrow) ProcFS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProcFS::ProcFS()
|
||||||
|
: m_root_inode(ProcFSComponentRegistry::the().root_directory().to_inode(*this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ProcFS::~ProcFS()
|
ProcFS::~ProcFS()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -69,90 +61,25 @@ Inode& ProcFS::root_inode()
|
||||||
return *m_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()
|
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()
|
void ProcFSInode::flush_metadata()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
KResultOr<size_t> ProcFSInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd)
|
KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return EROFS;
|
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;
|
return EROFS;
|
||||||
}
|
}
|
||||||
|
@ -177,13 +104,78 @@ KResult ProcFSInode::truncate(u64)
|
||||||
return EPERM;
|
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)
|
NonnullRefPtr<ProcFSDirectoryInode> ProcFSDirectoryInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component)
|
||||||
{
|
{
|
||||||
return adopt_ref(*new (nothrow) ProcFSDirectoryInode(procfs, component));
|
return adopt_ref(*new (nothrow) ProcFSDirectoryInode(procfs, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcFSDirectoryInode::ProcFSDirectoryInode(const ProcFS& fs, const ProcFSExposedComponent& 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
|
KResult ProcFSDirectoryInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||||
{
|
{
|
||||||
MutexLocker locker(fs().m_lock);
|
MutexLocker locker(procfs().m_lock);
|
||||||
return m_associated_component->traverse_as_directory(fs().fsid(), move(callback));
|
return m_associated_component->traverse_as_directory(procfs().fsid(), move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Inode> ProcFSDirectoryInode::lookup(StringView name)
|
RefPtr<Inode> ProcFSDirectoryInode::lookup(StringView name)
|
||||||
{
|
{
|
||||||
MutexLocker locker(fs().m_lock);
|
MutexLocker locker(procfs().m_lock);
|
||||||
auto component = m_associated_component->lookup(name);
|
auto component = m_associated_component->lookup(name);
|
||||||
if (!component)
|
if (!component)
|
||||||
return {};
|
return {};
|
||||||
return component->to_inode(fs());
|
return component->to_inode(procfs());
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<ProcFSLinkInode> ProcFSLinkInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component)
|
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)
|
ProcFSLinkInode::ProcFSLinkInode(const ProcFS& fs, const ProcFSExposedComponent& component)
|
||||||
: ProcFSInode(fs, component)
|
: ProcFSGlobalInode(fs, component)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
InodeMetadata ProcFSLinkInode::metadata() const
|
InodeMetadata ProcFSLinkInode::metadata() const
|
||||||
{
|
{
|
||||||
MutexLocker locker(m_inode_lock);
|
MutexLocker locker(m_inode_lock);
|
||||||
|
@ -239,4 +232,364 @@ InodeMetadata ProcFSLinkInode::metadata() const
|
||||||
return metadata;
|
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/KBufferBuilder.h>
|
||||||
#include <Kernel/Locking/Mutex.h>
|
#include <Kernel/Locking/Mutex.h>
|
||||||
#include <Kernel/ProcessExposed.h>
|
#include <Kernel/ProcessExposed.h>
|
||||||
|
#include <Kernel/UnixTypes.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class ProcFS final : public FileSystem {
|
class ProcFS final : public FileSystem {
|
||||||
friend class ProcFSInode;
|
friend class ProcFSInode;
|
||||||
friend class ProcFSDirectoryInode;
|
friend class ProcFSDirectoryInode;
|
||||||
|
friend class ProcFSProcessDirectoryInode;
|
||||||
|
friend class ProcFSGlobalInode;
|
||||||
|
friend class ProcFSAssociatedProcessInode;
|
||||||
|
friend class ProcFSProcessSubDirectoryInode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~ProcFS() override;
|
virtual ~ProcFS() override;
|
||||||
|
@ -40,33 +45,50 @@ class ProcFSInode : public Inode {
|
||||||
friend class ProcFS;
|
friend class ProcFS;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<ProcFSInode> create(const ProcFS&, const ProcFSExposedComponent&);
|
|
||||||
virtual ~ProcFSInode() override;
|
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;
|
StringView name() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ProcFSInode(const ProcFS&, const ProcFSExposedComponent&);
|
ProcFSGlobalInode(const ProcFS&, const ProcFSExposedComponent&);
|
||||||
|
|
||||||
// ^Inode
|
// ^Inode
|
||||||
virtual KResult attach(FileDescription& description) override;
|
virtual KResult attach(FileDescription& description) override final;
|
||||||
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override;
|
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final;
|
||||||
virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
|
virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override final;
|
||||||
virtual RefPtr<Inode> lookup(StringView name) override;
|
virtual void did_seek(FileDescription&, off_t) override final;
|
||||||
virtual void flush_metadata() override;
|
|
||||||
virtual InodeMetadata metadata() const override;
|
virtual InodeMetadata metadata() const override;
|
||||||
virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override;
|
virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
|
||||||
virtual KResultOr<NonnullRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, uid_t, gid_t) override;
|
virtual RefPtr<Inode> lookup(StringView) 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;
|
|
||||||
|
|
||||||
NonnullRefPtr<ProcFSExposedComponent> m_associated_component;
|
NonnullRefPtr<ProcFSExposedComponent> m_associated_component;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProcFSLinkInode : public ProcFSInode {
|
class ProcFSLinkInode : public ProcFSGlobalInode {
|
||||||
friend class ProcFS;
|
friend class ProcFS;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -77,16 +99,13 @@ protected:
|
||||||
virtual InodeMetadata metadata() const override;
|
virtual InodeMetadata metadata() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProcFSDirectoryInode : public ProcFSInode {
|
class ProcFSDirectoryInode final : public ProcFSGlobalInode {
|
||||||
friend class ProcFS;
|
friend class ProcFS;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<ProcFSDirectoryInode> create(const ProcFS&, const ProcFSExposedComponent&);
|
static NonnullRefPtr<ProcFSDirectoryInode> create(const ProcFS&, const ProcFSExposedComponent&);
|
||||||
virtual ~ProcFSDirectoryInode() override;
|
virtual ~ProcFSDirectoryInode() override;
|
||||||
|
|
||||||
ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); }
|
|
||||||
ProcFS const& fs() const { return static_cast<ProcFS const&>(Inode::fs()); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ProcFSDirectoryInode(const ProcFS&, const ProcFSExposedComponent&);
|
ProcFSDirectoryInode(const ProcFS&, const ProcFSExposedComponent&);
|
||||||
// ^Inode
|
// ^Inode
|
||||||
|
@ -95,4 +114,84 @@ protected:
|
||||||
virtual RefPtr<Inode> lookup(StringView name) override;
|
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/Routing.h>
|
||||||
#include <Kernel/Net/TCPSocket.h>
|
#include <Kernel/Net/TCPSocket.h>
|
||||||
#include <Kernel/Net/UDPSocket.h>
|
#include <Kernel/Net/UDPSocket.h>
|
||||||
|
#include <Kernel/Process.h>
|
||||||
#include <Kernel/ProcessExposed.h>
|
#include <Kernel/ProcessExposed.h>
|
||||||
#include <Kernel/Sections.h>
|
#include <Kernel/Sections.h>
|
||||||
#include <Kernel/TTY/TTY.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() };
|
InodeIdentifier identifier = { fsid, component.component_index() };
|
||||||
callback({ component.name(), identifier, 0 });
|
callback({ component.name(), identifier, 0 });
|
||||||
}
|
}
|
||||||
for (auto& component : m_process_directories) {
|
processes().for_each_shared([&](Process& process) {
|
||||||
InodeIdentifier identifier = { fsid, component.component_index() };
|
VERIFY(!(process.pid() < 0));
|
||||||
callback({ component.name(), identifier, 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;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,12 +921,12 @@ RefPtr<ProcFSExposedComponent> ProcFSRootDirectory::lookup(StringView name)
|
||||||
if (auto candidate = ProcFSExposedDirectory::lookup(name); !candidate.is_null())
|
if (auto candidate = ProcFSExposedDirectory::lookup(name); !candidate.is_null())
|
||||||
return candidate;
|
return candidate;
|
||||||
|
|
||||||
for (auto& component : m_process_directories) {
|
String process_directory_name = name;
|
||||||
if (component.name() == name) {
|
auto pid = process_directory_name.to_uint<unsigned>();
|
||||||
return component;
|
if (!pid.has_value())
|
||||||
}
|
return {};
|
||||||
}
|
auto actual_pid = pid.value();
|
||||||
return {};
|
return Process::from_pid(actual_pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
UNMAP_AFTER_INIT ProcFSRootDirectory::ProcFSRootDirectory()
|
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) {
|
processes().with_exclusive([&](auto& list) {
|
||||||
list.prepend(process);
|
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)
|
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_arguments.clear();
|
||||||
m_environment.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);
|
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!
|
// FIXME: Verify Process::m_fds_lock is locked!
|
||||||
m_description = nullptr;
|
m_description = nullptr;
|
||||||
m_flags = 0;
|
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)
|
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!
|
// FIXME: Verify Process::m_fds_lock is locked!
|
||||||
m_description = move(description);
|
m_description = move(description);
|
||||||
m_flags = flags;
|
m_flags = flags;
|
||||||
m_global_procfs_inode_index = ProcFSComponentRegistry::the().allocate_inode_index();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Custody& Process::root_directory()
|
Custody& Process::root_directory()
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <Kernel/Locking/ProtectedValue.h>
|
#include <Kernel/Locking/ProtectedValue.h>
|
||||||
#include <Kernel/Memory/AddressSpace.h>
|
#include <Kernel/Memory/AddressSpace.h>
|
||||||
#include <Kernel/PerformanceEventBuffer.h>
|
#include <Kernel/PerformanceEventBuffer.h>
|
||||||
|
#include <Kernel/ProcessExposed.h>
|
||||||
#include <Kernel/ProcessGroup.h>
|
#include <Kernel/ProcessGroup.h>
|
||||||
#include <Kernel/StdLib.h>
|
#include <Kernel/StdLib.h>
|
||||||
#include <Kernel/Thread.h>
|
#include <Kernel/Thread.h>
|
||||||
|
@ -84,7 +85,7 @@ typedef HashMap<FlatPtr, RefPtr<FutexQueue>> FutexQueues;
|
||||||
struct LoadResult;
|
struct LoadResult;
|
||||||
|
|
||||||
class Process
|
class Process
|
||||||
: public RefCounted<Process>
|
: public ProcFSExposedComponent
|
||||||
, public Weakable<Process> {
|
, public Weakable<Process> {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -551,6 +552,31 @@ private:
|
||||||
|
|
||||||
void setup_socket_fd(int fd, NonnullRefPtr<FileDescription> description, int type);
|
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()
|
inline PerformanceEventBuffer* current_perf_events_buffer()
|
||||||
{
|
{
|
||||||
if (g_profiling_all_threads)
|
if (g_profiling_all_threads)
|
||||||
|
@ -595,22 +621,16 @@ public:
|
||||||
|
|
||||||
FileDescription* description() { return m_description; }
|
FileDescription* description() { return m_description; }
|
||||||
const FileDescription* description() const { 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; }
|
u32 flags() const { return m_flags; }
|
||||||
void set_flags(u32 flags) { m_flags = flags; }
|
void set_flags(u32 flags) { m_flags = flags; }
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
void set(NonnullRefPtr<FileDescription>&&, u32 flags = 0);
|
void set(NonnullRefPtr<FileDescription>&&, u32 flags = 0);
|
||||||
void refresh_inode_index();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RefPtr<FileDescription> m_description;
|
RefPtr<FileDescription> m_description;
|
||||||
bool m_is_allocated { false };
|
bool m_is_allocated { false };
|
||||||
u32 m_flags { 0 };
|
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;
|
class ScopedDescriptionAllocation;
|
||||||
|
@ -626,9 +646,6 @@ public:
|
||||||
ScopedSpinLock lock(m_fds_lock);
|
ScopedSpinLock lock(m_fds_lock);
|
||||||
ScopedSpinLock lock_other(other.m_fds_lock);
|
ScopedSpinLock lock_other(other.m_fds_lock);
|
||||||
m_fds_metadatas = other.m_fds_metadatas;
|
m_fds_metadatas = other.m_fds_metadatas;
|
||||||
for (auto& file_description_metadata : m_fds_metadatas) {
|
|
||||||
file_description_metadata.refresh_inode_index();
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,35 +18,77 @@ namespace Kernel {
|
||||||
static SpinLock<u8> s_index_lock;
|
static SpinLock<u8> s_index_lock;
|
||||||
static InodeIndex s_next_inode_index = 0;
|
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);
|
ScopedSpinLock lock(s_index_lock);
|
||||||
s_next_inode_index = s_next_inode_index.value() + 1;
|
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 > 0);
|
||||||
|
VERIFY(s_next_inode_index < 0x100000000);
|
||||||
return s_next_inode_index.value();
|
return s_next_inode_index.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
InodeIndex ProcFSComponentRegistry::allocate_inode_index() const
|
ProcFSExposedComponent::ProcFSExposedComponent()
|
||||||
{
|
{
|
||||||
return s_allocate_inode_index();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcFSExposedComponent::ProcFSExposedComponent(StringView name)
|
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);
|
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)
|
ProcFSExposedDirectory::ProcFSExposedDirectory(StringView name)
|
||||||
: ProcFSExposedComponent(name)
|
: ProcFSExposedComponent(name)
|
||||||
{
|
{
|
||||||
|
@ -62,16 +104,6 @@ ProcFSExposedLink::ProcFSExposedLink(StringView name)
|
||||||
: ProcFSExposedComponent(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
|
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);
|
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;
|
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
|
KResultOr<size_t> ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const
|
||||||
{
|
{
|
||||||
VERIFY(offset == 0);
|
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
|
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
|
NonnullRefPtr<Inode> ProcFSExposedDirectory::to_inode(const ProcFS& procfs_instance) const
|
||||||
|
|
|
@ -13,27 +13,49 @@
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
#include <Kernel/Arch/x86/CPU.h>
|
#include <Kernel/Arch/x86/CPU.h>
|
||||||
#include <Kernel/FileSystem/File.h>
|
#include <Kernel/FileSystem/File.h>
|
||||||
|
#include <Kernel/FileSystem/FileDescription.h>
|
||||||
#include <Kernel/FileSystem/FileSystem.h>
|
#include <Kernel/FileSystem/FileSystem.h>
|
||||||
#include <Kernel/KBufferBuilder.h>
|
#include <Kernel/KBufferBuilder.h>
|
||||||
#include <Kernel/KResult.h>
|
#include <Kernel/KResult.h>
|
||||||
#include <Kernel/Process.h>
|
|
||||||
#include <Kernel/UserOrKernelBuffer.h>
|
#include <Kernel/UserOrKernelBuffer.h>
|
||||||
|
|
||||||
namespace Kernel {
|
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 {
|
class ProcFSComponentRegistry {
|
||||||
public:
|
public:
|
||||||
static ProcFSComponentRegistry& the();
|
static ProcFSComponentRegistry& the();
|
||||||
|
|
||||||
static void initialize();
|
static void initialize();
|
||||||
|
|
||||||
InodeIndex allocate_inode_index() const;
|
|
||||||
|
|
||||||
ProcFSComponentRegistry();
|
ProcFSComponentRegistry();
|
||||||
|
|
||||||
void register_new_process(Process&);
|
|
||||||
void unregister_process(Process&);
|
|
||||||
|
|
||||||
ProcFSRootDirectory& root_directory() { return *m_root_directory; }
|
ProcFSRootDirectory& root_directory() { return *m_root_directory; }
|
||||||
Mutex& get_lock() { return m_lock; }
|
Mutex& get_lock() { return m_lock; }
|
||||||
|
|
||||||
|
@ -64,13 +86,13 @@ public:
|
||||||
|
|
||||||
virtual NonnullRefPtr<Inode> to_inode(const ProcFS& procfs_instance) const;
|
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;
|
virtual ~ProcFSExposedComponent() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
ProcFSExposedComponent();
|
||||||
explicit ProcFSExposedComponent(StringView name);
|
explicit ProcFSExposedComponent(StringView name);
|
||||||
ProcFSExposedComponent(StringView name, InodeIndex preallocated_index);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OwnPtr<KString> m_name;
|
OwnPtr<KString> m_name;
|
||||||
|
@ -80,7 +102,6 @@ private:
|
||||||
class ProcFSExposedDirectory
|
class ProcFSExposedDirectory
|
||||||
: public ProcFSExposedComponent
|
: public ProcFSExposedComponent
|
||||||
, public Weakable<ProcFSExposedDirectory> {
|
, public Weakable<ProcFSExposedDirectory> {
|
||||||
friend class ProcFSProcessDirectory;
|
|
||||||
friend class ProcFSComponentRegistry;
|
friend class ProcFSComponentRegistry;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -114,62 +135,24 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual bool acquire_link(KBufferBuilder& builder) = 0;
|
virtual bool acquire_link(KBufferBuilder& builder) = 0;
|
||||||
explicit ProcFSExposedLink(StringView name);
|
explicit ProcFSExposedLink(StringView name);
|
||||||
ProcFSExposedLink(StringView name, InodeIndex preallocated_index);
|
|
||||||
mutable Mutex m_lock { "ProcFSLink" };
|
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 {
|
class ProcFSRootDirectory final : public ProcFSExposedDirectory {
|
||||||
friend class ProcFSComponentRegistry;
|
friend class ProcFSComponentRegistry;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
|
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
|
||||||
|
|
||||||
RefPtr<ProcFSProcessDirectory> process_directory_for(Process&);
|
|
||||||
static NonnullRefPtr<ProcFSRootDirectory> must_create();
|
static NonnullRefPtr<ProcFSRootDirectory> must_create();
|
||||||
virtual ~ProcFSRootDirectory();
|
virtual ~ProcFSRootDirectory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
|
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
|
||||||
ProcFSRootDirectory();
|
ProcFSRootDirectory();
|
||||||
|
};
|
||||||
|
|
||||||
IntrusiveList<ProcFSProcessDirectory, RefPtr<ProcFSProcessDirectory>, &ProcFSProcessDirectory::m_list_node> m_process_directories;
|
struct ProcFSInodeData : public FileDescriptionData {
|
||||||
|
OwnPtr<KBuffer> buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProcFSGlobalInformation : public ProcFSExposedComponent {
|
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 <AK/JsonValue.h>
|
||||||
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
#include <Kernel/Arch/x86/InterruptDisabler.h>
|
||||||
#include <Kernel/FileSystem/Custody.h>
|
#include <Kernel/FileSystem/Custody.h>
|
||||||
|
#include <Kernel/FileSystem/ProcFS.h>
|
||||||
#include <Kernel/KBufferBuilder.h>
|
#include <Kernel/KBufferBuilder.h>
|
||||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||||
#include <Kernel/Memory/MemoryManager.h>
|
#include <Kernel/Memory/MemoryManager.h>
|
||||||
|
#include <Kernel/Process.h>
|
||||||
#include <Kernel/ProcessExposed.h>
|
#include <Kernel/ProcessExposed.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class ProcFSProcessStacks;
|
KResultOr<size_t> Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const
|
||||||
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
|
|
||||||
{
|
{
|
||||||
MutexLocker locker(m_lock);
|
JsonArraySerializer array { builder };
|
||||||
auto parent_directory = m_process_directory.strong_ref();
|
auto thread = Thread::from_tid(thread_id);
|
||||||
if (parent_directory.is_null())
|
if (!thread)
|
||||||
return KResult(EINVAL);
|
|
||||||
callback({ ".", { fsid, component_index() }, 0 });
|
|
||||||
callback({ "..", { fsid, parent_directory->component_index() }, 0 });
|
|
||||||
|
|
||||||
auto process = parent_directory->associated_process();
|
|
||||||
if (process.is_null())
|
|
||||||
return KResult(ESRCH);
|
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();
|
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 });
|
callback({ String::number(tid), identifier, 0 });
|
||||||
});
|
});
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<ProcFSExposedComponent> ProcFSProcessStacks::lookup(StringView name)
|
RefPtr<Inode> Process::lookup_stacks_directory(const ProcFS& procfs, StringView name) const
|
||||||
{
|
{
|
||||||
MutexLocker locker(m_lock);
|
RefPtr<ProcFSProcessPropertyInode> thread_stack_inode;
|
||||||
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;
|
|
||||||
// FIXME: Try to exit the loop earlier
|
// 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();
|
int tid = thread.tid().value();
|
||||||
|
VERIFY(!(tid < 0));
|
||||||
if (name == String::number(tid)) {
|
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;
|
KResultOr<size_t> Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const
|
||||||
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
|
|
||||||
{
|
{
|
||||||
MutexLocker locker(m_lock);
|
auto file_description = m_fds.file_description(fd);
|
||||||
auto parent_directory = m_process_directory.strong_ref();
|
if (!file_description)
|
||||||
if (parent_directory.is_null())
|
return EBADF;
|
||||||
return KResult(EINVAL);
|
auto data = file_description->absolute_path();
|
||||||
callback({ ".", { fsid, component_index() }, 0 });
|
builder.append(data);
|
||||||
callback({ "..", { fsid, parent_directory->component_index() }, 0 });
|
return data.length();
|
||||||
|
}
|
||||||
|
|
||||||
auto process = parent_directory->associated_process();
|
KResult Process::traverse_file_descriptions_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
|
||||||
if (process.is_null())
|
{
|
||||||
return KResult(ESRCH);
|
callback({ ".", { fsid, component_index() }, 0 });
|
||||||
|
callback({ "..", { fsid, component_index() }, 0 });
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
process->fds().enumerate([&](auto& file_description_metadata) {
|
fds().enumerate([&](auto& file_description_metadata) {
|
||||||
if (!file_description_metadata.is_valid()) {
|
if (!file_description_metadata.is_valid()) {
|
||||||
count++;
|
count++;
|
||||||
return;
|
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 });
|
callback({ String::number(count), identifier, 0 });
|
||||||
count++;
|
count++;
|
||||||
});
|
});
|
||||||
return KSuccess;
|
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);
|
RefPtr<ProcFSProcessPropertyInode> file_description_link;
|
||||||
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;
|
|
||||||
// FIXME: Try to exit the loop earlier
|
// FIXME: Try to exit the loop earlier
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
process->fds().enumerate([&](auto& file_description_metadata) {
|
fds().enumerate([&](auto& file_description_metadata) {
|
||||||
if (!file_description_metadata.is_valid()) {
|
if (!file_description_metadata.is_valid()) {
|
||||||
count++;
|
count++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (name == String::number(count)) {
|
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++;
|
count++;
|
||||||
});
|
});
|
||||||
return procfd_fd;
|
return file_description_link;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProcFSProcessPledge final : public ProcFSProcessInformation {
|
KResult Process::procfs_get_pledge_stats(KBufferBuilder& builder) const
|
||||||
public:
|
{
|
||||||
static NonnullRefPtr<ProcFSProcessPledge> create(const ProcFSProcessDirectory& parent_directory)
|
JsonObjectSerializer obj { builder };
|
||||||
{
|
#define __ENUMERATE_PLEDGE_PROMISE(x) \
|
||||||
return adopt_ref(*new (nothrow) ProcFSProcessPledge(parent_directory));
|
if (has_promised(Pledge::x)) { \
|
||||||
|
if (!builder.is_empty()) \
|
||||||
|
builder.append(' '); \
|
||||||
|
builder.append(#x); \
|
||||||
}
|
}
|
||||||
|
if (has_promises()) {
|
||||||
private:
|
StringBuilder builder;
|
||||||
explicit ProcFSProcessPledge(const ProcFSProcessDirectory& parent_directory)
|
ENUMERATE_PLEDGE_PROMISES
|
||||||
: ProcFSProcessInformation("pledge"sv, parent_directory)
|
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
|
#undef __ENUMERATE_PLEDGE_PROMISE
|
||||||
obj.finish();
|
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();
|
|
||||||
return KSuccess;
|
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();
|
InterruptDisabler disabler;
|
||||||
m_associated_process.clear();
|
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)
|
KResult Process::procfs_get_fds_stats(KBufferBuilder& builder) const
|
||||||
: ProcFSExposedDirectory(String::formatted("{:d}", process.pid().value()), ProcFSComponentRegistry::the().root_directory())
|
|
||||||
, m_associated_process(process)
|
|
||||||
{
|
{
|
||||||
|
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_kernel_stack_region(move(kernel_stack_region))
|
||||||
, m_name(move(name))
|
, m_name(move(name))
|
||||||
, m_block_timer(block_timer)
|
, m_block_timer(block_timer)
|
||||||
, m_global_procfs_inode_index(ProcFSComponentRegistry::the().allocate_inode_index())
|
|
||||||
{
|
{
|
||||||
bool is_first_thread = m_process->add_thread(*this);
|
bool is_first_thread = m_process->add_thread(*this);
|
||||||
if (is_first_thread) {
|
if (is_first_thread) {
|
||||||
|
|
|
@ -1213,8 +1213,6 @@ public:
|
||||||
bool is_profiling_suppressed() const { return m_is_profiling_suppressed; }
|
bool is_profiling_suppressed() const { return m_is_profiling_suppressed; }
|
||||||
void set_profiling_suppressed() { m_is_profiling_suppressed = true; }
|
void set_profiling_suppressed() { m_is_profiling_suppressed = true; }
|
||||||
|
|
||||||
InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; }
|
|
||||||
|
|
||||||
String backtrace();
|
String backtrace();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -1364,10 +1362,6 @@ private:
|
||||||
|
|
||||||
RefPtr<Timer> m_block_timer;
|
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 };
|
bool m_is_profiling_suppressed { false };
|
||||||
|
|
||||||
void yield_and_release_relock_big_lock();
|
void yield_and_release_relock_big_lock();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue