mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 06:58:11 +00:00
Kernel/FileSystem: Simplify the ProcFS significantly
Since the ProcFS doesn't hold many global objects within it, the need for a fully-structured design of backing components and a registry like with the SysFS is no longer true. To acommodate this, let's remove all backing store and components of the ProcFS, so now it resembles what we had in the early days of ProcFS in the project - a mostly-static filesystem, with very small amount of kmalloc allocations needed. We still use the inode index mechanism to understand the role of each inode, but this is done in a much "static"ier way than before.
This commit is contained in:
parent
9216caeec2
commit
c56e1c5378
18 changed files with 362 additions and 952 deletions
|
@ -1,342 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/JsonArraySerializer.h>
|
||||
#include <AK/JsonObjectSerializer.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <Kernel/FileSystem/Custody.h>
|
||||
#include <Kernel/FileSystem/ProcFS/Inode.h>
|
||||
#include <Kernel/InterruptDisabler.h>
|
||||
#include <Kernel/KBufferBuilder.h>
|
||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/ProcessExposed.h>
|
||||
#include <Kernel/TTY/TTY.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ErrorOr<void> Process::procfs_get_thread_stack(ThreadID thread_id, KBufferBuilder& builder) const
|
||||
{
|
||||
auto array = TRY(JsonArraySerializer<>::try_create(builder));
|
||||
auto thread = Thread::from_tid(thread_id);
|
||||
if (!thread)
|
||||
return ESRCH;
|
||||
auto current_process_credentials = Process::current().credentials();
|
||||
bool show_kernel_addresses = current_process_credentials->is_superuser();
|
||||
bool kernel_address_added = false;
|
||||
for (auto address : TRY(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;
|
||||
}
|
||||
TRY(array.add(address));
|
||||
}
|
||||
|
||||
TRY(array.finish());
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::traverse_stacks_directory(FileSystemID fsid, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
TRY(callback({ "."sv, { fsid, SegmentedProcFSIndex::build_segmented_index_for_main_property(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Stacks, SegmentedProcFSIndex::MainProcessProperty::Reserved) }, 0 }));
|
||||
TRY(callback({ ".."sv, { fsid, m_procfs_traits->component_index() }, 0 }));
|
||||
|
||||
return thread_list().with([&](auto& list) -> ErrorOr<void> {
|
||||
for (auto const& thread : list) {
|
||||
int tid = thread.tid().value();
|
||||
InodeIdentifier identifier = { fsid, SegmentedProcFSIndex::build_segmented_index_for_thread_stack(pid(), thread.tid()) };
|
||||
auto name = TRY(KString::number(tid));
|
||||
TRY(callback({ name->view(), identifier, 0 }));
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<Inode>> Process::lookup_stacks_directory(ProcFS const& procfs, StringView name) const
|
||||
{
|
||||
auto maybe_needle = name.to_uint();
|
||||
if (!maybe_needle.has_value())
|
||||
return ENOENT;
|
||||
auto needle = maybe_needle.release_value();
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<ProcFSInode>> thread_stack_inode { ENOENT };
|
||||
for_each_thread([&](Thread const& thread) {
|
||||
int tid = thread.tid().value();
|
||||
VERIFY(!(tid < 0));
|
||||
if (needle == (unsigned)tid) {
|
||||
thread_stack_inode = ProcFSInode::try_create_as_thread_stack_inode(procfs, thread.tid(), pid());
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (thread_stack_inode.is_error())
|
||||
return thread_stack_inode.release_error();
|
||||
return thread_stack_inode.release_value();
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::traverse_children_directory(FileSystemID fsid, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
TRY(callback({ "."sv, { fsid, SegmentedProcFSIndex::build_segmented_index_for_sub_directory(pid(), SegmentedProcFSIndex::ProcessSubDirectory::Children) }, 0 }));
|
||||
TRY(callback({ ".."sv, { fsid, m_procfs_traits->component_index() }, 0 }));
|
||||
return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> {
|
||||
if (process.ppid() == pid()) {
|
||||
StringBuilder builder;
|
||||
builder.appendff("{}", process.pid());
|
||||
TRY(callback({ builder.string_view(), { fsid, SegmentedProcFSIndex::build_segmented_index_for_children(pid(), process.pid()) }, DT_LNK }));
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<Inode>> Process::lookup_children_directory(ProcFS const& procfs, StringView name) const
|
||||
{
|
||||
auto maybe_pid = name.to_uint();
|
||||
if (!maybe_pid.has_value())
|
||||
return ENOENT;
|
||||
|
||||
auto child_process = Process::from_pid_in_same_jail(*maybe_pid);
|
||||
if (!child_process || child_process->ppid() != pid())
|
||||
return ENOENT;
|
||||
|
||||
return TRY(ProcFSInode::try_create_as_child_process_link_inode(procfs, *maybe_pid, pid()));
|
||||
}
|
||||
|
||||
ErrorOr<size_t> Process::procfs_get_child_process_link(ProcessID child_pid, KBufferBuilder& builder) const
|
||||
{
|
||||
TRY(builder.appendff("../../{}", child_pid.value()));
|
||||
return builder.length();
|
||||
}
|
||||
|
||||
ErrorOr<size_t> Process::procfs_get_file_description_link(unsigned fd, KBufferBuilder& builder) const
|
||||
{
|
||||
auto file_description = TRY(open_file_description(fd));
|
||||
// Note: These links are not guaranteed to point to actual VFS paths, just like in other kernels.
|
||||
auto data = TRY(file_description->pseudo_path());
|
||||
TRY(builder.append(data->view()));
|
||||
return data->length();
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::traverse_file_descriptions_directory(FileSystemID fsid, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const
|
||||
{
|
||||
TRY(callback({ "."sv, { fsid, m_procfs_traits->component_index() }, 0 }));
|
||||
TRY(callback({ ".."sv, { fsid, m_procfs_traits->component_index() }, 0 }));
|
||||
size_t count = 0;
|
||||
fds().with_shared([&](auto& fds) {
|
||||
fds.enumerate([&](auto& file_description_metadata) {
|
||||
if (!file_description_metadata.is_valid()) {
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
StringBuilder builder;
|
||||
builder.appendff("{}", count);
|
||||
// FIXME: Propagate errors from callback.
|
||||
(void)callback({ builder.string_view(), { fsid, SegmentedProcFSIndex::build_segmented_index_for_file_description(pid(), count) }, DT_LNK });
|
||||
count++;
|
||||
});
|
||||
});
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<Inode>> Process::lookup_file_descriptions_directory(ProcFS const& procfs, StringView name) const
|
||||
{
|
||||
auto maybe_index = name.to_uint();
|
||||
if (!maybe_index.has_value())
|
||||
return ENOENT;
|
||||
|
||||
if (!m_fds.with_shared([&](auto& fds) { return fds.get_if_valid(*maybe_index); }))
|
||||
return ENOENT;
|
||||
|
||||
return TRY(ProcFSInode::try_create_as_file_description_link_inode(procfs, *maybe_index, pid()));
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_pledge_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
auto obj = TRY(JsonObjectSerializer<>::try_create(builder));
|
||||
#define __ENUMERATE_PLEDGE_PROMISE(x) \
|
||||
if (has_promised(Pledge::x)) { \
|
||||
if (!promises_builder.is_empty()) \
|
||||
TRY(promises_builder.try_append(' ')); \
|
||||
TRY(promises_builder.try_append(#x##sv)); \
|
||||
}
|
||||
if (has_promises()) {
|
||||
StringBuilder promises_builder;
|
||||
ENUMERATE_PLEDGE_PROMISES
|
||||
TRY(obj.add("promises"sv, promises_builder.string_view()));
|
||||
}
|
||||
#undef __ENUMERATE_PLEDGE_PROMISE
|
||||
TRY(obj.finish());
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_unveil_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
auto array = TRY(JsonArraySerializer<>::try_create(builder));
|
||||
TRY(m_unveil_data.with([&](auto& unveil_data) -> ErrorOr<void> {
|
||||
TRY(unveil_data.paths.for_each_node_in_tree_order([&](auto const& unveiled_path) -> ErrorOr<IterationDecision> {
|
||||
if (!unveiled_path.was_explicitly_unveiled())
|
||||
return IterationDecision::Continue;
|
||||
auto obj = TRY(array.add_object());
|
||||
TRY(obj.add("path"sv, 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');
|
||||
TRY(obj.add("permissions"sv, permissions_builder.string_view()));
|
||||
TRY(obj.finish());
|
||||
return IterationDecision::Continue;
|
||||
}));
|
||||
return {};
|
||||
}));
|
||||
TRY(array.finish());
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_perf_events(KBufferBuilder& builder) const
|
||||
{
|
||||
InterruptDisabler disabler;
|
||||
if (!perf_events()) {
|
||||
dbgln("ProcFS: No perf events for {}", pid());
|
||||
return Error::from_errno(ENOBUFS);
|
||||
}
|
||||
return perf_events()->to_json(builder);
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_fds_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
auto array = TRY(JsonArraySerializer<>::try_create(builder));
|
||||
|
||||
return fds().with_shared([&](auto& fds) -> ErrorOr<void> {
|
||||
if (fds.open_count() == 0) {
|
||||
TRY(array.finish());
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
TRY(fds.try_enumerate([&](auto& file_description_metadata) -> ErrorOr<void> {
|
||||
if (!file_description_metadata.is_valid()) {
|
||||
count++;
|
||||
return {};
|
||||
}
|
||||
bool cloexec = file_description_metadata.flags() & FD_CLOEXEC;
|
||||
LockRefPtr<OpenFileDescription> description = file_description_metadata.description();
|
||||
auto description_object = TRY(array.add_object());
|
||||
TRY(description_object.add("fd"sv, count));
|
||||
// TODO: Better OOM handling.
|
||||
auto pseudo_path_or_error = description->pseudo_path();
|
||||
TRY(description_object.add("absolute_path"sv, pseudo_path_or_error.is_error() ? "???"sv : pseudo_path_or_error.value()->view()));
|
||||
TRY(description_object.add("seekable"sv, description->file().is_seekable()));
|
||||
TRY(description_object.add("class"sv, description->file().class_name()));
|
||||
TRY(description_object.add("offset"sv, description->offset()));
|
||||
TRY(description_object.add("cloexec"sv, cloexec));
|
||||
TRY(description_object.add("blocking"sv, description->is_blocking()));
|
||||
TRY(description_object.add("can_read"sv, description->can_read()));
|
||||
TRY(description_object.add("can_write"sv, description->can_write()));
|
||||
Inode* inode = description->inode();
|
||||
if (inode != nullptr) {
|
||||
auto inode_object = TRY(description_object.add_object("inode"sv));
|
||||
TRY(inode_object.add("fsid"sv, inode->fsid().value()));
|
||||
TRY(inode_object.add("index"sv, inode->index().value()));
|
||||
TRY(inode_object.finish());
|
||||
}
|
||||
TRY(description_object.finish());
|
||||
count++;
|
||||
return {};
|
||||
}));
|
||||
|
||||
TRY(array.finish());
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_virtual_memory_stats(KBufferBuilder& builder) const
|
||||
{
|
||||
auto array = TRY(JsonArraySerializer<>::try_create(builder));
|
||||
TRY(address_space().with([&](auto& space) -> ErrorOr<void> {
|
||||
for (auto const& region : space->region_tree().regions()) {
|
||||
auto current_process_credentials = Process::current().credentials();
|
||||
if (!region.is_user() && !current_process_credentials->is_superuser())
|
||||
continue;
|
||||
auto region_object = TRY(array.add_object());
|
||||
TRY(region_object.add("readable"sv, region.is_readable()));
|
||||
TRY(region_object.add("writable"sv, region.is_writable()));
|
||||
TRY(region_object.add("executable"sv, region.is_executable()));
|
||||
TRY(region_object.add("stack"sv, region.is_stack()));
|
||||
TRY(region_object.add("shared"sv, region.is_shared()));
|
||||
TRY(region_object.add("syscall"sv, region.is_syscall_region()));
|
||||
TRY(region_object.add("purgeable"sv, region.vmobject().is_anonymous()));
|
||||
if (region.vmobject().is_anonymous()) {
|
||||
TRY(region_object.add("volatile"sv, static_cast<Memory::AnonymousVMObject const&>(region.vmobject()).is_volatile()));
|
||||
}
|
||||
TRY(region_object.add("cacheable"sv, region.is_cacheable()));
|
||||
TRY(region_object.add("address"sv, region.vaddr().get()));
|
||||
TRY(region_object.add("size"sv, region.size()));
|
||||
TRY(region_object.add("amount_resident"sv, region.amount_resident()));
|
||||
TRY(region_object.add("amount_dirty"sv, region.amount_dirty()));
|
||||
TRY(region_object.add("cow_pages"sv, region.cow_pages()));
|
||||
TRY(region_object.add("name"sv, region.name()));
|
||||
TRY(region_object.add("vmobject"sv, 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');
|
||||
}
|
||||
TRY(region_object.add("pagemap"sv, pagemap_builder.string_view()));
|
||||
TRY(region_object.finish());
|
||||
}
|
||||
return {};
|
||||
}));
|
||||
TRY(array.finish());
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_current_work_directory_link(KBufferBuilder& builder) const
|
||||
{
|
||||
return builder.append(TRY(const_cast<Process&>(*this).current_directory()->try_serialize_absolute_path())->view());
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_command_line(KBufferBuilder& builder) const
|
||||
{
|
||||
auto array = TRY(JsonArraySerializer<>::try_create(builder));
|
||||
for (auto const& arg : arguments()) {
|
||||
TRY(array.add(arg.view()));
|
||||
}
|
||||
TRY(array.finish());
|
||||
return {};
|
||||
}
|
||||
|
||||
mode_t Process::binary_link_required_mode() const
|
||||
{
|
||||
if (!executable())
|
||||
return 0;
|
||||
return m_procfs_traits->required_mode();
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::procfs_get_binary_link(KBufferBuilder& builder) const
|
||||
{
|
||||
auto custody = executable();
|
||||
if (!custody)
|
||||
return Error::from_errno(ENOEXEC);
|
||||
return builder.append(TRY(custody->try_serialize_absolute_path())->view());
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue