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