1use crate::bridge::LifecycleState;
2use crate::command_registry::{CommandDriver, CommandRegistry};
3use crate::device_layer::{create_device_layer, DeviceLayer};
4use crate::dns::{
5 format_dns_resource, resolve_dns, resolve_dns_records, DnsConfig, DnsLookupPolicy,
6 DnsRecordResolution, DnsResolution, DnsResolverErrorKind, HickoryDnsResolver,
7 SharedDnsResolver,
8};
9use crate::fd_table::{
10 FdEntry, FdStat, FdTableError, FdTableManager, FileDescription, FileLockManager,
11 FileLockTarget, FlockOperation, ProcessFdTable, FILETYPE_CHARACTER_DEVICE, FILETYPE_DIRECTORY,
12 FILETYPE_PIPE, FILETYPE_REGULAR_FILE, FILETYPE_SYMBOLIC_LINK, F_DUPFD, O_APPEND, O_CREAT,
13 O_EXCL, O_NONBLOCK, O_TRUNC,
14};
15use crate::mount_table::{MountEntry, MountOptions, MountTable, MountedFileSystem};
16use crate::network_policy::format_tcp_resource;
17use crate::permissions::{
18 check_command_execution, check_network_access, FsOperation, NetworkOperation, PermissionError,
19 PermissionedFileSystem, Permissions,
20};
21use crate::pipe_manager::{PipeError, PipeManager};
22use crate::poll::{
23 PollEvents, PollFd, PollNotifier, PollResult, PollTarget, PollTargetEntry, PollTargetResult,
24 POLLERR, POLLHUP, POLLIN, POLLNVAL, POLLOUT,
25};
26use crate::process_table::{
27 DriverProcess, ProcessContext, ProcessExitCallback, ProcessInfo, ProcessStatus, ProcessTable,
28 ProcessTableError, ProcessWaitResult, SigmaskHow, SignalSet, DEFAULT_PROCESS_UMASK, SIGCONT,
29 SIGPIPE, SIGSTOP, SIGTSTP, SIGWINCH,
30};
31use crate::pty::{
32 LineDisciplineConfig, PartialTermios, PtyError, PtyManager, PtyWindowSize, Termios,
33};
34use crate::resource_accounting::{
35 measure_filesystem_usage, FileSystemUsage, ResourceAccountant, ResourceError, ResourceLimits,
36 ResourceSnapshot, DEFAULT_MAX_OPEN_FDS,
37};
38use crate::root_fs::{RootFileSystem, RootFilesystemError, RootFilesystemSnapshot};
39use crate::socket_table::{
40 DatagramSocketOption, InetSocketAddress, ReceivedDatagram, SocketId, SocketMulticastMembership,
41 SocketReadiness, SocketRecord, SocketShutdown, SocketSpec, SocketState, SocketTable,
42 SocketTableError, SocketType,
43};
44use crate::user::{ProcessIdentity, UserConfig, UserManager};
45use crate::vfs::{
46 normalize_path, VfsError, VfsResult, VirtualDirEntry, VirtualFileSystem, VirtualStat,
47 VirtualTimeSpec, VirtualUtimeSpec,
48};
49use hickory_proto::rr::RecordType;
50use std::any::Any;
51use std::collections::{BTreeMap, BTreeSet};
52use std::error::Error;
53use std::fmt;
54#[cfg(test)]
55use std::sync::OnceLock;
56use std::sync::{Arc, Condvar, Mutex, MutexGuard, WaitTimeoutResult};
57use std::time::Duration;
58use web_time::{Instant, SystemTime, UNIX_EPOCH};
59
60pub type KernelResult<T> = Result<T, KernelError>;
61pub use crate::process_table::{ProcessWaitEvent as WaitPidEvent, WaitPidFlags};
62
63pub const SEEK_SET: u8 = 0;
64pub const SEEK_CUR: u8 = 1;
65pub const SEEK_END: u8 = 2;
66const EXECUTABLE_PERMISSION_BITS: u32 = 0o111;
67const SHEBANG_LINE_MAX_BYTES: usize = 256;
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct KernelError {
71 code: &'static str,
72 message: String,
73}
74
75impl KernelError {
76 pub fn code(&self) -> &'static str {
77 self.code
78 }
79
80 fn new(code: &'static str, message: impl Into<String>) -> Self {
81 Self {
82 code,
83 message: message.into(),
84 }
85 }
86
87 fn disposed() -> Self {
88 Self::new("EINVAL", "kernel VM is disposed")
89 }
90
91 fn no_such_process(pid: u32) -> Self {
92 Self::new("ESRCH", format!("no such process {pid}"))
93 }
94
95 fn bad_file_descriptor(fd: u32) -> Self {
96 Self::new("EBADF", format!("bad file descriptor {fd}"))
97 }
98
99 fn permission_denied(message: impl Into<String>) -> Self {
100 Self::new("EPERM", message)
101 }
102
103 fn command_not_found(command: &str) -> Self {
104 Self::new("ENOENT", format!("command not found: {command}"))
105 }
106}
107
108impl fmt::Display for KernelError {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 write!(f, "{}: {}", self.code, self.message)
111 }
112}
113
114impl Error for KernelError {}
115
116#[derive(Clone)]
117pub struct KernelVmConfig {
118 pub vm_id: String,
119 pub env: BTreeMap<String, String>,
120 pub cwd: String,
121 pub user: UserConfig,
122 pub permissions: Permissions,
123 pub loopback_exempt_ports: BTreeSet<u16>,
124 pub dns: DnsConfig,
125 pub dns_resolver: SharedDnsResolver,
126 pub resources: ResourceLimits,
127 pub zombie_ttl: Duration,
128}
129
130impl KernelVmConfig {
131 pub fn new(vm_id: impl Into<String>) -> Self {
132 Self {
133 vm_id: vm_id.into(),
134 env: BTreeMap::new(),
135 cwd: String::from("/workspace"),
136 user: UserConfig::default(),
137 permissions: Permissions::default(),
138 loopback_exempt_ports: BTreeSet::new(),
139 dns: DnsConfig::default(),
140 dns_resolver: Arc::new(HickoryDnsResolver::default()),
141 resources: ResourceLimits::default(),
142 zombie_ttl: Duration::from_secs(60),
143 }
144 }
145}
146
147#[derive(Debug, Clone, Default)]
148pub struct SpawnOptions {
149 pub requester_driver: Option<String>,
150 pub parent_pid: Option<u32>,
151 pub env: BTreeMap<String, String>,
152 pub cwd: Option<String>,
153}
154
155#[derive(Debug, Clone, Default, PartialEq, Eq)]
156pub struct VirtualProcessOptions {
157 pub parent_pid: Option<u32>,
158 pub env: BTreeMap<String, String>,
159 pub cwd: Option<String>,
160}
161
162#[derive(Debug, Clone, Default, PartialEq, Eq)]
163pub struct ExecOptions {
164 pub requester_driver: Option<String>,
165 pub parent_pid: Option<u32>,
166 pub env: BTreeMap<String, String>,
167 pub cwd: Option<String>,
168}
169
170#[derive(Debug, Clone, Default, PartialEq, Eq)]
171pub struct OpenShellOptions {
172 pub requester_driver: Option<String>,
173 pub command: Option<String>,
174 pub args: Vec<String>,
175 pub env: BTreeMap<String, String>,
176 pub cwd: Option<String>,
177}
178
179#[derive(Debug, Clone, PartialEq, Eq)]
180pub struct WaitPidResult {
181 pub pid: u32,
182 pub status: i32,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct WaitPidEventResult {
187 pub pid: u32,
188 pub status: i32,
189 pub event: WaitPidEvent,
190}
191
192#[derive(Debug, Clone)]
193struct ResolvedSpawnCommand {
194 command: String,
195 args: Vec<String>,
196 driver: CommandDriver,
197}
198
199#[derive(Debug, Clone)]
200struct ShebangCommand {
201 interpreter: String,
202 args: Vec<String>,
203}
204
205#[derive(Clone)]
206pub struct KernelProcessHandle {
207 pid: u32,
208 driver: String,
209 process: Arc<StubDriverProcess>,
210}
211
212impl fmt::Debug for KernelProcessHandle {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 f.debug_struct("KernelProcessHandle")
215 .field("pid", &self.pid)
216 .field("driver", &self.driver)
217 .finish_non_exhaustive()
218 }
219}
220
221impl KernelProcessHandle {
222 pub fn pid(&self) -> u32 {
223 self.pid
224 }
225
226 pub fn driver(&self) -> &str {
227 &self.driver
228 }
229
230 pub fn finish(&self, exit_code: i32) {
231 self.process.finish(exit_code);
232 }
233
234 pub fn kill(&self, signal: i32) {
235 self.process.kill(signal);
236 }
237
238 pub fn wait(&self, timeout: Duration) -> Option<i32> {
239 self.process.wait(timeout)
240 }
241
242 pub fn kill_signals(&self) -> Vec<i32> {
243 self.process.kill_signals()
244 }
245}
246
247#[derive(Debug, Clone)]
248pub struct OpenShellHandle {
249 process: KernelProcessHandle,
250 master_fd: u32,
251 slave_fd: u32,
252 pty_path: String,
253}
254
255impl OpenShellHandle {
256 pub fn process(&self) -> &KernelProcessHandle {
257 &self.process
258 }
259
260 pub fn pid(&self) -> u32 {
261 self.process.pid()
262 }
263
264 pub fn master_fd(&self) -> u32 {
265 self.master_fd
266 }
267
268 pub fn slave_fd(&self) -> u32 {
269 self.slave_fd
270 }
271
272 pub fn pty_path(&self) -> &str {
273 &self.pty_path
274 }
275}
276
277pub struct KernelVm<F> {
278 vm_id: String,
279 boot_time_ms: u64,
280 boot_instant: Instant,
281 filesystem: PermissionedFileSystem<DeviceLayer<F>>,
282 permissions: Permissions,
283 loopback_exempt_ports: BTreeSet<u16>,
284 dns: DnsConfig,
285 dns_resolver: SharedDnsResolver,
286 env: BTreeMap<String, String>,
287 cwd: String,
288 commands: CommandRegistry,
289 fd_tables: Arc<Mutex<FdTableManager>>,
290 processes: ProcessTable,
291 pipes: PipeManager,
292 ptys: PtyManager,
293 sockets: SocketTable,
294 poll_notifier: PollNotifier,
295 users: UserManager,
296 resources: ResourceAccountant,
297 filesystem_usage_cache: Option<FileSystemUsage>,
298 file_locks: FileLockManager,
299 driver_pids: Arc<Mutex<BTreeMap<String, BTreeSet<u32>>>>,
300 terminated: bool,
301}
302
303fn cleanup_process_resources(
304 fd_tables: &Mutex<FdTableManager>,
305 file_locks: &FileLockManager,
306 pipes: &PipeManager,
307 ptys: &PtyManager,
308 sockets: &SocketTable,
309 driver_pids: &Mutex<BTreeMap<String, BTreeSet<u32>>>,
310 pid: u32,
311) {
312 let mut cleanup = Vec::new();
313 {
314 let mut tables = lock_or_recover(fd_tables);
315 let descriptors = tables
316 .get(pid)
317 .map(|table| {
318 table
319 .iter()
320 .map(|entry| (entry.fd, Arc::clone(&entry.description), entry.filetype))
321 .collect::<Vec<_>>()
322 })
323 .unwrap_or_default();
324
325 cleanup_process_resources_test_hook();
326
327 if let Some(table) = tables.get_mut(pid) {
328 for (fd, description, filetype) in &descriptors {
329 table.close(*fd);
330 cleanup.push((Arc::clone(description), *filetype));
331 }
332 }
333 tables.remove(pid);
334 }
335
336 for (description, filetype) in cleanup {
337 close_special_resource_if_needed(file_locks, pipes, ptys, &description, filetype);
338 }
339
340 sockets.remove_all_for_pid(pid);
341
342 let mut owners = lock_or_recover(driver_pids);
343 for pids in owners.values_mut() {
344 pids.remove(&pid);
345 }
346}
347
348fn dispose_kernel_vm_resources<F>(kernel: &mut KernelVm<F>) {
349 kernel.processes.terminate_all();
350 let pids = lock_or_recover(&kernel.fd_tables).pids();
351 for pid in pids {
352 cleanup_process_resources(
353 kernel.fd_tables.as_ref(),
354 &kernel.file_locks,
355 &kernel.pipes,
356 &kernel.ptys,
357 &kernel.sockets,
358 kernel.driver_pids.as_ref(),
359 pid,
360 );
361 }
362 lock_or_recover(&kernel.driver_pids).clear();
363 kernel.terminated = true;
364}
365
366#[cfg(test)]
367type CleanupProcessResourcesHook = Arc<dyn Fn() + Send + Sync + 'static>;
368
369#[cfg(test)]
370fn cleanup_process_resources_test_hook() {
371 let hook = lock_or_recover(cleanup_process_resources_test_hook_slot()).clone();
372 if let Some(hook) = hook {
373 hook();
374 }
375}
376
377#[cfg(not(test))]
378fn cleanup_process_resources_test_hook() {}
379
380#[cfg(test)]
381fn cleanup_process_resources_test_hook_slot() -> &'static Mutex<Option<CleanupProcessResourcesHook>>
382{
383 static HOOK: OnceLock<Mutex<Option<CleanupProcessResourcesHook>>> = OnceLock::new();
384 HOOK.get_or_init(|| Mutex::new(None))
385}
386
387#[cfg(test)]
388fn set_cleanup_process_resources_test_hook(hook: Option<CleanupProcessResourcesHook>) {
389 *lock_or_recover(cleanup_process_resources_test_hook_slot()) = hook;
390}
391
392fn close_special_resource_if_needed(
393 file_locks: &FileLockManager,
394 pipes: &PipeManager,
395 ptys: &PtyManager,
396 description: &Arc<FileDescription>,
397 filetype: u8,
398) {
399 if description.ref_count() != 0 {
400 return;
401 }
402
403 file_locks.release_owner(description.id());
404
405 if filetype == FILETYPE_PIPE && pipes.is_pipe(description.id()) {
406 pipes.close(description.id());
407 }
408
409 if ptys.is_pty(description.id()) {
410 ptys.close(description.id());
411 }
412}
413
414#[derive(Debug, Clone, PartialEq, Eq)]
415enum ProcNode {
416 RootDir,
417 MountsFile,
418 CpuInfoFile,
419 MemInfoFile,
420 LoadAvgFile,
421 UptimeFile,
422 VersionFile,
423 SelfLink { pid: u32 },
424 PidDir { pid: u32 },
425 PidFdDir { pid: u32 },
426 PidCmdline { pid: u32 },
427 PidEnviron { pid: u32 },
428 PidCwdLink { pid: u32 },
429 PidStatFile { pid: u32 },
430 PidStatusFile { pid: u32 },
431 PidFdLink { pid: u32, fd: u32 },
432}
433
434impl<F: VirtualFileSystem + 'static> KernelVm<F> {
435 pub fn new(filesystem: F, config: KernelVmConfig) -> Self {
436 let vm_id = config.vm_id;
437 let boot_time_ms = now_ms();
438 let boot_instant = Instant::now();
439 let permissions = config.permissions.clone();
440 let users = UserManager::from_config(config.user);
441 let process_table = ProcessTable::with_zombie_ttl(config.zombie_ttl);
442 let process_table_for_pty = process_table.clone();
443 let fd_tables = Arc::new(Mutex::new(FdTableManager::with_max_fds(
444 config
445 .resources
446 .max_open_fds
447 .unwrap_or(DEFAULT_MAX_OPEN_FDS),
448 )));
449 let file_locks = FileLockManager::new();
450 let driver_pids = Arc::new(Mutex::new(BTreeMap::new()));
451 let poll_notifier = PollNotifier::default();
452 let pipes = PipeManager::with_notifier(poll_notifier.clone());
453 let ptys = PtyManager::with_signal_handler_and_notifier(
454 Arc::new(move |pgid, signal| {
455 let _ = process_table_for_pty.kill(-(pgid as i32), signal);
456 }),
457 poll_notifier.clone(),
458 );
459 let sockets = SocketTable::new();
460
461 let fd_tables_for_exit = Arc::clone(&fd_tables);
462 let file_locks_for_exit = file_locks.clone();
463 let driver_pids_for_exit = Arc::clone(&driver_pids);
464 let pipes_for_exit = pipes.clone();
465 let ptys_for_exit = ptys.clone();
466 let sockets_for_exit = sockets.clone();
467 process_table.set_on_process_exit(Some(Arc::new(move |pid| {
468 cleanup_process_resources(
469 fd_tables_for_exit.as_ref(),
470 &file_locks_for_exit,
471 &pipes_for_exit,
472 &ptys_for_exit,
473 &sockets_for_exit,
474 driver_pids_for_exit.as_ref(),
475 pid,
476 );
477 })));
478
479 let filesystem = PermissionedFileSystem::new(
480 create_device_layer(filesystem),
481 vm_id.clone(),
482 permissions.clone(),
483 );
484 let filesystem_usage_cache = None;
488
489 Self {
490 vm_id: vm_id.clone(),
491 boot_time_ms,
492 boot_instant,
493 filesystem,
494 permissions,
495 loopback_exempt_ports: config.loopback_exempt_ports,
496 dns: config.dns,
497 dns_resolver: config.dns_resolver,
498 env: config.env,
499 cwd: config.cwd,
500 commands: CommandRegistry::new(),
501 fd_tables,
502 processes: process_table,
503 pipes,
504 ptys,
505 sockets,
506 poll_notifier,
507 users,
508 resources: ResourceAccountant::new(config.resources),
509 filesystem_usage_cache,
510 file_locks,
511 driver_pids,
512 terminated: false,
513 }
514 }
515
516 pub fn vm_id(&self) -> &str {
517 &self.vm_id
518 }
519
520 pub fn state(&self) -> LifecycleState {
521 if self.terminated {
522 LifecycleState::Terminated
523 } else if self.processes.running_count() > 0 {
524 LifecycleState::Busy
525 } else {
526 LifecycleState::Ready
527 }
528 }
529
530 pub fn commands(&self) -> BTreeMap<String, String> {
531 self.commands.list()
532 }
533
534 pub fn filesystem(&self) -> &PermissionedFileSystem<DeviceLayer<F>> {
535 &self.filesystem
536 }
537
538 pub fn filesystem_mut(&mut self) -> &mut PermissionedFileSystem<DeviceLayer<F>> {
539 &mut self.filesystem
540 }
541
542 pub fn user_manager(&self) -> &UserManager {
543 &self.users
544 }
545
546 pub fn environment(&self) -> &BTreeMap<String, String> {
547 &self.env
548 }
549
550 pub fn process_identity(
551 &self,
552 requester_driver: &str,
553 pid: u32,
554 ) -> KernelResult<ProcessIdentity> {
555 self.assert_driver_owns(requester_driver, pid)?;
556 Ok(self
557 .processes
558 .get(pid)
559 .ok_or_else(|| KernelError::no_such_process(pid))?
560 .identity)
561 }
562
563 pub fn user_profile(&self) -> UserManager {
564 self.users.clone()
565 }
566
567 pub fn getuid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
568 Ok(self.process_identity(requester_driver, pid)?.uid)
569 }
570
571 pub fn getgid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
572 Ok(self.process_identity(requester_driver, pid)?.gid)
573 }
574
575 pub fn geteuid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
576 Ok(self.process_identity(requester_driver, pid)?.euid)
577 }
578
579 pub fn getegid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
580 Ok(self.process_identity(requester_driver, pid)?.egid)
581 }
582
583 pub fn getgroups(&self, requester_driver: &str, pid: u32) -> KernelResult<Vec<u32>> {
584 Ok(self
585 .process_identity(requester_driver, pid)?
586 .supplementary_gids)
587 }
588
589 pub fn getpwuid(&self, uid: u32) -> KernelResult<String> {
590 self.users
591 .getpwuid(uid)
592 .ok_or_else(|| KernelError::new("ENOENT", format!("unknown uid {uid}")))
593 }
594
595 pub fn getgrgid(&self, gid: u32) -> KernelResult<String> {
596 self.users
597 .getgrgid(gid)
598 .ok_or_else(|| KernelError::new("ENOENT", format!("unknown gid {gid}")))
599 }
600
601 pub fn resource_snapshot(&self) -> ResourceSnapshot {
602 let fd_tables = lock_or_recover(&self.fd_tables);
603 self.resources.snapshot(
604 &self.processes,
605 &fd_tables,
606 &self.pipes,
607 &self.ptys,
608 &self.sockets,
609 )
610 }
611
612 pub fn resource_limits(&self) -> &ResourceLimits {
613 self.resources.limits()
614 }
615
616 pub fn set_permissions(&mut self, permissions: Permissions) {
617 self.filesystem.set_permissions(permissions.clone());
618 self.permissions = permissions;
619 }
620
621 pub fn set_loopback_exempt_ports(&mut self, ports: BTreeSet<u16>) {
622 self.loopback_exempt_ports = ports;
623 }
624
625 pub fn extend_loopback_exempt_ports(&mut self, ports: impl IntoIterator<Item = u16>) {
626 self.loopback_exempt_ports.extend(ports);
627 }
628
629 pub fn resolve_dns(
630 &self,
631 hostname: &str,
632 policy: DnsLookupPolicy,
633 ) -> KernelResult<DnsResolution> {
634 self.assert_not_terminated()?;
635 if matches!(policy, DnsLookupPolicy::CheckPermissions) {
636 let resource = format_dns_resource(hostname).map_err(map_dns_resolver_error)?;
637 check_network_access(
638 &self.vm_id,
639 &self.permissions,
640 NetworkOperation::Dns,
641 &resource,
642 )?;
643 }
644
645 resolve_dns(&self.dns, self.dns_resolver.as_ref(), hostname).map_err(map_dns_resolver_error)
646 }
647
648 pub fn resolve_dns_records(
649 &self,
650 hostname: &str,
651 record_type: RecordType,
652 policy: DnsLookupPolicy,
653 ) -> KernelResult<DnsRecordResolution> {
654 self.assert_not_terminated()?;
655 if matches!(policy, DnsLookupPolicy::CheckPermissions) {
656 let resource = format_dns_resource(hostname).map_err(map_dns_resolver_error)?;
657 check_network_access(
658 &self.vm_id,
659 &self.permissions,
660 NetworkOperation::Dns,
661 &resource,
662 )?;
663 }
664
665 resolve_dns_records(&self.dns, self.dns_resolver.as_ref(), hostname, record_type)
666 .map_err(map_dns_resolver_error)
667 }
668
669 pub fn register_driver(&mut self, driver: CommandDriver) -> KernelResult<()> {
670 self.assert_not_terminated()?;
671 let driver_name = driver.name().to_owned();
672 let populate_driver = driver.clone();
673 self.commands.register(driver)?;
674 lock_or_recover(&self.driver_pids)
675 .entry(driver_name)
676 .or_default();
677 self.commands
678 .populate_driver_bin(&mut self.filesystem, &populate_driver)?;
679 Ok(())
680 }
681
682 pub fn exec(
683 &mut self,
684 command: &str,
685 options: ExecOptions,
686 ) -> KernelResult<KernelProcessHandle> {
687 self.spawn_process(
688 "sh",
689 vec![String::from("-c"), String::from(command)],
690 SpawnOptions {
691 requester_driver: options.requester_driver,
692 parent_pid: options.parent_pid,
693 env: options.env,
694 cwd: options.cwd,
695 },
696 )
697 }
698
699 pub fn open_shell(&mut self, options: OpenShellOptions) -> KernelResult<OpenShellHandle> {
700 let command = options.command.unwrap_or_else(|| String::from("sh"));
701 let requester_driver = options.requester_driver.clone();
702 let process = self.spawn_process(
703 &command,
704 options.args,
705 SpawnOptions {
706 requester_driver: requester_driver.clone(),
707 parent_pid: None,
708 env: options.env,
709 cwd: options.cwd,
710 },
711 )?;
712 let owner = requester_driver.as_deref().unwrap_or(process.driver());
713 let (master_fd, slave_fd, pty_path) = self.open_pty(owner, process.pid())?;
714 self.setpgid(owner, process.pid(), process.pid())?;
715 self.pty_set_foreground_pgid(owner, process.pid(), master_fd, process.pid())?;
716 Ok(OpenShellHandle {
717 process,
718 master_fd,
719 slave_fd,
720 pty_path,
721 })
722 }
723
724 pub fn read_file(&mut self, path: &str) -> KernelResult<Vec<u8>> {
725 self.assert_not_terminated()?;
726 self.read_file_internal(None, path)
727 }
728
729 pub fn pread_file(&mut self, path: &str, offset: u64, length: usize) -> KernelResult<Vec<u8>> {
730 self.assert_not_terminated()?;
731 self.resources.check_pread_length(length)?;
732 Ok(VirtualFileSystem::pread(
733 &mut self.filesystem,
734 path,
735 offset,
736 length,
737 )?)
738 }
739
740 pub fn read_file_for_process(
741 &mut self,
742 requester_driver: &str,
743 pid: u32,
744 path: &str,
745 ) -> KernelResult<Vec<u8>> {
746 self.assert_not_terminated()?;
747 self.assert_driver_owns(requester_driver, pid)?;
748 self.read_file_internal(Some(pid), path)
749 }
750
751 pub fn write_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> KernelResult<()> {
752 self.assert_not_terminated()?;
753 self.reject_read_only_resolved_write_path(path)?;
754 let content = content.into();
755 let new_size = content.len() as u64;
756 let existing = self.storage_stat(path)?;
757 self.check_write_file_limits_with_existing(path, existing.as_ref(), new_size)?;
758 self.filesystem.write_file(path, content)?;
759 self.update_filesystem_usage_cache_for_write(existing.as_ref(), new_size);
760 Ok(())
761 }
762
763 pub fn pwrite_file(
772 &mut self,
773 path: &str,
774 offset: u64,
775 content: impl Into<Vec<u8>>,
776 ) -> KernelResult<()> {
777 self.assert_not_terminated()?;
778 self.reject_read_only_resolved_write_path(path)?;
779 let content = content.into();
780 let existing = self.storage_stat(path)?;
781 let existing_size = existing.as_ref().map(|stat| stat.size).unwrap_or(0);
782 let end = offset.saturating_add(content.len() as u64);
783 self.check_write_file_limits_with_existing(
784 path,
785 existing.as_ref(),
786 existing_size.max(end),
787 )?;
788 self.filesystem.pwrite(path, content, offset)?;
789 self.update_filesystem_usage_cache_for_write(existing.as_ref(), existing_size.max(end));
790 Ok(())
791 }
792
793 pub fn write_file_for_process(
794 &mut self,
795 requester_driver: &str,
796 pid: u32,
797 path: &str,
798 content: impl Into<Vec<u8>>,
799 mode: Option<u32>,
800 ) -> KernelResult<()> {
801 self.assert_not_terminated()?;
802 self.assert_driver_owns(requester_driver, pid)?;
803 let existed = self.exists_internal(Some(pid), path)?;
804 let content = content.into();
805 let new_size = content.len() as u64;
806 self.reject_read_only_resolved_write_path(path)?;
807 let existing = self.storage_stat(path)?;
808 self.check_write_file_limits_with_existing(path, existing.as_ref(), new_size)?;
809 VirtualFileSystem::write_file_with_mode(&mut self.filesystem, path, content, mode)?;
810 self.update_filesystem_usage_cache_for_write(existing.as_ref(), new_size);
811 if !existed {
812 let umask = self.processes.get_umask(pid)?;
813 self.apply_creation_mode(path, mode.unwrap_or(0o666), umask)?;
814 }
815 Ok(())
816 }
817
818 pub fn create_dir(&mut self, path: &str) -> KernelResult<()> {
819 self.assert_not_terminated()?;
820 self.reject_read_only_entry_write_path(path)?;
821 self.check_create_dir_limits(path)?;
822 self.filesystem.create_dir(path)?;
823 self.update_filesystem_usage_cache_for_inode_create(0);
824 Ok(())
825 }
826
827 pub fn create_dir_for_process(
828 &mut self,
829 requester_driver: &str,
830 pid: u32,
831 path: &str,
832 mode: Option<u32>,
833 ) -> KernelResult<()> {
834 self.assert_not_terminated()?;
835 self.assert_driver_owns(requester_driver, pid)?;
836 let existed = self.exists_internal(Some(pid), path)?;
837 self.reject_read_only_entry_write_path(path)?;
838 self.check_create_dir_limits(path)?;
839 VirtualFileSystem::create_dir_with_mode(&mut self.filesystem, path, mode)?;
840 self.update_filesystem_usage_cache_for_inode_create(0);
841 if !existed {
842 let umask = self.processes.get_umask(pid)?;
843 self.apply_creation_mode(path, mode.unwrap_or(0o777), umask)?;
844 }
845 Ok(())
846 }
847
848 pub fn mkdir(&mut self, path: &str, recursive: bool) -> KernelResult<()> {
849 self.assert_not_terminated()?;
850 self.reject_read_only_entry_write_path(path)?;
851 let created_paths = self.missing_directory_paths(path, recursive)?;
852 self.check_mkdir_limits(path, recursive)?;
853 self.filesystem.mkdir(path, recursive)?;
854 self.update_filesystem_usage_cache_for_inode_creates(created_paths.len());
855 Ok(())
856 }
857
858 pub fn mkdir_for_process(
859 &mut self,
860 requester_driver: &str,
861 pid: u32,
862 path: &str,
863 recursive: bool,
864 mode: Option<u32>,
865 ) -> KernelResult<()> {
866 self.assert_not_terminated()?;
867 self.assert_driver_owns(requester_driver, pid)?;
868 let created_paths = self.missing_directory_paths(path, recursive)?;
869 self.reject_read_only_entry_write_path(path)?;
870 self.check_mkdir_limits(path, recursive)?;
871 VirtualFileSystem::mkdir_with_mode(&mut self.filesystem, path, recursive, mode)?;
872 if !created_paths.is_empty() {
873 let umask = self.processes.get_umask(pid)?;
874 let mode = mode.unwrap_or(0o777);
875 for created_path in &created_paths {
876 self.apply_creation_mode(created_path, mode, umask)?;
877 }
878 }
879 self.update_filesystem_usage_cache_for_inode_creates(created_paths.len());
880 Ok(())
881 }
882
883 pub fn umask(
884 &self,
885 requester_driver: &str,
886 pid: u32,
887 new_mask: Option<u32>,
888 ) -> KernelResult<u32> {
889 self.assert_driver_owns(requester_driver, pid)?;
890 match new_mask {
891 Some(mask) => Ok(self.processes.set_umask(pid, mask)?),
892 None => Ok(self.processes.get_umask(pid)?),
893 }
894 }
895
896 pub fn exists(&self, path: &str) -> KernelResult<bool> {
897 self.assert_not_terminated()?;
898 self.exists_internal(None, path)
899 }
900
901 pub fn exists_for_process(
902 &self,
903 requester_driver: &str,
904 pid: u32,
905 path: &str,
906 ) -> KernelResult<bool> {
907 self.assert_not_terminated()?;
908 self.assert_driver_owns(requester_driver, pid)?;
909 self.exists_internal(Some(pid), path)
910 }
911
912 pub fn stat(&mut self, path: &str) -> KernelResult<VirtualStat> {
913 self.assert_not_terminated()?;
914 self.stat_internal(None, path)
915 }
916
917 pub fn stat_for_process(
918 &mut self,
919 requester_driver: &str,
920 pid: u32,
921 path: &str,
922 ) -> KernelResult<VirtualStat> {
923 self.assert_not_terminated()?;
924 self.assert_driver_owns(requester_driver, pid)?;
925 self.stat_internal(Some(pid), path)
926 }
927
928 pub fn lstat(&self, path: &str) -> KernelResult<VirtualStat> {
929 self.assert_not_terminated()?;
930 self.lstat_internal(None, path)
931 }
932
933 pub fn lstat_for_process(
934 &self,
935 requester_driver: &str,
936 pid: u32,
937 path: &str,
938 ) -> KernelResult<VirtualStat> {
939 self.assert_not_terminated()?;
940 self.assert_driver_owns(requester_driver, pid)?;
941 self.lstat_internal(Some(pid), path)
942 }
943
944 pub fn read_link(&self, path: &str) -> KernelResult<String> {
945 self.assert_not_terminated()?;
946 self.read_link_internal(None, path)
947 }
948
949 pub fn read_link_for_process(
950 &self,
951 requester_driver: &str,
952 pid: u32,
953 path: &str,
954 ) -> KernelResult<String> {
955 self.assert_not_terminated()?;
956 self.assert_driver_owns(requester_driver, pid)?;
957 self.read_link_internal(Some(pid), path)
958 }
959
960 pub fn read_dir(&mut self, path: &str) -> KernelResult<Vec<String>> {
961 self.assert_not_terminated()?;
962 let entries = self.read_dir_internal(None, path)?;
963 self.resources.check_readdir_entries(entries.len())?;
964 Ok(entries)
965 }
966
967 pub fn read_dir_for_process(
968 &mut self,
969 requester_driver: &str,
970 pid: u32,
971 path: &str,
972 ) -> KernelResult<Vec<String>> {
973 self.assert_not_terminated()?;
974 self.assert_driver_owns(requester_driver, pid)?;
975 let entries = self.read_dir_internal(Some(pid), path)?;
976 self.resources.check_readdir_entries(entries.len())?;
977 Ok(entries)
978 }
979
980 pub fn read_dir_with_types_for_process(
981 &mut self,
982 requester_driver: &str,
983 pid: u32,
984 path: &str,
985 ) -> KernelResult<Vec<VirtualDirEntry>> {
986 self.assert_not_terminated()?;
987 self.assert_driver_owns(requester_driver, pid)?;
988 let entries = self.read_dir_with_types_internal(Some(pid), path)?;
989 self.resources.check_readdir_entries(entries.len())?;
990 Ok(entries)
991 }
992
993 pub fn read_dir_with_types(&mut self, path: &str) -> KernelResult<Vec<VirtualDirEntry>> {
1000 self.assert_not_terminated()?;
1001 let names = self.read_dir_internal(None, path)?;
1002 self.resources.check_readdir_entries(names.len())?;
1003 let mut entries = Vec::with_capacity(names.len());
1004 for name in names {
1005 let child = normalize_path(&format!("{path}/{name}"));
1006 let stat = self.lstat_internal(None, &child)?;
1007 entries.push(VirtualDirEntry {
1008 name,
1009 is_directory: stat.is_directory,
1010 is_symbolic_link: stat.is_symbolic_link,
1011 });
1012 }
1013 Ok(entries)
1014 }
1015
1016 pub fn remove_file(&mut self, path: &str) -> KernelResult<()> {
1017 self.assert_not_terminated()?;
1018 self.reject_read_only_entry_write_path(path)?;
1019 let removed = self.storage_lstat(path)?;
1020 self.filesystem.remove_file(path)?;
1021 self.update_filesystem_usage_cache_for_remove(removed.as_ref());
1022 Ok(())
1023 }
1024
1025 pub fn remove_dir(&mut self, path: &str) -> KernelResult<()> {
1026 self.assert_not_terminated()?;
1027 self.reject_read_only_entry_write_path(path)?;
1028 let removed = self.storage_lstat(path)?;
1029 self.filesystem.remove_dir(path)?;
1030 if removed.as_ref().is_some_and(|stat| stat.is_directory) {
1031 self.update_filesystem_usage_cache_for_inode_delete(0);
1032 }
1033 Ok(())
1034 }
1035
1036 pub fn rename(&mut self, old_path: &str, new_path: &str) -> KernelResult<()> {
1037 self.assert_not_terminated()?;
1038 self.reject_read_only_entry_write_path(old_path)?;
1039 self.reject_read_only_entry_write_path(new_path)?;
1040 self.check_rename_copy_up_limits(old_path, new_path)?;
1041 self.filesystem.rename(old_path, new_path)?;
1042 self.invalidate_filesystem_usage_cache();
1046 Ok(())
1047 }
1048
1049 pub fn realpath(&self, path: &str) -> KernelResult<String> {
1050 self.assert_not_terminated()?;
1051 self.realpath_internal(None, path)
1052 }
1053
1054 pub fn realpath_for_process(
1055 &self,
1056 requester_driver: &str,
1057 pid: u32,
1058 path: &str,
1059 ) -> KernelResult<String> {
1060 self.assert_not_terminated()?;
1061 self.assert_driver_owns(requester_driver, pid)?;
1062 self.realpath_internal(Some(pid), path)
1063 }
1064
1065 pub fn symlink(&mut self, target: &str, link_path: &str) -> KernelResult<()> {
1066 self.assert_not_terminated()?;
1067 if is_proc_path(target) {
1068 self.filesystem
1069 .check_virtual_path(FsOperation::Write, link_path)
1070 .map_err(KernelError::from)?;
1071 return Err(read_only_filesystem_error(link_path));
1072 }
1073 self.reject_read_only_entry_write_path(link_path)?;
1074 self.check_symlink_limits(target, link_path)?;
1075 self.filesystem.symlink(target, link_path)?;
1076 self.update_filesystem_usage_cache_for_inode_create(target.len() as u64);
1077 Ok(())
1078 }
1079
1080 pub fn chmod(&mut self, path: &str, mode: u32) -> KernelResult<()> {
1081 self.assert_not_terminated()?;
1082 self.reject_read_only_resolved_write_path(path)?;
1083 Ok(self.filesystem.chmod(path, mode)?)
1084 }
1085
1086 pub fn link(&mut self, old_path: &str, new_path: &str) -> KernelResult<()> {
1087 self.assert_not_terminated()?;
1088 if is_proc_path(old_path) {
1089 self.filesystem
1090 .check_virtual_path(FsOperation::Write, new_path)
1091 .map_err(KernelError::from)?;
1092 return Err(read_only_filesystem_error(new_path));
1093 }
1094 self.reject_read_only_resolved_write_path(old_path)?;
1095 self.reject_read_only_entry_write_path(new_path)?;
1096 self.filesystem.link(old_path, new_path)?;
1097 Ok(())
1100 }
1101
1102 pub fn chown(&mut self, path: &str, uid: u32, gid: u32) -> KernelResult<()> {
1103 self.assert_not_terminated()?;
1104 self.reject_read_only_resolved_write_path(path)?;
1105 Ok(self.filesystem.chown(path, uid, gid)?)
1106 }
1107
1108 pub fn utimes(&mut self, path: &str, atime_ms: u64, mtime_ms: u64) -> KernelResult<()> {
1109 self.utimes_spec(
1110 path,
1111 VirtualUtimeSpec::Set(VirtualTimeSpec::from_millis(atime_ms)),
1112 VirtualUtimeSpec::Set(VirtualTimeSpec::from_millis(mtime_ms)),
1113 )
1114 }
1115
1116 pub fn utimes_spec(
1117 &mut self,
1118 path: &str,
1119 atime: VirtualUtimeSpec,
1120 mtime: VirtualUtimeSpec,
1121 ) -> KernelResult<()> {
1122 self.assert_not_terminated()?;
1123 self.reject_read_only_resolved_write_path(path)?;
1124 Ok(self.filesystem.utimes_spec(path, atime, mtime, true)?)
1125 }
1126
1127 pub fn lutimes(
1128 &mut self,
1129 path: &str,
1130 atime: VirtualUtimeSpec,
1131 mtime: VirtualUtimeSpec,
1132 ) -> KernelResult<()> {
1133 self.assert_not_terminated()?;
1134 self.reject_read_only_entry_write_path(path)?;
1135 Ok(self.filesystem.utimes_spec(path, atime, mtime, false)?)
1136 }
1137
1138 pub fn futimes(
1139 &mut self,
1140 requester_driver: &str,
1141 pid: u32,
1142 fd: u32,
1143 atime: VirtualUtimeSpec,
1144 mtime: VirtualUtimeSpec,
1145 ) -> KernelResult<()> {
1146 self.assert_not_terminated()?;
1147 let path = self
1148 .description_for_fd(requester_driver, pid, fd)?
1149 .path()
1150 .to_owned();
1151 self.reject_read_only_resolved_write_path(&path)?;
1152 Ok(self.filesystem.utimes_spec(&path, atime, mtime, true)?)
1153 }
1154
1155 pub fn truncate(&mut self, path: &str, length: u64) -> KernelResult<()> {
1156 self.assert_not_terminated()?;
1157 self.reject_read_only_resolved_write_path(path)?;
1158 let existing = self.storage_stat(path)?;
1159 self.check_truncate_limits_with_existing(path, existing.as_ref(), length)?;
1160 self.filesystem.truncate(path, length)?;
1161 self.update_filesystem_usage_cache_for_write(existing.as_ref(), length);
1162 Ok(())
1163 }
1164
1165 pub fn list_processes(&self) -> BTreeMap<u32, ProcessInfo> {
1166 self.processes.list_processes()
1167 }
1168
1169 pub fn zombie_timer_count(&self) -> usize {
1170 self.processes.zombie_timer_count()
1171 }
1172
1173 pub fn spawn_process(
1174 &mut self,
1175 command: &str,
1176 args: Vec<String>,
1177 options: SpawnOptions,
1178 ) -> KernelResult<KernelProcessHandle> {
1179 self.assert_not_terminated()?;
1180 if let (Some(requester), Some(parent_pid)) =
1181 (options.requester_driver.as_deref(), options.parent_pid)
1182 {
1183 self.assert_driver_owns(requester, parent_pid)?;
1184 }
1185
1186 let cwd = options.cwd.clone().unwrap_or_else(|| self.cwd.clone());
1187 let resolved = self.resolve_spawn_command(command, &args, &cwd)?;
1188
1189 self.resources
1190 .check_process_argv_bytes(&resolved.command, &resolved.args)?;
1191 self.resources
1192 .check_process_env_bytes(&self.env, &options.env)?;
1193
1194 let mut env = self.env.clone();
1195 env.extend(options.env.clone());
1196 check_command_execution(
1197 &self.vm_id,
1198 &self.permissions,
1199 &resolved.command,
1200 &resolved.args,
1201 Some(&cwd),
1202 &env,
1203 )?;
1204
1205 let inherited_fds = {
1206 let tables = lock_or_recover(&self.fd_tables);
1207 options
1208 .parent_pid
1209 .and_then(|pid| tables.get(pid).map(ProcessFdTable::len))
1210 .unwrap_or(3)
1211 };
1212 self.resources
1213 .check_process_spawn(&self.resource_snapshot(), inherited_fds)?;
1214
1215 self.register_process(
1216 resolved.driver.name().to_owned(),
1217 resolved.command,
1218 resolved.args,
1219 ProcessContext {
1220 pid: 0,
1221 ppid: options.parent_pid.unwrap_or(0),
1222 env,
1223 cwd,
1224 umask: DEFAULT_PROCESS_UMASK,
1225 fds: Default::default(),
1226 identity: self.users.identity(),
1227 blocked_signals: SignalSet::empty(),
1228 pending_signals: SignalSet::empty(),
1229 },
1230 options.requester_driver.as_deref(),
1231 )
1232 }
1233
1234 pub fn create_virtual_process(
1235 &mut self,
1236 requester_driver: &str,
1237 driver: &str,
1238 command: &str,
1239 args: Vec<String>,
1240 options: VirtualProcessOptions,
1241 ) -> KernelResult<KernelProcessHandle> {
1242 self.assert_not_terminated()?;
1243 if let Some(parent_pid) = options.parent_pid {
1244 self.assert_driver_owns(requester_driver, parent_pid)?;
1245 }
1246
1247 let cwd = options.cwd.clone().unwrap_or_else(|| self.cwd.clone());
1248 self.resources.check_process_argv_bytes(command, &args)?;
1249 self.resources
1250 .check_process_env_bytes(&self.env, &options.env)?;
1251
1252 let mut env = self.env.clone();
1253 env.extend(options.env.clone());
1254 check_command_execution(
1255 &self.vm_id,
1256 &self.permissions,
1257 command,
1258 &args,
1259 Some(&cwd),
1260 &env,
1261 )?;
1262
1263 let inherited_fds = {
1264 let tables = lock_or_recover(&self.fd_tables);
1265 options
1266 .parent_pid
1267 .and_then(|pid| tables.get(pid).map(ProcessFdTable::len))
1268 .unwrap_or(3)
1269 };
1270 self.resources
1271 .check_process_spawn(&self.resource_snapshot(), inherited_fds)?;
1272
1273 self.register_process(
1274 String::from(driver),
1275 String::from(command),
1276 args,
1277 ProcessContext {
1278 pid: 0,
1279 ppid: options.parent_pid.unwrap_or(0),
1280 env,
1281 cwd,
1282 umask: DEFAULT_PROCESS_UMASK,
1283 fds: Default::default(),
1284 identity: self.users.identity(),
1285 blocked_signals: SignalSet::empty(),
1286 pending_signals: SignalSet::empty(),
1287 },
1288 Some(requester_driver),
1289 )
1290 }
1291
1292 pub fn read_process_stdin(
1293 &mut self,
1294 requester_driver: &str,
1295 pid: u32,
1296 length: usize,
1297 timeout: Option<Duration>,
1298 ) -> KernelResult<Option<Vec<u8>>> {
1299 self.fd_read_with_timeout_result(requester_driver, pid, 0, length, timeout)
1300 }
1301
1302 pub fn write_process_stdout(
1303 &mut self,
1304 requester_driver: &str,
1305 pid: u32,
1306 data: &[u8],
1307 ) -> KernelResult<usize> {
1308 self.fd_write(requester_driver, pid, 1, data)
1309 }
1310
1311 pub fn write_process_stderr(
1312 &mut self,
1313 requester_driver: &str,
1314 pid: u32,
1315 data: &[u8],
1316 ) -> KernelResult<usize> {
1317 self.fd_write(requester_driver, pid, 2, data)
1318 }
1319
1320 pub fn exit_process(
1321 &mut self,
1322 requester_driver: &str,
1323 pid: u32,
1324 exit_code: i32,
1325 ) -> KernelResult<()> {
1326 self.assert_driver_owns(requester_driver, pid)?;
1327 self.processes.mark_exited(pid, exit_code);
1328 Ok(())
1329 }
1330
1331 fn register_process(
1332 &mut self,
1333 driver_name: String,
1334 command: String,
1335 args: Vec<String>,
1336 mut ctx: ProcessContext,
1337 requester_driver: Option<&str>,
1338 ) -> KernelResult<KernelProcessHandle> {
1339 let pid = self.processes.allocate_pid()?;
1340 ctx.pid = pid;
1341
1342 {
1343 let mut tables = lock_or_recover(&self.fd_tables);
1344 if ctx.ppid != 0 {
1345 let parent_pid = ctx.ppid;
1346 tables.fork(parent_pid, pid);
1347 } else {
1348 tables.create(pid);
1349 }
1350 }
1351
1352 let process = Arc::new(StubDriverProcess::default());
1353 self.processes.register(
1354 pid,
1355 driver_name.clone(),
1356 command,
1357 args,
1358 ctx,
1359 process.clone(),
1360 );
1361
1362 let mut owners = lock_or_recover(&self.driver_pids);
1363 owners.entry(driver_name.clone()).or_default().insert(pid);
1364 if let Some(requester) = requester_driver {
1365 owners
1366 .entry(String::from(requester))
1367 .or_default()
1368 .insert(pid);
1369 }
1370
1371 Ok(KernelProcessHandle {
1372 pid,
1373 driver: driver_name,
1374 process,
1375 })
1376 }
1377
1378 pub fn waitpid(&mut self, pid: u32) -> KernelResult<WaitPidResult> {
1379 let (pid, status) = self.processes.waitpid(pid)?;
1380 self.cleanup_process_resources(pid);
1381 Ok(WaitPidResult { pid, status })
1382 }
1383
1384 pub fn waitpid_with_options(
1385 &mut self,
1386 requester_driver: &str,
1387 waiter_pid: u32,
1388 pid: i32,
1389 flags: WaitPidFlags,
1390 ) -> KernelResult<Option<WaitPidEventResult>> {
1391 self.assert_driver_owns(requester_driver, waiter_pid)?;
1392 let result = self.processes.waitpid_for(waiter_pid, pid, flags)?;
1393 Ok(result.map(|result| self.finish_waitpid_event(result)))
1394 }
1395
1396 pub fn wait_and_reap(&mut self, pid: u32) -> KernelResult<(u32, i32)> {
1397 let result = self.waitpid(pid)?;
1398 Ok((result.pid, result.status))
1399 }
1400
1401 pub fn open_pipe(&mut self, requester_driver: &str, pid: u32) -> KernelResult<(u32, u32)> {
1402 self.assert_not_terminated()?;
1403 self.assert_driver_owns(requester_driver, pid)?;
1404 self.resources
1405 .check_pipe_allocation(&self.resource_snapshot())?;
1406 let mut tables = lock_or_recover(&self.fd_tables);
1407 let table = tables
1408 .get_mut(pid)
1409 .ok_or_else(|| KernelError::no_such_process(pid))?;
1410 Ok(self.pipes.create_pipe_fds(table)?)
1411 }
1412
1413 pub fn open_pty(
1414 &mut self,
1415 requester_driver: &str,
1416 pid: u32,
1417 ) -> KernelResult<(u32, u32, String)> {
1418 self.assert_not_terminated()?;
1419 self.assert_driver_owns(requester_driver, pid)?;
1420 self.resources
1421 .check_pty_allocation(&self.resource_snapshot())?;
1422 let mut tables = lock_or_recover(&self.fd_tables);
1423 let table = tables
1424 .get_mut(pid)
1425 .ok_or_else(|| KernelError::no_such_process(pid))?;
1426 Ok(self.ptys.create_pty_fds(table)?)
1427 }
1428
1429 pub fn socket_create(
1430 &mut self,
1431 requester_driver: &str,
1432 pid: u32,
1433 spec: SocketSpec,
1434 ) -> KernelResult<SocketId> {
1435 self.assert_not_terminated()?;
1436 self.assert_driver_owns(requester_driver, pid)?;
1437 self.resources
1438 .check_socket_allocation(&self.resource_snapshot())?;
1439 Ok(self.sockets.allocate(pid, spec).id())
1440 }
1441
1442 pub fn set_socket_readiness_sink<S>(&mut self, sink: Option<S>)
1443 where
1444 S: Fn(SocketReadiness) + Send + Sync + 'static,
1445 {
1446 self.sockets.set_readiness_sink(sink);
1447 }
1448
1449 pub fn socket_get(&self, socket_id: SocketId) -> Option<SocketRecord> {
1450 self.sockets.get(socket_id)
1451 }
1452
1453 pub fn socket_records_for_pid(&self, pid: u32) -> Vec<SocketRecord> {
1454 self.sockets.records_for_owner(pid)
1455 }
1456
1457 pub fn socket_bind_inet(
1458 &mut self,
1459 requester_driver: &str,
1460 pid: u32,
1461 socket_id: SocketId,
1462 address: InetSocketAddress,
1463 ) -> KernelResult<()> {
1464 self.assert_not_terminated()?;
1465 self.assert_driver_owns(requester_driver, pid)?;
1466 let existing = self
1467 .sockets
1468 .get(socket_id)
1469 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1470 if existing.owner_pid() != pid {
1471 return Err(KernelError::permission_denied(format!(
1472 "process {pid} does not own socket {socket_id}"
1473 )));
1474 }
1475 check_network_access(
1476 &self.vm_id,
1477 &self.permissions,
1478 NetworkOperation::Listen,
1479 &format_tcp_resource(address.host(), address.port()),
1480 )?;
1481
1482 self.sockets.bind_inet(socket_id, address)?;
1483 self.poll_notifier.notify();
1484 Ok(())
1485 }
1486
1487 pub fn socket_bind_unix(
1488 &mut self,
1489 requester_driver: &str,
1490 pid: u32,
1491 socket_id: SocketId,
1492 path: impl Into<String>,
1493 ) -> KernelResult<()> {
1494 self.assert_not_terminated()?;
1495 self.assert_driver_owns(requester_driver, pid)?;
1496 let existing = self
1497 .sockets
1498 .get(socket_id)
1499 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1500 if existing.owner_pid() != pid {
1501 return Err(KernelError::permission_denied(format!(
1502 "process {pid} does not own socket {socket_id}"
1503 )));
1504 }
1505
1506 self.sockets
1507 .bind_unix(socket_id, normalize_path(&path.into()))?;
1508 self.poll_notifier.notify();
1509 Ok(())
1510 }
1511
1512 pub fn socket_listen(
1513 &mut self,
1514 requester_driver: &str,
1515 pid: u32,
1516 socket_id: SocketId,
1517 backlog: usize,
1518 ) -> KernelResult<()> {
1519 self.assert_not_terminated()?;
1520 self.assert_driver_owns(requester_driver, pid)?;
1521 let existing = self
1522 .sockets
1523 .get(socket_id)
1524 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1525 if existing.owner_pid() != pid {
1526 return Err(KernelError::permission_denied(format!(
1527 "process {pid} does not own socket {socket_id}"
1528 )));
1529 }
1530 if let Some(address) = existing.local_address() {
1531 check_network_access(
1532 &self.vm_id,
1533 &self.permissions,
1534 NetworkOperation::Listen,
1535 &format_tcp_resource(address.host(), address.port()),
1536 )?;
1537 }
1538
1539 self.sockets.listen(socket_id, backlog)?;
1540 self.poll_notifier.notify();
1541 Ok(())
1542 }
1543
1544 pub fn socket_queue_incoming_tcp_connection(
1545 &mut self,
1546 requester_driver: &str,
1547 pid: u32,
1548 listener_socket_id: SocketId,
1549 peer_address: InetSocketAddress,
1550 ) -> KernelResult<()> {
1551 self.assert_not_terminated()?;
1552 self.assert_driver_owns(requester_driver, pid)?;
1553 let existing = self.sockets.get(listener_socket_id).ok_or_else(|| {
1554 KernelError::new("ENOENT", format!("no such socket {listener_socket_id}"))
1555 })?;
1556 if existing.owner_pid() != pid {
1557 return Err(KernelError::permission_denied(format!(
1558 "process {pid} does not own socket {listener_socket_id}"
1559 )));
1560 }
1561
1562 self.sockets
1563 .enqueue_incoming_tcp_connection(listener_socket_id, peer_address)?;
1564 self.poll_notifier.notify();
1565 Ok(())
1566 }
1567
1568 pub fn socket_accept(
1569 &mut self,
1570 requester_driver: &str,
1571 pid: u32,
1572 listener_socket_id: SocketId,
1573 ) -> KernelResult<SocketId> {
1574 self.assert_not_terminated()?;
1575 self.assert_driver_owns(requester_driver, pid)?;
1576 let existing = self.sockets.get(listener_socket_id).ok_or_else(|| {
1577 KernelError::new("ENOENT", format!("no such socket {listener_socket_id}"))
1578 })?;
1579 if existing.owner_pid() != pid {
1580 return Err(KernelError::permission_denied(format!(
1581 "process {pid} does not own socket {listener_socket_id}"
1582 )));
1583 }
1584
1585 let snapshot = self.resource_snapshot();
1586 self.resources.check_socket_allocation(&snapshot)?;
1587 self.resources.check_socket_state_transition(
1588 &snapshot,
1589 SocketState::Created,
1590 SocketState::Connected,
1591 )?;
1592
1593 let socket_id = self.sockets.accept(listener_socket_id)?.id();
1594 self.poll_notifier.notify();
1595 Ok(socket_id)
1596 }
1597
1598 pub fn socket_connect_pair(
1599 &mut self,
1600 requester_driver: &str,
1601 pid: u32,
1602 socket_id: SocketId,
1603 peer_socket_id: SocketId,
1604 ) -> KernelResult<()> {
1605 self.assert_not_terminated()?;
1606 self.assert_driver_owns(requester_driver, pid)?;
1607 let existing = self
1608 .sockets
1609 .get(socket_id)
1610 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1611 if existing.owner_pid() != pid {
1612 return Err(KernelError::permission_denied(format!(
1613 "process {pid} does not own socket {socket_id}"
1614 )));
1615 }
1616
1617 let peer = self.sockets.get(peer_socket_id).ok_or_else(|| {
1618 KernelError::new("ENOENT", format!("no such socket {peer_socket_id}"))
1619 })?;
1620 self.assert_driver_owns(requester_driver, peer.owner_pid())?;
1621
1622 let mut snapshot = self.resource_snapshot();
1623 for current_state in [existing.state(), peer.state()] {
1624 self.resources.check_socket_state_transition(
1625 &snapshot,
1626 current_state,
1627 SocketState::Connected,
1628 )?;
1629 if !current_state.counts_as_connection() {
1630 snapshot.socket_connections = snapshot.socket_connections.saturating_add(1);
1631 }
1632 }
1633
1634 self.sockets.connect_pair(socket_id, peer_socket_id)?;
1635 self.poll_notifier.notify();
1636 Ok(())
1637 }
1638
1639 pub fn socket_connect_unix(
1640 &mut self,
1641 requester_driver: &str,
1642 pid: u32,
1643 socket_id: SocketId,
1644 target_path: impl Into<String>,
1645 ) -> KernelResult<()> {
1646 self.assert_not_terminated()?;
1647 self.assert_driver_owns(requester_driver, pid)?;
1648 let existing = self
1649 .sockets
1650 .get(socket_id)
1651 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1652 if existing.owner_pid() != pid {
1653 return Err(KernelError::permission_denied(format!(
1654 "process {pid} does not own socket {socket_id}"
1655 )));
1656 }
1657
1658 let target_path = normalize_path(&target_path.into());
1659 self.sockets
1660 .find_bound_unix_socket(&target_path)
1661 .ok_or_else(|| {
1662 KernelError::new(
1663 "ECONNREFUSED",
1664 format!("no listening socket bound at path {target_path}"),
1665 )
1666 })?;
1667
1668 let mut snapshot = self.resource_snapshot();
1669 self.resources.check_socket_allocation(&snapshot)?;
1670 for current_state in [existing.state(), SocketState::Created] {
1671 self.resources.check_socket_state_transition(
1672 &snapshot,
1673 current_state,
1674 SocketState::Connected,
1675 )?;
1676 if !current_state.counts_as_connection() {
1677 snapshot.socket_connections = snapshot.socket_connections.saturating_add(1);
1678 }
1679 }
1680
1681 self.sockets
1682 .connect_to_bound_unix_stream(socket_id, target_path)?;
1683 self.poll_notifier.notify();
1684 Ok(())
1685 }
1686
1687 pub fn socket_connect_inet_loopback(
1688 &mut self,
1689 requester_driver: &str,
1690 pid: u32,
1691 socket_id: SocketId,
1692 target_address: InetSocketAddress,
1693 ) -> KernelResult<()> {
1694 self.assert_not_terminated()?;
1695 self.assert_driver_owns(requester_driver, pid)?;
1696 let existing = self
1697 .sockets
1698 .get(socket_id)
1699 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1700 if existing.owner_pid() != pid {
1701 return Err(KernelError::permission_denied(format!(
1702 "process {pid} does not own socket {socket_id}"
1703 )));
1704 }
1705 check_network_access(
1706 &self.vm_id,
1707 &self.permissions,
1708 NetworkOperation::Http,
1709 &format_tcp_resource(target_address.host(), target_address.port()),
1710 )?;
1711 self.check_loopback_port_allowed(
1712 SocketSpec::tcp(),
1713 &target_address,
1714 "TCP loopback connect",
1715 )?;
1716
1717 self.sockets
1718 .find_bound_inet_socket(SocketSpec::tcp(), &target_address)
1719 .ok_or_else(|| {
1720 KernelError::new(
1721 "ECONNREFUSED",
1722 format!(
1723 "no listening socket bound at {}:{}",
1724 target_address.host(),
1725 target_address.port()
1726 ),
1727 )
1728 })?;
1729
1730 let mut snapshot = self.resource_snapshot();
1731 self.resources.check_socket_allocation(&snapshot)?;
1732 for current_state in [existing.state(), SocketState::Created] {
1733 self.resources.check_socket_state_transition(
1734 &snapshot,
1735 current_state,
1736 SocketState::Connected,
1737 )?;
1738 if !current_state.counts_as_connection() {
1739 snapshot.socket_connections = snapshot.socket_connections.saturating_add(1);
1740 }
1741 }
1742
1743 self.sockets
1744 .connect_to_bound_inet_stream(socket_id, target_address)?;
1745 self.poll_notifier.notify();
1746 Ok(())
1747 }
1748
1749 pub fn socket_send_to_inet_loopback(
1750 &mut self,
1751 requester_driver: &str,
1752 pid: u32,
1753 socket_id: SocketId,
1754 target_address: InetSocketAddress,
1755 data: &[u8],
1756 ) -> KernelResult<usize> {
1757 self.assert_not_terminated()?;
1758 self.assert_driver_owns(requester_driver, pid)?;
1759 let existing = self
1760 .sockets
1761 .get(socket_id)
1762 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1763 if existing.owner_pid() != pid {
1764 return Err(KernelError::permission_denied(format!(
1765 "process {pid} does not own socket {socket_id}"
1766 )));
1767 }
1768 if existing.spec() != SocketSpec::udp()
1769 || existing.state() != SocketState::Bound
1770 || existing.local_address().is_none()
1771 {
1772 self.sockets
1773 .check_send_to_bound_udp_socket(socket_id, target_address.clone())?;
1774 }
1775 check_network_access(
1776 &self.vm_id,
1777 &self.permissions,
1778 NetworkOperation::Http,
1779 &format_tcp_resource(target_address.host(), target_address.port()),
1780 )?;
1781 self.check_loopback_port_allowed(SocketSpec::udp(), &target_address, "UDP loopback send")?;
1782
1783 self.sockets
1784 .check_send_to_bound_udp_socket(socket_id, target_address.clone())?;
1785 self.resources
1786 .check_socket_datagram_enqueue(&self.resource_snapshot(), data.len())?;
1787 let written = self
1788 .sockets
1789 .send_to_bound_udp_socket(socket_id, target_address, data)?;
1790 if written > 0 {
1791 self.poll_notifier.notify();
1792 }
1793 Ok(written)
1794 }
1795
1796 fn check_loopback_port_allowed(
1797 &self,
1798 spec: SocketSpec,
1799 target_address: &InetSocketAddress,
1800 operation: &str,
1801 ) -> KernelResult<()> {
1802 if self
1803 .sockets
1804 .find_bound_inet_socket(spec, target_address)
1805 .is_some()
1806 || self.loopback_exempt_ports.contains(&target_address.port())
1807 {
1808 return Ok(());
1809 }
1810
1811 Err(KernelError::permission_denied(format!(
1812 "{operation} to {}:{} is not owned by this VM and is not loopback-exempt",
1813 target_address.host(),
1814 target_address.port()
1815 )))
1816 }
1817
1818 pub fn socket_recv_datagram(
1819 &mut self,
1820 requester_driver: &str,
1821 pid: u32,
1822 socket_id: SocketId,
1823 max_bytes: usize,
1824 ) -> KernelResult<Option<ReceivedDatagram>> {
1825 self.assert_not_terminated()?;
1826 self.assert_driver_owns(requester_driver, pid)?;
1827 let existing = self
1828 .sockets
1829 .get(socket_id)
1830 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1831 if existing.owner_pid() != pid {
1832 return Err(KernelError::permission_denied(format!(
1833 "process {pid} does not own socket {socket_id}"
1834 )));
1835 }
1836
1837 let result = self.sockets.recv_datagram(socket_id, max_bytes)?;
1838 if result.is_some() {
1839 self.poll_notifier.notify();
1840 }
1841 Ok(result)
1842 }
1843
1844 pub fn socket_set_datagram_option(
1845 &mut self,
1846 requester_driver: &str,
1847 pid: u32,
1848 socket_id: SocketId,
1849 option: DatagramSocketOption,
1850 enabled: bool,
1851 ) -> KernelResult<()> {
1852 self.assert_not_terminated()?;
1853 self.assert_driver_owns(requester_driver, pid)?;
1854 let existing = self
1855 .sockets
1856 .get(socket_id)
1857 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1858 if existing.owner_pid() != pid {
1859 return Err(KernelError::permission_denied(format!(
1860 "process {pid} does not own socket {socket_id}"
1861 )));
1862 }
1863
1864 self.sockets
1865 .set_datagram_socket_option(socket_id, option, enabled)?;
1866 self.poll_notifier.notify();
1867 Ok(())
1868 }
1869
1870 pub fn socket_add_membership(
1871 &mut self,
1872 requester_driver: &str,
1873 pid: u32,
1874 socket_id: SocketId,
1875 membership: SocketMulticastMembership,
1876 ) -> KernelResult<()> {
1877 self.assert_not_terminated()?;
1878 self.assert_driver_owns(requester_driver, pid)?;
1879 let existing = self
1880 .sockets
1881 .get(socket_id)
1882 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1883 if existing.owner_pid() != pid {
1884 return Err(KernelError::permission_denied(format!(
1885 "process {pid} does not own socket {socket_id}"
1886 )));
1887 }
1888
1889 self.sockets
1890 .add_multicast_membership(socket_id, membership)?;
1891 self.poll_notifier.notify();
1892 Ok(())
1893 }
1894
1895 pub fn socket_drop_membership(
1896 &mut self,
1897 requester_driver: &str,
1898 pid: u32,
1899 socket_id: SocketId,
1900 membership: SocketMulticastMembership,
1901 ) -> KernelResult<()> {
1902 self.assert_not_terminated()?;
1903 self.assert_driver_owns(requester_driver, pid)?;
1904 let existing = self
1905 .sockets
1906 .get(socket_id)
1907 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1908 if existing.owner_pid() != pid {
1909 return Err(KernelError::permission_denied(format!(
1910 "process {pid} does not own socket {socket_id}"
1911 )));
1912 }
1913
1914 self.sockets
1915 .drop_multicast_membership(socket_id, membership)?;
1916 self.poll_notifier.notify();
1917 Ok(())
1918 }
1919
1920 pub fn socket_set_state(
1921 &mut self,
1922 requester_driver: &str,
1923 pid: u32,
1924 socket_id: SocketId,
1925 state: SocketState,
1926 ) -> KernelResult<()> {
1927 self.assert_not_terminated()?;
1928 self.assert_driver_owns(requester_driver, pid)?;
1929 let existing = self
1930 .sockets
1931 .get(socket_id)
1932 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1933 if existing.owner_pid() != pid {
1934 return Err(KernelError::permission_denied(format!(
1935 "process {pid} does not own socket {socket_id}"
1936 )));
1937 }
1938
1939 self.resources.check_socket_state_transition(
1940 &self.resource_snapshot(),
1941 existing.state(),
1942 state,
1943 )?;
1944 self.sockets.update_state(socket_id, state)?;
1945 self.poll_notifier.notify();
1946 Ok(())
1947 }
1948
1949 pub fn socket_write(
1950 &mut self,
1951 requester_driver: &str,
1952 pid: u32,
1953 socket_id: SocketId,
1954 data: &[u8],
1955 ) -> KernelResult<usize> {
1956 self.assert_not_terminated()?;
1957 self.assert_driver_owns(requester_driver, pid)?;
1958 let existing = self
1959 .sockets
1960 .get(socket_id)
1961 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1962 if existing.owner_pid() != pid {
1963 return Err(KernelError::permission_denied(format!(
1964 "process {pid} does not own socket {socket_id}"
1965 )));
1966 }
1967
1968 self.sockets.check_write(socket_id)?;
1969 self.resources
1970 .check_socket_buffer_growth(&self.resource_snapshot(), data.len())?;
1971 let written = self.sockets.write(socket_id, data)?;
1972 if written > 0 {
1973 self.poll_notifier.notify();
1974 }
1975 Ok(written)
1976 }
1977
1978 pub fn socket_read(
1979 &mut self,
1980 requester_driver: &str,
1981 pid: u32,
1982 socket_id: SocketId,
1983 max_bytes: usize,
1984 ) -> KernelResult<Option<Vec<u8>>> {
1985 self.assert_not_terminated()?;
1986 self.assert_driver_owns(requester_driver, pid)?;
1987 let existing = self
1988 .sockets
1989 .get(socket_id)
1990 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
1991 if existing.owner_pid() != pid {
1992 return Err(KernelError::permission_denied(format!(
1993 "process {pid} does not own socket {socket_id}"
1994 )));
1995 }
1996
1997 let result = self.sockets.read(socket_id, max_bytes)?;
1998 if result.is_some() {
1999 self.poll_notifier.notify();
2000 }
2001 Ok(result)
2002 }
2003
2004 pub fn socket_shutdown(
2005 &mut self,
2006 requester_driver: &str,
2007 pid: u32,
2008 socket_id: SocketId,
2009 how: SocketShutdown,
2010 ) -> KernelResult<()> {
2011 self.assert_not_terminated()?;
2012 self.assert_driver_owns(requester_driver, pid)?;
2013 let existing = self
2014 .sockets
2015 .get(socket_id)
2016 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
2017 if existing.owner_pid() != pid {
2018 return Err(KernelError::permission_denied(format!(
2019 "process {pid} does not own socket {socket_id}"
2020 )));
2021 }
2022
2023 self.sockets.shutdown(socket_id, how)?;
2024 self.poll_notifier.notify();
2025 Ok(())
2026 }
2027
2028 pub fn socket_close(
2029 &mut self,
2030 requester_driver: &str,
2031 pid: u32,
2032 socket_id: SocketId,
2033 ) -> KernelResult<()> {
2034 self.assert_not_terminated()?;
2035 self.assert_driver_owns(requester_driver, pid)?;
2036 let existing = self
2037 .sockets
2038 .get(socket_id)
2039 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
2040 if existing.owner_pid() != pid {
2041 return Err(KernelError::permission_denied(format!(
2042 "process {pid} does not own socket {socket_id}"
2043 )));
2044 }
2045
2046 self.sockets.remove(socket_id)?;
2047 self.poll_notifier.notify();
2048 Ok(())
2049 }
2050
2051 pub fn fd_open(
2052 &mut self,
2053 requester_driver: &str,
2054 pid: u32,
2055 path: &str,
2056 flags: u32,
2057 mode: Option<u32>,
2058 ) -> KernelResult<u32> {
2059 self.assert_not_terminated()?;
2060 self.assert_driver_owns(requester_driver, pid)?;
2061 if let Some(existing_fd) = parse_dev_fd_path(path)? {
2062 {
2063 let tables = lock_or_recover(&self.fd_tables);
2064 let table = tables
2065 .get(pid)
2066 .ok_or_else(|| KernelError::no_such_process(pid))?;
2067 table
2068 .get(existing_fd)
2069 .ok_or_else(|| KernelError::bad_file_descriptor(existing_fd))?;
2070 }
2071 self.resources
2072 .check_fd_allocation(&self.resource_snapshot(), 1)?;
2073 let mut tables = lock_or_recover(&self.fd_tables);
2074 let table = tables
2075 .get_mut(pid)
2076 .ok_or_else(|| KernelError::no_such_process(pid))?;
2077 let entry = table
2078 .get(existing_fd)
2079 .cloned()
2080 .ok_or_else(|| KernelError::bad_file_descriptor(existing_fd))?;
2081 return Ok(table.dup_with_status_flags(
2082 existing_fd,
2083 Some(entry.status_flags | (flags & O_NONBLOCK)),
2084 )?);
2085 }
2086
2087 if let Some(proc_node) = self.resolve_proc_node(path, Some(pid))? {
2088 if open_requires_write_access(flags) {
2089 self.filesystem
2090 .check_virtual_path(FsOperation::Write, path)
2091 .map_err(KernelError::from)?;
2092 return Err(read_only_filesystem_error(path));
2093 }
2094
2095 if matches!(
2096 proc_node,
2097 ProcNode::SelfLink { .. }
2098 | ProcNode::PidCwdLink { .. }
2099 | ProcNode::PidFdLink { .. }
2100 ) {
2101 let target = self.proc_symlink_target(&proc_node)?;
2102 return self.fd_open(requester_driver, pid, &target, flags, mode);
2103 }
2104
2105 self.filesystem
2106 .check_virtual_path(FsOperation::Read, path)
2107 .map_err(KernelError::from)?;
2108 self.resources
2109 .check_fd_allocation(&self.resource_snapshot(), 1)?;
2110 let mut tables = lock_or_recover(&self.fd_tables);
2111 let table = tables
2112 .get_mut(pid)
2113 .ok_or_else(|| KernelError::no_such_process(pid))?;
2114 return Ok(table.open_with_details(
2115 &self.proc_canonical_path(&proc_node),
2116 flags,
2117 proc_filetype(&proc_node),
2118 None,
2119 )?);
2120 }
2121
2122 if open_requires_write_access(flags) {
2123 self.reject_read_only_resolved_write_path(path)?;
2124 }
2125 let existed = if flags & O_CREAT != 0 {
2126 self.exists_internal(Some(pid), path)?
2127 } else {
2128 false
2129 };
2130 let (filetype, lock_target) = self.prepare_fd_open(path, flags, mode)?;
2131 if flags & O_CREAT != 0 && !existed {
2132 let umask = self.processes.get_umask(pid)?;
2133 self.apply_creation_mode(path, mode.unwrap_or(0o666), umask)?;
2134 }
2135 self.resources
2136 .check_fd_allocation(&self.resource_snapshot(), 1)?;
2137 let mut tables = lock_or_recover(&self.fd_tables);
2138 let table = tables
2139 .get_mut(pid)
2140 .ok_or_else(|| KernelError::no_such_process(pid))?;
2141 Ok(table.open_with_details(path, flags, filetype, lock_target)?)
2142 }
2143
2144 pub fn fd_read(
2145 &mut self,
2146 requester_driver: &str,
2147 pid: u32,
2148 fd: u32,
2149 length: usize,
2150 ) -> KernelResult<Vec<u8>> {
2151 Ok(self
2152 .fd_read_with_timeout_result(requester_driver, pid, fd, length, None)?
2153 .unwrap_or_default())
2154 }
2155
2156 pub fn fd_read_with_timeout_result(
2157 &mut self,
2158 requester_driver: &str,
2159 pid: u32,
2160 fd: u32,
2161 length: usize,
2162 timeout: Option<Duration>,
2163 ) -> KernelResult<Option<Vec<u8>>> {
2164 self.assert_driver_owns(requester_driver, pid)?;
2165 let entry = {
2166 let tables = lock_or_recover(&self.fd_tables);
2167 tables
2168 .get(pid)
2169 .and_then(|table| table.get(fd))
2170 .cloned()
2171 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2172 };
2173
2174 if self.pipes.is_pipe(entry.description.id()) {
2175 return Ok(self.pipes.read_with_timeout(
2176 entry.description.id(),
2177 length,
2178 if entry.status_flags & O_NONBLOCK != 0 {
2179 Some(Duration::ZERO)
2180 } else {
2181 timeout.or_else(|| self.blocking_read_timeout())
2182 },
2183 )?);
2184 }
2185
2186 if self.ptys.is_pty(entry.description.id()) {
2187 return Ok(self.ptys.read_with_timeout(
2188 entry.description.id(),
2189 length,
2190 if entry.status_flags & O_NONBLOCK != 0 {
2191 Some(Duration::ZERO)
2192 } else {
2193 timeout.or_else(|| self.blocking_read_timeout())
2194 },
2195 )?);
2196 }
2197
2198 self.resources.check_pread_length(length)?;
2199
2200 if is_proc_path(entry.description.path()) {
2201 let bytes = self.proc_read_file_from_open_path(Some(pid), entry.description.path())?;
2202 let start = entry.description.cursor() as usize;
2203 let end = start.saturating_add(length).min(bytes.len());
2204 let chunk = if start >= bytes.len() {
2205 Vec::new()
2206 } else {
2207 bytes[start..end].to_vec()
2208 };
2209 entry.description.set_cursor(
2210 entry
2211 .description
2212 .cursor()
2213 .saturating_add(chunk.len() as u64),
2214 );
2215 return Ok(Some(chunk));
2216 }
2217
2218 let cursor = entry.description.cursor();
2219 let bytes = VirtualFileSystem::pread(
2220 &mut self.filesystem,
2221 entry.description.path(),
2222 cursor,
2223 length,
2224 )?;
2225 entry
2226 .description
2227 .set_cursor(cursor.saturating_add(bytes.len() as u64));
2228 Ok(Some(bytes))
2229 }
2230
2231 pub fn fd_write(
2232 &mut self,
2233 requester_driver: &str,
2234 pid: u32,
2235 fd: u32,
2236 data: &[u8],
2237 ) -> KernelResult<usize> {
2238 self.assert_driver_owns(requester_driver, pid)?;
2239 self.resources.check_fd_write_size(data.len())?;
2240 let entry = {
2241 let tables = lock_or_recover(&self.fd_tables);
2242 tables
2243 .get(pid)
2244 .and_then(|table| table.get(fd))
2245 .cloned()
2246 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2247 };
2248
2249 if self.pipes.is_pipe(entry.description.id()) {
2250 return match self.pipes.write_with_mode(
2251 entry.description.id(),
2252 data,
2253 entry.status_flags & O_NONBLOCK != 0,
2254 ) {
2255 Ok(bytes) => Ok(bytes),
2256 Err(error) => {
2257 if error.code() == "EPIPE" {
2258 self.processes.kill(pid as i32, SIGPIPE)?;
2259 }
2260 Err(error.into())
2261 }
2262 };
2263 }
2264
2265 if self.ptys.is_pty(entry.description.id()) {
2266 return Ok(self.ptys.write(entry.description.id(), data)?);
2267 }
2268
2269 self.reject_read_only_resolved_write_path(entry.description.path())?;
2270
2271 let path = entry.description.path().to_owned();
2272 if is_virtual_device_storage_path(&path) {
2273 VirtualFileSystem::write_file(&mut self.filesystem, &path, data.to_vec())?;
2274 let cursor = entry.description.cursor();
2275 entry
2276 .description
2277 .set_cursor(cursor.saturating_add(data.len() as u64));
2278 return Ok(data.len());
2279 }
2280 let current_size = self.current_storage_file_size(&path)?;
2281 let cursor = entry.description.cursor();
2282 if entry.description.flags() & O_APPEND != 0 {
2283 let required_size = current_size.max(checked_write_end(current_size, data.len())?);
2284 self.check_path_resize_limits(&path, required_size)?;
2285 let new_len = VirtualFileSystem::append_file(&mut self.filesystem, &path, data)?;
2286 self.update_filesystem_usage_cache_for_resize(current_size, new_len);
2287 entry.description.set_cursor(new_len);
2288 return Ok(data.len());
2289 }
2290
2291 let required_size = current_size.max(checked_write_end(cursor, data.len())?);
2292 self.check_path_resize_limits(&path, required_size)?;
2293 VirtualFileSystem::pwrite(&mut self.filesystem, &path, data, cursor)?;
2294 self.update_filesystem_usage_cache_for_resize(current_size, required_size);
2295 entry
2296 .description
2297 .set_cursor(cursor.saturating_add(data.len() as u64));
2298 Ok(data.len())
2299 }
2300
2301 pub fn poll_fds(
2302 &self,
2303 requester_driver: &str,
2304 pid: u32,
2305 fds: Vec<PollFd>,
2306 timeout_ms: i32,
2307 ) -> KernelResult<PollResult> {
2308 let targets = fds
2309 .into_iter()
2310 .map(|poll_fd| PollTargetEntry::fd(poll_fd.fd, poll_fd.events))
2311 .collect::<Vec<_>>();
2312 let result = self.poll_targets(requester_driver, pid, targets, timeout_ms)?;
2313 Ok(PollResult {
2314 ready_count: result.ready_count,
2315 fds: result
2316 .targets
2317 .into_iter()
2318 .map(|target| match target.target {
2319 PollTarget::Fd(fd) => PollFd {
2320 fd,
2321 events: target.events,
2322 revents: target.revents,
2323 },
2324 PollTarget::Socket(_) => unreachable!("fd poll should only include fd targets"),
2325 })
2326 .collect(),
2327 })
2328 }
2329
2330 pub fn poll_wait_handle(&self) -> crate::poll::PollWaitHandle {
2334 crate::poll::PollWaitHandle::new(self.poll_notifier.clone())
2335 }
2336
2337 pub fn poll_targets(
2338 &self,
2339 requester_driver: &str,
2340 pid: u32,
2341 mut targets: Vec<PollTargetEntry>,
2342 timeout_ms: i32,
2343 ) -> KernelResult<PollTargetResult> {
2344 self.assert_driver_owns(requester_driver, pid)?;
2345 if timeout_ms < -1 {
2346 return Err(KernelError::new(
2347 "EINVAL",
2348 format!("invalid poll timeout {timeout_ms}"),
2349 ));
2350 }
2351
2352 let timeout = if timeout_ms < 0 {
2353 None
2354 } else {
2355 Some(Duration::from_millis(timeout_ms as u64))
2356 };
2357 let deadline = timeout.map(|duration| Instant::now() + duration);
2358
2359 loop {
2360 let observed_generation = self.poll_notifier.snapshot();
2361 let ready_count = self.populate_poll_target_revents(pid, &mut targets)?;
2362 if ready_count > 0 || matches!(timeout, Some(duration) if duration.is_zero()) {
2363 return Ok(PollTargetResult {
2364 ready_count,
2365 targets,
2366 });
2367 }
2368
2369 let remaining = deadline.map(|target| target.saturating_duration_since(Instant::now()));
2370 if matches!(remaining, Some(duration) if duration.is_zero()) {
2371 return Ok(PollTargetResult {
2372 ready_count,
2373 targets,
2374 });
2375 }
2376
2377 if !self
2378 .poll_notifier
2379 .wait_for_change(observed_generation, remaining)
2380 {
2381 return Ok(PollTargetResult {
2382 ready_count,
2383 targets,
2384 });
2385 }
2386 }
2387 }
2388
2389 pub fn fd_seek(
2390 &mut self,
2391 requester_driver: &str,
2392 pid: u32,
2393 fd: u32,
2394 offset: i64,
2395 whence: u8,
2396 ) -> KernelResult<u64> {
2397 self.assert_driver_owns(requester_driver, pid)?;
2398 let entry = {
2399 let tables = lock_or_recover(&self.fd_tables);
2400 tables
2401 .get(pid)
2402 .and_then(|table| table.get(fd))
2403 .cloned()
2404 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2405 };
2406
2407 if self.pipes.is_pipe(entry.description.id()) || self.ptys.is_pty(entry.description.id()) {
2408 return Err(KernelError::new("ESPIPE", "illegal seek"));
2409 }
2410
2411 let base = match whence {
2412 SEEK_SET => 0_i128,
2413 SEEK_CUR => i128::from(entry.description.cursor()),
2414 SEEK_END => {
2415 let size = if is_proc_path(entry.description.path()) {
2416 self.proc_stat_from_open_path(Some(pid), entry.description.path())?
2417 .size
2418 } else {
2419 self.filesystem.stat(entry.description.path())?.size
2420 };
2421 i128::from(size)
2422 }
2423 _ => {
2424 return Err(KernelError::new(
2425 "EINVAL",
2426 format!("invalid whence {whence}"),
2427 ))
2428 }
2429 };
2430 let next = base + i128::from(offset);
2431 if next < 0 {
2432 return Err(KernelError::new("EINVAL", "negative seek position"));
2433 }
2434 let next = u64::try_from(next)
2435 .map_err(|_| KernelError::new("EINVAL", "seek position out of range"))?;
2436 entry.description.set_cursor(next);
2437 Ok(next)
2438 }
2439
2440 pub fn fd_pread(
2441 &mut self,
2442 requester_driver: &str,
2443 pid: u32,
2444 fd: u32,
2445 length: usize,
2446 offset: u64,
2447 ) -> KernelResult<Vec<u8>> {
2448 self.assert_driver_owns(requester_driver, pid)?;
2449 self.resources.check_pread_length(length)?;
2450 let entry = {
2451 let tables = lock_or_recover(&self.fd_tables);
2452 tables
2453 .get(pid)
2454 .and_then(|table| table.get(fd))
2455 .cloned()
2456 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2457 };
2458
2459 if self.pipes.is_pipe(entry.description.id()) || self.ptys.is_pty(entry.description.id()) {
2460 return Err(KernelError::new("ESPIPE", "illegal seek"));
2461 }
2462
2463 if is_proc_path(entry.description.path()) {
2464 let bytes = self.proc_read_file_from_open_path(Some(pid), entry.description.path())?;
2465 let start = usize::try_from(offset)
2466 .map_err(|_| KernelError::new("EINVAL", "pread offset out of range"))?;
2467 let end = start.saturating_add(length).min(bytes.len());
2468 return Ok(if start >= bytes.len() {
2469 Vec::new()
2470 } else {
2471 bytes[start..end].to_vec()
2472 });
2473 }
2474
2475 Ok(VirtualFileSystem::pread(
2476 &mut self.filesystem,
2477 entry.description.path(),
2478 offset,
2479 length,
2480 )?)
2481 }
2482
2483 pub fn fd_pwrite(
2484 &mut self,
2485 requester_driver: &str,
2486 pid: u32,
2487 fd: u32,
2488 data: &[u8],
2489 offset: u64,
2490 ) -> KernelResult<usize> {
2491 self.assert_driver_owns(requester_driver, pid)?;
2492 self.resources.check_fd_write_size(data.len())?;
2493 let entry = {
2494 let tables = lock_or_recover(&self.fd_tables);
2495 tables
2496 .get(pid)
2497 .and_then(|table| table.get(fd))
2498 .cloned()
2499 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2500 };
2501
2502 if self.pipes.is_pipe(entry.description.id()) || self.ptys.is_pty(entry.description.id()) {
2503 return Err(KernelError::new("ESPIPE", "illegal seek"));
2504 }
2505
2506 self.reject_read_only_resolved_write_path(entry.description.path())?;
2507
2508 let current_size = self.current_storage_file_size(entry.description.path())?;
2509 let required_size = current_size.max(checked_write_end(offset, data.len())?);
2510 self.check_path_resize_limits(entry.description.path(), required_size)?;
2511 VirtualFileSystem::pwrite(
2512 &mut self.filesystem,
2513 entry.description.path(),
2514 data.to_vec(),
2515 offset,
2516 )?;
2517 self.update_filesystem_usage_cache_for_resize(current_size, required_size);
2518 Ok(data.len())
2519 }
2520
2521 pub fn fd_dup(&mut self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<u32> {
2522 self.assert_driver_owns(requester_driver, pid)?;
2523 {
2524 let tables = lock_or_recover(&self.fd_tables);
2525 let table = tables
2526 .get(pid)
2527 .ok_or_else(|| KernelError::no_such_process(pid))?;
2528 table
2529 .get(fd)
2530 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
2531 }
2532 self.resources
2533 .check_fd_allocation(&self.resource_snapshot(), 1)?;
2534 let mut tables = lock_or_recover(&self.fd_tables);
2535 let table = tables
2536 .get_mut(pid)
2537 .ok_or_else(|| KernelError::no_such_process(pid))?;
2538 Ok(table.dup(fd)?)
2539 }
2540
2541 pub fn fd_dup2(
2542 &mut self,
2543 requester_driver: &str,
2544 pid: u32,
2545 old_fd: u32,
2546 new_fd: u32,
2547 ) -> KernelResult<()> {
2548 self.assert_driver_owns(requester_driver, pid)?;
2549 let (replaced, needs_fd_growth) = {
2550 let tables = lock_or_recover(&self.fd_tables);
2551 let table = tables
2552 .get(pid)
2553 .ok_or_else(|| KernelError::no_such_process(pid))?;
2554 table
2555 .get(old_fd)
2556 .ok_or_else(|| KernelError::bad_file_descriptor(old_fd))?;
2557 let replaced = if old_fd == new_fd {
2558 None
2559 } else {
2560 table.get(new_fd).cloned()
2561 };
2562 if new_fd as usize >= table.max_fds() {
2563 return Err(KernelError::bad_file_descriptor(new_fd));
2564 }
2565 let needs_fd_growth = old_fd != new_fd && replaced.is_none();
2566 (replaced, needs_fd_growth)
2567 };
2568 if needs_fd_growth {
2569 self.resources
2570 .check_fd_allocation(&self.resource_snapshot(), 1)?;
2571 }
2572 {
2573 let mut tables = lock_or_recover(&self.fd_tables);
2574 let table = tables
2575 .get_mut(pid)
2576 .ok_or_else(|| KernelError::no_such_process(pid))?;
2577 table.dup2(old_fd, new_fd)?;
2578 }
2579
2580 if let Some(entry) = replaced {
2581 self.close_special_resource_if_needed(&entry.description, entry.filetype);
2582 }
2583 Ok(())
2584 }
2585
2586 pub fn fd_close(&mut self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<()> {
2587 self.assert_driver_owns(requester_driver, pid)?;
2588 let (description, filetype) = {
2589 let mut tables = lock_or_recover(&self.fd_tables);
2590 let table = tables
2591 .get_mut(pid)
2592 .ok_or_else(|| KernelError::no_such_process(pid))?;
2593 let entry = table
2594 .get(fd)
2595 .cloned()
2596 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
2597 table.close(fd);
2598 (entry.description, entry.filetype)
2599 };
2600 self.close_special_resource_if_needed(&description, filetype);
2601 Ok(())
2602 }
2603
2604 pub fn fd_fcntl(
2605 &mut self,
2606 requester_driver: &str,
2607 pid: u32,
2608 fd: u32,
2609 command: u32,
2610 arg: u32,
2611 ) -> KernelResult<u32> {
2612 self.assert_driver_owns(requester_driver, pid)?;
2613 if command == F_DUPFD {
2614 {
2615 let tables = lock_or_recover(&self.fd_tables);
2616 let table = tables
2617 .get(pid)
2618 .ok_or_else(|| KernelError::no_such_process(pid))?;
2619 table
2620 .get(fd)
2621 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
2622 if arg as usize >= table.max_fds() {
2623 return Err(KernelError::new(
2624 "EINVAL",
2625 format!("fd {arg} exceeds process fd limit"),
2626 ));
2627 }
2628 }
2629 self.resources
2630 .check_fd_allocation(&self.resource_snapshot(), 1)?;
2631 }
2632 let mut tables = lock_or_recover(&self.fd_tables);
2633 let table = tables
2634 .get_mut(pid)
2635 .ok_or_else(|| KernelError::no_such_process(pid))?;
2636 let result = table.fcntl(fd, command, arg)?;
2637 if command == F_DUPFD {
2638 self.poll_notifier.notify();
2639 }
2640 Ok(result)
2641 }
2642
2643 pub fn fd_flock(
2644 &self,
2645 requester_driver: &str,
2646 pid: u32,
2647 fd: u32,
2648 operation: u32,
2649 ) -> KernelResult<()> {
2650 self.assert_driver_owns(requester_driver, pid)?;
2651 let entry = {
2652 let tables = lock_or_recover(&self.fd_tables);
2653 tables
2654 .get(pid)
2655 .and_then(|table| table.get(fd))
2656 .cloned()
2657 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2658 };
2659
2660 if entry.filetype != FILETYPE_REGULAR_FILE {
2661 return Err(KernelError::new(
2662 "EBADF",
2663 format!("file descriptor {fd} does not support advisory locking"),
2664 ));
2665 }
2666
2667 let target = entry.description.lock_target().ok_or_else(|| {
2668 KernelError::new(
2669 "EBADF",
2670 format!("file descriptor {fd} is missing advisory lock metadata"),
2671 )
2672 })?;
2673 let operation = FlockOperation::from_bits(operation)?;
2674 self.file_locks
2675 .apply(entry.description.id(), target, operation)?;
2676 Ok(())
2677 }
2678
2679 pub fn fd_stat(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<FdStat> {
2680 self.assert_driver_owns(requester_driver, pid)?;
2681 let tables = lock_or_recover(&self.fd_tables);
2682 Ok(tables
2683 .get(pid)
2684 .ok_or_else(|| KernelError::no_such_process(pid))?
2685 .stat(fd)?)
2686 }
2687
2688 pub fn fd_path(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<String> {
2689 let description = self.description_for_fd(requester_driver, pid, fd)?;
2690 Ok(description.path().to_owned())
2691 }
2692
2693 pub fn isatty(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<bool> {
2694 self.assert_driver_owns(requester_driver, pid)?;
2695 let entry = {
2696 let tables = lock_or_recover(&self.fd_tables);
2697 tables
2698 .get(pid)
2699 .and_then(|table| table.get(fd))
2700 .cloned()
2701 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2702 };
2703 Ok(self.ptys.is_slave(entry.description.id()))
2704 }
2705
2706 pub fn pty_window_size(
2707 &self,
2708 requester_driver: &str,
2709 pid: u32,
2710 fd: u32,
2711 ) -> KernelResult<PtyWindowSize> {
2712 let description = self.description_for_fd(requester_driver, pid, fd)?;
2713 Ok(self.ptys.window_size(description.id())?)
2714 }
2715
2716 pub fn pty_set_discipline(
2717 &self,
2718 requester_driver: &str,
2719 pid: u32,
2720 fd: u32,
2721 config: LineDisciplineConfig,
2722 ) -> KernelResult<()> {
2723 let description = self.description_for_fd(requester_driver, pid, fd)?;
2724 self.ptys.set_discipline(description.id(), config)?;
2725 Ok(())
2726 }
2727
2728 pub fn pty_set_foreground_pgid(
2729 &self,
2730 requester_driver: &str,
2731 pid: u32,
2732 fd: u32,
2733 pgid: u32,
2734 ) -> KernelResult<()> {
2735 let description = self.description_for_fd(requester_driver, pid, fd)?;
2736 let requester_sid = self.processes.getsid(pid)?;
2737 let group = self
2738 .processes
2739 .list_processes()
2740 .into_values()
2741 .find(|process| process.pgid == pgid && process.status != ProcessStatus::Exited)
2742 .ok_or_else(|| KernelError::new("ESRCH", format!("no such process group {pgid}")))?;
2743 if group.sid != requester_sid {
2744 return Err(KernelError::permission_denied(
2745 "cannot set foreground process group in different session",
2746 ));
2747 }
2748 self.ptys.set_foreground_pgid(description.id(), pgid)?;
2749 Ok(())
2750 }
2751
2752 pub fn tcgetattr(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<Termios> {
2753 let description = self.description_for_fd(requester_driver, pid, fd)?;
2754 Ok(self.ptys.get_termios(description.id())?)
2755 }
2756
2757 pub fn tcsetattr(
2758 &self,
2759 requester_driver: &str,
2760 pid: u32,
2761 fd: u32,
2762 termios: PartialTermios,
2763 ) -> KernelResult<()> {
2764 let description = self.description_for_fd(requester_driver, pid, fd)?;
2765 self.ptys.set_termios(description.id(), termios)?;
2766 Ok(())
2767 }
2768
2769 pub fn tcgetpgrp(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<u32> {
2770 let description = self.description_for_fd(requester_driver, pid, fd)?;
2771 Ok(self.ptys.get_foreground_pgid(description.id())?)
2772 }
2773
2774 pub fn pty_resize(
2775 &self,
2776 requester_driver: &str,
2777 pid: u32,
2778 fd: u32,
2779 cols: u16,
2780 rows: u16,
2781 ) -> KernelResult<()> {
2782 let description = self.description_for_fd(requester_driver, pid, fd)?;
2783 let target_pgid = self.ptys.resize(description.id(), cols, rows)?;
2784 if let Some(pgid) = target_pgid {
2785 match self.processes.kill(-(pgid as i32), SIGWINCH) {
2786 Ok(()) => {}
2787 Err(error) if error.code() == "ESRCH" => {}
2788 Err(error) => return Err(error.into()),
2789 }
2790 }
2791 Ok(())
2792 }
2793
2794 pub fn signal_process(
2795 &self,
2796 requester_driver: &str,
2797 pid: i32,
2798 signal: i32,
2799 ) -> KernelResult<()> {
2800 if pid < 0 {
2801 let pgid = pid.unsigned_abs();
2802 let members = self
2803 .processes
2804 .list_processes()
2805 .into_values()
2806 .filter(|process| process.pgid == pgid && process.status != ProcessStatus::Exited)
2807 .collect::<Vec<_>>();
2808 if members.is_empty() {
2809 self.processes.kill(pid, signal)?;
2810 return Ok(());
2811 }
2812 if let Some(process) = members
2813 .iter()
2814 .find(|process| process.driver != requester_driver)
2815 {
2816 return Err(KernelError::permission_denied(format!(
2817 "driver \"{requester_driver}\" does not own process group {pgid} containing PID {}",
2818 process.pid
2819 )));
2820 }
2821 self.processes.kill(pid, signal)?;
2822 return Ok(());
2823 }
2824
2825 let pid = u32::try_from(pid)
2826 .map_err(|_| KernelError::new("EINVAL", format!("invalid pid {pid}")))?;
2827 self.assert_driver_owns(requester_driver, pid)?;
2828 self.processes.kill(pid as i32, signal)?;
2829 Ok(())
2830 }
2831
2832 pub fn kill_process(&self, requester_driver: &str, pid: u32, signal: i32) -> KernelResult<()> {
2833 let pid = i32::try_from(pid)
2834 .map_err(|_| KernelError::new("EINVAL", format!("pid {pid} exceeds i32::MAX")))?;
2835 self.signal_process(requester_driver, pid, signal)
2836 }
2837
2838 pub fn setpgid(&self, requester_driver: &str, pid: u32, pgid: u32) -> KernelResult<()> {
2839 self.assert_driver_owns(requester_driver, pid)?;
2840 let target_pgid = if pgid == 0 { pid } else { pgid };
2841 if target_pgid != pid {
2842 if let Some(group_owner) =
2843 self.processes
2844 .list_processes()
2845 .into_values()
2846 .find(|process| {
2847 process.pgid == target_pgid && process.status == ProcessStatus::Running
2848 })
2849 {
2850 if group_owner.driver != requester_driver {
2851 return Err(KernelError::permission_denied(format!(
2852 "driver \"{requester_driver}\" cannot join process group {target_pgid} owned by \"{}\"",
2853 group_owner.driver
2854 )));
2855 }
2856 }
2857 }
2858 self.processes.setpgid(pid, pgid)?;
2859 Ok(())
2860 }
2861
2862 pub fn getpgid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2863 self.assert_driver_owns(requester_driver, pid)?;
2864 Ok(self.processes.getpgid(pid)?)
2865 }
2866
2867 pub fn getpid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2868 self.assert_driver_owns(requester_driver, pid)?;
2869 Ok(pid)
2870 }
2871
2872 pub fn sigprocmask(
2873 &self,
2874 requester_driver: &str,
2875 pid: u32,
2876 how: SigmaskHow,
2877 set: SignalSet,
2878 ) -> KernelResult<SignalSet> {
2879 self.assert_driver_owns(requester_driver, pid)?;
2880 Ok(self.processes.sigprocmask(pid, how, set)?)
2881 }
2882
2883 pub fn sigpending(&self, requester_driver: &str, pid: u32) -> KernelResult<SignalSet> {
2884 self.assert_driver_owns(requester_driver, pid)?;
2885 Ok(self.processes.sigpending(pid)?)
2886 }
2887
2888 pub fn getppid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2889 self.assert_driver_owns(requester_driver, pid)?;
2890 Ok(self.processes.getppid(pid)?)
2891 }
2892
2893 pub fn setsid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2894 self.assert_driver_owns(requester_driver, pid)?;
2895 Ok(self.processes.setsid(pid)?)
2896 }
2897
2898 pub fn getsid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2899 self.assert_driver_owns(requester_driver, pid)?;
2900 Ok(self.processes.getsid(pid)?)
2901 }
2902
2903 pub fn dev_fd_read_dir(&self, requester_driver: &str, pid: u32) -> KernelResult<Vec<String>> {
2904 self.assert_driver_owns(requester_driver, pid)?;
2905 let tables = lock_or_recover(&self.fd_tables);
2906 let table = tables
2907 .get(pid)
2908 .ok_or_else(|| KernelError::no_such_process(pid))?;
2909 let entry_count = table.len();
2910 self.resources.check_readdir_entries(entry_count)?;
2911 Ok(table.iter().map(|entry| entry.fd.to_string()).collect())
2912 }
2913
2914 pub fn dev_fd_stat(
2915 &mut self,
2916 requester_driver: &str,
2917 pid: u32,
2918 fd: u32,
2919 ) -> KernelResult<VirtualStat> {
2920 self.assert_driver_owns(requester_driver, pid)?;
2921 let entry = {
2922 let tables = lock_or_recover(&self.fd_tables);
2923 tables
2924 .get(pid)
2925 .and_then(|table| table.get(fd))
2926 .cloned()
2927 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2928 };
2929
2930 if self.pipes.is_pipe(entry.description.id()) || self.ptys.is_pty(entry.description.id()) {
2931 return Ok(synthetic_character_device_stat(entry.description.id()));
2932 }
2933
2934 if is_proc_path(entry.description.path()) {
2935 return self.proc_stat_from_open_path(Some(pid), entry.description.path());
2936 }
2937
2938 Ok(self.filesystem.stat(entry.description.path())?)
2939 }
2940
2941 pub fn dispose(&mut self) -> KernelResult<()> {
2942 if self.terminated {
2943 return Ok(());
2944 }
2945
2946 dispose_kernel_vm_resources(self);
2947 Ok(())
2948 }
2949
2950 fn prepare_fd_open(
2951 &mut self,
2952 path: &str,
2953 flags: u32,
2954 mode: Option<u32>,
2955 ) -> KernelResult<(u8, Option<FileLockTarget>)> {
2956 if open_requires_write_access(flags) {
2957 self.reject_read_only_resolved_write_path(path)?;
2958 }
2959
2960 if flags & O_CREAT != 0 && flags & O_EXCL != 0 {
2961 self.check_write_file_limits(path, 0)?;
2962 VirtualFileSystem::create_file_exclusive_with_mode(
2963 &mut self.filesystem,
2964 path,
2965 Vec::new(),
2966 mode,
2967 )?;
2968 self.update_filesystem_usage_cache_for_inode_create(0);
2969 let stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
2970 return Ok((
2971 filetype_for_path(path, &stat),
2972 Some(FileLockTarget::new(stat.ino)),
2973 ));
2974 }
2975
2976 let exists = self.filesystem.exists(path)?;
2977 if exists {
2978 if flags & O_TRUNC != 0 {
2979 let existing_size = self.current_storage_file_size(path)?;
2980 self.check_path_resize_limits_with_existing(existing_size, 0)?;
2981 VirtualFileSystem::truncate(&mut self.filesystem, path, 0)?;
2982 self.update_filesystem_usage_cache_for_resize(existing_size, 0);
2983 }
2984 } else if flags & O_CREAT != 0 {
2985 self.check_write_file_limits(path, 0)?;
2986 VirtualFileSystem::write_file_with_mode(&mut self.filesystem, path, Vec::new(), mode)?;
2987 self.update_filesystem_usage_cache_for_inode_create(0);
2988 } else {
2989 let _ = VirtualFileSystem::stat(&mut self.filesystem, path)?;
2990 unreachable!("stat should return an error when opening a missing path");
2991 }
2992
2993 let stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
2994 Ok((
2995 filetype_for_path(path, &stat),
2996 Some(FileLockTarget::new(stat.ino)),
2997 ))
2998 }
2999
3000 fn reject_read_only_write_path(&mut self, path: &str) -> KernelResult<()> {
3001 if is_proc_path(path) {
3002 self.filesystem
3003 .check_virtual_path(FsOperation::Write, path)
3004 .map_err(KernelError::from)?;
3005 return Err(read_only_filesystem_error(path));
3006 }
3007
3008 if is_agentos_path(path) {
3009 return Err(read_only_filesystem_error(path));
3010 }
3011
3012 Ok(())
3013 }
3014
3015 fn reject_read_only_resolved_write_path(&mut self, path: &str) -> KernelResult<()> {
3016 self.reject_read_only_write_path(path)?;
3017
3018 if let Some(resolved) = self.resolve_write_guard_path(path, true)? {
3019 if is_agentos_path(&resolved) {
3020 return Err(read_only_filesystem_error(&resolved));
3021 }
3022 if self.has_agentos_hardlink_alias(&resolved)? {
3023 return Err(read_only_filesystem_error(&resolved));
3024 }
3025 }
3026 if self.has_agentos_hardlink_alias(path)? {
3027 return Err(read_only_filesystem_error(path));
3028 }
3029
3030 Ok(())
3031 }
3032
3033 fn reject_read_only_entry_write_path(&mut self, path: &str) -> KernelResult<()> {
3034 self.reject_read_only_write_path(path)?;
3035
3036 if let Some(resolved) = self.resolve_write_guard_path(path, false)? {
3037 if is_agentos_path(&resolved) {
3038 return Err(read_only_filesystem_error(&resolved));
3039 }
3040 if self.has_agentos_hardlink_alias(&resolved)? {
3041 return Err(read_only_filesystem_error(&resolved));
3042 }
3043 }
3044 if self.has_agentos_hardlink_alias(path)? {
3045 return Err(read_only_filesystem_error(path));
3046 }
3047
3048 Ok(())
3049 }
3050
3051 fn has_agentos_hardlink_alias(&mut self, path: &str) -> KernelResult<bool> {
3052 let Some(target) = self.storage_lstat(path)? else {
3053 return Ok(false);
3054 };
3055 if target.is_directory || target.is_symbolic_link {
3056 return Ok(false);
3057 }
3058
3059 self.agentos_subtree_contains_inode("/etc/agentos", target.dev, target.ino)
3060 }
3061
3062 fn agentos_subtree_contains_inode(
3063 &mut self,
3064 path: &str,
3065 target_dev: u64,
3066 target_ino: u64,
3067 ) -> KernelResult<bool> {
3068 let Some(stat) = self.storage_lstat(path)? else {
3069 return Ok(false);
3070 };
3071 if !stat.is_directory && !stat.is_symbolic_link {
3072 return Ok(stat.dev == target_dev && stat.ino == target_ino);
3073 }
3074 if !stat.is_directory {
3075 return Ok(false);
3076 }
3077
3078 let children = self.raw_filesystem_mut().read_dir_with_types(path)?;
3079 for child in children {
3080 if child.name == "." || child.name == ".." {
3081 continue;
3082 }
3083 let child_path = join_absolute_path(path, &child.name);
3084 if self.agentos_subtree_contains_inode(&child_path, target_dev, target_ino)? {
3085 return Ok(true);
3086 }
3087 }
3088
3089 Ok(false)
3090 }
3091
3092 fn resolve_write_guard_path(
3093 &mut self,
3094 path: &str,
3095 follow_final_symlink: bool,
3096 ) -> KernelResult<Option<String>> {
3097 let normalized = normalize_path(path);
3098 if normalized == "/" {
3099 return Ok(Some(normalized));
3100 }
3101
3102 if follow_final_symlink {
3103 if let Ok(resolved) = self.filesystem.realpath(&normalized) {
3104 return Ok(Some(resolved));
3105 }
3106 }
3107
3108 let components: Vec<&str> = normalized
3109 .split('/')
3110 .filter(|component| !component.is_empty())
3111 .collect();
3112 let mut resolved_prefix = String::from("/");
3113 let mut raw_prefix = String::from("/");
3114
3115 for (index, component) in components.iter().enumerate() {
3116 let is_final = index + 1 == components.len();
3117 if is_final && !follow_final_symlink {
3118 return Ok(Some(join_absolute_path(&resolved_prefix, component)));
3119 }
3120
3121 raw_prefix = join_absolute_path(&raw_prefix, component);
3122 match self.filesystem.realpath(&raw_prefix) {
3123 Ok(resolved) => {
3124 resolved_prefix = resolved;
3125 }
3126 Err(error) if error.code() == "ENOENT" => {
3127 let mut resolved = resolved_prefix;
3128 for remaining in &components[index..] {
3129 resolved = join_absolute_path(&resolved, remaining);
3130 }
3131 return Ok(Some(resolved));
3132 }
3133 Err(error) => return Err(error.into()),
3134 }
3135 }
3136
3137 Ok(Some(resolved_prefix))
3138 }
3139
3140 fn populate_poll_target_revents(
3141 &self,
3142 pid: u32,
3143 targets: &mut [PollTargetEntry],
3144 ) -> KernelResult<usize> {
3145 let mut ready_count = 0;
3146 for target in targets.iter_mut() {
3147 target.revents = self.poll_target_entry(pid, target.target, target.events)?;
3148 if !target.revents.is_empty() {
3149 ready_count += 1;
3150 }
3151 }
3152
3153 Ok(ready_count)
3154 }
3155
3156 fn poll_target_entry(
3157 &self,
3158 pid: u32,
3159 target: PollTarget,
3160 requested: PollEvents,
3161 ) -> KernelResult<PollEvents> {
3162 match target {
3163 PollTarget::Fd(fd) => {
3164 let entry = {
3165 let tables = lock_or_recover(&self.fd_tables);
3166 tables
3167 .get(pid)
3168 .ok_or_else(|| KernelError::no_such_process(pid))?
3169 .get(fd)
3170 .cloned()
3171 };
3172 if let Some(entry) = entry {
3173 self.poll_entry(&entry, requested)
3174 } else {
3175 Ok(POLLNVAL)
3176 }
3177 }
3178 PollTarget::Socket(socket_id) => {
3179 let socket = self.sockets.get(socket_id);
3180 if let Some(socket) = socket {
3181 if socket.owner_pid() != pid {
3182 return Err(KernelError::permission_denied(format!(
3183 "process {pid} does not own socket {socket_id}"
3184 )));
3185 }
3186 let mut events = self.sockets.poll(socket_id, requested)?;
3187 if events.intersects(POLLOUT)
3188 && !self.socket_pollout_has_resource_capacity(&socket)
3189 {
3190 events = PollEvents::from_bits(events.bits() & !POLLOUT.bits());
3191 }
3192 Ok(events)
3193 } else {
3194 Ok(POLLNVAL)
3195 }
3196 }
3197 }
3198 }
3199
3200 fn socket_pollout_has_resource_capacity(&self, socket: &SocketRecord) -> bool {
3201 let snapshot = self.resource_snapshot();
3202 if self
3203 .resources
3204 .limits()
3205 .max_socket_buffered_bytes
3206 .is_some_and(|limit| snapshot.socket_buffered_bytes >= limit)
3207 {
3208 return false;
3209 }
3210
3211 if socket.spec().socket_type == SocketType::Datagram
3212 && self
3213 .resources
3214 .limits()
3215 .max_socket_datagram_queue_len
3216 .is_some_and(|limit| snapshot.socket_datagram_queue_len >= limit)
3217 {
3218 return false;
3219 }
3220
3221 true
3222 }
3223
3224 fn poll_entry(
3225 &self,
3226 entry: &crate::fd_table::FdEntry,
3227 requested: PollEvents,
3228 ) -> KernelResult<PollEvents> {
3229 if self.pipes.is_pipe(entry.description.id()) {
3230 return Ok(self.pipes.poll(entry.description.id(), requested)?);
3231 }
3232
3233 if self.ptys.is_pty(entry.description.id()) {
3234 return Ok(self.ptys.poll(entry.description.id(), requested)?);
3235 }
3236
3237 let access_mode = entry.description.flags() & 0b11;
3238 let mut events = PollEvents::empty();
3239 if requested.intersects(POLLIN) && access_mode != crate::fd_table::O_WRONLY {
3240 events |= POLLIN;
3241 }
3242 if requested.intersects(POLLOUT) && access_mode != crate::fd_table::O_RDONLY {
3243 events |= POLLOUT;
3244 }
3245 if entry.filetype == FILETYPE_DIRECTORY && requested.intersects(POLLOUT) {
3246 events |= POLLERR;
3247 }
3248 if self.terminated {
3249 events |= POLLHUP;
3250 }
3251 Ok(events)
3252 }
3253
3254 fn description_for_fd(
3255 &self,
3256 requester_driver: &str,
3257 pid: u32,
3258 fd: u32,
3259 ) -> KernelResult<Arc<FileDescription>> {
3260 self.assert_driver_owns(requester_driver, pid)?;
3261 lock_or_recover(&self.fd_tables)
3262 .get(pid)
3263 .and_then(|table| table.get(fd))
3264 .map(|entry| Arc::clone(&entry.description))
3265 .ok_or_else(|| KernelError::bad_file_descriptor(fd))
3266 }
3267
3268 fn assert_not_terminated(&self) -> KernelResult<()> {
3269 if self.terminated {
3270 Err(KernelError::disposed())
3271 } else {
3272 Ok(())
3273 }
3274 }
3275
3276 fn assert_driver_owns(&self, requester_driver: &str, pid: u32) -> KernelResult<()> {
3277 let driver_pids = lock_or_recover(&self.driver_pids);
3278 if driver_pids
3279 .get(requester_driver)
3280 .map(|pids| pids.contains(&pid))
3281 .unwrap_or(false)
3282 {
3283 return Ok(());
3284 }
3285
3286 if driver_pids.values().any(|pids| pids.contains(&pid)) {
3287 return Err(KernelError::permission_denied(format!(
3288 "driver \"{requester_driver}\" does not own PID {pid}"
3289 )));
3290 }
3291
3292 Err(KernelError::no_such_process(pid))
3293 }
3294
3295 fn cleanup_process_resources(&self, pid: u32) {
3296 cleanup_process_resources(
3297 self.fd_tables.as_ref(),
3298 &self.file_locks,
3299 &self.pipes,
3300 &self.ptys,
3301 &self.sockets,
3302 self.driver_pids.as_ref(),
3303 pid,
3304 );
3305 }
3306
3307 fn resolve_spawn_command(
3308 &mut self,
3309 command: &str,
3310 args: &[String],
3311 cwd: &str,
3312 ) -> KernelResult<ResolvedSpawnCommand> {
3313 if let Some(driver) = self.commands.resolve(command).cloned() {
3314 return Ok(ResolvedSpawnCommand {
3315 command: command.to_owned(),
3316 args: args.to_vec(),
3317 driver,
3318 });
3319 }
3320
3321 let Some(path) = self.resolve_executable_path(command, cwd)? else {
3322 return Err(KernelError::command_not_found(command));
3323 };
3324
3325 if let Some(registered_command) = self.resolve_registered_command_path(&path) {
3326 let driver = self
3327 .commands
3328 .resolve(®istered_command)
3329 .cloned()
3330 .ok_or_else(|| KernelError::command_not_found(®istered_command))?;
3331 return Ok(ResolvedSpawnCommand {
3332 command: registered_command,
3333 args: args.to_vec(),
3334 driver,
3335 });
3336 }
3337
3338 let shebang = self
3339 .parse_shebang_command(&path)?
3340 .ok_or_else(|| KernelError::new("ENOEXEC", format!("exec format error: {path}")))?;
3341 self.resolve_shebang_command(&path, args, shebang)
3342 }
3343
3344 fn resolve_executable_path(
3345 &mut self,
3346 command: &str,
3347 cwd: &str,
3348 ) -> KernelResult<Option<String>> {
3349 if !command.contains('/') {
3350 return Ok(None);
3351 }
3352
3353 let path = if command.starts_with('/') {
3354 normalize_path(command)
3355 } else {
3356 normalize_path(&format!("{cwd}/{command}"))
3357 };
3358 let stat = self.filesystem.stat(&path)?;
3359 if stat.is_directory {
3360 return Err(KernelError::new(
3361 "EACCES",
3362 format!("permission denied, execute '{path}'"),
3363 ));
3364 }
3365 if stat.mode & EXECUTABLE_PERMISSION_BITS == 0 {
3366 return Err(KernelError::new(
3367 "EACCES",
3368 format!("permission denied, execute '{path}'"),
3369 ));
3370 }
3371 Ok(Some(path))
3372 }
3373
3374 fn resolve_registered_command_path(&self, path: &str) -> Option<String> {
3375 let normalized = normalize_path(path);
3376 for prefix in ["/bin/", "/usr/bin/", "/usr/local/bin/"] {
3377 let Some(name) = normalized.strip_prefix(prefix) else {
3378 continue;
3379 };
3380 if !name.is_empty() && !name.contains('/') && self.commands.resolve(name).is_some() {
3381 return Some(name.to_owned());
3382 }
3383 }
3384
3385 if let Some(name) = normalized
3386 .strip_prefix("/__secure_exec/commands/")
3387 .and_then(|suffix| suffix.rsplit('/').next())
3388 {
3389 if !name.is_empty() && !name.contains('/') && self.commands.resolve(name).is_some() {
3390 return Some(name.to_owned());
3391 }
3392 }
3393
3394 None
3395 }
3396
3397 fn parse_shebang_command(&mut self, path: &str) -> KernelResult<Option<ShebangCommand>> {
3398 let header = self.filesystem.pread(path, 0, SHEBANG_LINE_MAX_BYTES + 1)?;
3399 if !header.starts_with(b"#!") {
3400 return Ok(None);
3401 }
3402
3403 let line_end = match header.iter().position(|byte| *byte == b'\n') {
3404 Some(index) => index,
3405 None if header.len() <= SHEBANG_LINE_MAX_BYTES => header.len(),
3406 None => {
3407 return Err(KernelError::new(
3408 "ENOEXEC",
3409 format!("shebang line exceeds {SHEBANG_LINE_MAX_BYTES} bytes: {path}"),
3410 ))
3411 }
3412 };
3413 let line = header[2..line_end]
3414 .strip_suffix(b"\r")
3415 .unwrap_or(&header[2..line_end]);
3416 let text = std::str::from_utf8(line)
3417 .map_err(|_| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
3418 let mut parts = text.split_ascii_whitespace();
3419 let interpreter = parts
3420 .next()
3421 .ok_or_else(|| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
3422 Ok(Some(ShebangCommand {
3423 interpreter: interpreter.to_owned(),
3424 args: parts.map(ToOwned::to_owned).collect(),
3425 }))
3426 }
3427
3428 fn resolve_shebang_command(
3429 &self,
3430 path: &str,
3431 args: &[String],
3432 shebang: ShebangCommand,
3433 ) -> KernelResult<ResolvedSpawnCommand> {
3434 let mut interpreter_args = shebang.args;
3435 let interpreter = normalize_path(&shebang.interpreter);
3436 let command = if interpreter == "/usr/bin/env" || interpreter == "/bin/env" {
3437 if interpreter_args.is_empty() {
3438 return Err(KernelError::new(
3439 "ENOENT",
3440 format!("missing interpreter after /usr/bin/env in shebang: {path}"),
3441 ));
3442 }
3443 interpreter_args.remove(0)
3444 } else if let Some(command) = self.resolve_registered_command_path(&interpreter) {
3445 command
3446 } else if self.commands.resolve(&shebang.interpreter).is_some() {
3447 shebang.interpreter
3448 } else {
3449 return Err(KernelError::command_not_found(&shebang.interpreter));
3450 };
3451
3452 let driver = self
3453 .commands
3454 .resolve(&command)
3455 .cloned()
3456 .ok_or_else(|| KernelError::command_not_found(&command))?;
3457 let mut resolved_args = interpreter_args;
3458 resolved_args.push(path.to_owned());
3459 resolved_args.extend(args.iter().cloned());
3460 Ok(ResolvedSpawnCommand {
3461 command,
3462 args: resolved_args,
3463 driver,
3464 })
3465 }
3466
3467 fn finish_waitpid_event(&mut self, result: ProcessWaitResult) -> WaitPidEventResult {
3468 if result.event == WaitPidEvent::Exited {
3469 self.cleanup_process_resources(result.pid);
3470 }
3471 WaitPidEventResult {
3472 pid: result.pid,
3473 status: result.status,
3474 event: result.event,
3475 }
3476 }
3477
3478 fn raw_filesystem_mut(&mut self) -> &mut F {
3479 self.filesystem.inner_mut().inner_mut()
3480 }
3481
3482 fn read_file_internal(
3483 &mut self,
3484 current_pid: Option<u32>,
3485 path: &str,
3486 ) -> KernelResult<Vec<u8>> {
3487 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3488 self.filesystem
3489 .check_virtual_path(FsOperation::Read, path)
3490 .map_err(KernelError::from)?;
3491 return self.proc_read_file(current_pid, &proc_node);
3492 }
3493
3494 Ok(self.filesystem.read_file(path)?)
3495 }
3496
3497 fn exists_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<bool> {
3498 match self.resolve_proc_node(path, current_pid) {
3499 Ok(Some(_)) => {
3500 self.filesystem
3501 .check_virtual_path(FsOperation::Read, path)
3502 .map_err(KernelError::from)?;
3503 Ok(true)
3504 }
3505 Ok(None) => Ok(self.filesystem.exists(path)?),
3506 Err(error) if error.code() == "ENOENT" => Ok(false),
3507 Err(error) => Err(error),
3508 }
3509 }
3510
3511 fn stat_internal(&mut self, current_pid: Option<u32>, path: &str) -> KernelResult<VirtualStat> {
3512 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3513 self.filesystem
3514 .check_virtual_path(FsOperation::Read, path)
3515 .map_err(KernelError::from)?;
3516 return self.proc_stat(current_pid, &proc_node);
3517 }
3518
3519 Ok(self.filesystem.stat(path)?)
3520 }
3521
3522 fn lstat_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<VirtualStat> {
3523 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3524 self.filesystem
3525 .check_virtual_path(FsOperation::Read, path)
3526 .map_err(KernelError::from)?;
3527 return self.proc_lstat(&proc_node);
3528 }
3529
3530 Ok(self.filesystem.lstat(path)?)
3531 }
3532
3533 fn read_link_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<String> {
3534 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3535 self.filesystem
3536 .check_virtual_path(FsOperation::Read, path)
3537 .map_err(KernelError::from)?;
3538 return self.proc_read_link(&proc_node);
3539 }
3540
3541 Ok(self.filesystem.read_link(path)?)
3542 }
3543
3544 fn read_dir_internal(
3545 &mut self,
3546 current_pid: Option<u32>,
3547 path: &str,
3548 ) -> KernelResult<Vec<String>> {
3549 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3550 self.filesystem
3551 .check_virtual_path(FsOperation::Read, path)
3552 .map_err(KernelError::from)?;
3553 return self.proc_read_dir(current_pid, &proc_node);
3554 }
3555
3556 if let Some(limit) = self.resources.max_readdir_entries() {
3557 Ok(self.filesystem.read_dir_limited(path, limit)?)
3558 } else {
3559 Ok(self.filesystem.read_dir(path)?)
3560 }
3561 }
3562
3563 fn read_dir_with_types_internal(
3564 &mut self,
3565 current_pid: Option<u32>,
3566 path: &str,
3567 ) -> KernelResult<Vec<VirtualDirEntry>> {
3568 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3569 self.filesystem
3570 .check_virtual_path(FsOperation::Read, path)
3571 .map_err(KernelError::from)?;
3572 return Ok(self
3573 .proc_read_dir(current_pid, &proc_node)?
3574 .into_iter()
3575 .map(|name| VirtualDirEntry {
3576 name,
3577 is_directory: false,
3578 is_symbolic_link: false,
3579 })
3580 .collect());
3581 }
3582
3583 Ok(self.filesystem.read_dir_with_types(path)?)
3584 }
3585
3586 fn realpath_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<String> {
3587 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3588 self.filesystem
3589 .check_virtual_path(FsOperation::Read, path)
3590 .map_err(KernelError::from)?;
3591 return self.proc_realpath(current_pid, &proc_node);
3592 }
3593
3594 Ok(self.filesystem.realpath(path)?)
3595 }
3596
3597 fn resolve_proc_node(
3598 &self,
3599 path: &str,
3600 current_pid: Option<u32>,
3601 ) -> KernelResult<Option<ProcNode>> {
3602 let normalized = normalize_path(path);
3603 if !is_proc_path(&normalized) {
3604 return Ok(None);
3605 }
3606
3607 if normalized == "/proc" {
3608 return Ok(Some(ProcNode::RootDir));
3609 }
3610
3611 let suffix = normalized
3612 .strip_prefix("/proc/")
3613 .expect("proc path should have /proc prefix");
3614 let parts = suffix.split('/').collect::<Vec<_>>();
3615 if parts.is_empty() {
3616 return Ok(Some(ProcNode::RootDir));
3617 }
3618
3619 let root_node = match parts.as_slice() {
3620 ["mounts"] => Some(ProcNode::MountsFile),
3621 ["cpuinfo"] => Some(ProcNode::CpuInfoFile),
3622 ["meminfo"] => Some(ProcNode::MemInfoFile),
3623 ["loadavg"] => Some(ProcNode::LoadAvgFile),
3624 ["uptime"] => Some(ProcNode::UptimeFile),
3625 ["version"] => Some(ProcNode::VersionFile),
3626 _ => None,
3627 };
3628 if let Some(node) = root_node {
3629 return Ok(Some(node));
3630 }
3631
3632 let pid = match parts[0] {
3633 "self" => current_pid.ok_or_else(|| proc_not_found_error(&normalized))?,
3634 raw => raw
3635 .parse::<u32>()
3636 .map_err(|_| proc_not_found_error(&normalized))?,
3637 };
3638 self.proc_entry(pid)?;
3639
3640 let node = match parts.as_slice() {
3641 ["self"] => ProcNode::SelfLink { pid },
3642 [_pid] => ProcNode::PidDir { pid },
3643 [_pid, "fd"] => ProcNode::PidFdDir { pid },
3644 [_pid, "cmdline"] => ProcNode::PidCmdline { pid },
3645 [_pid, "environ"] => ProcNode::PidEnviron { pid },
3646 [_pid, "cwd"] => ProcNode::PidCwdLink { pid },
3647 [_pid, "stat"] => ProcNode::PidStatFile { pid },
3648 [_pid, "status"] => ProcNode::PidStatusFile { pid },
3649 [_pid, "fd", fd] => {
3650 let fd = fd
3651 .parse::<u32>()
3652 .map_err(|_| proc_not_found_error(&normalized))?;
3653 self.proc_fd_entry(pid, fd)?;
3654 ProcNode::PidFdLink { pid, fd }
3655 }
3656 _ => return Err(proc_not_found_error(&normalized)),
3657 };
3658
3659 Ok(Some(node))
3660 }
3661
3662 fn proc_entry(&self, pid: u32) -> KernelResult<crate::process_table::ProcessEntry> {
3663 self.processes
3664 .get(pid)
3665 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}")))
3666 }
3667
3668 fn proc_fd_entry(&self, pid: u32, fd: u32) -> KernelResult<FdEntry> {
3669 lock_or_recover(&self.fd_tables)
3670 .get(pid)
3671 .and_then(|table| table.get(fd))
3672 .cloned()
3673 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}/fd/{fd}")))
3674 }
3675
3676 fn proc_read_file(
3677 &mut self,
3678 current_pid: Option<u32>,
3679 node: &ProcNode,
3680 ) -> KernelResult<Vec<u8>> {
3681 match node {
3682 ProcNode::SelfLink { .. }
3683 | ProcNode::PidCwdLink { .. }
3684 | ProcNode::PidFdLink { .. } => {
3685 let target = self.proc_symlink_target(node)?;
3686 self.read_file_internal(current_pid, &target)
3687 }
3688 ProcNode::MountsFile => Ok(self.proc_mounts_bytes()),
3689 ProcNode::CpuInfoFile => Ok(self.proc_cpuinfo_bytes()),
3690 ProcNode::MemInfoFile => Ok(self.proc_meminfo_bytes()),
3691 ProcNode::LoadAvgFile => Ok(self.proc_loadavg_bytes()),
3692 ProcNode::UptimeFile => Ok(self.proc_uptime_bytes()),
3693 ProcNode::VersionFile => Ok(self.proc_version_bytes()),
3694 ProcNode::PidCmdline { pid } => Ok(self.proc_cmdline_bytes(*pid)),
3695 ProcNode::PidEnviron { pid } => Ok(self.proc_environ_bytes(*pid)),
3696 ProcNode::PidStatFile { pid } => Ok(self.proc_stat_bytes(*pid)),
3697 ProcNode::PidStatusFile { pid } => Ok(self.proc_status_bytes(*pid)),
3698 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
3699 Err(KernelError::new(
3700 "EISDIR",
3701 format!(
3702 "illegal operation on a directory, read '{}'",
3703 self.proc_canonical_path(node)
3704 ),
3705 ))
3706 }
3707 }
3708 }
3709
3710 fn proc_stat(
3711 &mut self,
3712 current_pid: Option<u32>,
3713 node: &ProcNode,
3714 ) -> KernelResult<VirtualStat> {
3715 match node {
3716 ProcNode::SelfLink { .. }
3717 | ProcNode::PidCwdLink { .. }
3718 | ProcNode::PidFdLink { .. } => {
3719 let target = self.proc_symlink_target(node)?;
3720 self.stat_internal(current_pid, &target)
3721 }
3722 _ => self.proc_lstat(node),
3723 }
3724 }
3725
3726 fn proc_lstat(&self, node: &ProcNode) -> KernelResult<VirtualStat> {
3727 match node {
3728 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
3729 Ok(proc_dir_stat(proc_inode(node)))
3730 }
3731 ProcNode::MountsFile => Ok(proc_file_stat(
3732 proc_inode(node),
3733 self.proc_mounts_bytes().len() as u64,
3734 )),
3735 ProcNode::CpuInfoFile => Ok(proc_file_stat(
3736 proc_inode(node),
3737 self.proc_cpuinfo_bytes().len() as u64,
3738 )),
3739 ProcNode::MemInfoFile => Ok(proc_file_stat(
3740 proc_inode(node),
3741 self.proc_meminfo_bytes().len() as u64,
3742 )),
3743 ProcNode::LoadAvgFile => Ok(proc_file_stat(
3744 proc_inode(node),
3745 self.proc_loadavg_bytes().len() as u64,
3746 )),
3747 ProcNode::UptimeFile => Ok(proc_file_stat(
3748 proc_inode(node),
3749 self.proc_uptime_bytes().len() as u64,
3750 )),
3751 ProcNode::VersionFile => Ok(proc_file_stat(
3752 proc_inode(node),
3753 self.proc_version_bytes().len() as u64,
3754 )),
3755 ProcNode::PidCmdline { pid } => Ok(proc_file_stat(
3756 proc_inode(node),
3757 self.proc_cmdline_bytes(*pid).len() as u64,
3758 )),
3759 ProcNode::PidEnviron { pid } => Ok(proc_file_stat(
3760 proc_inode(node),
3761 self.proc_environ_bytes(*pid).len() as u64,
3762 )),
3763 ProcNode::PidStatFile { pid } => Ok(proc_file_stat(
3764 proc_inode(node),
3765 self.proc_stat_bytes(*pid).len() as u64,
3766 )),
3767 ProcNode::PidStatusFile { pid } => Ok(proc_file_stat(
3768 proc_inode(node),
3769 self.proc_status_bytes(*pid).len() as u64,
3770 )),
3771 ProcNode::SelfLink { .. }
3772 | ProcNode::PidCwdLink { .. }
3773 | ProcNode::PidFdLink { .. } => Ok(proc_symlink_stat(
3774 proc_inode(node),
3775 self.proc_read_link(node)?.len() as u64,
3776 )),
3777 }
3778 }
3779
3780 fn proc_read_link(&self, node: &ProcNode) -> KernelResult<String> {
3781 match node {
3782 ProcNode::SelfLink { .. }
3783 | ProcNode::PidCwdLink { .. }
3784 | ProcNode::PidFdLink { .. } => self.proc_symlink_target(node),
3785 _ => Err(KernelError::new(
3786 "EINVAL",
3787 format!(
3788 "invalid argument, readlink '{}'",
3789 self.proc_canonical_path(node)
3790 ),
3791 )),
3792 }
3793 }
3794
3795 fn proc_read_dir(
3796 &mut self,
3797 current_pid: Option<u32>,
3798 node: &ProcNode,
3799 ) -> KernelResult<Vec<String>> {
3800 match node {
3801 ProcNode::SelfLink { .. }
3802 | ProcNode::PidCwdLink { .. }
3803 | ProcNode::PidFdLink { .. } => {
3804 let target = self.proc_symlink_target(node)?;
3805 self.read_dir_internal(current_pid, &target)
3806 }
3807 ProcNode::RootDir => {
3808 let mut entries = self
3809 .processes
3810 .list_processes()
3811 .keys()
3812 .map(|pid| pid.to_string())
3813 .collect::<Vec<_>>();
3814 entries.push(String::from("cpuinfo"));
3815 entries.push(String::from("loadavg"));
3816 entries.push(String::from("meminfo"));
3817 entries.push(String::from("mounts"));
3818 entries.push(String::from("self"));
3819 entries.push(String::from("uptime"));
3820 entries.push(String::from("version"));
3821 entries.sort();
3822 Ok(entries)
3823 }
3824 ProcNode::PidDir { .. } => Ok(vec![
3825 String::from("cmdline"),
3826 String::from("cwd"),
3827 String::from("environ"),
3828 String::from("fd"),
3829 String::from("stat"),
3830 String::from("status"),
3831 ]),
3832 ProcNode::PidFdDir { pid } => {
3833 let tables = lock_or_recover(&self.fd_tables);
3834 let table = tables
3835 .get(*pid)
3836 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}/fd")))?;
3837 Ok(table.iter().map(|entry| entry.fd.to_string()).collect())
3838 }
3839 _ => Err(KernelError::new(
3840 "ENOTDIR",
3841 format!(
3842 "not a directory, scandir '{}'",
3843 self.proc_canonical_path(node)
3844 ),
3845 )),
3846 }
3847 }
3848
3849 fn proc_realpath(&self, current_pid: Option<u32>, node: &ProcNode) -> KernelResult<String> {
3850 match node {
3851 ProcNode::SelfLink { .. }
3852 | ProcNode::PidCwdLink { .. }
3853 | ProcNode::PidFdLink { .. } => {
3854 let target = self.proc_symlink_target(node)?;
3855 self.realpath_internal(current_pid, &target)
3856 }
3857 _ => Ok(self.proc_canonical_path(node)),
3858 }
3859 }
3860
3861 fn proc_symlink_target(&self, node: &ProcNode) -> KernelResult<String> {
3862 match node {
3863 ProcNode::SelfLink { pid } => Ok(format!("/proc/{pid}")),
3864 ProcNode::PidCwdLink { pid } => Ok(self.proc_entry(*pid)?.cwd),
3865 ProcNode::PidFdLink { pid, fd } => {
3866 Ok(self.proc_fd_entry(*pid, *fd)?.description.path().to_owned())
3867 }
3868 _ => Err(KernelError::new(
3869 "EINVAL",
3870 format!(
3871 "'{}' is not a symbolic link",
3872 self.proc_canonical_path(node)
3873 ),
3874 )),
3875 }
3876 }
3877
3878 fn proc_canonical_path(&self, node: &ProcNode) -> String {
3879 match node {
3880 ProcNode::RootDir => String::from("/proc"),
3881 ProcNode::MountsFile => String::from("/proc/mounts"),
3882 ProcNode::CpuInfoFile => String::from("/proc/cpuinfo"),
3883 ProcNode::MemInfoFile => String::from("/proc/meminfo"),
3884 ProcNode::LoadAvgFile => String::from("/proc/loadavg"),
3885 ProcNode::UptimeFile => String::from("/proc/uptime"),
3886 ProcNode::VersionFile => String::from("/proc/version"),
3887 ProcNode::SelfLink { pid } => format!("/proc/{pid}"),
3888 ProcNode::PidDir { pid } => format!("/proc/{pid}"),
3889 ProcNode::PidFdDir { pid } => format!("/proc/{pid}/fd"),
3890 ProcNode::PidCmdline { pid } => format!("/proc/{pid}/cmdline"),
3891 ProcNode::PidEnviron { pid } => format!("/proc/{pid}/environ"),
3892 ProcNode::PidCwdLink { pid } => format!("/proc/{pid}/cwd"),
3893 ProcNode::PidStatFile { pid } => format!("/proc/{pid}/stat"),
3894 ProcNode::PidStatusFile { pid } => format!("/proc/{pid}/status"),
3895 ProcNode::PidFdLink { pid, fd } => format!("/proc/{pid}/fd/{fd}"),
3896 }
3897 }
3898
3899 fn proc_cmdline_bytes(&self, pid: u32) -> Vec<u8> {
3900 let entry = self
3901 .processes
3902 .get(pid)
3903 .expect("process must exist while procfs path is resolved");
3904 let mut argv = vec![entry.command];
3905 argv.extend(entry.args);
3906 null_separated_bytes(argv)
3907 }
3908
3909 fn proc_environ_bytes(&self, pid: u32) -> Vec<u8> {
3910 let entry = self
3911 .processes
3912 .get(pid)
3913 .expect("process must exist while procfs path is resolved");
3914 null_separated_bytes(
3915 entry
3916 .env
3917 .into_iter()
3918 .map(|(key, value)| format!("{key}={value}"))
3919 .collect(),
3920 )
3921 }
3922
3923 fn proc_stat_bytes(&self, pid: u32) -> Vec<u8> {
3924 let entry = self
3925 .processes
3926 .get(pid)
3927 .expect("process must exist while procfs path is resolved");
3928 let command = entry.command.replace(')', "]");
3929 let state = match entry.status {
3930 ProcessStatus::Running => 'R',
3931 ProcessStatus::Stopped => 'T',
3932 ProcessStatus::Exited => 'Z',
3933 };
3934 format!(
3935 "{pid} ({command}) {state} {ppid} {pgid} {sid} 0 0 0 0 0 0 0 0 0 0 20 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
3936 ppid = entry.ppid,
3937 pgid = entry.pgid,
3938 sid = entry.sid,
3939 )
3940 .into_bytes()
3941 }
3942
3943 fn proc_mounts_bytes(&self) -> Vec<u8> {
3944 let mounts = if let Some(table) =
3945 (self.filesystem.inner().inner() as &dyn Any).downcast_ref::<MountTable>()
3946 {
3947 table.get_mounts()
3948 } else {
3949 vec![MountEntry {
3950 path: String::from("/"),
3951 plugin_id: String::from("root"),
3952 read_only: false,
3953 }]
3954 };
3955
3956 mounts
3957 .into_iter()
3958 .map(|mount| {
3959 let options = if mount.read_only { "ro" } else { "rw" };
3960 format!(
3961 "{source} {target} {fstype} {options} 0 0\n",
3962 source = mount.plugin_id,
3963 target = mount.path,
3964 fstype = mount.plugin_id,
3965 )
3966 })
3967 .collect::<String>()
3968 .into_bytes()
3969 }
3970
3971 fn proc_cpu_count(&self) -> usize {
3972 self.resource_limits().virtual_cpu_count.unwrap_or(1)
3973 }
3974
3975 fn proc_cpuinfo_bytes(&self) -> Vec<u8> {
3976 let mut body = String::new();
3977 for processor in 0..self.proc_cpu_count() {
3978 body.push_str(&format!(
3979 "processor\t: {processor}\nmodel name\t: secure-exec Virtual CPU\ncpu MHz\t\t: 1000.000\nsiblings\t: 1\ncpu cores\t: 1\n\n"
3980 ));
3981 }
3982 body.into_bytes()
3983 }
3984
3985 fn proc_mem_total_bytes(&self) -> u64 {
3986 self.resource_limits()
3987 .max_wasm_memory_bytes
3988 .or(self.resource_limits().max_filesystem_bytes)
3989 .unwrap_or(DEFAULT_MAX_OPEN_FDS as u64 * 1024 * 1024)
3990 }
3991
3992 fn proc_meminfo_bytes(&self) -> Vec<u8> {
3993 let total_kb = self.proc_mem_total_bytes().div_ceil(1024);
3994 let zero_kb = 0;
3995 format!(
3996 "MemTotal:{total_kb:>8} kB\nMemFree:{total_kb:>9} kB\nMemAvailable:{total_kb:>4} kB\nBuffers:{zero_kb:>9} kB\nCached:{zero_kb:>10} kB\n"
3997 )
3998 .into_bytes()
3999 }
4000
4001 fn proc_loadavg_bytes(&self) -> Vec<u8> {
4002 let processes = self.processes.list_processes();
4003 let running = processes
4004 .values()
4005 .filter(|process| process.status == ProcessStatus::Running)
4006 .count();
4007 let total = processes.len().max(1);
4008 let last_pid = processes.keys().next_back().copied().unwrap_or(0);
4009 format!("0.00 0.00 0.00 {running}/{total} {last_pid}\n").into_bytes()
4010 }
4011
4012 fn proc_uptime_bytes(&self) -> Vec<u8> {
4013 let uptime = self.boot_instant.elapsed().as_secs_f64();
4014 format!("{uptime:.2} {uptime:.2}\n").into_bytes()
4015 }
4016
4017 fn proc_version_bytes(&self) -> Vec<u8> {
4018 format!(
4019 "Linux version 6.8.0-agentos (agentos@localhost) #1 SMP boot={}\n",
4020 self.boot_time_ms
4021 )
4022 .into_bytes()
4023 }
4024
4025 fn proc_status_bytes(&self, pid: u32) -> Vec<u8> {
4026 let entry = self
4027 .processes
4028 .get(pid)
4029 .expect("process must exist while procfs path is resolved");
4030 let (state_code, state_name) = match entry.status {
4031 ProcessStatus::Running => ('R', "running"),
4032 ProcessStatus::Stopped => ('T', "stopped"),
4033 ProcessStatus::Exited => ('Z', "zombie"),
4034 };
4035 format!(
4036 "Name:\t{name}\nState:\t{state_code} ({state_name})\nPid:\t{pid}\nPPid:\t{ppid}\nUid:\t{uid}\t{euid}\t{euid}\t{euid}\nGid:\t{gid}\t{egid}\t{egid}\t{egid}\nVmSize:\t{:>8} kB\nVmRSS:\t{:>9} kB\nThreads:\t1\n",
4037 0,
4038 0,
4039 name = entry.command,
4040 ppid = entry.ppid,
4041 uid = entry.identity.uid,
4042 euid = entry.identity.euid,
4043 gid = entry.identity.gid,
4044 egid = entry.identity.egid,
4045 )
4046 .into_bytes()
4047 }
4048
4049 fn proc_read_file_from_open_path(
4050 &mut self,
4051 current_pid: Option<u32>,
4052 path: &str,
4053 ) -> KernelResult<Vec<u8>> {
4054 let node = self
4055 .resolve_proc_node(path, current_pid)?
4056 .ok_or_else(|| proc_not_found_error(path))?;
4057 self.proc_read_file(current_pid, &node)
4058 }
4059
4060 fn proc_stat_from_open_path(
4061 &mut self,
4062 current_pid: Option<u32>,
4063 path: &str,
4064 ) -> KernelResult<VirtualStat> {
4065 let node = self
4066 .resolve_proc_node(path, current_pid)?
4067 .ok_or_else(|| proc_not_found_error(path))?;
4068 self.proc_stat(current_pid, &node)
4069 }
4070
4071 fn filesystem_usage(&mut self) -> KernelResult<FileSystemUsage> {
4072 if let Some(usage) = self.filesystem_usage_cache.clone() {
4073 return Ok(usage);
4074 }
4075 let filesystem = self.raw_filesystem_mut();
4076 let filesystem_any = filesystem as &mut dyn Any;
4077 let usage = if let Some(mount_table) = filesystem_any.downcast_mut::<MountTable>() {
4078 mount_table.root_usage()?
4079 } else {
4080 measure_filesystem_usage(filesystem)?
4081 };
4082 self.filesystem_usage_cache = Some(usage.clone());
4083 Ok(usage)
4084 }
4085
4086 fn invalidate_filesystem_usage_cache(&mut self) {
4087 self.filesystem_usage_cache = None;
4088 }
4089
4090 fn update_filesystem_usage_cache_for_resize(&mut self, old_size: u64, new_size: u64) {
4091 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4092 usage.total_bytes = usage
4093 .total_bytes
4094 .saturating_sub(old_size)
4095 .saturating_add(new_size);
4096 }
4097 }
4098
4099 fn update_filesystem_usage_cache_for_write(
4100 &mut self,
4101 existing: Option<&VirtualStat>,
4102 new_size: u64,
4103 ) {
4104 if is_storage_directory(existing) {
4105 return;
4106 }
4107
4108 if let Some(stat) = existing {
4109 self.update_filesystem_usage_cache_for_resize(stat.size, new_size);
4110 } else {
4111 self.update_filesystem_usage_cache_for_inode_create(new_size);
4112 }
4113 }
4114
4115 fn update_filesystem_usage_cache_for_inode_create(&mut self, size: u64) {
4116 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4117 usage.total_bytes = usage.total_bytes.saturating_add(size);
4118 usage.inode_count = usage.inode_count.saturating_add(1);
4119 }
4120 }
4121
4122 fn update_filesystem_usage_cache_for_inode_creates(&mut self, count: usize) {
4123 if count == 0 {
4124 return;
4125 }
4126 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4127 usage.inode_count = usage.inode_count.saturating_add(count);
4128 }
4129 }
4130
4131 fn update_filesystem_usage_cache_for_inode_delete(&mut self, size: u64) {
4132 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4133 usage.total_bytes = usage.total_bytes.saturating_sub(size);
4134 usage.inode_count = usage.inode_count.saturating_sub(1);
4135 }
4136 }
4137
4138 fn update_filesystem_usage_cache_for_remove(&mut self, removed: Option<&VirtualStat>) {
4139 let Some(stat) = removed else {
4140 return;
4141 };
4142 if stat.is_directory || stat.nlink > 1 {
4143 return;
4144 }
4145 self.update_filesystem_usage_cache_for_inode_delete(stat.size);
4146 }
4147
4148 fn storage_stat(&mut self, path: &str) -> KernelResult<Option<VirtualStat>> {
4149 if is_virtual_device_storage_path(path) {
4150 return Ok(None);
4151 }
4152
4153 match self.raw_filesystem_mut().stat(path) {
4154 Ok(stat) => Ok(Some(stat)),
4155 Err(error) if error.code() == "ENOENT" => Ok(None),
4156 Err(error) => Err(error.into()),
4157 }
4158 }
4159
4160 fn storage_lstat(&mut self, path: &str) -> KernelResult<Option<VirtualStat>> {
4161 if is_virtual_device_storage_path(path) {
4162 return Ok(None);
4163 }
4164
4165 match self.raw_filesystem_mut().lstat(path) {
4166 Ok(stat) => Ok(Some(stat)),
4167 Err(error) if error.code() == "ENOENT" => Ok(None),
4168 Err(error) => Err(error.into()),
4169 }
4170 }
4171
4172 fn current_storage_file_size(&mut self, path: &str) -> KernelResult<u64> {
4173 Ok(self
4174 .storage_stat(path)?
4175 .filter(|stat| !stat.is_directory)
4176 .map(|stat| stat.size)
4177 .unwrap_or(0))
4178 }
4179
4180 fn apply_creation_mode(&mut self, path: &str, mode: u32, umask: u32) -> KernelResult<()> {
4181 let masked_mode = (mode & !0o777) | ((mode & 0o777) & !(umask & 0o777));
4182 Ok(self.filesystem.chmod(path, masked_mode)?)
4183 }
4184
4185 fn missing_directory_paths(
4186 &mut self,
4187 path: &str,
4188 recursive: bool,
4189 ) -> KernelResult<Vec<String>> {
4190 let normalized = normalize_path(path);
4191 if normalized == "/" {
4192 return Ok(Vec::new());
4193 }
4194
4195 if !recursive {
4196 return Ok(if self.storage_lstat(&normalized)?.is_none() {
4197 vec![normalized]
4198 } else {
4199 Vec::new()
4200 });
4201 }
4202
4203 let mut created = Vec::new();
4204 let mut current = String::from("/");
4205 for component in normalized
4206 .split('/')
4207 .filter(|component| !component.is_empty())
4208 {
4209 current = if current == "/" {
4210 format!("/{component}")
4211 } else {
4212 format!("{current}/{component}")
4213 };
4214 if self.storage_lstat(¤t)?.is_none() {
4215 created.push(current.clone());
4216 }
4217 }
4218 Ok(created)
4219 }
4220
4221 fn check_write_file_limits(&mut self, path: &str, new_size: u64) -> KernelResult<()> {
4222 let existing = self.storage_stat(path)?;
4223 self.check_write_file_limits_with_existing(path, existing.as_ref(), new_size)
4224 }
4225
4226 fn check_write_file_limits_with_existing(
4227 &mut self,
4228 path: &str,
4229 existing: Option<&VirtualStat>,
4230 new_size: u64,
4231 ) -> KernelResult<()> {
4232 if is_virtual_device_storage_path(path) {
4233 return Ok(());
4234 }
4235
4236 if let Some(existing) = existing {
4237 if is_storage_directory(Some(existing)) {
4238 return Ok(());
4239 }
4240 if new_size <= existing.size {
4241 return Ok(());
4242 }
4243
4244 let usage = self.filesystem_usage()?;
4245 self.resources.check_filesystem_usage(
4246 &usage,
4247 usage
4248 .total_bytes
4249 .saturating_sub(existing.size)
4250 .saturating_add(new_size),
4251 usage.inode_count,
4252 )?;
4253 return Ok(());
4254 }
4255
4256 let usage = self.filesystem_usage()?;
4257 self.resources.check_filesystem_usage(
4258 &usage,
4259 usage.total_bytes.saturating_add(new_size),
4260 usage.inode_count.saturating_add(1),
4261 )?;
4262 Ok(())
4263 }
4264
4265 fn check_create_dir_limits(&mut self, path: &str) -> KernelResult<()> {
4266 if is_virtual_device_storage_path(path) || self.storage_lstat(path)?.is_some() {
4267 return Ok(());
4268 }
4269
4270 let parent = parent_path(path);
4271 let Some(parent_stat) = self.storage_stat(&parent)? else {
4272 return Ok(());
4273 };
4274 if !parent_stat.is_directory {
4275 return Ok(());
4276 }
4277
4278 let usage = self.filesystem_usage()?;
4279 self.resources.check_filesystem_usage(
4280 &usage,
4281 usage.total_bytes,
4282 usage.inode_count.saturating_add(1),
4283 )?;
4284 Ok(())
4285 }
4286
4287 fn check_mkdir_limits(&mut self, path: &str, recursive: bool) -> KernelResult<()> {
4288 if is_virtual_device_storage_path(path) {
4289 return Ok(());
4290 }
4291
4292 if !recursive {
4293 return self.check_create_dir_limits(path);
4294 }
4295
4296 let usage = self.filesystem_usage()?;
4297 let new_inodes = count_missing_directory_components(self.raw_filesystem_mut(), path, true)?;
4298 self.resources.check_filesystem_usage(
4299 &usage,
4300 usage.total_bytes,
4301 usage.inode_count.saturating_add(new_inodes),
4302 )?;
4303 Ok(())
4304 }
4305
4306 fn check_symlink_limits(&mut self, target: &str, link_path: &str) -> KernelResult<()> {
4307 if is_virtual_device_storage_path(link_path) || self.storage_lstat(link_path)?.is_some() {
4308 return Ok(());
4309 }
4310
4311 let parent = parent_path(link_path);
4312 let Some(parent_stat) = self.storage_stat(&parent)? else {
4313 return Ok(());
4314 };
4315 if !parent_stat.is_directory {
4316 return Ok(());
4317 }
4318
4319 let usage = self.filesystem_usage()?;
4320 self.resources.check_filesystem_usage(
4321 &usage,
4322 usage.total_bytes.saturating_add(target.len() as u64),
4323 usage.inode_count.saturating_add(1),
4324 )?;
4325 Ok(())
4326 }
4327
4328 fn check_truncate_limits_with_existing(
4329 &mut self,
4330 path: &str,
4331 existing: Option<&VirtualStat>,
4332 length: u64,
4333 ) -> KernelResult<()> {
4334 if is_virtual_device_storage_path(path) {
4335 return Ok(());
4336 }
4337
4338 let Some(existing) = existing else {
4339 return Ok(());
4340 };
4341 if is_storage_directory(Some(existing)) {
4342 return Ok(());
4343 }
4344 self.check_path_resize_limits_with_existing(existing.size, length)
4345 }
4346
4347 fn check_rename_copy_up_limits(&mut self, old_path: &str, new_path: &str) -> KernelResult<()> {
4348 let max_bytes = self.resource_limits().max_filesystem_bytes;
4349 let max_inodes = self.resource_limits().max_inode_count;
4350 let filesystem_any = self.raw_filesystem_mut() as &mut dyn Any;
4351
4352 if let Some(root) = filesystem_any.downcast_mut::<RootFileSystem>() {
4353 root.check_rename_copy_up_limits(old_path, new_path, max_bytes, max_inodes)?;
4354 return Ok(());
4355 }
4356
4357 if let Some(mount_table) = filesystem_any.downcast_mut::<MountTable>() {
4358 mount_table.check_rename_copy_up_limits(old_path, new_path, max_bytes, max_inodes)?;
4359 }
4360
4361 Ok(())
4362 }
4363
4364 fn check_path_resize_limits(&mut self, path: &str, new_size: u64) -> KernelResult<()> {
4365 if is_virtual_device_storage_path(path) {
4366 return Ok(());
4367 }
4368
4369 let Some(existing) = self.storage_stat(path)? else {
4370 return Ok(());
4371 };
4372 if existing.is_directory {
4373 return Ok(());
4374 }
4375 self.check_path_resize_limits_with_existing(existing.size, new_size)
4376 }
4377
4378 fn check_path_resize_limits_with_existing(
4379 &mut self,
4380 existing_size: u64,
4381 new_size: u64,
4382 ) -> KernelResult<()> {
4383 if new_size <= existing_size {
4384 return Ok(());
4385 }
4386
4387 let usage = self.filesystem_usage()?;
4388 self.resources.check_filesystem_usage(
4389 &usage,
4390 usage
4391 .total_bytes
4392 .saturating_sub(existing_size)
4393 .saturating_add(new_size),
4394 usage.inode_count,
4395 )?;
4396 Ok(())
4397 }
4398
4399 fn blocking_read_timeout(&self) -> Option<Duration> {
4400 self.resources
4401 .limits()
4402 .max_blocking_read_ms
4403 .map(Duration::from_millis)
4404 }
4405
4406 fn close_special_resource_if_needed(&self, description: &Arc<FileDescription>, filetype: u8) {
4407 close_special_resource_if_needed(
4408 &self.file_locks,
4409 &self.pipes,
4410 &self.ptys,
4411 description,
4412 filetype,
4413 );
4414 }
4415}
4416
4417impl KernelVm<MountTable> {
4418 fn check_mount_permissions(&self, path: &str) -> KernelResult<()> {
4419 self.filesystem
4420 .check_path(FsOperation::Write, path)
4421 .map_err(KernelError::from)?;
4422 if is_sensitive_mount_path(path) {
4423 self.filesystem
4424 .check_path(FsOperation::MountSensitive, path)
4425 .map_err(KernelError::from)?;
4426 }
4427 Ok(())
4428 }
4429
4430 pub fn mount_filesystem(
4431 &mut self,
4432 path: &str,
4433 filesystem: impl VirtualFileSystem + 'static,
4434 options: MountOptions,
4435 ) -> KernelResult<()> {
4436 self.assert_not_terminated()?;
4437 self.check_mount_permissions(path)?;
4438 self.filesystem
4439 .inner_mut()
4440 .inner_mut()
4441 .mount(path, filesystem, options)
4442 .map_err(KernelError::from)
4443 }
4444
4445 pub fn mount_boxed_filesystem(
4446 &mut self,
4447 path: &str,
4448 filesystem: Box<dyn MountedFileSystem>,
4449 options: MountOptions,
4450 ) -> KernelResult<()> {
4451 self.assert_not_terminated()?;
4452 self.check_mount_permissions(path)?;
4453 self.filesystem
4454 .inner_mut()
4455 .inner_mut()
4456 .mount_boxed(path, filesystem, options)
4457 .map_err(KernelError::from)
4458 }
4459
4460 pub fn unmount_filesystem(&mut self, path: &str) -> KernelResult<()> {
4461 self.assert_not_terminated()?;
4462 self.check_mount_permissions(path)?;
4463 self.filesystem
4464 .inner_mut()
4465 .inner_mut()
4466 .unmount(path)
4467 .map_err(KernelError::from)
4468 }
4469
4470 pub fn mounted_filesystems(&self) -> Vec<MountEntry> {
4471 self.filesystem.inner().inner().get_mounts()
4472 }
4473
4474 pub fn root_filesystem_mut(&mut self) -> Option<&mut RootFileSystem> {
4475 self.filesystem
4476 .inner_mut()
4477 .inner_mut()
4478 .root_virtual_filesystem_mut::<RootFileSystem>()
4479 }
4480
4481 pub fn snapshot_root_filesystem(&mut self) -> KernelResult<RootFilesystemSnapshot> {
4482 let usage = self.filesystem_usage()?;
4483 self.resources
4484 .check_filesystem_usage(&usage, usage.total_bytes, usage.inode_count)?;
4485 let root = self
4486 .root_filesystem_mut()
4487 .ok_or_else(|| KernelError::new("EINVAL", "native root filesystem is not available"))?;
4488 root.snapshot().map_err(KernelError::from)
4489 }
4490}
4491
4492#[derive(Default)]
4493struct StubDriverState {
4494 exit_code: Option<i32>,
4495 on_exit: Option<ProcessExitCallback>,
4496 kill_signals: Vec<i32>,
4497}
4498
4499#[derive(Default)]
4500struct StubDriverProcess {
4501 state: Mutex<StubDriverState>,
4502 waiters: Condvar,
4503}
4504
4505impl StubDriverProcess {
4506 fn finish(&self, exit_code: i32) {
4507 let callback = {
4508 let mut state = lock_or_recover(&self.state);
4509 if state.exit_code.is_some() {
4510 return;
4511 }
4512 state.exit_code = Some(exit_code);
4513 self.waiters.notify_all();
4514 state.on_exit.clone()
4515 };
4516
4517 if let Some(callback) = callback {
4518 callback(exit_code);
4519 }
4520 }
4521
4522 fn kill_signals(&self) -> Vec<i32> {
4523 lock_or_recover(&self.state).kill_signals.clone()
4524 }
4525}
4526
4527impl DriverProcess for StubDriverProcess {
4528 fn kill(&self, signal: i32) {
4529 {
4530 let mut state = lock_or_recover(&self.state);
4531 state.kill_signals.push(signal);
4532 }
4533 if matches!(
4534 signal,
4535 crate::process_table::SIGCHLD | SIGCONT | SIGSTOP | SIGTSTP | SIGWINCH
4536 ) {
4537 return;
4538 }
4539 self.finish(128 + signal);
4540 }
4541
4542 fn wait(&self, timeout: Duration) -> Option<i32> {
4543 let state = lock_or_recover(&self.state);
4544 if let Some(code) = state.exit_code {
4545 return Some(code);
4546 }
4547
4548 let (state, _) = wait_timeout_or_recover(&self.waiters, state, timeout);
4549 state.exit_code
4550 }
4551
4552 fn set_on_exit(&self, callback: ProcessExitCallback) {
4553 let maybe_exit = {
4554 let mut state = lock_or_recover(&self.state);
4555 state.on_exit = Some(callback.clone());
4556 state.exit_code
4557 };
4558
4559 if let Some(code) = maybe_exit {
4560 callback(code);
4561 }
4562 }
4563}
4564
4565impl From<VfsError> for KernelError {
4566 fn from(error: VfsError) -> Self {
4567 map_error(error.code(), error.to_string())
4568 }
4569}
4570
4571fn lock_or_recover<'a, T>(mutex: &'a Mutex<T>) -> MutexGuard<'a, T> {
4572 match mutex.lock() {
4573 Ok(guard) => guard,
4574 Err(poisoned) => poisoned.into_inner(),
4575 }
4576}
4577
4578fn wait_timeout_or_recover<'a, T>(
4579 condvar: &Condvar,
4580 guard: MutexGuard<'a, T>,
4581 timeout: Duration,
4582) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
4583 match condvar.wait_timeout(guard, timeout) {
4584 Ok(result) => result,
4585 Err(poisoned) => poisoned.into_inner(),
4586 }
4587}
4588
4589fn is_sensitive_mount_path(path: &str) -> bool {
4590 let normalized = crate::vfs::normalize_path(path);
4591 normalized == "/"
4592 || normalized == "/etc"
4593 || normalized.starts_with("/etc/")
4594 || normalized == "/proc"
4595 || normalized.starts_with("/proc/")
4596}
4597
4598impl From<FdTableError> for KernelError {
4599 fn from(error: FdTableError) -> Self {
4600 map_error(error.code(), error.to_string())
4601 }
4602}
4603
4604impl From<PipeError> for KernelError {
4605 fn from(error: PipeError) -> Self {
4606 map_error(error.code(), error.to_string())
4607 }
4608}
4609
4610impl From<PtyError> for KernelError {
4611 fn from(error: PtyError) -> Self {
4612 map_error(error.code(), error.to_string())
4613 }
4614}
4615
4616impl From<ProcessTableError> for KernelError {
4617 fn from(error: ProcessTableError) -> Self {
4618 map_error(error.code(), error.to_string())
4619 }
4620}
4621
4622impl From<PermissionError> for KernelError {
4623 fn from(error: PermissionError) -> Self {
4624 map_error(error.code(), error.to_string())
4625 }
4626}
4627
4628impl From<ResourceError> for KernelError {
4629 fn from(error: ResourceError) -> Self {
4630 map_error(error.code(), error.to_string())
4631 }
4632}
4633
4634impl From<SocketTableError> for KernelError {
4635 fn from(error: SocketTableError) -> Self {
4636 map_error(error.code(), error.to_string())
4637 }
4638}
4639
4640impl From<RootFilesystemError> for KernelError {
4641 fn from(error: RootFilesystemError) -> Self {
4642 map_error("EINVAL", error.to_string())
4643 }
4644}
4645
4646fn map_dns_resolver_error(error: crate::dns::DnsResolverError) -> KernelError {
4647 let code = match error.kind() {
4648 DnsResolverErrorKind::InvalidInput => "EINVAL",
4649 DnsResolverErrorKind::LookupFailed => "EHOSTUNREACH",
4650 };
4651 map_error(code, error.to_string())
4652}
4653
4654fn map_error(code: &'static str, message: String) -> KernelError {
4655 let trimmed = strip_error_prefix(code, &message)
4656 .map(ToOwned::to_owned)
4657 .unwrap_or(message);
4658 KernelError::new(code, trimmed)
4659}
4660
4661fn strip_error_prefix<'a>(code: &str, message: &'a str) -> Option<&'a str> {
4662 let prefix = format!("{code}: ");
4663 message.strip_prefix(&prefix)
4664}
4665
4666fn parse_dev_fd_path(path: &str) -> KernelResult<Option<u32>> {
4667 let Some(raw_fd) = path.strip_prefix("/dev/fd/") else {
4668 return Ok(None);
4669 };
4670 if raw_fd.is_empty() {
4671 return Err(KernelError::new(
4672 "EBADF",
4673 format!("bad file descriptor: {path}"),
4674 ));
4675 }
4676 let fd = raw_fd
4677 .parse::<u32>()
4678 .map_err(|_| KernelError::new("EBADF", format!("bad file descriptor: {path}")))?;
4679 Ok(Some(fd))
4680}
4681
4682fn count_missing_directory_components<F: VirtualFileSystem>(
4683 filesystem: &mut F,
4684 path: &str,
4685 include_final: bool,
4686) -> VfsResult<usize> {
4687 let normalized = normalize_path(path);
4688 let parts = normalized
4689 .split('/')
4690 .filter(|part| !part.is_empty())
4691 .collect::<Vec<_>>();
4692 let limit = if include_final {
4693 parts.len()
4694 } else {
4695 parts.len().saturating_sub(1)
4696 };
4697
4698 let mut current = String::from("/");
4699 for (index, part) in parts.iter().take(limit).enumerate() {
4700 let candidate = if current == "/" {
4701 format!("/{}", part)
4702 } else {
4703 format!("{current}/{}", part)
4704 };
4705
4706 match filesystem.stat(&candidate) {
4707 Ok(stat) => {
4708 if !stat.is_directory {
4709 return Err(VfsError::new(
4710 "ENOTDIR",
4711 format!("not a directory, mkdir '{candidate}'"),
4712 ));
4713 }
4714 current = candidate;
4715 }
4716 Err(error) if error.code() == "ENOENT" => {
4717 return Ok(limit.saturating_sub(index));
4718 }
4719 Err(error) => return Err(error),
4720 }
4721 }
4722
4723 Ok(0)
4724}
4725
4726fn parent_path(path: &str) -> String {
4727 let normalized = normalize_path(path);
4728 let Some((head, _)) = normalized.rsplit_once('/') else {
4729 return String::from("/");
4730 };
4731
4732 if head.is_empty() {
4733 String::from("/")
4734 } else {
4735 String::from(head)
4736 }
4737}
4738
4739fn join_absolute_path(parent: &str, child: &str) -> String {
4740 if parent == "/" {
4741 format!("/{child}")
4742 } else {
4743 format!("{parent}/{child}")
4744 }
4745}
4746
4747fn is_virtual_device_storage_path(path: &str) -> bool {
4748 matches!(
4749 path,
4750 "/dev/null" | "/dev/zero" | "/dev/stdin" | "/dev/stdout" | "/dev/stderr" | "/dev/urandom"
4751 ) || path == "/dev"
4752 || path == "/dev/fd"
4753 || path == "/dev/pts"
4754 || path.starts_with("/dev/fd/")
4755 || path.starts_with("/dev/pts/")
4756}
4757
4758fn is_storage_directory(stat: Option<&VirtualStat>) -> bool {
4759 stat.is_some_and(|stat| stat.is_directory && !stat.is_symbolic_link)
4760}
4761
4762fn is_proc_path(path: &str) -> bool {
4763 let normalized = normalize_path(path);
4764 normalized == "/proc" || normalized.starts_with("/proc/")
4765}
4766
4767fn is_agentos_path(path: &str) -> bool {
4768 let normalized = normalize_path(path);
4769 normalized == "/etc/agentos" || normalized.starts_with("/etc/agentos/")
4770}
4771
4772fn open_requires_write_access(flags: u32) -> bool {
4773 flags & (O_CREAT | O_EXCL | O_TRUNC) != 0 || (flags & 0b11) != crate::fd_table::O_RDONLY
4774}
4775
4776fn checked_write_end(offset: u64, len: usize) -> KernelResult<u64> {
4777 offset
4778 .checked_add(len as u64)
4779 .ok_or_else(|| KernelError::new("EINVAL", "write offset out of range"))
4780}
4781
4782fn filetype_for_path(path: &str, stat: &VirtualStat) -> u8 {
4783 if stat.is_directory {
4784 FILETYPE_DIRECTORY
4785 } else if path.starts_with("/dev/") {
4786 FILETYPE_CHARACTER_DEVICE
4787 } else if stat.is_symbolic_link {
4788 FILETYPE_SYMBOLIC_LINK
4789 } else {
4790 FILETYPE_REGULAR_FILE
4791 }
4792}
4793
4794fn synthetic_character_device_stat(ino: u64) -> VirtualStat {
4795 let now = now_ms();
4796 VirtualStat {
4797 mode: 0o666,
4798 size: 0,
4799 blocks: 0,
4800 dev: 2,
4801 rdev: 0,
4802 is_directory: false,
4803 is_symbolic_link: false,
4804 atime_ms: now,
4805 atime_nsec: 0,
4806 mtime_ms: now,
4807 mtime_nsec: 0,
4808 ctime_ms: now,
4809 ctime_nsec: 0,
4810 birthtime_ms: now,
4811 ino,
4812 nlink: 1,
4813 uid: 0,
4814 gid: 0,
4815 }
4816}
4817
4818fn proc_dir_stat(ino: u64) -> VirtualStat {
4819 let now = now_ms();
4820 VirtualStat {
4821 mode: 0o555,
4822 size: 0,
4823 blocks: 0,
4824 dev: 3,
4825 rdev: 0,
4826 is_directory: true,
4827 is_symbolic_link: false,
4828 atime_ms: now,
4829 atime_nsec: 0,
4830 mtime_ms: now,
4831 mtime_nsec: 0,
4832 ctime_ms: now,
4833 ctime_nsec: 0,
4834 birthtime_ms: now,
4835 ino,
4836 nlink: 2,
4837 uid: 0,
4838 gid: 0,
4839 }
4840}
4841
4842fn proc_file_stat(ino: u64, size: u64) -> VirtualStat {
4843 let now = now_ms();
4844 VirtualStat {
4845 mode: 0o444,
4846 size,
4847 blocks: if size == 0 { 0 } else { size.div_ceil(512) },
4848 dev: 3,
4849 rdev: 0,
4850 is_directory: false,
4851 is_symbolic_link: false,
4852 atime_ms: now,
4853 atime_nsec: 0,
4854 mtime_ms: now,
4855 mtime_nsec: 0,
4856 ctime_ms: now,
4857 ctime_nsec: 0,
4858 birthtime_ms: now,
4859 ino,
4860 nlink: 1,
4861 uid: 0,
4862 gid: 0,
4863 }
4864}
4865
4866fn proc_symlink_stat(ino: u64, size: u64) -> VirtualStat {
4867 let now = now_ms();
4868 VirtualStat {
4869 mode: 0o777,
4870 size,
4871 blocks: if size == 0 { 0 } else { size.div_ceil(512) },
4872 dev: 3,
4873 rdev: 0,
4874 is_directory: false,
4875 is_symbolic_link: true,
4876 atime_ms: now,
4877 atime_nsec: 0,
4878 mtime_ms: now,
4879 mtime_nsec: 0,
4880 ctime_ms: now,
4881 ctime_nsec: 0,
4882 birthtime_ms: now,
4883 ino,
4884 nlink: 1,
4885 uid: 0,
4886 gid: 0,
4887 }
4888}
4889
4890fn proc_filetype(node: &ProcNode) -> u8 {
4891 match node {
4892 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
4893 FILETYPE_DIRECTORY
4894 }
4895 ProcNode::SelfLink { .. } | ProcNode::PidCwdLink { .. } | ProcNode::PidFdLink { .. } => {
4896 FILETYPE_SYMBOLIC_LINK
4897 }
4898 ProcNode::MountsFile
4899 | ProcNode::CpuInfoFile
4900 | ProcNode::MemInfoFile
4901 | ProcNode::LoadAvgFile
4902 | ProcNode::UptimeFile
4903 | ProcNode::VersionFile
4904 | ProcNode::PidCmdline { .. }
4905 | ProcNode::PidEnviron { .. }
4906 | ProcNode::PidStatFile { .. }
4907 | ProcNode::PidStatusFile { .. } => FILETYPE_REGULAR_FILE,
4908 }
4909}
4910
4911fn proc_inode(node: &ProcNode) -> u64 {
4912 match node {
4913 ProcNode::RootDir => 0xfffe_0001,
4914 ProcNode::MountsFile => 0xfffe_0002,
4915 ProcNode::CpuInfoFile => 0xfffe_0003,
4916 ProcNode::MemInfoFile => 0xfffe_0004,
4917 ProcNode::LoadAvgFile => 0xfffe_0005,
4918 ProcNode::UptimeFile => 0xfffe_0006,
4919 ProcNode::VersionFile => 0xfffe_0007,
4920 ProcNode::SelfLink { pid } => 0xfffe_1000 + u64::from(*pid),
4921 ProcNode::PidDir { pid } => 0xfffe_2000 + u64::from(*pid),
4922 ProcNode::PidFdDir { pid } => 0xfffe_3000 + u64::from(*pid),
4923 ProcNode::PidCmdline { pid } => 0xfffe_4000 + u64::from(*pid),
4924 ProcNode::PidEnviron { pid } => 0xfffe_5000 + u64::from(*pid),
4925 ProcNode::PidCwdLink { pid } => 0xfffe_6000 + u64::from(*pid),
4926 ProcNode::PidStatFile { pid } => 0xfffe_7000 + u64::from(*pid),
4927 ProcNode::PidStatusFile { pid } => 0xfffe_8000 + u64::from(*pid),
4928 ProcNode::PidFdLink { pid, fd } => 0xffff_0000 + ((u64::from(*pid)) << 8) + u64::from(*fd),
4929 }
4930}
4931
4932fn null_separated_bytes(parts: Vec<String>) -> Vec<u8> {
4933 if parts.is_empty() {
4934 return Vec::new();
4935 }
4936
4937 let mut bytes = parts.join("\0").into_bytes();
4938 bytes.push(0);
4939 bytes
4940}
4941
4942fn proc_not_found_error(path: &str) -> KernelError {
4943 KernelError::new(
4944 "ENOENT",
4945 format!("no such file or directory, stat '{path}'"),
4946 )
4947}
4948
4949fn read_only_filesystem_error(path: &str) -> KernelError {
4950 KernelError::new("EROFS", format!("read-only filesystem: {path}"))
4951}
4952
4953fn now_ms() -> u64 {
4954 SystemTime::now()
4955 .duration_since(UNIX_EPOCH)
4956 .unwrap_or_default()
4957 .as_millis() as u64
4958}
4959
4960impl<F> Drop for KernelVm<F> {
4961 fn drop(&mut self) {
4962 if !self.terminated {
4963 dispose_kernel_vm_resources(self);
4964 }
4965 }
4966}
4967
4968#[cfg(test)]
4969mod tests {
4970 use super::*;
4971 use crate::vfs::MemoryFileSystem;
4972 use std::panic::{catch_unwind, AssertUnwindSafe};
4973 use std::thread;
4974
4975 struct RetainedKernelResources {
4976 process: KernelProcessHandle,
4977 fd_tables: Arc<Mutex<FdTableManager>>,
4978 pipes: PipeManager,
4979 ptys: PtyManager,
4980 sockets: SocketTable,
4981 driver_pids: Arc<Mutex<BTreeMap<String, BTreeSet<u32>>>>,
4982 }
4983
4984 fn kernel_with_live_resources() -> (KernelVm<MemoryFileSystem>, RetainedKernelResources) {
4985 let mut config = KernelVmConfig::new("vm-drop-resources");
4986 config.permissions = Permissions::allow_all();
4987 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
4988 kernel
4989 .register_driver(CommandDriver::new("shell", ["sh"]))
4990 .expect("register shell");
4991
4992 let process = kernel
4993 .spawn_process(
4994 "sh",
4995 Vec::new(),
4996 SpawnOptions {
4997 requester_driver: Some(String::from("shell")),
4998 ..SpawnOptions::default()
4999 },
5000 )
5001 .expect("spawn shell");
5002 let _ = kernel.open_pipe("shell", process.pid()).expect("open pipe");
5003 let _ = kernel.open_pty("shell", process.pid()).expect("open pty");
5004 let socket = kernel
5005 .socket_create("shell", process.pid(), SocketSpec::tcp())
5006 .expect("create socket");
5007 kernel
5008 .socket_set_state("shell", process.pid(), socket, SocketState::Listening)
5009 .expect("mark listener");
5010
5011 let retained = RetainedKernelResources {
5012 process: process.clone(),
5013 fd_tables: Arc::clone(&kernel.fd_tables),
5014 pipes: kernel.pipes.clone(),
5015 ptys: kernel.ptys.clone(),
5016 sockets: kernel.sockets.clone(),
5017 driver_pids: Arc::clone(&kernel.driver_pids),
5018 };
5019
5020 assert_eq!(lock_or_recover(retained.fd_tables.as_ref()).len(), 1);
5021 assert_eq!(retained.pipes.pipe_count(), 1);
5022 assert_eq!(retained.ptys.pty_count(), 1);
5023 assert_eq!(retained.sockets.snapshot().sockets, 1);
5024
5025 (kernel, retained)
5026 }
5027
5028 fn assert_kernel_drop_released_resources(retained: &RetainedKernelResources) {
5029 assert_eq!(retained.process.wait(Duration::from_millis(50)), Some(143));
5030 assert_eq!(retained.process.kill_signals(), vec![15]);
5031 assert!(
5032 lock_or_recover(retained.fd_tables.as_ref()).is_empty(),
5033 "kernel drop should remove fd tables"
5034 );
5035 assert_eq!(
5036 retained.pipes.pipe_count(),
5037 0,
5038 "kernel drop should close pipes"
5039 );
5040 assert_eq!(
5041 retained.ptys.pty_count(),
5042 0,
5043 "kernel drop should close PTYs"
5044 );
5045 assert_eq!(
5046 retained.sockets.snapshot().sockets,
5047 0,
5048 "kernel drop should reclaim sockets"
5049 );
5050 assert!(
5051 lock_or_recover(retained.driver_pids.as_ref()).is_empty(),
5052 "kernel drop should clear driver-owned pid tracking"
5053 );
5054 }
5055
5056 #[test]
5057 fn setpgid_rejects_joining_a_process_group_owned_by_another_driver() {
5058 let kernel = KernelVm::new(MemoryFileSystem::new(), KernelVmConfig::new("vm-setpgid"));
5059
5060 let leader_pid = kernel.processes.allocate_pid().expect("allocate pid");
5061 kernel.processes.register(
5062 leader_pid,
5063 String::from("driver-a"),
5064 String::from("sh"),
5065 Vec::new(),
5066 ProcessContext {
5067 pid: leader_pid,
5068 ppid: 0,
5069 env: BTreeMap::new(),
5070 cwd: String::from("/"),
5071 umask: DEFAULT_PROCESS_UMASK,
5072 fds: Default::default(),
5073 identity: ProcessIdentity::default(),
5074 blocked_signals: SignalSet::empty(),
5075 pending_signals: SignalSet::empty(),
5076 },
5077 Arc::new(StubDriverProcess::default()),
5078 );
5079
5080 let peer_pid = kernel.processes.allocate_pid().expect("allocate pid");
5081 kernel.processes.register(
5082 peer_pid,
5083 String::from("driver-b"),
5084 String::from("sh"),
5085 Vec::new(),
5086 ProcessContext {
5087 pid: peer_pid,
5088 ppid: leader_pid,
5089 env: BTreeMap::new(),
5090 cwd: String::from("/"),
5091 umask: DEFAULT_PROCESS_UMASK,
5092 fds: Default::default(),
5093 identity: ProcessIdentity::default(),
5094 blocked_signals: SignalSet::empty(),
5095 pending_signals: SignalSet::empty(),
5096 },
5097 Arc::new(StubDriverProcess::default()),
5098 );
5099
5100 lock_or_recover(&kernel.driver_pids)
5101 .entry(String::from("driver-a"))
5102 .or_default()
5103 .insert(leader_pid);
5104 lock_or_recover(&kernel.driver_pids)
5105 .entry(String::from("driver-b"))
5106 .or_default()
5107 .insert(peer_pid);
5108
5109 let error = kernel
5110 .setpgid("driver-b", peer_pid, leader_pid)
5111 .expect_err("cross-driver process-group join should be denied");
5112 assert_eq!(error.code(), "EPERM");
5113 }
5114
5115 #[test]
5116 fn sigprocmask_and_sigpending_require_process_ownership() {
5117 let mut kernel = KernelVm::new(MemoryFileSystem::new(), KernelVmConfig::new("vm-sigmask"));
5118 let process = kernel
5119 .register_process(
5120 String::from("driver-a"),
5121 String::from("sleep"),
5122 Vec::new(),
5123 ProcessContext {
5124 pid: 0,
5125 ppid: 0,
5126 env: BTreeMap::new(),
5127 cwd: String::from("/"),
5128 umask: DEFAULT_PROCESS_UMASK,
5129 fds: Default::default(),
5130 identity: ProcessIdentity::default(),
5131 blocked_signals: SignalSet::empty(),
5132 pending_signals: SignalSet::empty(),
5133 },
5134 None,
5135 )
5136 .expect("create virtual process");
5137 let mask =
5138 SignalSet::from_signal(crate::process_table::SIGCHLD).expect("SIGCHLD should be valid");
5139
5140 let previous = kernel
5141 .sigprocmask("driver-a", process.pid(), SigmaskHow::Block, mask)
5142 .expect("owner should update signal mask");
5143 assert_eq!(previous, SignalSet::empty());
5144 assert_eq!(
5145 kernel
5146 .sigpending("driver-a", process.pid())
5147 .expect("owner should read pending signals"),
5148 SignalSet::empty()
5149 );
5150
5151 let error = kernel
5152 .sigprocmask("driver-b", process.pid(), SigmaskHow::Block, mask)
5153 .expect_err("foreign driver should be rejected");
5154 assert_eq!(error.code(), "EPERM");
5155 let error = kernel
5156 .sigpending("driver-b", process.pid())
5157 .expect_err("foreign driver should be rejected");
5158 assert_eq!(error.code(), "EPERM");
5159 }
5160
5161 #[test]
5162 fn cleanup_process_resources_blocks_concurrent_dup2_until_pipe_cleanup_finishes() {
5163 let fd_tables = Arc::new(Mutex::new(FdTableManager::new()));
5164 let file_locks = FileLockManager::new();
5165 let pipes = PipeManager::new();
5166 let ptys = PtyManager::new();
5167 let sockets = SocketTable::new();
5168 let driver_pids = Arc::new(Mutex::new(BTreeMap::from([(
5169 String::from("driver"),
5170 BTreeSet::from([41]),
5171 )])));
5172 let pipe = pipes.create_pipe();
5173
5174 {
5175 let mut tables = lock_or_recover(fd_tables.as_ref());
5176 let table = tables.create(41);
5177 table
5178 .open_with(
5179 Arc::clone(&pipe.read.description),
5180 pipe.read.filetype,
5181 Some(10),
5182 )
5183 .expect("open pipe read end");
5184 table
5185 .open_with(
5186 Arc::clone(&pipe.write.description),
5187 pipe.write.filetype,
5188 Some(11),
5189 )
5190 .expect("open pipe write end");
5191 }
5192
5193 let hook_state = Arc::new((Mutex::new((false, false)), Condvar::new()));
5194 let hook_state_for_cleanup = Arc::clone(&hook_state);
5195 set_cleanup_process_resources_test_hook(Some(Arc::new(move || {
5196 let (state, wake) = &*hook_state_for_cleanup;
5197 let mut state = lock_or_recover(state);
5198 state.0 = true;
5199 wake.notify_all();
5200 while !state.1 {
5201 state = wake.wait(state).expect("wait for cleanup release");
5202 }
5203 })));
5204
5205 let fd_tables_for_cleanup = Arc::clone(&fd_tables);
5206 let pipes_for_cleanup = pipes.clone();
5207 let driver_pids_for_cleanup = Arc::clone(&driver_pids);
5208 let cleanup_thread = thread::spawn(move || {
5209 cleanup_process_resources(
5210 fd_tables_for_cleanup.as_ref(),
5211 &file_locks,
5212 &pipes_for_cleanup,
5213 &ptys,
5214 &sockets,
5215 driver_pids_for_cleanup.as_ref(),
5216 41,
5217 );
5218 });
5219
5220 {
5221 let (state, wake) = &*hook_state;
5222 let mut state = lock_or_recover(state);
5223 while !state.0 {
5224 state = wake.wait(state).expect("wait for cleanup hook");
5225 }
5226 }
5227
5228 let fd_tables_for_dup = Arc::clone(&fd_tables);
5229 let dup_thread = thread::spawn(move || {
5230 let mut tables = lock_or_recover(fd_tables_for_dup.as_ref());
5231 let Some(table) = tables.get_mut(41) else {
5232 return Err(String::from("ESRCH"));
5233 };
5234 table.dup2(10, 12).map_err(|error| error.code().to_string())
5235 });
5236
5237 {
5238 let (state, wake) = &*hook_state;
5239 let mut state = lock_or_recover(state);
5240 state.1 = true;
5241 wake.notify_all();
5242 }
5243
5244 cleanup_thread.join().expect("cleanup thread should finish");
5245 let dup_result = dup_thread.join().expect("dup thread should finish");
5246 set_cleanup_process_resources_test_hook(None);
5247
5248 assert_eq!(dup_result, Err(String::from("ESRCH")));
5249 assert!(
5250 lock_or_recover(fd_tables.as_ref()).get(41).is_none(),
5251 "cleanup should remove the process FD table"
5252 );
5253 assert_eq!(pipes.pipe_count(), 0, "pipe cleanup should not leak");
5254 assert!(
5255 lock_or_recover(driver_pids.as_ref())
5256 .get("driver")
5257 .is_none_or(|pids| pids.is_empty()),
5258 "driver ownership should be cleared"
5259 );
5260 }
5261
5262 #[test]
5263 fn drop_disposes_live_kernel_vm_resources() {
5264 let (kernel, retained) = kernel_with_live_resources();
5265 drop(kernel);
5266 assert_kernel_drop_released_resources(&retained);
5267 }
5268
5269 #[test]
5270 fn drop_during_panic_still_disposes_live_kernel_vm_resources() {
5271 let retained = Arc::new(Mutex::new(None::<RetainedKernelResources>));
5272 let retained_for_panic = Arc::clone(&retained);
5273
5274 let panic_result = catch_unwind(AssertUnwindSafe(move || {
5275 let (kernel, resources) = kernel_with_live_resources();
5276 *lock_or_recover(retained_for_panic.as_ref()) = Some(resources);
5277 let _kernel = kernel;
5278 panic!("intentional panic to exercise KernelVm::drop");
5279 }));
5280
5281 assert!(panic_result.is_err(), "panic should be observed");
5282 let retained = lock_or_recover(retained.as_ref())
5283 .take()
5284 .expect("panic path should retain resources for assertions");
5285 assert_kernel_drop_released_resources(&retained);
5286 }
5287}