ladybird/Kernel/ProcessExposed.cpp
Liav A 47149e625f Kernel/ProcFS: Split code into more separate files
Instead of using one file for the entire "backend" of the ProcFS data
and metadata, we could split that file into two files that represent
2 logical chunks of the ProcFS exposed objects:
1. Global and inter-process information. This includes all fixed data in
the root folder of the ProcFS, networking information and the bus
folder.
2. Per-process information. This includes all dynamic data about a
process that resides in the /proc/PID/ folder.

This change makes it more easier to read the code and to change it,
hence we do it although there's no technical benefit from it now :)
2021-06-29 20:53:59 +02:00

255 lines
8.3 KiB
C++

/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Debug.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/FileSystem/ProcFS.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/KBufferBuilder.h>
#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
#include <Kernel/ProcessExposed.h>
namespace Kernel {
static SpinLock<u8> s_index_lock;
static InodeIndex s_next_inode_index = 0;
static size_t s_allocate_inode_index()
{
ScopedSpinLock lock(s_index_lock);
s_next_inode_index = s_next_inode_index.value() + 1;
VERIFY(s_next_inode_index > 0);
return s_next_inode_index.value();
}
InodeIndex ProcFSComponentsRegistrar::allocate_inode_index() const
{
return s_allocate_inode_index();
}
ProcFSExposedComponent::ProcFSExposedComponent(StringView name)
: m_component_index(s_allocate_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);
}
ProcFSExposedFolder::ProcFSExposedFolder(StringView name)
: ProcFSExposedComponent(name)
{
}
ProcFSExposedFolder::ProcFSExposedFolder(StringView name, const ProcFSExposedFolder& parent_folder)
: ProcFSExposedComponent(name)
, m_parent_folder(parent_folder)
{
}
ProcFSExposedLink::ProcFSExposedLink(StringView name)
: ProcFSExposedComponent(name)
{
}
ProcFSExposedLink::ProcFSExposedLink(StringView name, InodeIndex preallocated_index)
: ProcFSExposedComponent(name, preallocated_index)
{
}
struct ProcFSInodeData : public FileDescriptionData {
RefPtr<KBufferImpl> 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);
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);
}
// Be sure to keep a reference to data_buffer while we use it!
RefPtr<KBufferImpl> data_buffer = static_cast<ProcFSInodeData&>(*description->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 ProcFSGlobalInformation::refresh_data(FileDescription& description) const
{
ScopedSpinLock lock(m_refresh_lock);
auto& cached_data = description.data();
if (!cached_data)
cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData);
VERIFY(description.data());
auto& buffer = static_cast<ProcFSInodeData&>(*cached_data).buffer;
if (buffer) {
// If we're reusing the buffer, reset the size to 0 first. This
// ensures we don't accidentally leak previously written data.
buffer->set_size(0);
}
KBufferBuilder builder(buffer, true);
if (!const_cast<ProcFSGlobalInformation&>(*this).output(builder))
return ENOENT;
// We don't use builder.build() here, which would steal our buffer
// and turn it into an OwnPtr. Instead, just flush to the buffer so
// that we can read all the data that was written.
if (!builder.flush())
return ENOMEM;
if (!buffer)
return ENOMEM;
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);
}
// Be sure to keep a reference to data_buffer while we use it!
RefPtr<KBufferImpl> data_buffer = static_cast<ProcFSInodeData&>(*description->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 process = const_cast<ProcFSProcessInformation&>(*this).m_parent_folder->m_associated_process;
process->ptrace_lock().lock();
if (!process->is_dumpable()) {
process->ptrace_lock().unlock();
return EPERM;
}
ScopeGuard guard = [&] {
process->ptrace_lock().unlock();
};
ScopedSpinLock lock(m_refresh_lock);
auto& cached_data = description.data();
if (!cached_data)
cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData);
VERIFY(description.data());
auto& buffer = static_cast<ProcFSInodeData&>(*cached_data).buffer;
if (buffer) {
// If we're reusing the buffer, reset the size to 0 first. This
// ensures we don't accidentally leak previously written data.
buffer->set_size(0);
}
KBufferBuilder builder(buffer, true);
if (!const_cast<ProcFSProcessInformation&>(*this).output(builder))
return ENOENT;
// We don't use builder.build() here, which would steal our buffer
// and turn it into an OwnPtr. Instead, just flush to the buffer so
// that we can read all the data that was written.
if (!builder.flush())
return ENOMEM;
if (!buffer)
return ENOMEM;
return KSuccess;
}
KResultOr<size_t> ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const
{
VERIFY(offset == 0);
Locker locker(m_lock);
KBufferBuilder builder;
if (!const_cast<ProcFSExposedLink&>(*this).acquire_link(builder))
return KResult(EFAULT);
auto blob = builder.build();
if (!blob)
return KResult(EFAULT);
ssize_t nread = min(static_cast<off_t>(blob->size() - offset), static_cast<off_t>(count));
if (!buffer.write(blob->data() + offset, nread))
return KResult(EFAULT);
return nread;
}
NonnullRefPtr<Inode> ProcFSExposedLink::to_inode(const ProcFS& procfs_instance) const
{
return ProcFSLinkInode::create(procfs_instance, *this);
}
NonnullRefPtr<Inode> ProcFSExposedComponent::to_inode(const ProcFS& procfs_instance) const
{
return ProcFSInode::create(procfs_instance, *this);
}
NonnullRefPtr<Inode> ProcFSExposedFolder::to_inode(const ProcFS& procfs_instance) const
{
return ProcFSDirectoryInode::create(procfs_instance, *this);
}
void ProcFSExposedFolder::add_component(const ProcFSExposedComponent&)
{
TODO();
}
RefPtr<ProcFSExposedComponent> ProcFSExposedFolder::lookup(StringView name)
{
for (auto& component : m_components) {
if (component.name() == name) {
return component;
}
}
return {};
}
KResult ProcFSExposedFolder::traverse_as_directory(unsigned fsid, Function<bool(const FS::DirectoryEntryView&)> callback) const
{
Locker locker(ProcFSComponentsRegistrar::the().m_lock);
VERIFY(m_parent_folder);
callback({ ".", { fsid, component_index() }, 0 });
callback({ "..", { fsid, m_parent_folder->component_index() }, 0 });
for (auto& component : m_components) {
InodeIdentifier identifier = { fsid, component.component_index() };
callback({ component.name(), identifier, 0 });
}
return KSuccess;
}
}