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:
Liav A 2021-08-10 20:51:28 +03:00 committed by Andreas Kling
parent bf1adc2d5d
commit 7ba991dc37
Notes: sideshowbarker 2024-07-18 07:04:26 +09:00
11 changed files with 991 additions and 879 deletions

View 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.

View file

@ -13,6 +13,7 @@
#include <Kernel/FileSystem/ProcFS.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Heap/kmalloc.h>
#include <Kernel/Process.h>
#include <Kernel/Sections.h>
#include <LibC/errno_numbers.h>
@ -36,25 +37,16 @@ UNMAP_AFTER_INIT ProcFSComponentRegistry::ProcFSComponentRegistry()
{
}
void ProcFSComponentRegistry::register_new_process(Process& new_process)
{
MutexLocker locker(m_lock);
m_root_directory->m_process_directories.append(ProcFSProcessDirectory::create(new_process));
}
void ProcFSComponentRegistry::unregister_process(Process& deleted_process)
{
auto process_directory = m_root_directory->process_directory_for(deleted_process).release_nonnull();
process_directory->prepare_for_deletion();
process_directory->m_list_node.remove();
dbgln_if(PROCFS_DEBUG, "ProcFSExposedDirectory ref_count now: {}", process_directory->ref_count());
}
RefPtr<ProcFS> ProcFS::create()
{
return adopt_ref_if_nonnull(new (nothrow) ProcFS);
}
ProcFS::ProcFS()
: m_root_inode(ProcFSComponentRegistry::the().root_directory().to_inode(*this))
{
}
ProcFS::~ProcFS()
{
}
@ -69,90 +61,25 @@ Inode& ProcFS::root_inode()
return *m_root_inode;
}
NonnullRefPtr<ProcFSInode> ProcFSInode::create(const ProcFS& fs, const ProcFSExposedComponent& component)
ProcFSInode::ProcFSInode(const ProcFS& fs, InodeIndex index)
: Inode(const_cast<ProcFS&>(fs), index)
{
return adopt_ref(*new (nothrow) ProcFSInode(fs, component));
}
ProcFSInode::ProcFSInode(const ProcFS& fs, const ProcFSExposedComponent& component)
: Inode(const_cast<ProcFS&>(fs), component.component_index())
, m_associated_component(component)
{
}
KResult ProcFSInode::attach(FileDescription& description)
{
return m_associated_component->refresh_data(description);
}
void ProcFSInode::did_seek(FileDescription& description, off_t new_offset)
{
if (new_offset != 0)
return;
auto result = m_associated_component->refresh_data(description);
if (result.is_error()) {
// Subsequent calls to read will return EIO!
dbgln("ProcFS: Could not refresh contents: {}", result.error());
}
}
ProcFSInode::~ProcFSInode()
{
}
ProcFS::ProcFS()
: m_root_inode(ProcFSComponentRegistry::the().root_directory().to_inode(*this))
{
}
KResultOr<size_t> ProcFSInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const
{
return m_associated_component->read_bytes(offset, count, buffer, fd);
}
StringView ProcFSInode::name() const
{
return m_associated_component->name();
}
KResult ProcFSInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const
{
VERIFY_NOT_REACHED();
}
RefPtr<Inode> ProcFSInode::lookup(StringView)
{
VERIFY_NOT_REACHED();
}
InodeMetadata ProcFSInode::metadata() const
{
MutexLocker locker(m_inode_lock);
InodeMetadata metadata;
metadata.inode = { fsid(), m_associated_component->component_index() };
metadata.mode = S_IFREG | m_associated_component->required_mode();
metadata.uid = m_associated_component->owner_user();
metadata.gid = m_associated_component->owner_group();
metadata.size = m_associated_component->size();
metadata.mtime = m_associated_component->modified_time();
return metadata;
}
void ProcFSInode::flush_metadata()
{
}
KResultOr<size_t> ProcFSInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd)
{
return m_associated_component->write_bytes(offset, count, buffer, fd);
}
KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(StringView, mode_t, dev_t, uid_t, gid_t)
KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t)
{
return EROFS;
}
KResult ProcFSInode::add_child(Inode&, const StringView&, mode_t)
KResultOr<NonnullRefPtr<Inode>> ProcFSInode::create_child(StringView, mode_t, dev_t, uid_t, gid_t)
{
return EROFS;
}
@ -177,13 +104,78 @@ KResult ProcFSInode::truncate(u64)
return EPERM;
}
NonnullRefPtr<ProcFSGlobalInode> ProcFSGlobalInode::create(const ProcFS& fs, const ProcFSExposedComponent& component)
{
return adopt_ref(*new (nothrow) ProcFSGlobalInode(fs, component));
}
ProcFSGlobalInode::ProcFSGlobalInode(const ProcFS& fs, const ProcFSExposedComponent& component)
: ProcFSInode(fs, component.component_index())
, m_associated_component(component)
{
}
void ProcFSGlobalInode::did_seek(FileDescription& description, off_t new_offset)
{
if (new_offset != 0)
return;
auto result = m_associated_component->refresh_data(description);
if (result.is_error()) {
// Subsequent calls to read will return EIO!
dbgln("ProcFS: Could not refresh contents: {}", result.error());
}
}
KResult ProcFSGlobalInode::attach(FileDescription& description)
{
return m_associated_component->refresh_data(description);
}
KResultOr<size_t> ProcFSGlobalInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* fd) const
{
return m_associated_component->read_bytes(offset, count, buffer, fd);
}
StringView ProcFSGlobalInode::name() const
{
return m_associated_component->name();
}
KResult ProcFSGlobalInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const
{
VERIFY_NOT_REACHED();
}
RefPtr<Inode> ProcFSGlobalInode::lookup(StringView)
{
VERIFY_NOT_REACHED();
}
InodeMetadata ProcFSGlobalInode::metadata() const
{
MutexLocker locker(m_inode_lock);
InodeMetadata metadata;
metadata.inode = { fsid(), m_associated_component->component_index() };
metadata.mode = S_IFREG | m_associated_component->required_mode();
metadata.uid = m_associated_component->owner_user();
metadata.gid = m_associated_component->owner_group();
metadata.size = m_associated_component->size();
metadata.mtime = m_associated_component->modified_time();
return metadata;
}
KResultOr<size_t> ProcFSGlobalInode::write_bytes(off_t offset, size_t count, const UserOrKernelBuffer& buffer, FileDescription* fd)
{
return m_associated_component->write_bytes(offset, count, buffer, fd);
}
NonnullRefPtr<ProcFSDirectoryInode> ProcFSDirectoryInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component)
{
return adopt_ref(*new (nothrow) ProcFSDirectoryInode(procfs, component));
}
ProcFSDirectoryInode::ProcFSDirectoryInode(const ProcFS& fs, const ProcFSExposedComponent& component)
: ProcFSInode(fs, component)
: ProcFSGlobalInode(fs, component)
{
}
@ -204,17 +196,17 @@ InodeMetadata ProcFSDirectoryInode::metadata() const
}
KResult ProcFSDirectoryInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
{
MutexLocker locker(fs().m_lock);
return m_associated_component->traverse_as_directory(fs().fsid(), move(callback));
MutexLocker locker(procfs().m_lock);
return m_associated_component->traverse_as_directory(procfs().fsid(), move(callback));
}
RefPtr<Inode> ProcFSDirectoryInode::lookup(StringView name)
{
MutexLocker locker(fs().m_lock);
MutexLocker locker(procfs().m_lock);
auto component = m_associated_component->lookup(name);
if (!component)
return {};
return component->to_inode(fs());
return component->to_inode(procfs());
}
NonnullRefPtr<ProcFSLinkInode> ProcFSLinkInode::create(const ProcFS& procfs, const ProcFSExposedComponent& component)
@ -223,9 +215,10 @@ NonnullRefPtr<ProcFSLinkInode> ProcFSLinkInode::create(const ProcFS& procfs, con
}
ProcFSLinkInode::ProcFSLinkInode(const ProcFS& fs, const ProcFSExposedComponent& component)
: ProcFSInode(fs, component)
: ProcFSGlobalInode(fs, component)
{
}
InodeMetadata ProcFSLinkInode::metadata() const
{
MutexLocker locker(m_inode_lock);
@ -239,4 +232,364 @@ InodeMetadata ProcFSLinkInode::metadata() const
return metadata;
}
ProcFSProcessAssociatedInode::ProcFSProcessAssociatedInode(const ProcFS& fs, ProcessID associated_pid, InodeIndex determined_index)
: ProcFSInode(fs, determined_index)
, m_pid(associated_pid)
{
}
KResultOr<size_t> ProcFSProcessAssociatedInode::write_bytes(off_t, size_t, const UserOrKernelBuffer&, FileDescription*)
{
VERIFY_NOT_REACHED();
}
NonnullRefPtr<ProcFSProcessDirectoryInode> ProcFSProcessDirectoryInode::create(const ProcFS& procfs, ProcessID pid)
{
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessDirectoryInode(procfs, pid)).release_nonnull();
}
ProcFSProcessDirectoryInode::ProcFSProcessDirectoryInode(const ProcFS& procfs, ProcessID pid)
: ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid))
{
}
KResult ProcFSProcessDirectoryInode::attach(FileDescription&)
{
return KSuccess;
}
InodeMetadata ProcFSProcessDirectoryInode::metadata() const
{
MutexLocker locker(m_inode_lock);
auto process = Process::from_pid(associated_pid());
if (!process)
return {};
InodeMetadata metadata;
metadata.inode = { fsid(), process->component_index() };
metadata.mode = S_IFDIR | process->required_mode();
metadata.uid = process->owner_user();
metadata.gid = process->owner_group();
metadata.size = 0;
metadata.mtime = process->modified_time();
return metadata;
}
KResultOr<size_t> ProcFSProcessDirectoryInode::read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const
{
VERIFY_NOT_REACHED();
}
KResult ProcFSProcessDirectoryInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
if (!process)
return EINVAL;
return process->traverse_as_directory(procfs().fsid(), move(callback));
}
RefPtr<Inode> ProcFSProcessDirectoryInode::lookup(StringView name)
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
if (!process)
return nullptr;
if (name == "fd")
return ProcFSProcessSubDirectoryInode::create(procfs(), SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions, associated_pid());
if (name == "stacks")
return ProcFSProcessSubDirectoryInode::create(procfs(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks, associated_pid());
if (name == "unveil")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::Unveil, associated_pid());
if (name == "pledge")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::Pledge, associated_pid());
if (name == "fds")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::FileDescriptions, associated_pid());
if (name == "exe")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::BinaryLink, associated_pid());
if (name == "cwd")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink, associated_pid());
if (name == "perf_events")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::PerformanceEvents, associated_pid());
if (name == "vm")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::VirtualMemoryStats, associated_pid());
if (name == "root")
return ProcFSProcessPropertyInode::create_for_pid_property(procfs(), SegmentedProcFSIndex::MainProcessProperty::RootLink, associated_pid());
return nullptr;
}
NonnullRefPtr<ProcFSProcessSubDirectoryInode> ProcFSProcessSubDirectoryInode::create(const ProcFS& procfs, SegmentedProcFSIndex::ProcessSubDirectory sub_directory_type, ProcessID pid)
{
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessSubDirectoryInode(procfs, sub_directory_type, pid)).release_nonnull();
}
ProcFSProcessSubDirectoryInode::ProcFSProcessSubDirectoryInode(const ProcFS& procfs, SegmentedProcFSIndex::ProcessSubDirectory sub_directory_type, ProcessID pid)
: ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid, sub_directory_type))
, m_sub_directory_type(sub_directory_type)
{
}
KResultOr<size_t> ProcFSProcessSubDirectoryInode::read_bytes(off_t, size_t, UserOrKernelBuffer&, FileDescription*) const
{
VERIFY_NOT_REACHED();
}
KResult ProcFSProcessSubDirectoryInode::attach(FileDescription&)
{
return KSuccess;
}
void ProcFSProcessSubDirectoryInode::did_seek(FileDescription&, off_t)
{
VERIFY_NOT_REACHED();
}
InodeMetadata ProcFSProcessSubDirectoryInode::metadata() const
{
MutexLocker locker(m_inode_lock);
auto process = Process::from_pid(associated_pid());
if (!process)
return {};
InodeMetadata metadata;
metadata.inode = { fsid(), process->component_index() };
metadata.mode = S_IFDIR | process->required_mode();
metadata.uid = process->owner_user();
metadata.gid = process->owner_group();
metadata.size = 0;
metadata.mtime = process->modified_time();
return metadata;
}
KResult ProcFSProcessSubDirectoryInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
if (!process)
return EINVAL;
switch (m_sub_directory_type) {
case SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions:
return process->traverse_file_descriptions_directory(procfs().fsid(), move(callback));
case SegmentedProcFSIndex::ProcessSubDirectory::Stacks:
return process->traverse_stacks_directory(procfs().fsid(), move(callback));
default:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
RefPtr<Inode> ProcFSProcessSubDirectoryInode::lookup(StringView name)
{
MutexLocker locker(procfs().m_lock);
auto process = Process::from_pid(associated_pid());
if (!process)
return {};
switch (m_sub_directory_type) {
case SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions:
return process->lookup_file_descriptions_directory(procfs(), name);
case SegmentedProcFSIndex::ProcessSubDirectory::Stacks:
return process->lookup_stacks_directory(procfs(), name);
default:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
NonnullRefPtr<ProcFSProcessPropertyInode> ProcFSProcessPropertyInode::create_for_file_description_link(const ProcFS& procfs, unsigned file_description_index, ProcessID pid)
{
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessPropertyInode(procfs, file_description_index, pid)).release_nonnull();
}
NonnullRefPtr<ProcFSProcessPropertyInode> ProcFSProcessPropertyInode::create_for_thread_stack(const ProcFS& procfs, ThreadID stack_thread_index, ProcessID pid)
{
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessPropertyInode(procfs, stack_thread_index, pid)).release_nonnull();
}
NonnullRefPtr<ProcFSProcessPropertyInode> ProcFSProcessPropertyInode::create_for_pid_property(const ProcFS& procfs, SegmentedProcFSIndex::MainProcessProperty main_property_type, ProcessID pid)
{
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessPropertyInode(procfs, main_property_type, pid)).release_nonnull();
}
ProcFSProcessPropertyInode::ProcFSProcessPropertyInode(const ProcFS& procfs, SegmentedProcFSIndex::MainProcessProperty main_property_type, ProcessID pid)
: ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid, main_property_type))
, m_parent_sub_directory_type(SegmentedProcFSIndex::ProcessSubDirectory::Reserved)
{
m_possible_data.property_type = main_property_type;
}
ProcFSProcessPropertyInode::ProcFSProcessPropertyInode(const ProcFS& procfs, unsigned file_description_index, ProcessID pid)
: ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_file_description(pid, file_description_index))
, m_parent_sub_directory_type(SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions)
{
m_possible_data.property_index = file_description_index;
}
ProcFSProcessPropertyInode::ProcFSProcessPropertyInode(const ProcFS& procfs, ThreadID thread_stack_index, ProcessID pid)
: ProcFSProcessAssociatedInode(procfs, pid, SegmentedProcFSIndex::build_segmented_index_for_thread_stack(pid, thread_stack_index))
, m_parent_sub_directory_type(SegmentedProcFSIndex::ProcessSubDirectory::Stacks)
{
m_possible_data.property_index = thread_stack_index.value();
}
KResult ProcFSProcessPropertyInode::attach(FileDescription& description)
{
return refresh_data(description);
}
void ProcFSProcessPropertyInode::did_seek(FileDescription& description, off_t offset)
{
if (offset != 0)
return;
(void)refresh_data(description);
}
static mode_t determine_procfs_process_inode_mode(SegmentedProcFSIndex::ProcessSubDirectory parent_sub_directory_type, SegmentedProcFSIndex::MainProcessProperty main_property)
{
if (parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions)
return S_IFLNK | 0400;
if (parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Stacks)
return S_IFREG | 0400;
VERIFY(parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Reserved);
if (main_property == SegmentedProcFSIndex::MainProcessProperty::BinaryLink)
return S_IFLNK | 0777;
if (main_property == SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink)
return S_IFLNK | 0777;
if (main_property == SegmentedProcFSIndex::MainProcessProperty::RootLink)
return S_IFLNK | 0777;
return S_IFREG | 0400;
}
InodeMetadata ProcFSProcessPropertyInode::metadata() const
{
MutexLocker locker(m_inode_lock);
auto process = Process::from_pid(associated_pid());
if (!process)
return {};
InodeMetadata metadata;
metadata.inode = { fsid(), process->component_index() };
metadata.mode = determine_procfs_process_inode_mode(m_parent_sub_directory_type, m_possible_data.property_type);
metadata.uid = process->owner_user();
metadata.gid = process->owner_group();
metadata.size = 0;
metadata.mtime = process->modified_time();
return metadata;
}
KResult ProcFSProcessPropertyInode::traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const
{
VERIFY_NOT_REACHED();
}
KResultOr<size_t> ProcFSProcessPropertyInode::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const
{
dbgln_if(PROCFS_DEBUG, "ProcFS ProcessInformation: read_bytes offset: {} count: {}", offset, count);
VERIFY(offset >= 0);
VERIFY(buffer.user_or_kernel_ptr());
if (!description) {
KBufferBuilder builder;
auto process = Process::from_pid(associated_pid());
if (!process)
return KResult(ESRCH);
if (auto result = try_to_acquire_data(*process, builder); result.is_error())
return result.error();
auto data_buffer = builder.build();
if (!data_buffer)
return KResult(EFAULT);
ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(count));
if (!buffer.write(data_buffer->data() + offset, nread))
return KResult(EFAULT);
return nread;
}
if (!description->data()) {
dbgln("ProcFS Process Information: Do not have cached data!");
return KResult(EIO);
}
MutexLocker locker(m_refresh_lock);
auto& typed_cached_data = static_cast<ProcFSInodeData&>(*description->data());
auto& data_buffer = typed_cached_data.buffer;
if (!data_buffer || (size_t)offset >= data_buffer->size())
return 0;
ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(count));
if (!buffer.write(data_buffer->data() + offset, nread))
return KResult(EFAULT);
return nread;
}
RefPtr<Inode> ProcFSProcessPropertyInode::lookup(StringView)
{
VERIFY_NOT_REACHED();
}
static KResult build_from_cached_data(KBufferBuilder& builder, ProcFSInodeData& cached_data)
{
cached_data.buffer = builder.build();
if (!cached_data.buffer)
return ENOMEM;
return KSuccess;
}
KResult ProcFSProcessPropertyInode::try_to_acquire_data(Process& process, KBufferBuilder& builder) const
{
// FIXME: Verify process is already ref-counted
if (m_parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions) {
if (auto result = process.procfs_get_file_description_link(m_possible_data.property_index, builder); result.is_error())
return result.error();
return KSuccess;
}
if (m_parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Stacks) {
if (auto result = process.procfs_get_thread_stack(m_possible_data.property_index, builder); result.is_error())
return result.error();
return KSuccess;
}
VERIFY(m_parent_sub_directory_type == SegmentedProcFSIndex::ProcessSubDirectory::Reserved);
switch (m_possible_data.property_type) {
case SegmentedProcFSIndex::MainProcessProperty::Unveil:
return process.procfs_get_fds_stats(builder);
case SegmentedProcFSIndex::MainProcessProperty::Pledge:
return process.procfs_get_pledge_stats(builder);
case SegmentedProcFSIndex::MainProcessProperty::FileDescriptions:
return process.procfs_get_fds_stats(builder);
case SegmentedProcFSIndex::MainProcessProperty::BinaryLink:
return process.procfs_get_binary_link(builder);
case SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink:
return process.procfs_get_current_work_directory_link(builder);
case SegmentedProcFSIndex::MainProcessProperty::PerformanceEvents:
return process.procfs_get_perf_events(builder);
case SegmentedProcFSIndex::MainProcessProperty::VirtualMemoryStats:
return process.procfs_get_virtual_memory_stats(builder);
case SegmentedProcFSIndex::MainProcessProperty::RootLink:
return process.procfs_get_root_link(builder);
default:
VERIFY_NOT_REACHED();
}
}
KResult ProcFSProcessPropertyInode::refresh_data(FileDescription& description)
{
// For process-specific inodes, hold the process's ptrace lock across refresh
// and refuse to load data if the process is not dumpable.
// Without this, files opened before a process went non-dumpable could still be used for dumping.
auto process = Process::from_pid(associated_pid());
if (!process)
return KResult(ESRCH);
process->ptrace_lock().lock();
if (!process->is_dumpable()) {
process->ptrace_lock().unlock();
return EPERM;
}
ScopeGuard guard = [&] {
process->ptrace_lock().unlock();
};
MutexLocker locker(m_refresh_lock);
auto& cached_data = description.data();
if (!cached_data) {
cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData);
if (!cached_data)
return ENOMEM;
}
KBufferBuilder builder;
if (auto result = try_to_acquire_data(*process, builder); result.is_error())
return result;
return build_from_cached_data(builder, static_cast<ProcFSInodeData&>(*cached_data));
}
}

View file

@ -14,12 +14,17 @@
#include <Kernel/KBufferBuilder.h>
#include <Kernel/Locking/Mutex.h>
#include <Kernel/ProcessExposed.h>
#include <Kernel/UnixTypes.h>
namespace Kernel {
class ProcFS final : public FileSystem {
friend class ProcFSInode;
friend class ProcFSDirectoryInode;
friend class ProcFSProcessDirectoryInode;
friend class ProcFSGlobalInode;
friend class ProcFSAssociatedProcessInode;
friend class ProcFSProcessSubDirectoryInode;
public:
virtual ~ProcFS() override;
@ -40,33 +45,50 @@ class ProcFSInode : public Inode {
friend class ProcFS;
public:
static NonnullRefPtr<ProcFSInode> create(const ProcFS&, const ProcFSExposedComponent&);
virtual ~ProcFSInode() override;
protected:
ProcFSInode(const ProcFS&, InodeIndex);
ProcFS& procfs() { return static_cast<ProcFS&>(Inode::fs()); }
ProcFS const& procfs() const { return static_cast<ProcFS const&>(Inode::fs()); }
// ^Inode
virtual KResult attach(FileDescription& description) = 0;
virtual void did_seek(FileDescription&, off_t) = 0;
virtual void flush_metadata() override final;
virtual KResultOr<NonnullRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, uid_t, gid_t) override final;
virtual KResult add_child(Inode&, const StringView& name, mode_t) override final;
virtual KResult remove_child(const StringView& name) override final;
virtual KResult chmod(mode_t) override final;
virtual KResult chown(uid_t, gid_t) override final;
virtual KResult truncate(u64) override final;
};
class ProcFSGlobalInode : public ProcFSInode {
friend class ProcFS;
public:
static NonnullRefPtr<ProcFSGlobalInode> create(const ProcFS&, const ProcFSExposedComponent&);
virtual ~ProcFSGlobalInode() override {};
StringView name() const;
protected:
ProcFSInode(const ProcFS&, const ProcFSExposedComponent&);
ProcFSGlobalInode(const ProcFS&, const ProcFSExposedComponent&);
// ^Inode
virtual KResult attach(FileDescription& description) override;
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override;
virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual RefPtr<Inode> lookup(StringView name) override;
virtual void flush_metadata() override;
virtual KResult attach(FileDescription& description) override final;
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final;
virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override final;
virtual void did_seek(FileDescription&, off_t) override final;
virtual InodeMetadata metadata() const override;
virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override;
virtual KResultOr<NonnullRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, uid_t, gid_t) override;
virtual KResult add_child(Inode&, const StringView& name, mode_t) override;
virtual KResult remove_child(const StringView& name) override;
virtual void did_seek(FileDescription&, off_t) override;
virtual KResult chmod(mode_t) override;
virtual KResult chown(uid_t, gid_t) override;
virtual KResult truncate(u64) override;
virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual RefPtr<Inode> lookup(StringView) override;
NonnullRefPtr<ProcFSExposedComponent> m_associated_component;
};
class ProcFSLinkInode : public ProcFSInode {
class ProcFSLinkInode : public ProcFSGlobalInode {
friend class ProcFS;
public:
@ -77,16 +99,13 @@ protected:
virtual InodeMetadata metadata() const override;
};
class ProcFSDirectoryInode : public ProcFSInode {
class ProcFSDirectoryInode final : public ProcFSGlobalInode {
friend class ProcFS;
public:
static NonnullRefPtr<ProcFSDirectoryInode> create(const ProcFS&, const ProcFSExposedComponent&);
virtual ~ProcFSDirectoryInode() override;
ProcFS& fs() { return static_cast<ProcFS&>(Inode::fs()); }
ProcFS const& fs() const { return static_cast<ProcFS const&>(Inode::fs()); }
protected:
ProcFSDirectoryInode(const ProcFS&, const ProcFSExposedComponent&);
// ^Inode
@ -95,4 +114,84 @@ protected:
virtual RefPtr<Inode> lookup(StringView name) override;
};
class ProcFSProcessAssociatedInode : public ProcFSInode {
friend class ProcFS;
protected:
ProcFSProcessAssociatedInode(const ProcFS&, ProcessID, InodeIndex);
ProcessID associated_pid() const { return m_pid; }
// ^Inode
virtual KResultOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, FileDescription*) override final;
private:
const ProcessID m_pid;
};
class ProcFSProcessDirectoryInode final : public ProcFSProcessAssociatedInode {
friend class ProcFS;
public:
static NonnullRefPtr<ProcFSProcessDirectoryInode> create(const ProcFS&, ProcessID);
private:
ProcFSProcessDirectoryInode(const ProcFS&, ProcessID);
// ^Inode
virtual KResult attach(FileDescription& description) override;
virtual void did_seek(FileDescription&, off_t) override { }
virtual InodeMetadata metadata() const override;
virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final;
virtual RefPtr<Inode> lookup(StringView name) override;
};
class ProcFSProcessSubDirectoryInode final : public ProcFSProcessAssociatedInode {
friend class ProcFS;
public:
static NonnullRefPtr<ProcFSProcessSubDirectoryInode> create(const ProcFS&, SegmentedProcFSIndex::ProcessSubDirectory, ProcessID);
private:
ProcFSProcessSubDirectoryInode(const ProcFS&, SegmentedProcFSIndex::ProcessSubDirectory, ProcessID);
// ^Inode
virtual KResult attach(FileDescription& description) override;
virtual void did_seek(FileDescription&, off_t) override;
virtual InodeMetadata metadata() const override;
virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final;
virtual RefPtr<Inode> lookup(StringView name) override;
const SegmentedProcFSIndex::ProcessSubDirectory m_sub_directory_type;
};
class ProcFSProcessPropertyInode final : public ProcFSProcessAssociatedInode {
friend class ProcFS;
public:
static NonnullRefPtr<ProcFSProcessPropertyInode> create_for_file_description_link(const ProcFS&, unsigned, ProcessID);
static NonnullRefPtr<ProcFSProcessPropertyInode> create_for_thread_stack(const ProcFS&, ThreadID, ProcessID);
static NonnullRefPtr<ProcFSProcessPropertyInode> create_for_pid_property(const ProcFS&, SegmentedProcFSIndex::MainProcessProperty, ProcessID);
private:
ProcFSProcessPropertyInode(const ProcFS&, SegmentedProcFSIndex::MainProcessProperty, ProcessID);
ProcFSProcessPropertyInode(const ProcFS&, ThreadID, ProcessID);
ProcFSProcessPropertyInode(const ProcFS&, unsigned, ProcessID);
// ^Inode
virtual KResult attach(FileDescription& description) override;
virtual void did_seek(FileDescription&, off_t) override;
virtual InodeMetadata metadata() const override;
virtual KResult traverse_as_directory(Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual KResultOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, FileDescription*) const override final;
virtual RefPtr<Inode> lookup(StringView name) override final;
KResult refresh_data(FileDescription& description);
KResult try_to_acquire_data(Process& process, KBufferBuilder& builder) const;
const SegmentedProcFSIndex::ProcessSubDirectory m_parent_sub_directory_type;
union {
SegmentedProcFSIndex::MainProcessProperty property_type;
unsigned property_index;
} m_possible_data;
mutable Mutex m_refresh_lock;
};
}

View file

@ -27,6 +27,7 @@
#include <Kernel/Net/Routing.h>
#include <Kernel/Net/TCPSocket.h>
#include <Kernel/Net/UDPSocket.h>
#include <Kernel/Process.h>
#include <Kernel/ProcessExposed.h>
#include <Kernel/Sections.h>
#include <Kernel/TTY/TTY.h>
@ -905,10 +906,13 @@ KResult ProcFSRootDirectory::traverse_as_directory(unsigned fsid, Function<bool(
InodeIdentifier identifier = { fsid, component.component_index() };
callback({ component.name(), identifier, 0 });
}
for (auto& component : m_process_directories) {
InodeIdentifier identifier = { fsid, component.component_index() };
callback({ component.name(), identifier, 0 });
}
processes().for_each_shared([&](Process& process) {
VERIFY(!(process.pid() < 0));
u64 process_id = (u64)process.pid().value();
InodeIdentifier identifier = { fsid, static_cast<InodeIndex>(process_id << 36) };
callback({ String::formatted("{:d}", process.pid().value()), identifier, 0 });
return IterationDecision::Continue;
});
return KSuccess;
}
@ -917,12 +921,12 @@ RefPtr<ProcFSExposedComponent> ProcFSRootDirectory::lookup(StringView name)
if (auto candidate = ProcFSExposedDirectory::lookup(name); !candidate.is_null())
return candidate;
for (auto& component : m_process_directories) {
if (component.name() == name) {
return component;
}
}
return {};
String process_directory_name = name;
auto pid = process_directory_name.to_uint<unsigned>();
if (!pid.has_value())
return {};
auto actual_pid = pid.value();
return Process::from_pid(actual_pid);
}
UNMAP_AFTER_INIT ProcFSRootDirectory::ProcFSRootDirectory()
@ -934,14 +938,4 @@ UNMAP_AFTER_INIT ProcFSRootDirectory::~ProcFSRootDirectory()
{
}
RefPtr<ProcFSProcessDirectory> ProcFSRootDirectory::process_directory_for(Process& process)
{
RefPtr<Process> checked_process = process;
for (auto& directory : m_process_directories) {
if (directory.associated_process().ptr() == checked_process.ptr())
return directory;
}
return {};
}
}

View file

@ -141,7 +141,6 @@ void Process::register_new(Process& process)
processes().with_exclusive([&](auto& list) {
list.prepend(process);
});
ProcFSComponentRegistry::the().register_new_process(process);
}
RefPtr<Process> Process::create_user_process(RefPtr<Thread>& first_thread, const String& path, uid_t uid, gid_t gid, ProcessID parent_pid, int& error, Vector<String>&& arguments, Vector<String>&& environment, TTY* tty)
@ -594,12 +593,6 @@ void Process::finalize()
m_arguments.clear();
m_environment.clear();
// Note: We need to remove the references from the ProcFS registrar
// If we don't do it here, we can't drop the object later, and we can't
// do this from the destructor because the state of the object doesn't
// allow us to take references anymore.
ProcFSComponentRegistry::the().unregister_process(*this);
m_state.store(State::Dead, AK::MemoryOrder::memory_order_release);
{
@ -752,13 +745,6 @@ void Process::FileDescriptionAndFlags::clear()
// FIXME: Verify Process::m_fds_lock is locked!
m_description = nullptr;
m_flags = 0;
m_global_procfs_inode_index = 0;
}
void Process::FileDescriptionAndFlags::refresh_inode_index()
{
// FIXME: Verify Process::m_fds_lock is locked!
m_global_procfs_inode_index = ProcFSComponentRegistry::the().allocate_inode_index();
}
void Process::FileDescriptionAndFlags::set(NonnullRefPtr<FileDescription>&& description, u32 flags)
@ -766,7 +752,6 @@ void Process::FileDescriptionAndFlags::set(NonnullRefPtr<FileDescription>&& desc
// FIXME: Verify Process::m_fds_lock is locked!
m_description = move(description);
m_flags = flags;
m_global_procfs_inode_index = ProcFSComponentRegistry::the().allocate_inode_index();
}
Custody& Process::root_directory()

View file

@ -27,6 +27,7 @@
#include <Kernel/Locking/ProtectedValue.h>
#include <Kernel/Memory/AddressSpace.h>
#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/ProcessExposed.h>
#include <Kernel/ProcessGroup.h>
#include <Kernel/StdLib.h>
#include <Kernel/Thread.h>
@ -84,7 +85,7 @@ typedef HashMap<FlatPtr, RefPtr<FutexQueue>> FutexQueues;
struct LoadResult;
class Process
: public RefCounted<Process>
: public ProcFSExposedComponent
, public Weakable<Process> {
private:
@ -551,6 +552,31 @@ private:
void setup_socket_fd(int fd, NonnullRefPtr<FileDescription> description, int type);
public:
// ^ProcFSExposedComponent stats
virtual InodeIndex component_index() const override;
virtual NonnullRefPtr<Inode> to_inode(const ProcFS& procfs_instance) const override;
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual mode_t required_mode() const override { return 0555; }
virtual uid_t owner_user() const override { return uid(); }
virtual gid_t owner_group() const override { return gid(); }
KResult procfs_get_fds_stats(KBufferBuilder& builder) const;
KResult procfs_get_perf_events(KBufferBuilder& builder) const;
KResult procfs_get_unveil_stats(KBufferBuilder& builder) const;
KResult procfs_get_pledge_stats(KBufferBuilder& builder) const;
KResult procfs_get_virtual_memory_stats(KBufferBuilder& builder) const;
KResult procfs_get_binary_link(KBufferBuilder& builder) const;
KResult procfs_get_root_link(KBufferBuilder& builder) const;
KResult procfs_get_current_work_directory_link(KBufferBuilder& builder) const;
mode_t binary_link_required_mode() const;
KResultOr<size_t> procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const;
KResult traverse_stacks_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const;
RefPtr<Inode> lookup_stacks_directory(const ProcFS&, StringView name) const;
KResultOr<size_t> procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const;
KResult traverse_file_descriptions_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const;
RefPtr<Inode> lookup_file_descriptions_directory(const ProcFS&, StringView name) const;
private:
inline PerformanceEventBuffer* current_perf_events_buffer()
{
if (g_profiling_all_threads)
@ -595,22 +621,16 @@ public:
FileDescription* description() { return m_description; }
const FileDescription* description() const { return m_description; }
InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; }
u32 flags() const { return m_flags; }
void set_flags(u32 flags) { m_flags = flags; }
void clear();
void set(NonnullRefPtr<FileDescription>&&, u32 flags = 0);
void refresh_inode_index();
private:
RefPtr<FileDescription> m_description;
bool m_is_allocated { false };
u32 m_flags { 0 };
// Note: This is needed so when we generate inodes for ProcFS, we know that
// we assigned a global Inode index to it so we can use it later
InodeIndex m_global_procfs_inode_index;
};
class ScopedDescriptionAllocation;
@ -626,9 +646,6 @@ public:
ScopedSpinLock lock(m_fds_lock);
ScopedSpinLock lock_other(other.m_fds_lock);
m_fds_metadatas = other.m_fds_metadatas;
for (auto& file_description_metadata : m_fds_metadatas) {
file_description_metadata.refresh_inode_index();
}
return *this;
}

View file

@ -18,35 +18,77 @@ namespace Kernel {
static SpinLock<u8> s_index_lock;
static InodeIndex s_next_inode_index = 0;
static size_t s_allocate_inode_index()
namespace SegmentedProcFSIndex {
static InodeIndex __build_raw_segmented_index(u32 primary, u16 sub_directory, u32 property)
{
VERIFY(primary < 0x10000000);
VERIFY(property < 0x100000);
// Note: The sub-directory part is already limited to 0xFFFF, so no need to VERIFY it.
return static_cast<u64>((static_cast<u64>(primary) << 36) | (static_cast<u64>(sub_directory) << 20) | property);
}
static InodeIndex build_segmented_index_with_known_pid(ProcessID pid, u16 sub_directory, u32 property)
{
return __build_raw_segmented_index(pid.value() + 1, sub_directory, property);
}
static InodeIndex build_segmented_index_with_unknown_property(ProcessID pid, ProcessSubDirectory sub_directory, unsigned property)
{
return build_segmented_index_with_known_pid(pid, to_underlying(sub_directory), static_cast<u32>(property));
}
InodeIndex build_segmented_index_for_pid_directory(ProcessID pid)
{
return build_segmented_index_with_unknown_property(pid, ProcessSubDirectory::Reserved, to_underlying(MainProcessProperty::Reserved));
}
InodeIndex build_segmented_index_for_sub_directory(ProcessID pid, ProcessSubDirectory sub_directory)
{
return build_segmented_index_with_unknown_property(pid, sub_directory, to_underlying(MainProcessProperty::Reserved));
}
InodeIndex build_segmented_index_for_main_property(ProcessID pid, ProcessSubDirectory sub_directory, MainProcessProperty property)
{
return build_segmented_index_with_known_pid(pid, to_underlying(sub_directory), to_underlying(property));
}
InodeIndex build_segmented_index_for_main_property_in_pid_directory(ProcessID pid, MainProcessProperty property)
{
return build_segmented_index_with_known_pid(pid, to_underlying(ProcessSubDirectory::Reserved), to_underlying(property));
}
InodeIndex build_segmented_index_for_thread_stack(ProcessID pid, ThreadID thread_id)
{
return build_segmented_index_with_unknown_property(pid, ProcessSubDirectory::Stacks, thread_id.value());
}
InodeIndex build_segmented_index_for_file_description(ProcessID pid, unsigned fd)
{
return build_segmented_index_with_unknown_property(pid, ProcessSubDirectory::FileDescriptions, fd);
}
}
static size_t s_allocate_global_inode_index()
{
ScopedSpinLock lock(s_index_lock);
s_next_inode_index = s_next_inode_index.value() + 1;
// Note: Global ProcFS indices must be above 0 and up to maximum of what 36 bit (2 ^ 36 - 1) can represent.
VERIFY(s_next_inode_index > 0);
VERIFY(s_next_inode_index < 0x100000000);
return s_next_inode_index.value();
}
InodeIndex ProcFSComponentRegistry::allocate_inode_index() const
ProcFSExposedComponent::ProcFSExposedComponent()
{
return s_allocate_inode_index();
}
ProcFSExposedComponent::ProcFSExposedComponent(StringView name)
: m_component_index(s_allocate_inode_index())
: m_component_index(s_allocate_global_inode_index())
{
m_name = KString::try_create(name);
}
// Note: This constructor is intended to be used in /proc/pid/fd/* symlinks
// so we preallocated inode index for them so we just need to set it here.
ProcFSExposedComponent::ProcFSExposedComponent(StringView name, InodeIndex preallocated_index)
: m_component_index(preallocated_index.value())
{
VERIFY(preallocated_index.value() != 0);
VERIFY(preallocated_index <= s_next_inode_index);
m_name = KString::try_create(name);
}
ProcFSExposedDirectory::ProcFSExposedDirectory(StringView name)
: ProcFSExposedComponent(name)
{
@ -62,16 +104,6 @@ ProcFSExposedLink::ProcFSExposedLink(StringView name)
: ProcFSExposedComponent(name)
{
}
ProcFSExposedLink::ProcFSExposedLink(StringView name, InodeIndex preallocated_index)
: ProcFSExposedComponent(name, preallocated_index)
{
}
struct ProcFSInodeData : public FileDescriptionData {
OwnPtr<KBuffer> buffer;
};
KResultOr<size_t> ProcFSGlobalInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const
{
dbgln_if(PROCFS_DEBUG, "ProcFSGlobalInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count);
@ -121,71 +153,6 @@ KResult ProcFSGlobalInformation::refresh_data(FileDescription& description) cons
return KSuccess;
}
KResultOr<size_t> ProcFSProcessInformation::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const
{
dbgln_if(PROCFS_DEBUG, "ProcFSProcessInformation @ {}: read_bytes offset: {} count: {}", name(), offset, count);
VERIFY(offset >= 0);
VERIFY(buffer.user_or_kernel_ptr());
if (!description)
return KResult(EIO);
if (!description->data()) {
dbgln("ProcFSGlobalInformation: Do not have cached data!");
return KResult(EIO);
}
MutexLocker locker(m_refresh_lock);
auto& typed_cached_data = static_cast<ProcFSInodeData&>(*description->data());
auto& data_buffer = typed_cached_data.buffer;
if (!data_buffer || (size_t)offset >= data_buffer->size())
return 0;
ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(count));
if (!buffer.write(data_buffer->data() + offset, nread))
return KResult(EFAULT);
return nread;
}
KResult ProcFSProcessInformation::refresh_data(FileDescription& description) const
{
// For process-specific inodes, hold the process's ptrace lock across refresh
// and refuse to load data if the process is not dumpable.
// Without this, files opened before a process went non-dumpable could still be used for dumping.
auto parent_directory = const_cast<ProcFSProcessInformation&>(*this).m_parent_directory.strong_ref();
if (parent_directory.is_null())
return KResult(EINVAL);
auto process = parent_directory->associated_process();
if (!process)
return KResult(ESRCH);
process->ptrace_lock().lock();
if (!process->is_dumpable()) {
process->ptrace_lock().unlock();
return EPERM;
}
ScopeGuard guard = [&] {
process->ptrace_lock().unlock();
};
MutexLocker locker(m_refresh_lock);
auto& cached_data = description.data();
if (!cached_data) {
cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData);
if (!cached_data)
return ENOMEM;
}
KBufferBuilder builder;
if (!const_cast<ProcFSProcessInformation&>(*this).output(builder))
return ENOENT;
auto& typed_cached_data = static_cast<ProcFSInodeData&>(*cached_data);
typed_cached_data.buffer = builder.build();
if (!typed_cached_data.buffer)
return ENOMEM;
return KSuccess;
}
KResultOr<size_t> ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const
{
VERIFY(offset == 0);
@ -210,7 +177,7 @@ NonnullRefPtr<Inode> ProcFSExposedLink::to_inode(const ProcFS& procfs_instance)
NonnullRefPtr<Inode> ProcFSExposedComponent::to_inode(const ProcFS& procfs_instance) const
{
return ProcFSInode::create(procfs_instance, *this);
return ProcFSGlobalInode::create(procfs_instance, *this);
}
NonnullRefPtr<Inode> ProcFSExposedDirectory::to_inode(const ProcFS& procfs_instance) const

View file

@ -13,27 +13,49 @@
#include <AK/Types.h>
#include <Kernel/Arch/x86/CPU.h>
#include <Kernel/FileSystem/File.h>
#include <Kernel/FileSystem/FileDescription.h>
#include <Kernel/FileSystem/FileSystem.h>
#include <Kernel/KBufferBuilder.h>
#include <Kernel/KResult.h>
#include <Kernel/Process.h>
#include <Kernel/UserOrKernelBuffer.h>
namespace Kernel {
namespace SegmentedProcFSIndex {
enum class MainProcessProperty {
Reserved = 0,
Unveil = 1,
Pledge = 2,
FileDescriptions = 3,
BinaryLink = 4,
CurrentWorkDirectoryLink = 5,
PerformanceEvents = 6,
VirtualMemoryStats = 7,
RootLink = 8,
};
enum class ProcessSubDirectory {
Reserved = 0,
FileDescriptions = 1,
Stacks = 2,
};
void read_segments(u32& primary, ProcessSubDirectory& sub_directory, MainProcessProperty& property);
InodeIndex build_segmented_index_for_pid_directory(ProcessID);
InodeIndex build_segmented_index_for_sub_directory(ProcessID, ProcessSubDirectory sub_directory);
InodeIndex build_segmented_index_for_main_property(ProcessID, ProcessSubDirectory sub_directory, MainProcessProperty property);
InodeIndex build_segmented_index_for_main_property_in_pid_directory(ProcessID, MainProcessProperty property);
InodeIndex build_segmented_index_for_thread_stack(ProcessID, ThreadID);
InodeIndex build_segmented_index_for_file_description(ProcessID, unsigned);
}
class ProcFSComponentRegistry {
public:
static ProcFSComponentRegistry& the();
static void initialize();
InodeIndex allocate_inode_index() const;
ProcFSComponentRegistry();
void register_new_process(Process&);
void unregister_process(Process&);
ProcFSRootDirectory& root_directory() { return *m_root_directory; }
Mutex& get_lock() { return m_lock; }
@ -64,13 +86,13 @@ public:
virtual NonnullRefPtr<Inode> to_inode(const ProcFS& procfs_instance) const;
InodeIndex component_index() const { return m_component_index; };
virtual InodeIndex component_index() const { return m_component_index; }
virtual ~ProcFSExposedComponent() = default;
protected:
ProcFSExposedComponent();
explicit ProcFSExposedComponent(StringView name);
ProcFSExposedComponent(StringView name, InodeIndex preallocated_index);
private:
OwnPtr<KString> m_name;
@ -80,7 +102,6 @@ private:
class ProcFSExposedDirectory
: public ProcFSExposedComponent
, public Weakable<ProcFSExposedDirectory> {
friend class ProcFSProcessDirectory;
friend class ProcFSComponentRegistry;
public:
@ -114,62 +135,24 @@ public:
protected:
virtual bool acquire_link(KBufferBuilder& builder) = 0;
explicit ProcFSExposedLink(StringView name);
ProcFSExposedLink(StringView name, InodeIndex preallocated_index);
mutable Mutex m_lock { "ProcFSLink" };
};
class ProcFSProcessDirectory final
: public ProcFSExposedDirectory {
friend class ProcFSComponentRegistry;
friend class ProcFSRootDirectory;
friend class ProcFSProcessInformation;
friend class ProcFSProcessPledge;
friend class ProcFSProcessUnveil;
friend class ProcFSProcessPerformanceEvents;
friend class ProcFSProcessFileDescription;
friend class ProcFSProcessFileDescriptions;
friend class ProcFSProcessOverallFileDescriptions;
friend class ProcFSProcessRoot;
friend class ProcFSProcessVirtualMemory;
friend class ProcFSProcessCurrentWorkDirectory;
friend class ProcFSProcessBinary;
friend class ProcFSProcessStacks;
public:
static NonnullRefPtr<ProcFSProcessDirectory> create(const Process&);
RefPtr<Process> associated_process() { return m_associated_process; }
virtual uid_t owner_user() const override { return m_associated_process ? m_associated_process->uid() : 0; }
virtual gid_t owner_group() const override { return m_associated_process ? m_associated_process->gid() : 0; }
virtual KResult refresh_data(FileDescription&) const override;
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
virtual mode_t required_mode() const override { return 0500; }
virtual void prepare_for_deletion() override;
private:
void on_attach();
IntrusiveListNode<ProcFSProcessDirectory, RefPtr<ProcFSProcessDirectory>> m_list_node;
explicit ProcFSProcessDirectory(const Process&);
RefPtr<Process> m_associated_process;
};
class ProcFSRootDirectory final : public ProcFSExposedDirectory {
friend class ProcFSComponentRegistry;
public:
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
RefPtr<ProcFSProcessDirectory> process_directory_for(Process&);
static NonnullRefPtr<ProcFSRootDirectory> must_create();
virtual ~ProcFSRootDirectory();
private:
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
ProcFSRootDirectory();
};
IntrusiveList<ProcFSProcessDirectory, RefPtr<ProcFSProcessDirectory>, &ProcFSProcessDirectory::m_list_node> m_process_directories;
struct ProcFSInodeData : public FileDescriptionData {
OwnPtr<KBuffer> buffer;
};
class ProcFSGlobalInformation : public ProcFSExposedComponent {
@ -208,47 +191,4 @@ protected:
}
};
class ProcFSProcessInformation : public ProcFSExposedComponent {
public:
virtual ~ProcFSProcessInformation() override {};
virtual KResultOr<size_t> read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription* description) const override;
virtual uid_t owner_user() const override
{
auto parent_directory = m_parent_directory.strong_ref();
if (!parent_directory)
return false;
auto process = parent_directory->associated_process();
if (!process)
return false;
return process->uid();
}
virtual gid_t owner_group() const override
{
auto parent_directory = m_parent_directory.strong_ref();
if (!parent_directory)
return false;
auto process = parent_directory->associated_process();
if (!process)
return false;
return process->gid();
}
virtual mode_t required_mode() const override { return 0400; }
protected:
ProcFSProcessInformation(StringView name, const ProcFSProcessDirectory& process_directory)
: ProcFSExposedComponent(name)
, m_parent_directory(process_directory)
{
}
virtual KResult refresh_data(FileDescription&) const override;
virtual bool output(KBufferBuilder& builder) = 0;
WeakPtr<ProcFSProcessDirectory> m_parent_directory;
mutable Mutex m_refresh_lock;
};
}

View file

@ -9,601 +9,294 @@
#include <AK/JsonValue.h>
#include <Kernel/Arch/x86/InterruptDisabler.h>
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/ProcFS.h>
#include <Kernel/KBufferBuilder.h>
#include <Kernel/Memory/AnonymousVMObject.h>
#include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Process.h>
#include <Kernel/ProcessExposed.h>
namespace Kernel {
class ProcFSProcessStacks;
class ProcFSThreadStack final : public ProcFSProcessInformation {
public:
// Note: We pass const ProcFSProcessStacks& to enforce creation with this type of directory
static NonnullRefPtr<ProcFSThreadStack> create(const ProcFSProcessDirectory& process_directory, const ProcFSProcessStacks&, const Thread& thread)
{
return adopt_ref(*new (nothrow) ProcFSThreadStack(process_directory, thread));
}
private:
explicit ProcFSThreadStack(const ProcFSProcessDirectory& process_directory, const Thread& thread)
: ProcFSProcessInformation(String::formatted("{}", thread.tid()), process_directory)
, m_associated_thread(thread)
{
}
virtual bool output(KBufferBuilder& builder) override
{
JsonArraySerializer array { builder };
bool show_kernel_addresses = Process::current()->is_superuser();
bool kernel_address_added = false;
for (auto address : Processor::capture_stack_trace(*m_associated_thread, 1024)) {
if (!show_kernel_addresses && !Memory::is_user_address(VirtualAddress { address })) {
if (kernel_address_added)
continue;
address = 0xdeadc0de;
kernel_address_added = true;
}
array.add(address);
}
array.finish();
return true;
}
NonnullRefPtr<Thread> m_associated_thread;
};
class ProcFSProcessStacks final : public ProcFSExposedDirectory {
// Note: This directory is special, because everything that is created here is dynamic!
// This means we don't register anything in the m_components Vector, and every inode
// is created in runtime when called to get it
// Every ProcFSThreadStack (that represents a thread stack) is created only as a temporary object
// therefore, we don't use m_components so when we are done with the ProcFSThreadStack object,
// It should be deleted (as soon as possible)
public:
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
static NonnullRefPtr<ProcFSProcessStacks> create(const ProcFSProcessDirectory& parent_directory)
{
auto directory = adopt_ref(*new (nothrow) ProcFSProcessStacks(parent_directory));
return directory;
}
virtual void prepare_for_deletion() override
{
ProcFSExposedDirectory::prepare_for_deletion();
m_process_directory.clear();
}
private:
ProcFSProcessStacks(const ProcFSProcessDirectory& parent_directory)
: ProcFSExposedDirectory("stacks"sv, parent_directory)
, m_process_directory(parent_directory)
{
}
WeakPtr<ProcFSProcessDirectory> m_process_directory;
mutable Mutex m_lock;
};
KResult ProcFSProcessStacks::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
KResultOr<size_t> Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const
{
MutexLocker locker(m_lock);
auto parent_directory = m_process_directory.strong_ref();
if (parent_directory.is_null())
return KResult(EINVAL);
callback({ ".", { fsid, component_index() }, 0 });
callback({ "..", { fsid, parent_directory->component_index() }, 0 });
auto process = parent_directory->associated_process();
if (process.is_null())
JsonArraySerializer array { builder };
auto thread = Thread::from_tid(thread_id);
if (!thread)
return KResult(ESRCH);
process->for_each_thread([&](const Thread& thread) {
bool show_kernel_addresses = Process::current()->is_superuser();
bool kernel_address_added = false;
for (auto address : Processor::capture_stack_trace(*thread, 1024)) {
if (!show_kernel_addresses && !Memory::is_user_address(VirtualAddress { address })) {
if (kernel_address_added)
continue;
address = 0xdeadc0de;
kernel_address_added = true;
}
array.add(address);
}
array.finish();
return KSuccess;
}
InodeIndex Process::component_index() const
{
return SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid());
}
NonnullRefPtr<Inode> Process::to_inode(const ProcFS& procfs_instance) const
{
return ProcFSProcessDirectoryInode::create(procfs_instance, m_protected_values.pid);
}
KResult Process::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
{
callback({ ".", { fsid, SegmentedProcFSIndex::build_segmented_index_for_pid_directory(pid()) }, 0 });
callback({ "..", { fsid, ProcFSComponentRegistry::the().root_directory().component_index() }, 0 });
callback({ "fd", { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::FileDescriptions) }, 0 });
callback({ "stacks", { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks) }, 0 });
callback({ "unveil", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::Unveil) }, 0 });
callback({ "pledge", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::Pledge) }, 0 });
callback({ "fds", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::FileDescriptions) }, 0 });
callback({ "exe", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::BinaryLink) }, 0 });
callback({ "cwd", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::CurrentWorkDirectoryLink) }, 0 });
callback({ "perf_events", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::PerformanceEvents) }, 0 });
callback({ "vm", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::VirtualMemoryStats) }, 0 });
callback({ "root", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property_in_pid_directory(pid(), SegmentedProcFSIndex::MainProcessProperty::RootLink) }, 0 });
return KSuccess;
}
KResult Process::traverse_stacks_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
{
callback({ ".", { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks, SegmentedProcFSIndex::MainProcessProperty::Reserved) }, 0 });
callback({ "..", { fsid, component_index() }, 0 });
for_each_thread([&](const Thread& thread) {
int tid = thread.tid().value();
InodeIdentifier identifier = { fsid, thread.global_procfs_inode_index() };
InodeIdentifier identifier = { fsid, SegmentedProcFSIndex::build_segmented_index_for_thread_stack(pid(), thread.tid()) };
callback({ String::number(tid), identifier, 0 });
});
return KSuccess;
}
RefPtr<ProcFSExposedComponent> ProcFSProcessStacks::lookup(StringView name)
RefPtr<Inode> Process::lookup_stacks_directory(const ProcFS& procfs, StringView name) const
{
MutexLocker locker(m_lock);
auto parent_directory = m_process_directory.strong_ref();
if (parent_directory.is_null())
return nullptr;
auto process = parent_directory->associated_process();
if (process.is_null())
return nullptr;
RefPtr<ProcFSThreadStack> procfd_stack;
RefPtr<ProcFSProcessPropertyInode> thread_stack_inode;
// FIXME: Try to exit the loop earlier
process->for_each_thread([&](const Thread& thread) {
for_each_thread([&](const Thread& thread) {
int tid = thread.tid().value();
VERIFY(!(tid < 0));
if (name == String::number(tid)) {
procfd_stack = ProcFSThreadStack::create(*parent_directory, *this, thread);
thread_stack_inode = ProcFSProcessPropertyInode::create_for_thread_stack(procfs, thread.tid(), pid());
}
});
return procfd_stack;
return thread_stack_inode;
}
class ProcFSProcessFileDescriptions;
class ProcFSProcessFileDescription final : public ProcFSExposedLink {
public:
// Note: we pass const ProcFSProcessFileDescriptions& just to enforce creation of this in the correct directory.
static NonnullRefPtr<ProcFSProcessFileDescription> create(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index, const ProcFSProcessFileDescriptions&)
{
return adopt_ref(*new (nothrow) ProcFSProcessFileDescription(fd_number, fd, preallocated_index));
}
private:
explicit ProcFSProcessFileDescription(unsigned fd_number, const FileDescription& fd, InodeIndex preallocated_index)
: ProcFSExposedLink(String::formatted("{}", fd_number), preallocated_index)
, m_associated_file_description(fd)
{
}
virtual bool acquire_link(KBufferBuilder& builder) override
{
builder.append_bytes(m_associated_file_description->absolute_path().bytes());
return true;
}
NonnullRefPtr<FileDescription> m_associated_file_description;
};
class ProcFSProcessFileDescriptions final : public ProcFSExposedDirectory {
// Note: This directory is special, because everything that is created here is dynamic!
// This means we don't register anything in the m_components Vector, and every inode
// is created in runtime when called to get it
// Every ProcFSProcessFileDescription (that represents a file descriptor) is created only as a temporary object
// therefore, we don't use m_components so when we are done with the ProcFSProcessFileDescription object,
// It should be deleted (as soon as possible)
public:
virtual KResult traverse_as_directory(unsigned, Function<bool(FileSystem::DirectoryEntryView const&)>) const override;
virtual RefPtr<ProcFSExposedComponent> lookup(StringView name) override;
static NonnullRefPtr<ProcFSProcessFileDescriptions> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessFileDescriptions(parent_directory));
}
virtual void prepare_for_deletion() override
{
ProcFSExposedDirectory::prepare_for_deletion();
m_process_directory.clear();
}
private:
explicit ProcFSProcessFileDescriptions(const ProcFSProcessDirectory& parent_directory)
: ProcFSExposedDirectory("fd"sv, parent_directory)
, m_process_directory(parent_directory)
{
}
WeakPtr<ProcFSProcessDirectory> m_process_directory;
mutable Mutex m_lock;
};
KResult ProcFSProcessFileDescriptions::traverse_as_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
KResultOr<size_t> Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const
{
MutexLocker locker(m_lock);
auto parent_directory = m_process_directory.strong_ref();
if (parent_directory.is_null())
return KResult(EINVAL);
callback({ ".", { fsid, component_index() }, 0 });
callback({ "..", { fsid, parent_directory->component_index() }, 0 });
auto file_description = m_fds.file_description(fd);
if (!file_description)
return EBADF;
auto data = file_description->absolute_path();
builder.append(data);
return data.length();
}
auto process = parent_directory->associated_process();
if (process.is_null())
return KResult(ESRCH);
KResult Process::traverse_file_descriptions_directory(unsigned fsid, Function<bool(FileSystem::DirectoryEntryView const&)> callback) const
{
callback({ ".", { fsid, component_index() }, 0 });
callback({ "..", { fsid, component_index() }, 0 });
size_t count = 0;
process->fds().enumerate([&](auto& file_description_metadata) {
fds().enumerate([&](auto& file_description_metadata) {
if (!file_description_metadata.is_valid()) {
count++;
return;
}
InodeIdentifier identifier = { fsid, file_description_metadata.global_procfs_inode_index() };
InodeIdentifier identifier = { fsid, SegmentedProcFSIndex::build_segmented_index_for_file_description(pid(), count) };
callback({ String::number(count), identifier, 0 });
count++;
});
return KSuccess;
}
RefPtr<ProcFSExposedComponent> ProcFSProcessFileDescriptions::lookup(StringView name)
RefPtr<Inode> Process::lookup_file_descriptions_directory(const ProcFS& procfs, StringView name) const
{
MutexLocker locker(m_lock);
auto parent_directory = m_process_directory.strong_ref();
if (parent_directory.is_null())
return nullptr;
auto process = parent_directory->associated_process();
if (process.is_null())
return nullptr;
RefPtr<ProcFSProcessFileDescription> procfd_fd;
RefPtr<ProcFSProcessPropertyInode> file_description_link;
// FIXME: Try to exit the loop earlier
size_t count = 0;
process->fds().enumerate([&](auto& file_description_metadata) {
fds().enumerate([&](auto& file_description_metadata) {
if (!file_description_metadata.is_valid()) {
count++;
return;
}
if (name == String::number(count)) {
procfd_fd = ProcFSProcessFileDescription::create(count, *file_description_metadata.description(), file_description_metadata.global_procfs_inode_index(), *this);
file_description_link = ProcFSProcessPropertyInode::create_for_file_description_link(procfs, static_cast<unsigned>(count), pid());
}
count++;
});
return procfd_fd;
return file_description_link;
}
class ProcFSProcessPledge final : public ProcFSProcessInformation {
public:
static NonnullRefPtr<ProcFSProcessPledge> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessPledge(parent_directory));
KResult Process::procfs_get_pledge_stats(KBufferBuilder& builder) const
{
JsonObjectSerializer obj { builder };
#define __ENUMERATE_PLEDGE_PROMISE(x) \
if (has_promised(Pledge::x)) { \
if (!builder.is_empty()) \
builder.append(' '); \
builder.append(#x); \
}
private:
explicit ProcFSProcessPledge(const ProcFSProcessDirectory& parent_directory)
: ProcFSProcessInformation("pledge"sv, parent_directory)
{
if (has_promises()) {
StringBuilder builder;
ENUMERATE_PLEDGE_PROMISES
obj.add("promises", builder.build());
}
virtual bool output(KBufferBuilder& builder) override
{
auto parent_directory = m_parent_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
JsonObjectSerializer obj { builder };
#define __ENUMERATE_PLEDGE_PROMISE(x) \
if (process->has_promised(Pledge::x)) { \
if (!builder.is_empty()) \
builder.append(' '); \
builder.append(#x); \
}
if (process->has_promises()) {
StringBuilder builder;
ENUMERATE_PLEDGE_PROMISES
obj.add("promises", builder.build());
}
#undef __ENUMERATE_PLEDGE_PROMISE
obj.finish();
return true;
}
};
class ProcFSProcessUnveil final : public ProcFSProcessInformation {
public:
static NonnullRefPtr<ProcFSProcessUnveil> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessUnveil(parent_directory));
}
private:
explicit ProcFSProcessUnveil(const ProcFSProcessDirectory& parent_directory)
: ProcFSProcessInformation("unveil"sv, parent_directory)
{
}
virtual bool output(KBufferBuilder& builder) override
{
auto parent_directory = m_parent_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
JsonArraySerializer array { builder };
for (auto& unveiled_path : process->unveiled_paths()) {
if (!unveiled_path.was_explicitly_unveiled())
continue;
auto obj = array.add_object();
obj.add("path", unveiled_path.path());
StringBuilder permissions_builder;
if (unveiled_path.permissions() & UnveilAccess::Read)
permissions_builder.append('r');
if (unveiled_path.permissions() & UnveilAccess::Write)
permissions_builder.append('w');
if (unveiled_path.permissions() & UnveilAccess::Execute)
permissions_builder.append('x');
if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove)
permissions_builder.append('c');
if (unveiled_path.permissions() & UnveilAccess::Browse)
permissions_builder.append('b');
obj.add("permissions", permissions_builder.to_string());
}
array.finish();
return true;
}
};
class ProcFSProcessPerformanceEvents final : public ProcFSProcessInformation {
public:
static NonnullRefPtr<ProcFSProcessPerformanceEvents> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessPerformanceEvents(parent_directory));
}
private:
explicit ProcFSProcessPerformanceEvents(const ProcFSProcessDirectory& parent_directory)
: ProcFSProcessInformation("perf_events"sv, parent_directory)
{
}
virtual bool output(KBufferBuilder& builder) override
{
InterruptDisabler disabler;
auto parent_directory = m_parent_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
if (!process->perf_events()) {
dbgln("ProcFS: No perf events for {}", process->pid());
return false;
}
return process->perf_events()->to_json(builder);
}
};
class ProcFSProcessOverallFileDescriptions final : public ProcFSProcessInformation {
public:
static NonnullRefPtr<ProcFSProcessOverallFileDescriptions> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessOverallFileDescriptions(parent_directory));
}
private:
explicit ProcFSProcessOverallFileDescriptions(const ProcFSProcessDirectory& parent_directory)
: ProcFSProcessInformation("fds"sv, parent_directory)
{
}
virtual bool output(KBufferBuilder& builder) override
{
auto parent_directory = m_parent_directory.strong_ref();
if (parent_directory.is_null())
return false;
JsonArraySerializer array { builder };
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
if (process->fds().open_count() == 0) {
array.finish();
return true;
}
size_t count = 0;
process->fds().enumerate([&](auto& file_description_metadata) {
if (!file_description_metadata.is_valid()) {
count++;
return;
}
bool cloexec = file_description_metadata.flags() & FD_CLOEXEC;
RefPtr<FileDescription> description = file_description_metadata.description();
auto description_object = array.add_object();
description_object.add("fd", count);
description_object.add("absolute_path", description->absolute_path());
description_object.add("seekable", description->file().is_seekable());
description_object.add("class", description->file().class_name());
description_object.add("offset", description->offset());
description_object.add("cloexec", cloexec);
description_object.add("blocking", description->is_blocking());
description_object.add("can_read", description->can_read());
description_object.add("can_write", description->can_write());
count++;
});
array.finish();
return true;
}
};
class ProcFSProcessRoot final : public ProcFSExposedLink {
public:
static NonnullRefPtr<ProcFSProcessRoot> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessRoot(parent_directory));
}
private:
explicit ProcFSProcessRoot(const ProcFSProcessDirectory& parent_directory)
: ProcFSExposedLink("root"sv)
, m_parent_process_directory(parent_directory)
{
}
virtual bool acquire_link(KBufferBuilder& builder) override
{
auto parent_directory = m_parent_process_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
builder.append_bytes(process->root_directory_relative_to_global_root().absolute_path().to_byte_buffer());
return true;
}
WeakPtr<ProcFSProcessDirectory> m_parent_process_directory;
};
class ProcFSProcessVirtualMemory final : public ProcFSProcessInformation {
public:
static NonnullRefPtr<ProcFSProcessRoot> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessVirtualMemory(parent_directory));
}
private:
explicit ProcFSProcessVirtualMemory(const ProcFSProcessDirectory& parent_directory)
: ProcFSProcessInformation("vm"sv, parent_directory)
{
}
virtual bool output(KBufferBuilder& builder) override
{
auto parent_directory = m_parent_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
JsonArraySerializer array { builder };
{
ScopedSpinLock lock(process->address_space().get_lock());
for (auto& region : process->address_space().regions()) {
if (!region->is_user() && !Process::current()->is_superuser())
continue;
auto region_object = array.add_object();
region_object.add("readable", region->is_readable());
region_object.add("writable", region->is_writable());
region_object.add("executable", region->is_executable());
region_object.add("stack", region->is_stack());
region_object.add("shared", region->is_shared());
region_object.add("syscall", region->is_syscall_region());
region_object.add("purgeable", region->vmobject().is_anonymous());
if (region->vmobject().is_anonymous()) {
region_object.add("volatile", static_cast<Memory::AnonymousVMObject const&>(region->vmobject()).is_volatile());
}
region_object.add("cacheable", region->is_cacheable());
region_object.add("address", region->vaddr().get());
region_object.add("size", region->size());
region_object.add("amount_resident", region->amount_resident());
region_object.add("amount_dirty", region->amount_dirty());
region_object.add("cow_pages", region->cow_pages());
region_object.add("name", region->name());
region_object.add("vmobject", region->vmobject().class_name());
StringBuilder pagemap_builder;
for (size_t i = 0; i < region->page_count(); ++i) {
auto* page = region->physical_page(i);
if (!page)
pagemap_builder.append('N');
else if (page->is_shared_zero_page() || page->is_lazy_committed_page())
pagemap_builder.append('Z');
else
pagemap_builder.append('P');
}
region_object.add("pagemap", pagemap_builder.to_string());
}
}
array.finish();
return true;
}
};
class ProcFSProcessCurrentWorkDirectory final : public ProcFSExposedLink {
public:
static NonnullRefPtr<ProcFSProcessCurrentWorkDirectory> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessCurrentWorkDirectory(parent_directory));
}
private:
explicit ProcFSProcessCurrentWorkDirectory(const ProcFSProcessDirectory& parent_directory)
: ProcFSExposedLink("cwd"sv)
, m_parent_process_directory(parent_directory)
{
}
virtual bool acquire_link(KBufferBuilder& builder) override
{
auto parent_directory = m_parent_process_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
builder.append_bytes(process->current_directory().absolute_path().bytes());
return true;
}
WeakPtr<ProcFSProcessDirectory> m_parent_process_directory;
};
class ProcFSProcessBinary final : public ProcFSExposedLink {
public:
static NonnullRefPtr<ProcFSProcessBinary> create(const ProcFSProcessDirectory& parent_directory)
{
return adopt_ref(*new (nothrow) ProcFSProcessBinary(parent_directory));
}
virtual mode_t required_mode() const override
{
auto parent_directory = m_parent_process_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
if (!process->executable())
return 0;
return ProcFSExposedComponent::required_mode();
}
private:
explicit ProcFSProcessBinary(const ProcFSProcessDirectory& parent_directory)
: ProcFSExposedLink("exe"sv)
, m_parent_process_directory(parent_directory)
{
}
virtual bool acquire_link(KBufferBuilder& builder) override
{
auto parent_directory = m_parent_process_directory.strong_ref();
if (parent_directory.is_null())
return false;
auto process = parent_directory->associated_process();
if (process.is_null())
return false;
auto* custody = process->executable();
if (!custody)
return false;
builder.append(custody->absolute_path().bytes());
return true;
}
WeakPtr<ProcFSProcessDirectory> m_parent_process_directory;
};
void ProcFSProcessDirectory::on_attach()
{
VERIFY(m_components.size() == 0);
m_components.append(ProcFSProcessPledge::create(*this));
m_components.append(ProcFSProcessUnveil::create(*this));
m_components.append(ProcFSProcessPerformanceEvents::create(*this));
m_components.append(ProcFSProcessFileDescriptions::create(*this));
m_components.append(ProcFSProcessOverallFileDescriptions::create(*this));
m_components.append(ProcFSProcessRoot::create(*this));
m_components.append(ProcFSProcessVirtualMemory::create(*this));
m_components.append(ProcFSProcessCurrentWorkDirectory::create(*this));
m_components.append(ProcFSProcessBinary::create(*this));
m_components.append(ProcFSProcessStacks::create(*this));
}
RefPtr<ProcFSExposedComponent> ProcFSProcessDirectory::lookup(StringView name)
{
// Note: we need to allocate all sub components when doing a lookup, because
// for some reason, the caller may not call ProcFSInode::attach method before calling this.
if (m_components.size() == 0)
on_attach();
return ProcFSExposedDirectory::lookup(name);
}
KResult ProcFSProcessDirectory::refresh_data(FileDescription&) const
{
if (m_components.size() != 0)
return KSuccess;
const_cast<ProcFSProcessDirectory&>(*this).on_attach();
obj.finish();
return KSuccess;
}
NonnullRefPtr<ProcFSProcessDirectory> ProcFSProcessDirectory::create(const Process& process)
KResult Process::procfs_get_unveil_stats(KBufferBuilder& builder) const
{
return adopt_ref_if_nonnull(new (nothrow) ProcFSProcessDirectory(process)).release_nonnull();
JsonArraySerializer array { builder };
for (auto& unveiled_path : unveiled_paths()) {
if (!unveiled_path.was_explicitly_unveiled())
continue;
auto obj = array.add_object();
obj.add("path", unveiled_path.path());
StringBuilder permissions_builder;
if (unveiled_path.permissions() & UnveilAccess::Read)
permissions_builder.append('r');
if (unveiled_path.permissions() & UnveilAccess::Write)
permissions_builder.append('w');
if (unveiled_path.permissions() & UnveilAccess::Execute)
permissions_builder.append('x');
if (unveiled_path.permissions() & UnveilAccess::CreateOrRemove)
permissions_builder.append('c');
if (unveiled_path.permissions() & UnveilAccess::Browse)
permissions_builder.append('b');
obj.add("permissions", permissions_builder.to_string());
}
array.finish();
return KSuccess;
}
void ProcFSProcessDirectory::prepare_for_deletion()
KResult Process::procfs_get_perf_events(KBufferBuilder& builder) const
{
ProcFSExposedDirectory::prepare_for_deletion();
m_associated_process.clear();
InterruptDisabler disabler;
if (!const_cast<Process&>(*this).perf_events()) {
dbgln("ProcFS: No perf events for {}", pid());
return KResult(ENOBUFS);
}
return const_cast<Process&>(*this).perf_events()->to_json(builder) ? KSuccess : KResult(EINVAL);
}
ProcFSProcessDirectory::ProcFSProcessDirectory(const Process& process)
: ProcFSExposedDirectory(String::formatted("{:d}", process.pid().value()), ProcFSComponentRegistry::the().root_directory())
, m_associated_process(process)
KResult Process::procfs_get_fds_stats(KBufferBuilder& builder) const
{
JsonArraySerializer array { builder };
if (fds().open_count() == 0) {
array.finish();
return KSuccess;
}
size_t count = 0;
fds().enumerate([&](auto& file_description_metadata) {
if (!file_description_metadata.is_valid()) {
count++;
return;
}
bool cloexec = file_description_metadata.flags() & FD_CLOEXEC;
RefPtr<FileDescription> description = file_description_metadata.description();
auto description_object = array.add_object();
description_object.add("fd", count);
description_object.add("absolute_path", description->absolute_path());
description_object.add("seekable", description->file().is_seekable());
description_object.add("class", description->file().class_name());
description_object.add("offset", description->offset());
description_object.add("cloexec", cloexec);
description_object.add("blocking", description->is_blocking());
description_object.add("can_read", description->can_read());
description_object.add("can_write", description->can_write());
count++;
});
array.finish();
return KSuccess;
}
KResult Process::procfs_get_root_link(KBufferBuilder& builder) const
{
builder.append_bytes(const_cast<Process&>(*this).root_directory_relative_to_global_root().absolute_path().to_byte_buffer());
return KSuccess;
}
KResult Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const
{
JsonArraySerializer array { builder };
{
ScopedSpinLock lock(address_space().get_lock());
for (auto& region : address_space().regions()) {
if (!region->is_user() && !Process::current()->is_superuser())
continue;
auto region_object = array.add_object();
region_object.add("readable", region->is_readable());
region_object.add("writable", region->is_writable());
region_object.add("executable", region->is_executable());
region_object.add("stack", region->is_stack());
region_object.add("shared", region->is_shared());
region_object.add("syscall", region->is_syscall_region());
region_object.add("purgeable", region->vmobject().is_anonymous());
if (region->vmobject().is_anonymous()) {
region_object.add("volatile", static_cast<Memory::AnonymousVMObject const&>(region->vmobject()).is_volatile());
}
region_object.add("cacheable", region->is_cacheable());
region_object.add("address", region->vaddr().get());
region_object.add("size", region->size());
region_object.add("amount_resident", region->amount_resident());
region_object.add("amount_dirty", region->amount_dirty());
region_object.add("cow_pages", region->cow_pages());
region_object.add("name", region->name());
region_object.add("vmobject", region->vmobject().class_name());
StringBuilder pagemap_builder;
for (size_t i = 0; i < region->page_count(); ++i) {
auto* page = region->physical_page(i);
if (!page)
pagemap_builder.append('N');
else if (page->is_shared_zero_page() || page->is_lazy_committed_page())
pagemap_builder.append('Z');
else
pagemap_builder.append('P');
}
region_object.add("pagemap", pagemap_builder.to_string());
}
}
array.finish();
return KSuccess;
}
KResult Process::procfs_get_current_work_directory_link(KBufferBuilder& builder) const
{
builder.append_bytes(const_cast<Process&>(*this).current_directory().absolute_path().bytes());
return KSuccess;
}
mode_t Process::binary_link_required_mode() const
{
if (!executable())
return 0;
return ProcFSExposedComponent::required_mode();
}
KResult Process::procfs_get_binary_link(KBufferBuilder& builder) const
{
auto* custody = executable();
if (!custody)
return KResult(ENOEXEC);
builder.append(custody->absolute_path().bytes());
return KSuccess;
}
}

View file

@ -62,7 +62,6 @@ Thread::Thread(NonnullRefPtr<Process> process, NonnullOwnPtr<Memory::Region> ker
, m_kernel_stack_region(move(kernel_stack_region))
, m_name(move(name))
, m_block_timer(block_timer)
, m_global_procfs_inode_index(ProcFSComponentRegistry::the().allocate_inode_index())
{
bool is_first_thread = m_process->add_thread(*this);
if (is_first_thread) {

View file

@ -1213,8 +1213,6 @@ public:
bool is_profiling_suppressed() const { return m_is_profiling_suppressed; }
void set_profiling_suppressed() { m_is_profiling_suppressed = true; }
InodeIndex global_procfs_inode_index() const { return m_global_procfs_inode_index; }
String backtrace();
private:
@ -1364,10 +1362,6 @@ private:
RefPtr<Timer> m_block_timer;
// Note: This is needed so when we generate thread stack inodes for ProcFS, we know that
// we assigned a global Inode index to it so we can use it later
InodeIndex m_global_procfs_inode_index;
bool m_is_profiling_suppressed { false };
void yield_and_release_relock_big_lock();