/* * Copyright (c) 2021, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include namespace Kernel { static SpinLock 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 ProcFSComponentRegistry::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); } ProcFSExposedDirectory::ProcFSExposedDirectory(StringView name) : ProcFSExposedComponent(name) { } ProcFSExposedDirectory::ProcFSExposedDirectory(StringView name, const ProcFSExposedDirectory& parent_directory) : ProcFSExposedComponent(name) , m_parent_directory(parent_directory) { } ProcFSExposedLink::ProcFSExposedLink(StringView name) : ProcFSExposedComponent(name) { } ProcFSExposedLink::ProcFSExposedLink(StringView name, InodeIndex preallocated_index) : ProcFSExposedComponent(name, preallocated_index) { } struct ProcFSInodeData : public FileDescriptionData { OwnPtr buffer; }; KResultOr 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); MutexLocker locker(m_refresh_lock); if (!description->data()) { dbgln("ProcFSGlobalInformation: Do not have cached data!"); return KResult(EIO); } auto& typed_cached_data = static_cast(*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(data_buffer->size() - offset), static_cast(count)); if (!buffer.write(data_buffer->data() + offset, nread)) return KResult(EFAULT); return nread; } KResult ProcFSGlobalInformation::refresh_data(FileDescription& description) const { MutexLocker lock(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(*this).output(builder)) return ENOENT; auto& typed_cached_data = static_cast(*cached_data); typed_cached_data.buffer = builder.build(); if (!typed_cached_data.buffer) return ENOMEM; return KSuccess; } KResultOr 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(*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(data_buffer->size() - offset), static_cast(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(*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(*this).output(builder)) return ENOENT; auto& typed_cached_data = static_cast(*cached_data); typed_cached_data.buffer = builder.build(); if (!typed_cached_data.buffer) return ENOMEM; return KSuccess; } KResultOr ProcFSExposedLink::read_bytes(off_t offset, size_t count, UserOrKernelBuffer& buffer, FileDescription*) const { VERIFY(offset == 0); MutexLocker locker(m_lock); KBufferBuilder builder; if (!const_cast(*this).acquire_link(builder)) return KResult(EFAULT); auto blob = builder.build(); if (!blob) return KResult(EFAULT); ssize_t nread = min(static_cast(blob->size() - offset), static_cast(count)); if (!buffer.write(blob->data() + offset, nread)) return KResult(EFAULT); return nread; } NonnullRefPtr ProcFSExposedLink::to_inode(const ProcFS& procfs_instance) const { return ProcFSLinkInode::create(procfs_instance, *this); } NonnullRefPtr ProcFSExposedComponent::to_inode(const ProcFS& procfs_instance) const { return ProcFSInode::create(procfs_instance, *this); } NonnullRefPtr ProcFSExposedDirectory::to_inode(const ProcFS& procfs_instance) const { return ProcFSDirectoryInode::create(procfs_instance, *this); } void ProcFSExposedDirectory::add_component(const ProcFSExposedComponent&) { TODO(); } RefPtr ProcFSExposedDirectory::lookup(StringView name) { for (auto& component : m_components) { if (component.name() == name) { return component; } } return {}; } KResult ProcFSExposedDirectory::traverse_as_directory(unsigned fsid, Function callback) const { MutexLocker locker(ProcFSComponentRegistry::the().get_lock()); auto parent_directory = m_parent_directory.strong_ref(); if (parent_directory.is_null()) return KResult(EINVAL); callback({ ".", { fsid, component_index() }, 0 }); callback({ "..", { fsid, parent_directory->component_index() }, 0 }); for (auto& component : m_components) { InodeIdentifier identifier = { fsid, component.component_index() }; callback({ component.name(), identifier, 0 }); } return KSuccess; } }