/* * 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_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 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); 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 data_buffer = static_cast(*description->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 { 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(*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(*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 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 data_buffer = static_cast(*description->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_folder = const_cast(*this).m_parent_folder.strong_ref(); if (parent_folder.is_null()) return KResult(EINVAL); auto process = 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(*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(*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 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(*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 { Locker locker(ProcFSComponentRegistry::the().get_lock()); auto parent_folder = m_parent_folder.strong_ref(); if (parent_folder.is_null()) return KResult(EINVAL); callback({ ".", { fsid, component_index() }, 0 }); callback({ "..", { fsid, parent_folder->component_index() }, 0 }); for (auto& component : m_components) { InodeIdentifier identifier = { fsid, component.component_index() }; callback({ component.name(), identifier, 0 }); } return KSuccess; } }