1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 04:17:34 +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:
Liav A 2021-08-10 20:51:28 +03:00 committed by Andreas Kling
parent bf1adc2d5d
commit 7ba991dc37
11 changed files with 991 additions and 879 deletions

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();