Serenity Operating System
at master 434 lines 16 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, Spencer Dixon <spencercdixon@gmail.com> 4 * Copyright (c) 2021-2023, Liav A. <liavalb@hotmail.co.il> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <Kernel/FileSystem/ProcFS/Inode.h> 10#include <Kernel/Process.h> 11#include <Kernel/Time/TimeManagement.h> 12 13namespace Kernel { 14 15ProcFSInode::~ProcFSInode() = default; 16 17static mode_t determine_procfs_process_inode_mode(u32 subdirectory, u32 property) 18{ 19 if (subdirectory == process_fd_subdirectory_root_entry.subdirectory) 20 return S_IFLNK | 0400; 21 if (subdirectory == process_stacks_subdirectory_root_entry.subdirectory) 22 return S_IFREG | 0400; 23 if (subdirectory == process_children_subdirectory_root_entry.subdirectory) 24 return S_IFLNK | 0400; 25 VERIFY(subdirectory == main_process_directory_root_entry.subdirectory); 26 if (property == process_exe_symlink_entry.property) 27 return S_IFLNK | 0777; 28 if (property == process_cwd_symlink_entry.property) 29 return S_IFLNK | 0777; 30 return S_IFREG | 0400; 31} 32 33static u16 extract_subdirectory_index_from_inode_index(InodeIndex inode_index) 34{ 35 return (inode_index.value() >> 20) & 0xFFFF; 36} 37 38static u32 extract_property_index_from_inode_index(InodeIndex inode_index) 39{ 40 return inode_index.value() & 0xFFFFF; 41} 42 43InodeIndex ProcFSInode::create_index_from_global_directory_entry(segmented_global_inode_index entry) 44{ 45 u64 inode_index = 0; 46 VERIFY(entry.primary < 0x10000000); 47 u64 tmp = entry.primary; 48 inode_index |= tmp << 36; 49 50 // NOTE: The sub-directory part is already limited to 0xFFFF, so no need to VERIFY it. 51 tmp = entry.subdirectory; 52 inode_index |= tmp << 20; 53 54 VERIFY(entry.property < 0x100000); 55 inode_index |= entry.property; 56 return inode_index; 57} 58 59InodeIndex ProcFSInode::create_index_from_process_directory_entry(ProcessID pid, segmented_process_directory_entry entry) 60{ 61 u64 inode_index = 0; 62 // NOTE: We use 0xFFFFFFF because PID part (bits 64-36) as 0 is reserved for global inodes. 63 VERIFY(pid.value() < 0xFFFFFFF); 64 u64 tmp = (pid.value() + 1); 65 inode_index |= tmp << 36; 66 // NOTE: The sub-directory part is already limited to 0xFFFF, so no need to VERIFY it. 67 tmp = entry.subdirectory; 68 inode_index |= tmp << 20; 69 VERIFY(entry.property < 0x100000); 70 inode_index |= entry.property; 71 return inode_index; 72} 73 74static Optional<ProcessID> extract_possible_pid_from_inode_index(InodeIndex inode_index) 75{ 76 auto pid_part = inode_index.value() >> 36; 77 // NOTE: pid_part is set to 0 for global inodes. 78 if (pid_part == 0) 79 return {}; 80 return pid_part - 1; 81} 82 83ProcFSInode::ProcFSInode(ProcFS const& procfs_instance, InodeIndex inode_index) 84 : Inode(const_cast<ProcFS&>(procfs_instance), inode_index) 85 , m_associated_pid(extract_possible_pid_from_inode_index(inode_index)) 86 , m_subdirectory(extract_subdirectory_index_from_inode_index(inode_index)) 87 , m_property(extract_property_index_from_inode_index(inode_index)) 88{ 89 if (inode_index == 1) { 90 m_type = Type::RootDirectory; 91 return; 92 } 93 if (inode_index == 2) { 94 m_type = Type::SelfProcessLink; 95 return; 96 } 97 98 if (m_property == 0) { 99 if (m_subdirectory > 0) 100 m_type = Type::ProcessSubdirectory; 101 else 102 m_type = Type::ProcessDirectory; 103 return; 104 } 105 106 m_type = Type::ProcessProperty; 107} 108 109ErrorOr<void> ProcFSInode::traverse_as_root_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const 110{ 111 TRY(callback({ "."sv, { fsid(), 1 }, 0 })); 112 TRY(callback({ ".."sv, { fsid(), 0 }, 0 })); 113 TRY(callback({ "self"sv, { fsid(), 2 }, 0 })); 114 115 return Process::for_each_in_same_jail([&](Process& process) -> ErrorOr<void> { 116 VERIFY(!(process.pid() < 0)); 117 u64 process_id = (u64)process.pid().value(); 118 InodeIdentifier identifier = { fsid(), static_cast<InodeIndex>(process_id << 36) }; 119 auto process_id_string = TRY(KString::formatted("{:d}", process_id)); 120 TRY(callback({ process_id_string->view(), identifier, 0 })); 121 return {}; 122 }); 123} 124 125ErrorOr<void> ProcFSInode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const 126{ 127 MutexLocker locker(procfs().m_lock); 128 if (m_type == Type::ProcessSubdirectory) { 129 VERIFY(m_associated_pid.has_value()); 130 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 131 if (!process) 132 return EINVAL; 133 switch (m_subdirectory) { 134 case process_fd_subdirectory_root_entry.subdirectory: 135 return process->traverse_file_descriptions_directory(procfs().fsid(), move(callback)); 136 case process_stacks_subdirectory_root_entry.subdirectory: 137 return process->traverse_stacks_directory(procfs().fsid(), move(callback)); 138 case process_children_subdirectory_root_entry.subdirectory: 139 return process->traverse_children_directory(procfs().fsid(), move(callback)); 140 default: 141 VERIFY_NOT_REACHED(); 142 } 143 VERIFY_NOT_REACHED(); 144 } 145 146 if (m_type == Type::RootDirectory) { 147 return traverse_as_root_directory(move(callback)); 148 } 149 150 VERIFY(m_type == Type::ProcessDirectory); 151 VERIFY(m_associated_pid.has_value()); 152 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 153 if (!process) 154 return EINVAL; 155 return process->traverse_as_directory(procfs().fsid(), move(callback)); 156} 157 158ErrorOr<NonnullRefPtr<Inode>> ProcFSInode::lookup_as_root_directory(StringView name) 159{ 160 if (name == "self"sv) 161 return procfs().get_inode({ fsid(), 2 }); 162 163 auto pid = name.to_uint<unsigned>(); 164 if (!pid.has_value()) 165 return ESRCH; 166 auto actual_pid = pid.value(); 167 168 if (auto maybe_process = Process::from_pid_in_same_jail(actual_pid)) { 169 InodeIndex id = (static_cast<u64>(maybe_process->pid().value()) + 1) << 36; 170 return procfs().get_inode({ fsid(), id }); 171 } 172 return ENOENT; 173} 174 175ErrorOr<NonnullRefPtr<Inode>> ProcFSInode::lookup(StringView name) 176{ 177 MutexLocker locker(procfs().m_lock); 178 if (m_type == Type::ProcessSubdirectory) { 179 VERIFY(m_associated_pid.has_value()); 180 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 181 if (!process) 182 return ESRCH; 183 switch (m_subdirectory) { 184 case process_fd_subdirectory_root_entry.subdirectory: 185 return process->lookup_file_descriptions_directory(procfs(), name); 186 case process_stacks_subdirectory_root_entry.subdirectory: 187 return process->lookup_stacks_directory(procfs(), name); 188 case process_children_subdirectory_root_entry.subdirectory: 189 return process->lookup_children_directory(procfs(), name); 190 default: 191 VERIFY_NOT_REACHED(); 192 } 193 VERIFY_NOT_REACHED(); 194 } 195 196 if (m_type == Type::RootDirectory) { 197 return lookup_as_root_directory(name); 198 } 199 200 VERIFY(m_type == Type::ProcessDirectory); 201 VERIFY(m_associated_pid.has_value()); 202 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 203 if (!process) 204 return ESRCH; 205 return process->lookup_as_directory(procfs(), name); 206} 207 208ErrorOr<void> ProcFSInode::attach(OpenFileDescription& description) 209{ 210 if (m_type == Type::RootDirectory || m_type == Type::SelfProcessLink || m_type == Type::ProcessDirectory || m_type == Type::ProcessSubdirectory) 211 return {}; 212 VERIFY(m_type == Type::ProcessProperty); 213 return refresh_process_property_data(description); 214} 215 216void ProcFSInode::did_seek(OpenFileDescription& description, off_t offset) 217{ 218 if (m_type == Type::SelfProcessLink) { 219 return; 220 } 221 VERIFY(m_type == Type::ProcessProperty); 222 if (offset != 0) 223 return; 224 (void)refresh_process_property_data(description); 225} 226 227ErrorOr<size_t> ProcFSInode::read_bytes_locked(off_t offset, size_t count, UserOrKernelBuffer& buffer, OpenFileDescription* description) const 228{ 229 dbgln_if(PROCFS_DEBUG, "ProcFSInode: read_bytes_locked offset: {} count: {}", offset, count); 230 VERIFY(offset >= 0); 231 VERIFY(buffer.user_or_kernel_ptr()); 232 233 if (m_type == Type::SelfProcessLink) { 234 auto builder = TRY(KBufferBuilder::try_create()); 235 TRY(builder.appendff("{}", Process::current().pid().value())); 236 auto data_buffer = builder.build(); 237 if (!data_buffer) 238 return Error::from_errno(EFAULT); 239 if ((size_t)offset >= data_buffer->size()) 240 return 0; 241 ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(count)); 242 TRY(buffer.write(data_buffer->data() + offset, nread)); 243 return nread; 244 } 245 246 VERIFY(m_type == Type::ProcessProperty); 247 248 if (!description) { 249 auto builder = TRY(KBufferBuilder::try_create()); 250 VERIFY(m_associated_pid.has_value()); 251 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 252 if (!process) 253 return Error::from_errno(ESRCH); 254 TRY(try_fetch_process_property_data(*process, builder)); 255 auto data_buffer = builder.build(); 256 if (!data_buffer) 257 return Error::from_errno(EFAULT); 258 if ((size_t)offset >= data_buffer->size()) 259 return 0; 260 ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(count)); 261 TRY(buffer.write(data_buffer->data() + offset, nread)); 262 return nread; 263 } 264 265 if (!description->data()) { 266 dbgln("ProcFS Process Information: Do not have cached data!"); 267 return Error::from_errno(EIO); 268 } 269 270 MutexLocker locker(m_refresh_lock); 271 272 auto& typed_cached_data = static_cast<ProcFSInodeData&>(*description->data()); 273 auto& data_buffer = typed_cached_data.buffer; 274 275 if (!data_buffer || (size_t)offset >= data_buffer->size()) 276 return 0; 277 278 ssize_t nread = min(static_cast<off_t>(data_buffer->size() - offset), static_cast<off_t>(count)); 279 TRY(buffer.write(data_buffer->data() + offset, nread)); 280 return nread; 281} 282 283static ErrorOr<void> build_from_cached_data(KBufferBuilder& builder, ProcFSInodeData& cached_data) 284{ 285 cached_data.buffer = builder.build(); 286 if (!cached_data.buffer) 287 return ENOMEM; 288 return {}; 289} 290 291ErrorOr<void> ProcFSInode::try_fetch_process_property_data(NonnullLockRefPtr<Process> process, KBufferBuilder& builder) const 292{ 293 VERIFY(m_type == Type::ProcessProperty); 294 if (m_subdirectory == process_fd_subdirectory_root_entry.subdirectory) { 295 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 296 // Therefore subtract 1 to get the actual correct fd number. 297 TRY(process->procfs_get_file_description_link(m_property - 1, builder)); 298 return {}; 299 } 300 if (m_subdirectory == process_stacks_subdirectory_root_entry.subdirectory) { 301 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 302 // Therefore subtract 1 to get the actual correct thread stack number. 303 TRY(process->procfs_get_thread_stack(m_property - 1, builder)); 304 return {}; 305 } 306 if (m_subdirectory == process_children_subdirectory_root_entry.subdirectory) { 307 // NOTE: All property numbers should start from 1 as 0 is reserved for the directory itself. 308 // Therefore subtract 1 to get the actual correct child process index number for a correct symlink. 309 TRY(process->procfs_get_child_process_link(m_property - 1, builder)); 310 return {}; 311 } 312 313 VERIFY(m_subdirectory == main_process_directory_root_entry.subdirectory); 314 switch (m_property) { 315 case process_unveil_list_entry.property: 316 return process->procfs_get_unveil_stats(builder); 317 case process_pledge_list_entry.property: 318 return process->procfs_get_pledge_stats(builder); 319 case process_fds_list_entry.property: 320 return process->procfs_get_fds_stats(builder); 321 case process_exe_symlink_entry.property: 322 return process->procfs_get_binary_link(builder); 323 case process_cwd_symlink_entry.property: 324 return process->procfs_get_current_work_directory_link(builder); 325 case process_perf_events_entry.property: 326 return process->procfs_get_perf_events(builder); 327 case process_vm_entry.property: 328 return process->procfs_get_virtual_memory_stats(builder); 329 case process_cmdline_entry.property: 330 return process->procfs_get_command_line(builder); 331 default: 332 VERIFY_NOT_REACHED(); 333 } 334} 335 336ErrorOr<void> ProcFSInode::refresh_process_property_data(OpenFileDescription& description) 337{ 338 // For process-specific inodes, hold the process's ptrace lock across refresh 339 // and refuse to load data if the process is not dumpable. 340 // Without this, files opened before a process went non-dumpable could still be used for dumping. 341 VERIFY(m_type == Type::ProcessProperty); 342 VERIFY(m_associated_pid.has_value()); 343 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 344 if (!process) 345 return Error::from_errno(ESRCH); 346 process->ptrace_lock().lock(); 347 if (!process->is_dumpable()) { 348 process->ptrace_lock().unlock(); 349 return EPERM; 350 } 351 ScopeGuard guard = [&] { 352 process->ptrace_lock().unlock(); 353 }; 354 MutexLocker locker(m_refresh_lock); 355 auto& cached_data = description.data(); 356 if (!cached_data) { 357 cached_data = adopt_own_if_nonnull(new (nothrow) ProcFSInodeData); 358 if (!cached_data) 359 return ENOMEM; 360 } 361 auto builder = TRY(KBufferBuilder::try_create()); 362 TRY(try_fetch_process_property_data(*process, builder)); 363 return build_from_cached_data(builder, static_cast<ProcFSInodeData&>(*cached_data)); 364} 365 366InodeMetadata ProcFSInode::metadata() const 367{ 368 InodeMetadata metadata; 369 switch (m_type) { 370 case Type::SelfProcessLink: { 371 metadata.inode = { fsid(), 2 }; 372 metadata.mode = S_IFLNK | 0777; 373 metadata.uid = 0; 374 metadata.gid = 0; 375 metadata.size = 0; 376 metadata.mtime = TimeManagement::boot_time(); 377 break; 378 } 379 case Type::RootDirectory: { 380 metadata.inode = { fsid(), 1 }; 381 metadata.mode = S_IFDIR | 0555; 382 metadata.uid = 0; 383 metadata.gid = 0; 384 metadata.size = 0; 385 metadata.mtime = TimeManagement::boot_time(); 386 break; 387 } 388 case Type::ProcessProperty: { 389 VERIFY(m_associated_pid.has_value()); 390 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 391 if (!process) 392 return {}; 393 metadata.inode = identifier(); 394 metadata.mode = determine_procfs_process_inode_mode(m_subdirectory, m_property); 395 auto credentials = process->credentials(); 396 metadata.uid = credentials->uid(); 397 metadata.gid = credentials->gid(); 398 metadata.size = 0; 399 metadata.mtime = TimeManagement::now(); 400 break; 401 } 402 case Type::ProcessDirectory: { 403 VERIFY(m_associated_pid.has_value()); 404 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 405 if (!process) 406 return {}; 407 metadata.inode = identifier(); 408 metadata.mode = S_IFDIR | 0555; 409 auto credentials = process->credentials(); 410 metadata.uid = credentials->uid(); 411 metadata.gid = credentials->gid(); 412 metadata.size = 0; 413 metadata.mtime = TimeManagement::now(); 414 break; 415 } 416 case Type::ProcessSubdirectory: { 417 VERIFY(m_associated_pid.has_value()); 418 auto process = Process::from_pid_in_same_jail(m_associated_pid.value()); 419 if (!process) 420 return {}; 421 metadata.inode = identifier(); 422 metadata.mode = S_IFDIR | 0555; 423 auto credentials = process->credentials(); 424 metadata.uid = credentials->uid(); 425 metadata.gid = credentials->gid(); 426 metadata.size = 0; 427 metadata.mtime = TimeManagement::now(); 428 break; 429 } 430 } 431 return metadata; 432} 433 434}