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_foreground_pgid(
2807 &self,
2808 requester_driver: &str,
2809 pid: u32,
2810 fd: u32,
2811 pgid: u32,
2812 ) -> KernelResult<()> {
2813 let description = self.description_for_fd(requester_driver, pid, fd)?;
2814 let requester_sid = self.processes.getsid(pid)?;
2815 let group = self
2816 .processes
2817 .list_processes()
2818 .into_values()
2819 .find(|process| process.pgid == pgid && process.status != ProcessStatus::Exited)
2820 .ok_or_else(|| KernelError::new("ESRCH", format!("no such process group {pgid}")))?;
2821 if group.sid != requester_sid {
2822 return Err(KernelError::permission_denied(
2823 "cannot set foreground process group in different session",
2824 ));
2825 }
2826 self.ptys.set_foreground_pgid(description.id(), pgid)?;
2827 Ok(())
2828 }
2829
2830 pub fn tcgetattr(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<Termios> {
2831 let description = self.description_for_fd(requester_driver, pid, fd)?;
2832 Ok(self.ptys.get_termios(description.id())?)
2833 }
2834
2835 pub fn tcsetattr(
2836 &self,
2837 requester_driver: &str,
2838 pid: u32,
2839 fd: u32,
2840 termios: PartialTermios,
2841 ) -> KernelResult<()> {
2842 let description = self.description_for_fd(requester_driver, pid, fd)?;
2843 self.ptys.set_termios(description.id(), termios)?;
2844 Ok(())
2845 }
2846
2847 pub fn tcgetpgrp(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<u32> {
2848 let description = self.description_for_fd(requester_driver, pid, fd)?;
2849 Ok(self.ptys.get_foreground_pgid(description.id())?)
2850 }
2851
2852 pub fn pty_resize(
2853 &self,
2854 requester_driver: &str,
2855 pid: u32,
2856 fd: u32,
2857 cols: u16,
2858 rows: u16,
2859 ) -> KernelResult<()> {
2860 let description = self.description_for_fd(requester_driver, pid, fd)?;
2861 let target_pgid = self.ptys.resize(description.id(), cols, rows)?;
2862 if let Some(pgid) = target_pgid {
2863 match self.processes.kill(-(pgid as i32), SIGWINCH) {
2864 Ok(()) => {}
2865 Err(error) if error.code() == "ESRCH" => {}
2866 Err(error) => return Err(error.into()),
2867 }
2868 }
2869 Ok(())
2870 }
2871
2872 pub fn signal_process(
2873 &self,
2874 requester_driver: &str,
2875 pid: i32,
2876 signal: i32,
2877 ) -> KernelResult<()> {
2878 if pid < 0 {
2879 let pgid = pid.unsigned_abs();
2880 let members = self
2881 .processes
2882 .list_processes()
2883 .into_values()
2884 .filter(|process| process.pgid == pgid && process.status != ProcessStatus::Exited)
2885 .collect::<Vec<_>>();
2886 if members.is_empty() {
2887 self.processes.kill(pid, signal)?;
2888 return Ok(());
2889 }
2890 if let Some(process) = members
2891 .iter()
2892 .find(|process| process.driver != requester_driver)
2893 {
2894 return Err(KernelError::permission_denied(format!(
2895 "driver \"{requester_driver}\" does not own process group {pgid} containing PID {}",
2896 process.pid
2897 )));
2898 }
2899 self.processes.kill(pid, signal)?;
2900 return Ok(());
2901 }
2902
2903 let pid = u32::try_from(pid)
2904 .map_err(|_| KernelError::new("EINVAL", format!("invalid pid {pid}")))?;
2905 self.assert_driver_owns(requester_driver, pid)?;
2906 self.processes.kill(pid as i32, signal)?;
2907 Ok(())
2908 }
2909
2910 pub fn kill_process(&self, requester_driver: &str, pid: u32, signal: i32) -> KernelResult<()> {
2911 let pid = i32::try_from(pid)
2912 .map_err(|_| KernelError::new("EINVAL", format!("pid {pid} exceeds i32::MAX")))?;
2913 self.signal_process(requester_driver, pid, signal)
2914 }
2915
2916 pub fn setpgid(&self, requester_driver: &str, pid: u32, pgid: u32) -> KernelResult<()> {
2917 self.assert_driver_owns(requester_driver, pid)?;
2918 let target_pgid = if pgid == 0 { pid } else { pgid };
2919 if target_pgid != pid {
2920 if let Some(group_owner) =
2921 self.processes
2922 .list_processes()
2923 .into_values()
2924 .find(|process| {
2925 process.pgid == target_pgid && process.status == ProcessStatus::Running
2926 })
2927 {
2928 if group_owner.driver != requester_driver {
2929 return Err(KernelError::permission_denied(format!(
2930 "driver \"{requester_driver}\" cannot join process group {target_pgid} owned by \"{}\"",
2931 group_owner.driver
2932 )));
2933 }
2934 }
2935 }
2936 self.processes.setpgid(pid, pgid)?;
2937 Ok(())
2938 }
2939
2940 pub fn getpgid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2941 self.assert_driver_owns(requester_driver, pid)?;
2942 Ok(self.processes.getpgid(pid)?)
2943 }
2944
2945 pub fn getpid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2946 self.assert_driver_owns(requester_driver, pid)?;
2947 Ok(pid)
2948 }
2949
2950 pub fn sigprocmask(
2951 &self,
2952 requester_driver: &str,
2953 pid: u32,
2954 how: SigmaskHow,
2955 set: SignalSet,
2956 ) -> KernelResult<SignalSet> {
2957 self.assert_driver_owns(requester_driver, pid)?;
2958 Ok(self.processes.sigprocmask(pid, how, set)?)
2959 }
2960
2961 pub fn sigpending(&self, requester_driver: &str, pid: u32) -> KernelResult<SignalSet> {
2962 self.assert_driver_owns(requester_driver, pid)?;
2963 Ok(self.processes.sigpending(pid)?)
2964 }
2965
2966 pub fn getppid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2967 self.assert_driver_owns(requester_driver, pid)?;
2968 Ok(self.processes.getppid(pid)?)
2969 }
2970
2971 pub fn setsid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2972 self.assert_driver_owns(requester_driver, pid)?;
2973 Ok(self.processes.setsid(pid)?)
2974 }
2975
2976 pub fn getsid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
2977 self.assert_driver_owns(requester_driver, pid)?;
2978 Ok(self.processes.getsid(pid)?)
2979 }
2980
2981 pub fn dev_fd_read_dir(&self, requester_driver: &str, pid: u32) -> KernelResult<Vec<String>> {
2982 self.assert_driver_owns(requester_driver, pid)?;
2983 let tables = lock_or_recover(&self.fd_tables);
2984 let table = tables
2985 .get(pid)
2986 .ok_or_else(|| KernelError::no_such_process(pid))?;
2987 let entry_count = table.len();
2988 self.resources.check_readdir_entries(entry_count)?;
2989 Ok(table.iter().map(|entry| entry.fd.to_string()).collect())
2990 }
2991
2992 pub fn dev_fd_stat(
2993 &mut self,
2994 requester_driver: &str,
2995 pid: u32,
2996 fd: u32,
2997 ) -> KernelResult<VirtualStat> {
2998 self.assert_driver_owns(requester_driver, pid)?;
2999 let entry = {
3000 let tables = lock_or_recover(&self.fd_tables);
3001 tables
3002 .get(pid)
3003 .and_then(|table| table.get(fd))
3004 .cloned()
3005 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
3006 };
3007
3008 if self.pipes.is_pipe(entry.description.id()) || self.ptys.is_pty(entry.description.id()) {
3009 return Ok(synthetic_character_device_stat(entry.description.id()));
3010 }
3011
3012 if is_proc_path(entry.description.path()) {
3013 return self.proc_stat_from_open_path(Some(pid), entry.description.path());
3014 }
3015
3016 Ok(self.filesystem.stat(entry.description.path())?)
3017 }
3018
3019 pub fn dispose(&mut self) -> KernelResult<()> {
3020 if self.terminated {
3021 return Ok(());
3022 }
3023
3024 dispose_kernel_vm_resources(self);
3025 Ok(())
3026 }
3027
3028 fn prepare_fd_open(
3029 &mut self,
3030 path: &str,
3031 flags: u32,
3032 mode: Option<u32>,
3033 ) -> KernelResult<(u8, Option<FileLockTarget>)> {
3034 if open_requires_write_access(flags) {
3035 self.reject_read_only_resolved_write_path(path)?;
3036 }
3037
3038 if flags & O_CREAT != 0 && flags & O_EXCL != 0 {
3039 self.check_write_file_limits(path, 0)?;
3040 VirtualFileSystem::create_file_exclusive_with_mode(
3041 &mut self.filesystem,
3042 path,
3043 Vec::new(),
3044 mode,
3045 )?;
3046 self.update_filesystem_usage_cache_for_inode_create(0);
3047 let stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
3048 return Ok((
3049 filetype_for_path(path, &stat),
3050 Some(FileLockTarget::new(stat.ino)),
3051 ));
3052 }
3053
3054 let exists = self.filesystem.exists(path)?;
3055 if exists {
3056 if flags & O_TRUNC != 0 {
3057 let existing_size = self.current_storage_file_size(path)?;
3058 self.check_path_resize_limits_with_existing(existing_size, 0)?;
3059 VirtualFileSystem::truncate(&mut self.filesystem, path, 0)?;
3060 self.update_filesystem_usage_cache_for_resize(existing_size, 0);
3061 }
3062 } else if flags & O_CREAT != 0 {
3063 self.check_write_file_limits(path, 0)?;
3064 VirtualFileSystem::write_file_with_mode(&mut self.filesystem, path, Vec::new(), mode)?;
3065 self.update_filesystem_usage_cache_for_inode_create(0);
3066 } else {
3067 let _ = VirtualFileSystem::stat(&mut self.filesystem, path)?;
3068 unreachable!("stat should return an error when opening a missing path");
3069 }
3070
3071 let stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
3072 Ok((
3073 filetype_for_path(path, &stat),
3074 Some(FileLockTarget::new(stat.ino)),
3075 ))
3076 }
3077
3078 fn reject_read_only_write_path(&mut self, path: &str) -> KernelResult<()> {
3079 if is_proc_path(path) {
3080 self.filesystem
3081 .check_virtual_path(FsOperation::Write, path)
3082 .map_err(KernelError::from)?;
3083 return Err(read_only_filesystem_error(path));
3084 }
3085
3086 if is_agentos_path(path) {
3087 return Err(read_only_filesystem_error(path));
3088 }
3089
3090 Ok(())
3091 }
3092
3093 fn reject_read_only_resolved_write_path(&mut self, path: &str) -> KernelResult<()> {
3094 self.reject_read_only_write_path(path)?;
3095
3096 if let Some(resolved) = self.resolve_write_guard_path(path, true)? {
3097 if is_agentos_path(&resolved) {
3098 return Err(read_only_filesystem_error(&resolved));
3099 }
3100 if self.has_agentos_hardlink_alias(&resolved)? {
3101 return Err(read_only_filesystem_error(&resolved));
3102 }
3103 }
3104 if self.has_agentos_hardlink_alias(path)? {
3105 return Err(read_only_filesystem_error(path));
3106 }
3107
3108 Ok(())
3109 }
3110
3111 fn reject_read_only_entry_write_path(&mut self, path: &str) -> KernelResult<()> {
3112 self.reject_read_only_write_path(path)?;
3113
3114 if let Some(resolved) = self.resolve_write_guard_path(path, false)? {
3115 if is_agentos_path(&resolved) {
3116 return Err(read_only_filesystem_error(&resolved));
3117 }
3118 if self.has_agentos_hardlink_alias(&resolved)? {
3119 return Err(read_only_filesystem_error(&resolved));
3120 }
3121 }
3122 if self.has_agentos_hardlink_alias(path)? {
3123 return Err(read_only_filesystem_error(path));
3124 }
3125
3126 Ok(())
3127 }
3128
3129 fn has_agentos_hardlink_alias(&mut self, path: &str) -> KernelResult<bool> {
3130 let Some(target) = self.storage_lstat(path)? else {
3131 return Ok(false);
3132 };
3133 if target.is_directory || target.is_symbolic_link {
3134 return Ok(false);
3135 }
3136
3137 self.agentos_subtree_contains_inode("/etc/agentos", target.dev, target.ino)
3138 }
3139
3140 fn agentos_subtree_contains_inode(
3141 &mut self,
3142 path: &str,
3143 target_dev: u64,
3144 target_ino: u64,
3145 ) -> KernelResult<bool> {
3146 let Some(stat) = self.storage_lstat(path)? else {
3147 return Ok(false);
3148 };
3149 if !stat.is_directory && !stat.is_symbolic_link {
3150 return Ok(stat.dev == target_dev && stat.ino == target_ino);
3151 }
3152 if !stat.is_directory {
3153 return Ok(false);
3154 }
3155
3156 let children = self.raw_filesystem_mut().read_dir_with_types(path)?;
3157 for child in children {
3158 if child.name == "." || child.name == ".." {
3159 continue;
3160 }
3161 let child_path = join_absolute_path(path, &child.name);
3162 if self.agentos_subtree_contains_inode(&child_path, target_dev, target_ino)? {
3163 return Ok(true);
3164 }
3165 }
3166
3167 Ok(false)
3168 }
3169
3170 fn resolve_write_guard_path(
3171 &mut self,
3172 path: &str,
3173 follow_final_symlink: bool,
3174 ) -> KernelResult<Option<String>> {
3175 let normalized = normalize_path(path);
3176 if normalized == "/" {
3177 return Ok(Some(normalized));
3178 }
3179
3180 if follow_final_symlink {
3181 if let Ok(resolved) = self.filesystem.realpath(&normalized) {
3182 return Ok(Some(resolved));
3183 }
3184 }
3185
3186 let components: Vec<&str> = normalized
3187 .split('/')
3188 .filter(|component| !component.is_empty())
3189 .collect();
3190 let mut resolved_prefix = String::from("/");
3191 let mut raw_prefix = String::from("/");
3192
3193 for (index, component) in components.iter().enumerate() {
3194 let is_final = index + 1 == components.len();
3195 if is_final && !follow_final_symlink {
3196 return Ok(Some(join_absolute_path(&resolved_prefix, component)));
3197 }
3198
3199 raw_prefix = join_absolute_path(&raw_prefix, component);
3200 match self.filesystem.realpath(&raw_prefix) {
3201 Ok(resolved) => {
3202 resolved_prefix = resolved;
3203 }
3204 Err(error) if error.code() == "ENOENT" => {
3205 let mut resolved = resolved_prefix;
3206 for remaining in &components[index..] {
3207 resolved = join_absolute_path(&resolved, remaining);
3208 }
3209 return Ok(Some(resolved));
3210 }
3211 Err(error) => return Err(error.into()),
3212 }
3213 }
3214
3215 Ok(Some(resolved_prefix))
3216 }
3217
3218 fn populate_poll_target_revents(
3219 &self,
3220 pid: u32,
3221 targets: &mut [PollTargetEntry],
3222 ) -> KernelResult<usize> {
3223 let mut ready_count = 0;
3224 for target in targets.iter_mut() {
3225 target.revents = self.poll_target_entry(pid, target.target, target.events)?;
3226 if !target.revents.is_empty() {
3227 ready_count += 1;
3228 }
3229 }
3230
3231 Ok(ready_count)
3232 }
3233
3234 fn poll_target_entry(
3235 &self,
3236 pid: u32,
3237 target: PollTarget,
3238 requested: PollEvents,
3239 ) -> KernelResult<PollEvents> {
3240 match target {
3241 PollTarget::Fd(fd) => {
3242 let entry = {
3243 let tables = lock_or_recover(&self.fd_tables);
3244 tables
3245 .get(pid)
3246 .ok_or_else(|| KernelError::no_such_process(pid))?
3247 .get(fd)
3248 .cloned()
3249 };
3250 if let Some(entry) = entry {
3251 self.poll_entry(&entry, requested)
3252 } else {
3253 Ok(POLLNVAL)
3254 }
3255 }
3256 PollTarget::Socket(socket_id) => {
3257 let socket = self.sockets.get(socket_id);
3258 if let Some(socket) = socket {
3259 if socket.owner_pid() != pid {
3260 return Err(KernelError::permission_denied(format!(
3261 "process {pid} does not own socket {socket_id}"
3262 )));
3263 }
3264 let mut events = self.sockets.poll(socket_id, requested)?;
3265 if events.intersects(POLLOUT)
3266 && !self.socket_pollout_has_resource_capacity(&socket)
3267 {
3268 events = PollEvents::from_bits(events.bits() & !POLLOUT.bits());
3269 }
3270 Ok(events)
3271 } else {
3272 Ok(POLLNVAL)
3273 }
3274 }
3275 }
3276 }
3277
3278 fn socket_pollout_has_resource_capacity(&self, socket: &SocketRecord) -> bool {
3279 let snapshot = self.resource_snapshot();
3280 if self
3281 .resources
3282 .limits()
3283 .max_socket_buffered_bytes
3284 .is_some_and(|limit| snapshot.socket_buffered_bytes >= limit)
3285 {
3286 return false;
3287 }
3288
3289 if socket.spec().socket_type == SocketType::Datagram
3290 && self
3291 .resources
3292 .limits()
3293 .max_socket_datagram_queue_len
3294 .is_some_and(|limit| snapshot.socket_datagram_queue_len >= limit)
3295 {
3296 return false;
3297 }
3298
3299 true
3300 }
3301
3302 fn poll_entry(
3303 &self,
3304 entry: &crate::fd_table::FdEntry,
3305 requested: PollEvents,
3306 ) -> KernelResult<PollEvents> {
3307 if self.pipes.is_pipe(entry.description.id()) {
3308 return Ok(self.pipes.poll(entry.description.id(), requested)?);
3309 }
3310
3311 if self.ptys.is_pty(entry.description.id()) {
3312 return Ok(self.ptys.poll(entry.description.id(), requested)?);
3313 }
3314
3315 let access_mode = entry.description.flags() & 0b11;
3316 let mut events = PollEvents::empty();
3317 if requested.intersects(POLLIN) && access_mode != crate::fd_table::O_WRONLY {
3318 events |= POLLIN;
3319 }
3320 if requested.intersects(POLLOUT) && access_mode != crate::fd_table::O_RDONLY {
3321 events |= POLLOUT;
3322 }
3323 if entry.filetype == FILETYPE_DIRECTORY && requested.intersects(POLLOUT) {
3324 events |= POLLERR;
3325 }
3326 if self.terminated {
3327 events |= POLLHUP;
3328 }
3329 Ok(events)
3330 }
3331
3332 fn description_for_fd(
3333 &self,
3334 requester_driver: &str,
3335 pid: u32,
3336 fd: u32,
3337 ) -> KernelResult<Arc<FileDescription>> {
3338 self.assert_driver_owns(requester_driver, pid)?;
3339 lock_or_recover(&self.fd_tables)
3340 .get(pid)
3341 .and_then(|table| table.get(fd))
3342 .map(|entry| Arc::clone(&entry.description))
3343 .ok_or_else(|| KernelError::bad_file_descriptor(fd))
3344 }
3345
3346 fn assert_not_terminated(&self) -> KernelResult<()> {
3347 if self.terminated {
3348 Err(KernelError::disposed())
3349 } else {
3350 Ok(())
3351 }
3352 }
3353
3354 fn assert_driver_owns(&self, requester_driver: &str, pid: u32) -> KernelResult<()> {
3355 let driver_pids = lock_or_recover(&self.driver_pids);
3356 if driver_pids
3357 .get(requester_driver)
3358 .map(|pids| pids.contains(&pid))
3359 .unwrap_or(false)
3360 {
3361 return Ok(());
3362 }
3363
3364 if driver_pids.values().any(|pids| pids.contains(&pid)) {
3365 return Err(KernelError::permission_denied(format!(
3366 "driver \"{requester_driver}\" does not own PID {pid}"
3367 )));
3368 }
3369
3370 Err(KernelError::no_such_process(pid))
3371 }
3372
3373 fn cleanup_process_resources(&self, pid: u32) {
3374 cleanup_process_resources(
3375 self.fd_tables.as_ref(),
3376 &self.file_locks,
3377 &self.pipes,
3378 &self.ptys,
3379 &self.sockets,
3380 self.driver_pids.as_ref(),
3381 pid,
3382 );
3383 }
3384
3385 fn resolve_spawn_command(
3386 &mut self,
3387 command: &str,
3388 args: &[String],
3389 cwd: &str,
3390 ) -> KernelResult<ResolvedSpawnCommand> {
3391 if let Some(driver) = self.commands.resolve(command).cloned() {
3392 return Ok(ResolvedSpawnCommand {
3393 command: command.to_owned(),
3394 args: args.to_vec(),
3395 driver,
3396 });
3397 }
3398
3399 let Some(path) = self.resolve_executable_path(command, cwd)? else {
3400 return Err(KernelError::command_not_found(command));
3401 };
3402
3403 if let Some(registered_command) = self.resolve_registered_command_path(&path) {
3404 let driver = self
3405 .commands
3406 .resolve(®istered_command)
3407 .cloned()
3408 .ok_or_else(|| KernelError::command_not_found(®istered_command))?;
3409 return Ok(ResolvedSpawnCommand {
3410 command: registered_command,
3411 args: args.to_vec(),
3412 driver,
3413 });
3414 }
3415
3416 let shebang = self
3417 .parse_shebang_command(&path)?
3418 .ok_or_else(|| KernelError::new("ENOEXEC", format!("exec format error: {path}")))?;
3419 self.resolve_shebang_command(&path, args, shebang)
3420 }
3421
3422 fn resolve_executable_path(
3423 &mut self,
3424 command: &str,
3425 cwd: &str,
3426 ) -> KernelResult<Option<String>> {
3427 if !command.contains('/') {
3428 return Ok(None);
3429 }
3430
3431 let path = if command.starts_with('/') {
3432 normalize_path(command)
3433 } else {
3434 normalize_path(&format!("{cwd}/{command}"))
3435 };
3436 let path = self.filesystem.realpath(&path).unwrap_or(path);
3442 let stat = self.filesystem.stat(&path)?;
3443 if stat.is_directory {
3444 return Err(KernelError::new(
3445 "EACCES",
3446 format!("permission denied, execute '{path}'"),
3447 ));
3448 }
3449 if stat.mode & EXECUTABLE_PERMISSION_BITS == 0 {
3450 return Err(KernelError::new(
3451 "EACCES",
3452 format!("permission denied, execute '{path}'"),
3453 ));
3454 }
3455 Ok(Some(path))
3456 }
3457
3458 fn resolve_registered_command_path(&self, path: &str) -> Option<String> {
3459 let normalized = normalize_path(path);
3460 for prefix in ["/bin/", "/usr/bin/", "/usr/local/bin/"] {
3461 let Some(name) = normalized.strip_prefix(prefix) else {
3462 continue;
3463 };
3464 if !name.is_empty() && !name.contains('/') && self.commands.resolve(name).is_some() {
3465 return Some(name.to_owned());
3466 }
3467 }
3468
3469 if let Some(name) = normalized
3470 .strip_prefix("/__secure_exec/commands/")
3471 .and_then(|suffix| suffix.rsplit('/').next())
3472 {
3473 if !name.is_empty() && !name.contains('/') && self.commands.resolve(name).is_some() {
3474 return Some(name.to_owned());
3475 }
3476 }
3477
3478 None
3479 }
3480
3481 fn parse_shebang_command(&mut self, path: &str) -> KernelResult<Option<ShebangCommand>> {
3482 let header = self.filesystem.pread(path, 0, SHEBANG_LINE_MAX_BYTES + 1)?;
3483 if !header.starts_with(b"#!") {
3484 return Ok(None);
3485 }
3486
3487 let line_end = match header.iter().position(|byte| *byte == b'\n') {
3488 Some(index) => index,
3489 None if header.len() <= SHEBANG_LINE_MAX_BYTES => header.len(),
3490 None => {
3491 return Err(KernelError::new(
3492 "ENOEXEC",
3493 format!("shebang line exceeds {SHEBANG_LINE_MAX_BYTES} bytes: {path}"),
3494 ));
3495 }
3496 };
3497 let line = header[2..line_end]
3498 .strip_suffix(b"\r")
3499 .unwrap_or(&header[2..line_end]);
3500 let text = std::str::from_utf8(line)
3501 .map_err(|_| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
3502 let mut parts = text.split_ascii_whitespace();
3503 let interpreter = parts
3504 .next()
3505 .ok_or_else(|| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
3506 Ok(Some(ShebangCommand {
3507 interpreter: interpreter.to_owned(),
3508 args: parts.map(ToOwned::to_owned).collect(),
3509 }))
3510 }
3511
3512 fn resolve_shebang_command(
3513 &self,
3514 path: &str,
3515 args: &[String],
3516 shebang: ShebangCommand,
3517 ) -> KernelResult<ResolvedSpawnCommand> {
3518 let mut interpreter_args = shebang.args;
3519 let interpreter = normalize_path(&shebang.interpreter);
3520 let command = if interpreter == "/usr/bin/env" || interpreter == "/bin/env" {
3521 if interpreter_args.is_empty() {
3522 return Err(KernelError::new(
3523 "ENOENT",
3524 format!("missing interpreter after /usr/bin/env in shebang: {path}"),
3525 ));
3526 }
3527 interpreter_args.remove(0)
3528 } else if let Some(command) = self.resolve_registered_command_path(&interpreter) {
3529 command
3530 } else if self.commands.resolve(&shebang.interpreter).is_some() {
3531 shebang.interpreter
3532 } else {
3533 return Err(KernelError::command_not_found(&shebang.interpreter));
3534 };
3535
3536 let driver = self
3537 .commands
3538 .resolve(&command)
3539 .cloned()
3540 .ok_or_else(|| KernelError::command_not_found(&command))?;
3541 let mut resolved_args = interpreter_args;
3542 resolved_args.push(path.to_owned());
3543 resolved_args.extend(args.iter().cloned());
3544 Ok(ResolvedSpawnCommand {
3545 command,
3546 args: resolved_args,
3547 driver,
3548 })
3549 }
3550
3551 fn finish_waitpid_event(&mut self, result: ProcessWaitResult) -> WaitPidEventResult {
3552 if result.event == WaitPidEvent::Exited {
3553 self.cleanup_process_resources(result.pid);
3554 }
3555 WaitPidEventResult {
3556 pid: result.pid,
3557 status: result.status,
3558 event: result.event,
3559 }
3560 }
3561
3562 fn raw_filesystem_mut(&mut self) -> &mut F {
3563 self.filesystem.inner_mut().inner_mut()
3564 }
3565
3566 fn read_file_internal(
3567 &mut self,
3568 current_pid: Option<u32>,
3569 path: &str,
3570 ) -> KernelResult<Vec<u8>> {
3571 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3572 self.filesystem
3573 .check_virtual_path(FsOperation::Read, path)
3574 .map_err(KernelError::from)?;
3575 return self.proc_read_file(current_pid, &proc_node);
3576 }
3577
3578 Ok(self.filesystem.read_file(path)?)
3579 }
3580
3581 fn effective_recursive_fs_depth(
3582 &self,
3583 requested_max_depth: Option<usize>,
3584 ) -> KernelResult<usize> {
3585 match (requested_max_depth, self.resources.max_recursive_fs_depth()) {
3586 (Some(requested), Some(limit)) if requested > limit => Err(KernelError::new(
3587 "EINVAL",
3588 format!(
3589 "requested recursive filesystem max depth {requested} exceeds configured limit {limit}"
3590 ),
3591 )),
3592 (Some(requested), _) => Ok(requested),
3593 (None, Some(limit)) => Ok(limit),
3594 (None, None) => Ok(usize::MAX),
3595 }
3596 }
3597
3598 fn copy_path_inner(
3599 &mut self,
3600 from: &str,
3601 to: &str,
3602 recursive: bool,
3603 depth: usize,
3604 entries: &mut usize,
3605 ) -> KernelResult<()> {
3606 self.resources.check_recursive_fs_depth(depth)?;
3607 *entries = entries.saturating_add(1);
3608 self.resources.check_recursive_fs_entries(*entries)?;
3609 let source_stat = self.lstat_internal(None, from)?;
3610
3611 if source_stat.is_symbolic_link {
3612 let target = self.read_link_internal(None, from)?;
3613 self.symlink(&target, to)?;
3614 return Ok(());
3615 }
3616
3617 if source_stat.is_directory {
3618 if !recursive {
3619 return Err(KernelError::new(
3620 "EISDIR",
3621 format!("illegal operation on a directory, copy '{from}'"),
3622 ));
3623 }
3624
3625 let source_root = normalize_path(from);
3626 let destination_root = normalize_path(to);
3627 if destination_root.starts_with(&(source_root.clone() + "/")) {
3628 return Err(KernelError::new(
3629 "EINVAL",
3630 format!("cannot copy '{from}' into its own descendant '{to}'"),
3631 ));
3632 }
3633
3634 self.mkdir(&parent_path(&destination_root), true)?;
3635 if !self.exists_internal(None, &destination_root)? {
3636 self.create_dir(&destination_root)?;
3637 }
3638 self.chmod(&destination_root, source_stat.mode)?;
3639 self.chown(&destination_root, source_stat.uid, source_stat.gid)?;
3640
3641 let names = self.read_dir_internal(None, from)?;
3642 self.resources.check_readdir_entries(names.len())?;
3643 for name in names {
3644 if matches!(name.as_str(), "." | "..") {
3645 continue;
3646 }
3647 let child_from = join_child_path(from, &name);
3648 let child_to = join_child_path(to, &name);
3649 self.copy_path_inner(
3650 &child_from,
3651 &child_to,
3652 true,
3653 depth.saturating_add(1),
3654 entries,
3655 )?;
3656 }
3657 return Ok(());
3658 }
3659
3660 let content = self.read_file_internal(None, from)?;
3661 self.write_file(to, content)?;
3662 self.chmod(to, source_stat.mode)?;
3663 self.chown(to, source_stat.uid, source_stat.gid)
3664 }
3665
3666 fn remove_path_inner(
3667 &mut self,
3668 path: &str,
3669 recursive: bool,
3670 depth: usize,
3671 entries: &mut usize,
3672 ) -> KernelResult<()> {
3673 self.resources.check_recursive_fs_depth(depth)?;
3674 *entries = entries.saturating_add(1);
3675 self.resources.check_recursive_fs_entries(*entries)?;
3676 let stat = self.lstat_internal(None, path)?;
3677 if stat.is_directory && !stat.is_symbolic_link {
3678 if recursive {
3679 let names = self.read_dir_internal(None, path)?;
3680 self.resources.check_readdir_entries(names.len())?;
3681 for name in names {
3682 if matches!(name.as_str(), "." | "..") {
3683 continue;
3684 }
3685 let child = join_child_path(path, &name);
3686 self.remove_path_inner(&child, true, depth.saturating_add(1), entries)?;
3687 }
3688 }
3689 return self.remove_dir(path);
3690 }
3691
3692 self.remove_file(path)
3693 }
3694
3695 fn exists_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<bool> {
3696 match self.resolve_proc_node(path, current_pid) {
3697 Ok(Some(_)) => {
3698 self.filesystem
3699 .check_virtual_path(FsOperation::Read, path)
3700 .map_err(KernelError::from)?;
3701 Ok(true)
3702 }
3703 Ok(None) => Ok(self.filesystem.exists(path)?),
3704 Err(error) if error.code() == "ENOENT" => Ok(false),
3705 Err(error) => Err(error),
3706 }
3707 }
3708
3709 fn stat_internal(&mut self, current_pid: Option<u32>, path: &str) -> KernelResult<VirtualStat> {
3710 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3711 self.filesystem
3712 .check_virtual_path(FsOperation::Read, path)
3713 .map_err(KernelError::from)?;
3714 return self.proc_stat(current_pid, &proc_node);
3715 }
3716
3717 Ok(self.filesystem.stat(path)?)
3718 }
3719
3720 fn lstat_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<VirtualStat> {
3721 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3722 self.filesystem
3723 .check_virtual_path(FsOperation::Read, path)
3724 .map_err(KernelError::from)?;
3725 return self.proc_lstat(&proc_node);
3726 }
3727
3728 Ok(self.filesystem.lstat(path)?)
3729 }
3730
3731 fn read_link_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<String> {
3732 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3733 self.filesystem
3734 .check_virtual_path(FsOperation::Read, path)
3735 .map_err(KernelError::from)?;
3736 return self.proc_read_link(&proc_node);
3737 }
3738
3739 Ok(self.filesystem.read_link(path)?)
3740 }
3741
3742 fn read_dir_internal(
3743 &mut self,
3744 current_pid: Option<u32>,
3745 path: &str,
3746 ) -> KernelResult<Vec<String>> {
3747 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3748 self.filesystem
3749 .check_virtual_path(FsOperation::Read, path)
3750 .map_err(KernelError::from)?;
3751 return self.proc_read_dir(current_pid, &proc_node);
3752 }
3753
3754 if let Some(limit) = self.resources.max_readdir_entries() {
3755 Ok(self.filesystem.read_dir_limited(path, limit)?)
3756 } else {
3757 Ok(self.filesystem.read_dir(path)?)
3758 }
3759 }
3760
3761 fn read_dir_with_types_internal(
3762 &mut self,
3763 current_pid: Option<u32>,
3764 path: &str,
3765 ) -> KernelResult<Vec<VirtualDirEntry>> {
3766 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3767 self.filesystem
3768 .check_virtual_path(FsOperation::Read, path)
3769 .map_err(KernelError::from)?;
3770 return Ok(self
3771 .proc_read_dir(current_pid, &proc_node)?
3772 .into_iter()
3773 .map(|name| VirtualDirEntry {
3774 name,
3775 is_directory: false,
3776 is_symbolic_link: false,
3777 })
3778 .collect());
3779 }
3780
3781 Ok(self.filesystem.read_dir_with_types(path)?)
3782 }
3783
3784 fn realpath_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<String> {
3785 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
3786 self.filesystem
3787 .check_virtual_path(FsOperation::Read, path)
3788 .map_err(KernelError::from)?;
3789 return self.proc_realpath(current_pid, &proc_node);
3790 }
3791
3792 Ok(self.filesystem.realpath(path)?)
3793 }
3794
3795 fn resolve_proc_node(
3796 &self,
3797 path: &str,
3798 current_pid: Option<u32>,
3799 ) -> KernelResult<Option<ProcNode>> {
3800 let normalized = normalize_path(path);
3801 if !is_proc_path(&normalized) {
3802 return Ok(None);
3803 }
3804
3805 if normalized == "/proc" {
3806 return Ok(Some(ProcNode::RootDir));
3807 }
3808
3809 let suffix = normalized
3810 .strip_prefix("/proc/")
3811 .expect("proc path should have /proc prefix");
3812 let parts = suffix.split('/').collect::<Vec<_>>();
3813 if parts.is_empty() {
3814 return Ok(Some(ProcNode::RootDir));
3815 }
3816
3817 let root_node = match parts.as_slice() {
3818 ["mounts"] => Some(ProcNode::MountsFile),
3819 ["cpuinfo"] => Some(ProcNode::CpuInfoFile),
3820 ["meminfo"] => Some(ProcNode::MemInfoFile),
3821 ["loadavg"] => Some(ProcNode::LoadAvgFile),
3822 ["uptime"] => Some(ProcNode::UptimeFile),
3823 ["version"] => Some(ProcNode::VersionFile),
3824 _ => None,
3825 };
3826 if let Some(node) = root_node {
3827 return Ok(Some(node));
3828 }
3829
3830 let pid = match parts[0] {
3831 "self" => current_pid.ok_or_else(|| proc_not_found_error(&normalized))?,
3832 raw => raw
3833 .parse::<u32>()
3834 .map_err(|_| proc_not_found_error(&normalized))?,
3835 };
3836 self.proc_entry(pid)?;
3837
3838 let node = match parts.as_slice() {
3839 ["self"] => ProcNode::SelfLink { pid },
3840 [_pid] => ProcNode::PidDir { pid },
3841 [_pid, "fd"] => ProcNode::PidFdDir { pid },
3842 [_pid, "cmdline"] => ProcNode::PidCmdline { pid },
3843 [_pid, "environ"] => ProcNode::PidEnviron { pid },
3844 [_pid, "cwd"] => ProcNode::PidCwdLink { pid },
3845 [_pid, "stat"] => ProcNode::PidStatFile { pid },
3846 [_pid, "status"] => ProcNode::PidStatusFile { pid },
3847 [_pid, "fd", fd] => {
3848 let fd = fd
3849 .parse::<u32>()
3850 .map_err(|_| proc_not_found_error(&normalized))?;
3851 self.proc_fd_entry(pid, fd)?;
3852 ProcNode::PidFdLink { pid, fd }
3853 }
3854 _ => return Err(proc_not_found_error(&normalized)),
3855 };
3856
3857 Ok(Some(node))
3858 }
3859
3860 fn proc_entry(&self, pid: u32) -> KernelResult<crate::process_table::ProcessEntry> {
3861 self.processes
3862 .get(pid)
3863 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}")))
3864 }
3865
3866 fn proc_fd_entry(&self, pid: u32, fd: u32) -> KernelResult<FdEntry> {
3867 lock_or_recover(&self.fd_tables)
3868 .get(pid)
3869 .and_then(|table| table.get(fd))
3870 .cloned()
3871 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}/fd/{fd}")))
3872 }
3873
3874 fn proc_read_file(
3875 &mut self,
3876 current_pid: Option<u32>,
3877 node: &ProcNode,
3878 ) -> KernelResult<Vec<u8>> {
3879 match node {
3880 ProcNode::SelfLink { .. }
3881 | ProcNode::PidCwdLink { .. }
3882 | ProcNode::PidFdLink { .. } => {
3883 let target = self.proc_symlink_target(node)?;
3884 self.read_file_internal(current_pid, &target)
3885 }
3886 ProcNode::MountsFile => Ok(self.proc_mounts_bytes()),
3887 ProcNode::CpuInfoFile => Ok(self.proc_cpuinfo_bytes()),
3888 ProcNode::MemInfoFile => Ok(self.proc_meminfo_bytes()),
3889 ProcNode::LoadAvgFile => Ok(self.proc_loadavg_bytes()),
3890 ProcNode::UptimeFile => Ok(self.proc_uptime_bytes()),
3891 ProcNode::VersionFile => Ok(self.proc_version_bytes()),
3892 ProcNode::PidCmdline { pid } => Ok(self.proc_cmdline_bytes(*pid)),
3893 ProcNode::PidEnviron { pid } => Ok(self.proc_environ_bytes(*pid)),
3894 ProcNode::PidStatFile { pid } => Ok(self.proc_stat_bytes(*pid)),
3895 ProcNode::PidStatusFile { pid } => Ok(self.proc_status_bytes(*pid)),
3896 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
3897 Err(KernelError::new(
3898 "EISDIR",
3899 format!(
3900 "illegal operation on a directory, read '{}'",
3901 self.proc_canonical_path(node)
3902 ),
3903 ))
3904 }
3905 }
3906 }
3907
3908 fn proc_stat(
3909 &mut self,
3910 current_pid: Option<u32>,
3911 node: &ProcNode,
3912 ) -> KernelResult<VirtualStat> {
3913 match node {
3914 ProcNode::SelfLink { .. }
3915 | ProcNode::PidCwdLink { .. }
3916 | ProcNode::PidFdLink { .. } => {
3917 let target = self.proc_symlink_target(node)?;
3918 self.stat_internal(current_pid, &target)
3919 }
3920 _ => self.proc_lstat(node),
3921 }
3922 }
3923
3924 fn proc_lstat(&self, node: &ProcNode) -> KernelResult<VirtualStat> {
3925 match node {
3926 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
3927 Ok(proc_dir_stat(proc_inode(node)))
3928 }
3929 ProcNode::MountsFile => Ok(proc_file_stat(
3930 proc_inode(node),
3931 self.proc_mounts_bytes().len() as u64,
3932 )),
3933 ProcNode::CpuInfoFile => Ok(proc_file_stat(
3934 proc_inode(node),
3935 self.proc_cpuinfo_bytes().len() as u64,
3936 )),
3937 ProcNode::MemInfoFile => Ok(proc_file_stat(
3938 proc_inode(node),
3939 self.proc_meminfo_bytes().len() as u64,
3940 )),
3941 ProcNode::LoadAvgFile => Ok(proc_file_stat(
3942 proc_inode(node),
3943 self.proc_loadavg_bytes().len() as u64,
3944 )),
3945 ProcNode::UptimeFile => Ok(proc_file_stat(
3946 proc_inode(node),
3947 self.proc_uptime_bytes().len() as u64,
3948 )),
3949 ProcNode::VersionFile => Ok(proc_file_stat(
3950 proc_inode(node),
3951 self.proc_version_bytes().len() as u64,
3952 )),
3953 ProcNode::PidCmdline { pid } => Ok(proc_file_stat(
3954 proc_inode(node),
3955 self.proc_cmdline_bytes(*pid).len() as u64,
3956 )),
3957 ProcNode::PidEnviron { pid } => Ok(proc_file_stat(
3958 proc_inode(node),
3959 self.proc_environ_bytes(*pid).len() as u64,
3960 )),
3961 ProcNode::PidStatFile { pid } => Ok(proc_file_stat(
3962 proc_inode(node),
3963 self.proc_stat_bytes(*pid).len() as u64,
3964 )),
3965 ProcNode::PidStatusFile { pid } => Ok(proc_file_stat(
3966 proc_inode(node),
3967 self.proc_status_bytes(*pid).len() as u64,
3968 )),
3969 ProcNode::SelfLink { .. }
3970 | ProcNode::PidCwdLink { .. }
3971 | ProcNode::PidFdLink { .. } => Ok(proc_symlink_stat(
3972 proc_inode(node),
3973 self.proc_read_link(node)?.len() as u64,
3974 )),
3975 }
3976 }
3977
3978 fn proc_read_link(&self, node: &ProcNode) -> KernelResult<String> {
3979 match node {
3980 ProcNode::SelfLink { .. }
3981 | ProcNode::PidCwdLink { .. }
3982 | ProcNode::PidFdLink { .. } => self.proc_symlink_target(node),
3983 _ => Err(KernelError::new(
3984 "EINVAL",
3985 format!(
3986 "invalid argument, readlink '{}'",
3987 self.proc_canonical_path(node)
3988 ),
3989 )),
3990 }
3991 }
3992
3993 fn proc_read_dir(
3994 &mut self,
3995 current_pid: Option<u32>,
3996 node: &ProcNode,
3997 ) -> KernelResult<Vec<String>> {
3998 match node {
3999 ProcNode::SelfLink { .. }
4000 | ProcNode::PidCwdLink { .. }
4001 | ProcNode::PidFdLink { .. } => {
4002 let target = self.proc_symlink_target(node)?;
4003 self.read_dir_internal(current_pid, &target)
4004 }
4005 ProcNode::RootDir => {
4006 let mut entries = self
4007 .processes
4008 .list_processes()
4009 .keys()
4010 .map(|pid| pid.to_string())
4011 .collect::<Vec<_>>();
4012 entries.push(String::from("cpuinfo"));
4013 entries.push(String::from("loadavg"));
4014 entries.push(String::from("meminfo"));
4015 entries.push(String::from("mounts"));
4016 entries.push(String::from("self"));
4017 entries.push(String::from("uptime"));
4018 entries.push(String::from("version"));
4019 entries.sort();
4020 Ok(entries)
4021 }
4022 ProcNode::PidDir { .. } => Ok(vec![
4023 String::from("cmdline"),
4024 String::from("cwd"),
4025 String::from("environ"),
4026 String::from("fd"),
4027 String::from("stat"),
4028 String::from("status"),
4029 ]),
4030 ProcNode::PidFdDir { pid } => {
4031 let tables = lock_or_recover(&self.fd_tables);
4032 let table = tables
4033 .get(*pid)
4034 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}/fd")))?;
4035 Ok(table.iter().map(|entry| entry.fd.to_string()).collect())
4036 }
4037 _ => Err(KernelError::new(
4038 "ENOTDIR",
4039 format!(
4040 "not a directory, scandir '{}'",
4041 self.proc_canonical_path(node)
4042 ),
4043 )),
4044 }
4045 }
4046
4047 fn proc_realpath(&self, current_pid: Option<u32>, node: &ProcNode) -> KernelResult<String> {
4048 match node {
4049 ProcNode::SelfLink { .. }
4050 | ProcNode::PidCwdLink { .. }
4051 | ProcNode::PidFdLink { .. } => {
4052 let target = self.proc_symlink_target(node)?;
4053 self.realpath_internal(current_pid, &target)
4054 }
4055 _ => Ok(self.proc_canonical_path(node)),
4056 }
4057 }
4058
4059 fn proc_symlink_target(&self, node: &ProcNode) -> KernelResult<String> {
4060 match node {
4061 ProcNode::SelfLink { pid } => Ok(format!("/proc/{pid}")),
4062 ProcNode::PidCwdLink { pid } => Ok(self.proc_entry(*pid)?.cwd),
4063 ProcNode::PidFdLink { pid, fd } => {
4064 Ok(self.proc_fd_entry(*pid, *fd)?.description.path().to_owned())
4065 }
4066 _ => Err(KernelError::new(
4067 "EINVAL",
4068 format!(
4069 "'{}' is not a symbolic link",
4070 self.proc_canonical_path(node)
4071 ),
4072 )),
4073 }
4074 }
4075
4076 fn proc_canonical_path(&self, node: &ProcNode) -> String {
4077 match node {
4078 ProcNode::RootDir => String::from("/proc"),
4079 ProcNode::MountsFile => String::from("/proc/mounts"),
4080 ProcNode::CpuInfoFile => String::from("/proc/cpuinfo"),
4081 ProcNode::MemInfoFile => String::from("/proc/meminfo"),
4082 ProcNode::LoadAvgFile => String::from("/proc/loadavg"),
4083 ProcNode::UptimeFile => String::from("/proc/uptime"),
4084 ProcNode::VersionFile => String::from("/proc/version"),
4085 ProcNode::SelfLink { pid } => format!("/proc/{pid}"),
4086 ProcNode::PidDir { pid } => format!("/proc/{pid}"),
4087 ProcNode::PidFdDir { pid } => format!("/proc/{pid}/fd"),
4088 ProcNode::PidCmdline { pid } => format!("/proc/{pid}/cmdline"),
4089 ProcNode::PidEnviron { pid } => format!("/proc/{pid}/environ"),
4090 ProcNode::PidCwdLink { pid } => format!("/proc/{pid}/cwd"),
4091 ProcNode::PidStatFile { pid } => format!("/proc/{pid}/stat"),
4092 ProcNode::PidStatusFile { pid } => format!("/proc/{pid}/status"),
4093 ProcNode::PidFdLink { pid, fd } => format!("/proc/{pid}/fd/{fd}"),
4094 }
4095 }
4096
4097 fn proc_cmdline_bytes(&self, pid: u32) -> Vec<u8> {
4098 let entry = self
4099 .processes
4100 .get(pid)
4101 .expect("process must exist while procfs path is resolved");
4102 let mut argv = vec![entry.command];
4103 argv.extend(entry.args);
4104 null_separated_bytes(argv)
4105 }
4106
4107 fn proc_environ_bytes(&self, pid: u32) -> Vec<u8> {
4108 let entry = self
4109 .processes
4110 .get(pid)
4111 .expect("process must exist while procfs path is resolved");
4112 null_separated_bytes(
4113 entry
4114 .env
4115 .into_iter()
4116 .map(|(key, value)| format!("{key}={value}"))
4117 .collect(),
4118 )
4119 }
4120
4121 fn proc_stat_bytes(&self, pid: u32) -> Vec<u8> {
4122 let entry = self
4123 .processes
4124 .get(pid)
4125 .expect("process must exist while procfs path is resolved");
4126 let command = entry.command.replace(')', "]");
4127 let state = match entry.status {
4128 ProcessStatus::Running => 'R',
4129 ProcessStatus::Stopped => 'T',
4130 ProcessStatus::Exited => 'Z',
4131 };
4132 format!(
4133 "{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",
4134 ppid = entry.ppid,
4135 pgid = entry.pgid,
4136 sid = entry.sid,
4137 )
4138 .into_bytes()
4139 }
4140
4141 fn proc_mounts_bytes(&self) -> Vec<u8> {
4142 let mounts = if let Some(table) =
4143 (self.filesystem.inner().inner() as &dyn Any).downcast_ref::<MountTable>()
4144 {
4145 table.get_mounts()
4146 } else {
4147 vec![MountEntry {
4148 path: String::from("/"),
4149 plugin_id: String::from("root"),
4150 read_only: false,
4151 }]
4152 };
4153
4154 mounts
4155 .into_iter()
4156 .map(|mount| {
4157 let options = if mount.read_only { "ro" } else { "rw" };
4158 format!(
4159 "{source} {target} {fstype} {options} 0 0\n",
4160 source = mount.plugin_id,
4161 target = mount.path,
4162 fstype = mount.plugin_id,
4163 )
4164 })
4165 .collect::<String>()
4166 .into_bytes()
4167 }
4168
4169 fn proc_cpu_count(&self) -> usize {
4170 self.resource_limits().virtual_cpu_count.unwrap_or(1)
4171 }
4172
4173 fn proc_cpuinfo_bytes(&self) -> Vec<u8> {
4174 let mut body = String::new();
4175 for processor in 0..self.proc_cpu_count() {
4176 body.push_str(&format!(
4177 "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"
4178 ));
4179 }
4180 body.into_bytes()
4181 }
4182
4183 fn proc_mem_total_bytes(&self) -> u64 {
4184 self.resource_limits()
4185 .max_wasm_memory_bytes
4186 .or(self.resource_limits().max_filesystem_bytes)
4187 .unwrap_or(DEFAULT_MAX_OPEN_FDS as u64 * 1024 * 1024)
4188 }
4189
4190 fn proc_meminfo_bytes(&self) -> Vec<u8> {
4191 let total_kb = self.proc_mem_total_bytes().div_ceil(1024);
4192 let zero_kb = 0;
4193 format!(
4194 "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"
4195 )
4196 .into_bytes()
4197 }
4198
4199 fn proc_loadavg_bytes(&self) -> Vec<u8> {
4200 let processes = self.processes.list_processes();
4201 let running = processes
4202 .values()
4203 .filter(|process| process.status == ProcessStatus::Running)
4204 .count();
4205 let total = processes.len().max(1);
4206 let last_pid = processes.keys().next_back().copied().unwrap_or(0);
4207 format!("0.00 0.00 0.00 {running}/{total} {last_pid}\n").into_bytes()
4208 }
4209
4210 fn proc_uptime_bytes(&self) -> Vec<u8> {
4211 let uptime = self.boot_instant.elapsed().as_secs_f64();
4212 format!("{uptime:.2} {uptime:.2}\n").into_bytes()
4213 }
4214
4215 fn proc_version_bytes(&self) -> Vec<u8> {
4216 format!(
4217 "Linux version 6.8.0-agentos (agentos@localhost) #1 SMP boot={}\n",
4218 self.boot_time_ms
4219 )
4220 .into_bytes()
4221 }
4222
4223 fn proc_status_bytes(&self, pid: u32) -> Vec<u8> {
4224 let entry = self
4225 .processes
4226 .get(pid)
4227 .expect("process must exist while procfs path is resolved");
4228 let (state_code, state_name) = match entry.status {
4229 ProcessStatus::Running => ('R', "running"),
4230 ProcessStatus::Stopped => ('T', "stopped"),
4231 ProcessStatus::Exited => ('Z', "zombie"),
4232 };
4233 format!(
4234 "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",
4235 0,
4236 0,
4237 name = entry.command,
4238 ppid = entry.ppid,
4239 uid = entry.identity.uid,
4240 euid = entry.identity.euid,
4241 gid = entry.identity.gid,
4242 egid = entry.identity.egid,
4243 )
4244 .into_bytes()
4245 }
4246
4247 fn proc_read_file_from_open_path(
4248 &mut self,
4249 current_pid: Option<u32>,
4250 path: &str,
4251 ) -> KernelResult<Vec<u8>> {
4252 let node = self
4253 .resolve_proc_node(path, current_pid)?
4254 .ok_or_else(|| proc_not_found_error(path))?;
4255 self.proc_read_file(current_pid, &node)
4256 }
4257
4258 fn proc_stat_from_open_path(
4259 &mut self,
4260 current_pid: Option<u32>,
4261 path: &str,
4262 ) -> KernelResult<VirtualStat> {
4263 let node = self
4264 .resolve_proc_node(path, current_pid)?
4265 .ok_or_else(|| proc_not_found_error(path))?;
4266 self.proc_stat(current_pid, &node)
4267 }
4268
4269 fn filesystem_usage(&mut self) -> KernelResult<FileSystemUsage> {
4270 if let Some(usage) = self.filesystem_usage_cache.clone() {
4271 return Ok(usage);
4272 }
4273 let filesystem = self.raw_filesystem_mut();
4274 let filesystem_any = filesystem as &mut dyn Any;
4275 let usage = if let Some(mount_table) = filesystem_any.downcast_mut::<MountTable>() {
4276 mount_table.root_usage()?
4277 } else {
4278 measure_filesystem_usage(filesystem)?
4279 };
4280 self.filesystem_usage_cache = Some(usage.clone());
4281 Ok(usage)
4282 }
4283
4284 fn invalidate_filesystem_usage_cache(&mut self) {
4285 self.filesystem_usage_cache = None;
4286 }
4287
4288 fn update_filesystem_usage_cache_for_resize(&mut self, old_size: u64, new_size: u64) {
4289 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4290 usage.total_bytes = usage
4291 .total_bytes
4292 .saturating_sub(old_size)
4293 .saturating_add(new_size);
4294 }
4295 }
4296
4297 fn update_filesystem_usage_cache_for_write(
4298 &mut self,
4299 existing: Option<&VirtualStat>,
4300 new_size: u64,
4301 ) {
4302 if is_storage_directory(existing) {
4303 return;
4304 }
4305
4306 if let Some(stat) = existing {
4307 self.update_filesystem_usage_cache_for_resize(stat.size, new_size);
4308 } else {
4309 self.update_filesystem_usage_cache_for_inode_create(new_size);
4310 }
4311 }
4312
4313 fn update_filesystem_usage_cache_for_inode_create(&mut self, size: u64) {
4314 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4315 usage.total_bytes = usage.total_bytes.saturating_add(size);
4316 usage.inode_count = usage.inode_count.saturating_add(1);
4317 }
4318 }
4319
4320 fn update_filesystem_usage_cache_for_inode_creates(&mut self, count: usize) {
4321 if count == 0 {
4322 return;
4323 }
4324 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4325 usage.inode_count = usage.inode_count.saturating_add(count);
4326 }
4327 }
4328
4329 fn update_filesystem_usage_cache_for_inode_delete(&mut self, size: u64) {
4330 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
4331 usage.total_bytes = usage.total_bytes.saturating_sub(size);
4332 usage.inode_count = usage.inode_count.saturating_sub(1);
4333 }
4334 }
4335
4336 fn update_filesystem_usage_cache_for_remove(&mut self, removed: Option<&VirtualStat>) {
4337 let Some(stat) = removed else {
4338 return;
4339 };
4340 if stat.is_directory || stat.nlink > 1 {
4341 return;
4342 }
4343 self.update_filesystem_usage_cache_for_inode_delete(stat.size);
4344 }
4345
4346 fn storage_stat(&mut self, path: &str) -> KernelResult<Option<VirtualStat>> {
4347 if is_virtual_device_storage_path(path) {
4348 return Ok(None);
4349 }
4350
4351 match self.raw_filesystem_mut().stat(path) {
4352 Ok(stat) => Ok(Some(stat)),
4353 Err(error) if error.code() == "ENOENT" => Ok(None),
4354 Err(error) => Err(error.into()),
4355 }
4356 }
4357
4358 fn storage_lstat(&mut self, path: &str) -> KernelResult<Option<VirtualStat>> {
4359 if is_virtual_device_storage_path(path) {
4360 return Ok(None);
4361 }
4362
4363 match self.raw_filesystem_mut().lstat(path) {
4364 Ok(stat) => Ok(Some(stat)),
4365 Err(error) if error.code() == "ENOENT" => Ok(None),
4366 Err(error) => Err(error.into()),
4367 }
4368 }
4369
4370 fn current_storage_file_size(&mut self, path: &str) -> KernelResult<u64> {
4371 Ok(self
4372 .storage_stat(path)?
4373 .filter(|stat| !stat.is_directory)
4374 .map(|stat| stat.size)
4375 .unwrap_or(0))
4376 }
4377
4378 fn apply_creation_mode(&mut self, path: &str, mode: u32, umask: u32) -> KernelResult<()> {
4379 let masked_mode = (mode & !0o777) | ((mode & 0o777) & !(umask & 0o777));
4380 Ok(self.filesystem.chmod(path, masked_mode)?)
4381 }
4382
4383 fn missing_directory_paths(
4384 &mut self,
4385 path: &str,
4386 recursive: bool,
4387 ) -> KernelResult<Vec<String>> {
4388 let normalized = normalize_path(path);
4389 if normalized == "/" {
4390 return Ok(Vec::new());
4391 }
4392
4393 if !recursive {
4394 return Ok(if self.storage_lstat(&normalized)?.is_none() {
4395 vec![normalized]
4396 } else {
4397 Vec::new()
4398 });
4399 }
4400
4401 let mut created = Vec::new();
4402 let mut current = String::from("/");
4403 for component in normalized
4404 .split('/')
4405 .filter(|component| !component.is_empty())
4406 {
4407 current = if current == "/" {
4408 format!("/{component}")
4409 } else {
4410 format!("{current}/{component}")
4411 };
4412 if self.storage_lstat(¤t)?.is_none() {
4413 created.push(current.clone());
4414 }
4415 }
4416 Ok(created)
4417 }
4418
4419 fn check_write_file_limits(&mut self, path: &str, new_size: u64) -> KernelResult<()> {
4420 let existing = self.storage_stat(path)?;
4421 self.check_write_file_limits_with_existing(path, existing.as_ref(), new_size)
4422 }
4423
4424 fn check_write_file_limits_with_existing(
4425 &mut self,
4426 path: &str,
4427 existing: Option<&VirtualStat>,
4428 new_size: u64,
4429 ) -> KernelResult<()> {
4430 if is_virtual_device_storage_path(path) {
4431 return Ok(());
4432 }
4433
4434 if let Some(existing) = existing {
4435 if is_storage_directory(Some(existing)) {
4436 return Ok(());
4437 }
4438 if new_size <= existing.size {
4439 return Ok(());
4440 }
4441
4442 let usage = self.filesystem_usage()?;
4443 self.resources.check_filesystem_usage(
4444 &usage,
4445 usage
4446 .total_bytes
4447 .saturating_sub(existing.size)
4448 .saturating_add(new_size),
4449 usage.inode_count,
4450 )?;
4451 return Ok(());
4452 }
4453
4454 let usage = self.filesystem_usage()?;
4455 self.resources.check_filesystem_usage(
4456 &usage,
4457 usage.total_bytes.saturating_add(new_size),
4458 usage.inode_count.saturating_add(1),
4459 )?;
4460 Ok(())
4461 }
4462
4463 fn check_create_dir_limits(&mut self, path: &str) -> KernelResult<()> {
4464 if is_virtual_device_storage_path(path) || self.storage_lstat(path)?.is_some() {
4465 return Ok(());
4466 }
4467
4468 let parent = parent_path(path);
4469 let Some(parent_stat) = self.storage_stat(&parent)? else {
4470 return Ok(());
4471 };
4472 if !parent_stat.is_directory {
4473 return Ok(());
4474 }
4475
4476 let usage = self.filesystem_usage()?;
4477 self.resources.check_filesystem_usage(
4478 &usage,
4479 usage.total_bytes,
4480 usage.inode_count.saturating_add(1),
4481 )?;
4482 Ok(())
4483 }
4484
4485 fn check_mkdir_limits(&mut self, path: &str, recursive: bool) -> KernelResult<()> {
4486 if is_virtual_device_storage_path(path) {
4487 return Ok(());
4488 }
4489
4490 if !recursive {
4491 return self.check_create_dir_limits(path);
4492 }
4493
4494 let usage = self.filesystem_usage()?;
4495 let new_inodes = count_missing_directory_components(self.raw_filesystem_mut(), path, true)?;
4496 self.resources.check_filesystem_usage(
4497 &usage,
4498 usage.total_bytes,
4499 usage.inode_count.saturating_add(new_inodes),
4500 )?;
4501 Ok(())
4502 }
4503
4504 fn check_symlink_limits(&mut self, target: &str, link_path: &str) -> KernelResult<()> {
4505 if is_virtual_device_storage_path(link_path) || self.storage_lstat(link_path)?.is_some() {
4506 return Ok(());
4507 }
4508
4509 let parent = parent_path(link_path);
4510 let Some(parent_stat) = self.storage_stat(&parent)? else {
4511 return Ok(());
4512 };
4513 if !parent_stat.is_directory {
4514 return Ok(());
4515 }
4516
4517 let usage = self.filesystem_usage()?;
4518 self.resources.check_filesystem_usage(
4519 &usage,
4520 usage.total_bytes.saturating_add(target.len() as u64),
4521 usage.inode_count.saturating_add(1),
4522 )?;
4523 Ok(())
4524 }
4525
4526 fn check_truncate_limits_with_existing(
4527 &mut self,
4528 path: &str,
4529 existing: Option<&VirtualStat>,
4530 length: u64,
4531 ) -> KernelResult<()> {
4532 if is_virtual_device_storage_path(path) {
4533 return Ok(());
4534 }
4535
4536 let Some(existing) = existing else {
4537 return Ok(());
4538 };
4539 if is_storage_directory(Some(existing)) {
4540 return Ok(());
4541 }
4542 self.check_path_resize_limits_with_existing(existing.size, length)
4543 }
4544
4545 fn check_rename_copy_up_limits(&mut self, old_path: &str, new_path: &str) -> KernelResult<()> {
4546 let max_bytes = self.resource_limits().max_filesystem_bytes;
4547 let max_inodes = self.resource_limits().max_inode_count;
4548 let filesystem_any = self.raw_filesystem_mut() as &mut dyn Any;
4549
4550 if let Some(root) = filesystem_any.downcast_mut::<RootFileSystem>() {
4551 root.check_rename_copy_up_limits(old_path, new_path, max_bytes, max_inodes)?;
4552 return Ok(());
4553 }
4554
4555 if let Some(mount_table) = filesystem_any.downcast_mut::<MountTable>() {
4556 mount_table.check_rename_copy_up_limits(old_path, new_path, max_bytes, max_inodes)?;
4557 }
4558
4559 Ok(())
4560 }
4561
4562 fn check_path_resize_limits(&mut self, path: &str, new_size: u64) -> KernelResult<()> {
4563 if is_virtual_device_storage_path(path) {
4564 return Ok(());
4565 }
4566
4567 let Some(existing) = self.storage_stat(path)? else {
4568 return Ok(());
4569 };
4570 if existing.is_directory {
4571 return Ok(());
4572 }
4573 self.check_path_resize_limits_with_existing(existing.size, new_size)
4574 }
4575
4576 fn check_path_resize_limits_with_existing(
4577 &mut self,
4578 existing_size: u64,
4579 new_size: u64,
4580 ) -> KernelResult<()> {
4581 if new_size <= existing_size {
4582 return Ok(());
4583 }
4584
4585 let usage = self.filesystem_usage()?;
4586 self.resources.check_filesystem_usage(
4587 &usage,
4588 usage
4589 .total_bytes
4590 .saturating_sub(existing_size)
4591 .saturating_add(new_size),
4592 usage.inode_count,
4593 )?;
4594 Ok(())
4595 }
4596
4597 fn blocking_read_timeout(&self) -> Option<Duration> {
4598 self.resources
4599 .limits()
4600 .max_blocking_read_ms
4601 .map(Duration::from_millis)
4602 }
4603
4604 fn close_special_resource_if_needed(&self, description: &Arc<FileDescription>, filetype: u8) {
4605 close_special_resource_if_needed(
4606 &self.file_locks,
4607 &self.pipes,
4608 &self.ptys,
4609 description,
4610 filetype,
4611 );
4612 }
4613}
4614
4615impl KernelVm<MountTable> {
4616 fn check_mount_permissions(&self, path: &str) -> KernelResult<()> {
4617 self.filesystem
4618 .check_path(FsOperation::Write, path)
4619 .map_err(KernelError::from)?;
4620 if is_sensitive_mount_path(path) {
4621 self.filesystem
4622 .check_path(FsOperation::MountSensitive, path)
4623 .map_err(KernelError::from)?;
4624 }
4625 Ok(())
4626 }
4627
4628 pub fn mount_filesystem(
4629 &mut self,
4630 path: &str,
4631 filesystem: impl VirtualFileSystem + 'static,
4632 options: MountOptions,
4633 ) -> KernelResult<()> {
4634 self.assert_not_terminated()?;
4635 self.check_mount_permissions(path)?;
4636 self.filesystem
4637 .inner_mut()
4638 .inner_mut()
4639 .mount(path, filesystem, options)
4640 .map_err(KernelError::from)
4641 }
4642
4643 pub fn mount_boxed_filesystem(
4644 &mut self,
4645 path: &str,
4646 filesystem: Box<dyn MountedFileSystem>,
4647 options: MountOptions,
4648 ) -> KernelResult<()> {
4649 self.assert_not_terminated()?;
4650 self.check_mount_permissions(path)?;
4651 self.filesystem
4652 .inner_mut()
4653 .inner_mut()
4654 .mount_boxed(path, filesystem, options)
4655 .map_err(KernelError::from)
4656 }
4657
4658 pub fn unmount_filesystem(&mut self, path: &str) -> KernelResult<()> {
4659 self.assert_not_terminated()?;
4660 self.check_mount_permissions(path)?;
4661 self.filesystem
4662 .inner_mut()
4663 .inner_mut()
4664 .unmount(path)
4665 .map_err(KernelError::from)
4666 }
4667
4668 pub fn mounted_filesystems(&self) -> Vec<MountEntry> {
4669 self.filesystem.inner().inner().get_mounts()
4670 }
4671
4672 pub fn root_filesystem_mut(&mut self) -> Option<&mut RootFileSystem> {
4673 self.filesystem
4674 .inner_mut()
4675 .inner_mut()
4676 .root_virtual_filesystem_mut::<RootFileSystem>()
4677 }
4678
4679 pub fn snapshot_root_filesystem(&mut self) -> KernelResult<RootFilesystemSnapshot> {
4680 let usage = self.filesystem_usage()?;
4681 self.resources
4682 .check_filesystem_usage(&usage, usage.total_bytes, usage.inode_count)?;
4683 let root = self
4684 .root_filesystem_mut()
4685 .ok_or_else(|| KernelError::new("EINVAL", "native root filesystem is not available"))?;
4686 root.snapshot().map_err(KernelError::from)
4687 }
4688}
4689
4690#[derive(Default)]
4691struct StubDriverState {
4692 exit_code: Option<i32>,
4693 on_exit: Option<ProcessExitCallback>,
4694 kill_signals: Vec<i32>,
4695}
4696
4697#[derive(Default)]
4698struct StubDriverProcess {
4699 state: Mutex<StubDriverState>,
4700 waiters: Condvar,
4701}
4702
4703impl StubDriverProcess {
4704 fn finish(&self, exit_code: i32) {
4705 let callback = {
4706 let mut state = lock_or_recover(&self.state);
4707 if state.exit_code.is_some() {
4708 return;
4709 }
4710 state.exit_code = Some(exit_code);
4711 self.waiters.notify_all();
4712 state.on_exit.clone()
4713 };
4714
4715 if let Some(callback) = callback {
4716 callback(exit_code);
4717 }
4718 }
4719
4720 fn kill_signals(&self) -> Vec<i32> {
4721 lock_or_recover(&self.state).kill_signals.clone()
4722 }
4723}
4724
4725impl DriverProcess for StubDriverProcess {
4726 fn kill(&self, signal: i32) {
4727 {
4728 let mut state = lock_or_recover(&self.state);
4729 state.kill_signals.push(signal);
4730 }
4731 if matches!(
4732 signal,
4733 crate::process_table::SIGCHLD | SIGCONT | SIGSTOP | SIGTSTP | SIGWINCH
4734 ) {
4735 return;
4736 }
4737 self.finish(128 + signal);
4738 }
4739
4740 fn wait(&self, timeout: Duration) -> Option<i32> {
4741 let state = lock_or_recover(&self.state);
4742 if let Some(code) = state.exit_code {
4743 return Some(code);
4744 }
4745
4746 let (state, _) = wait_timeout_or_recover(&self.waiters, state, timeout);
4747 state.exit_code
4748 }
4749
4750 fn set_on_exit(&self, callback: ProcessExitCallback) {
4751 let maybe_exit = {
4752 let mut state = lock_or_recover(&self.state);
4753 state.on_exit = Some(callback.clone());
4754 state.exit_code
4755 };
4756
4757 if let Some(code) = maybe_exit {
4758 callback(code);
4759 }
4760 }
4761}
4762
4763impl From<VfsError> for KernelError {
4764 fn from(error: VfsError) -> Self {
4765 map_error(error.code(), error.to_string())
4766 }
4767}
4768
4769fn lock_or_recover<'a, T>(mutex: &'a Mutex<T>) -> MutexGuard<'a, T> {
4770 match mutex.lock() {
4771 Ok(guard) => guard,
4772 Err(poisoned) => poisoned.into_inner(),
4773 }
4774}
4775
4776fn wait_timeout_or_recover<'a, T>(
4777 condvar: &Condvar,
4778 guard: MutexGuard<'a, T>,
4779 timeout: Duration,
4780) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
4781 match condvar.wait_timeout(guard, timeout) {
4782 Ok(result) => result,
4783 Err(poisoned) => poisoned.into_inner(),
4784 }
4785}
4786
4787fn is_sensitive_mount_path(path: &str) -> bool {
4788 let normalized = crate::vfs::normalize_path(path);
4789 normalized == "/"
4790 || normalized == "/etc"
4791 || normalized.starts_with("/etc/")
4792 || normalized == "/proc"
4793 || normalized.starts_with("/proc/")
4794}
4795
4796impl From<FdTableError> for KernelError {
4797 fn from(error: FdTableError) -> Self {
4798 map_error(error.code(), error.to_string())
4799 }
4800}
4801
4802impl From<PipeError> for KernelError {
4803 fn from(error: PipeError) -> Self {
4804 map_error(error.code(), error.to_string())
4805 }
4806}
4807
4808impl From<PtyError> for KernelError {
4809 fn from(error: PtyError) -> Self {
4810 map_error(error.code(), error.to_string())
4811 }
4812}
4813
4814impl From<ProcessTableError> for KernelError {
4815 fn from(error: ProcessTableError) -> Self {
4816 map_error(error.code(), error.to_string())
4817 }
4818}
4819
4820impl From<PermissionError> for KernelError {
4821 fn from(error: PermissionError) -> Self {
4822 map_error(error.code(), error.to_string())
4823 }
4824}
4825
4826impl From<ResourceError> for KernelError {
4827 fn from(error: ResourceError) -> Self {
4828 map_error(error.code(), error.to_string())
4829 }
4830}
4831
4832impl From<SocketTableError> for KernelError {
4833 fn from(error: SocketTableError) -> Self {
4834 map_error(error.code(), error.to_string())
4835 }
4836}
4837
4838impl From<RootFilesystemError> for KernelError {
4839 fn from(error: RootFilesystemError) -> Self {
4840 map_error("EINVAL", error.to_string())
4841 }
4842}
4843
4844fn map_dns_resolver_error(error: crate::dns::DnsResolverError) -> KernelError {
4845 let code = match error.kind() {
4846 DnsResolverErrorKind::InvalidInput => "EINVAL",
4847 DnsResolverErrorKind::LookupFailed => "EHOSTUNREACH",
4848 };
4849 map_error(code, error.to_string())
4850}
4851
4852fn map_error(code: &'static str, message: String) -> KernelError {
4853 let trimmed = strip_error_prefix(code, &message)
4854 .map(ToOwned::to_owned)
4855 .unwrap_or(message);
4856 KernelError::new(code, trimmed)
4857}
4858
4859fn strip_error_prefix<'a>(code: &str, message: &'a str) -> Option<&'a str> {
4860 let prefix = format!("{code}: ");
4861 message.strip_prefix(&prefix)
4862}
4863
4864fn parse_dev_fd_path(path: &str) -> KernelResult<Option<u32>> {
4865 let Some(raw_fd) = path.strip_prefix("/dev/fd/") else {
4866 return Ok(None);
4867 };
4868 if raw_fd.is_empty() {
4869 return Err(KernelError::new(
4870 "EBADF",
4871 format!("bad file descriptor: {path}"),
4872 ));
4873 }
4874 let fd = raw_fd
4875 .parse::<u32>()
4876 .map_err(|_| KernelError::new("EBADF", format!("bad file descriptor: {path}")))?;
4877 Ok(Some(fd))
4878}
4879
4880fn count_missing_directory_components<F: VirtualFileSystem>(
4881 filesystem: &mut F,
4882 path: &str,
4883 include_final: bool,
4884) -> VfsResult<usize> {
4885 let normalized = normalize_path(path);
4886 let parts = normalized
4887 .split('/')
4888 .filter(|part| !part.is_empty())
4889 .collect::<Vec<_>>();
4890 let limit = if include_final {
4891 parts.len()
4892 } else {
4893 parts.len().saturating_sub(1)
4894 };
4895
4896 let mut current = String::from("/");
4897 for (index, part) in parts.iter().take(limit).enumerate() {
4898 let candidate = if current == "/" {
4899 format!("/{}", part)
4900 } else {
4901 format!("{current}/{}", part)
4902 };
4903
4904 match filesystem.stat(&candidate) {
4905 Ok(stat) => {
4906 if !stat.is_directory {
4907 return Err(VfsError::new(
4908 "ENOTDIR",
4909 format!("not a directory, mkdir '{candidate}'"),
4910 ));
4911 }
4912 current = candidate;
4913 }
4914 Err(error) if error.code() == "ENOENT" => {
4915 return Ok(limit.saturating_sub(index));
4916 }
4917 Err(error) => return Err(error),
4918 }
4919 }
4920
4921 Ok(0)
4922}
4923
4924fn parent_path(path: &str) -> String {
4925 let normalized = normalize_path(path);
4926 let Some((head, _)) = normalized.rsplit_once('/') else {
4927 return String::from("/");
4928 };
4929
4930 if head.is_empty() {
4931 String::from("/")
4932 } else {
4933 String::from(head)
4934 }
4935}
4936
4937fn join_absolute_path(parent: &str, child: &str) -> String {
4938 if parent == "/" {
4939 format!("/{child}")
4940 } else {
4941 format!("{parent}/{child}")
4942 }
4943}
4944
4945fn join_child_path(parent: &str, child: &str) -> String {
4946 normalize_path(&join_absolute_path(parent, child))
4947}
4948
4949fn is_virtual_device_storage_path(path: &str) -> bool {
4950 matches!(
4951 path,
4952 "/dev/null" | "/dev/zero" | "/dev/stdin" | "/dev/stdout" | "/dev/stderr" | "/dev/urandom"
4953 ) || path == "/dev"
4954 || path == "/dev/fd"
4955 || path == "/dev/pts"
4956 || path.starts_with("/dev/fd/")
4957 || path.starts_with("/dev/pts/")
4958}
4959
4960fn is_storage_directory(stat: Option<&VirtualStat>) -> bool {
4961 stat.is_some_and(|stat| stat.is_directory && !stat.is_symbolic_link)
4962}
4963
4964fn is_proc_path(path: &str) -> bool {
4965 let normalized = normalize_path(path);
4966 normalized == "/proc" || normalized.starts_with("/proc/")
4967}
4968
4969fn is_agentos_path(path: &str) -> bool {
4970 let normalized = normalize_path(path);
4971 normalized == "/etc/agentos" || normalized.starts_with("/etc/agentos/")
4972}
4973
4974fn open_requires_write_access(flags: u32) -> bool {
4975 flags & (O_CREAT | O_EXCL | O_TRUNC) != 0 || (flags & 0b11) != crate::fd_table::O_RDONLY
4976}
4977
4978fn checked_write_end(offset: u64, len: usize) -> KernelResult<u64> {
4979 offset
4980 .checked_add(len as u64)
4981 .ok_or_else(|| KernelError::new("EINVAL", "write offset out of range"))
4982}
4983
4984fn filetype_for_path(path: &str, stat: &VirtualStat) -> u8 {
4985 if stat.is_directory {
4986 FILETYPE_DIRECTORY
4987 } else if path.starts_with("/dev/") {
4988 FILETYPE_CHARACTER_DEVICE
4989 } else if stat.is_symbolic_link {
4990 FILETYPE_SYMBOLIC_LINK
4991 } else {
4992 FILETYPE_REGULAR_FILE
4993 }
4994}
4995
4996fn synthetic_character_device_stat(ino: u64) -> VirtualStat {
4997 let now = now_ms();
4998 VirtualStat {
4999 mode: 0o666,
5000 size: 0,
5001 blocks: 0,
5002 dev: 2,
5003 rdev: 0,
5004 is_directory: false,
5005 is_symbolic_link: false,
5006 atime_ms: now,
5007 atime_nsec: 0,
5008 mtime_ms: now,
5009 mtime_nsec: 0,
5010 ctime_ms: now,
5011 ctime_nsec: 0,
5012 birthtime_ms: now,
5013 ino,
5014 nlink: 1,
5015 uid: 0,
5016 gid: 0,
5017 }
5018}
5019
5020fn proc_dir_stat(ino: u64) -> VirtualStat {
5021 let now = now_ms();
5022 VirtualStat {
5023 mode: 0o555,
5024 size: 0,
5025 blocks: 0,
5026 dev: 3,
5027 rdev: 0,
5028 is_directory: true,
5029 is_symbolic_link: false,
5030 atime_ms: now,
5031 atime_nsec: 0,
5032 mtime_ms: now,
5033 mtime_nsec: 0,
5034 ctime_ms: now,
5035 ctime_nsec: 0,
5036 birthtime_ms: now,
5037 ino,
5038 nlink: 2,
5039 uid: 0,
5040 gid: 0,
5041 }
5042}
5043
5044fn proc_file_stat(ino: u64, size: u64) -> VirtualStat {
5045 let now = now_ms();
5046 VirtualStat {
5047 mode: 0o444,
5048 size,
5049 blocks: if size == 0 { 0 } else { size.div_ceil(512) },
5050 dev: 3,
5051 rdev: 0,
5052 is_directory: false,
5053 is_symbolic_link: false,
5054 atime_ms: now,
5055 atime_nsec: 0,
5056 mtime_ms: now,
5057 mtime_nsec: 0,
5058 ctime_ms: now,
5059 ctime_nsec: 0,
5060 birthtime_ms: now,
5061 ino,
5062 nlink: 1,
5063 uid: 0,
5064 gid: 0,
5065 }
5066}
5067
5068fn proc_symlink_stat(ino: u64, size: u64) -> VirtualStat {
5069 let now = now_ms();
5070 VirtualStat {
5071 mode: 0o777,
5072 size,
5073 blocks: if size == 0 { 0 } else { size.div_ceil(512) },
5074 dev: 3,
5075 rdev: 0,
5076 is_directory: false,
5077 is_symbolic_link: true,
5078 atime_ms: now,
5079 atime_nsec: 0,
5080 mtime_ms: now,
5081 mtime_nsec: 0,
5082 ctime_ms: now,
5083 ctime_nsec: 0,
5084 birthtime_ms: now,
5085 ino,
5086 nlink: 1,
5087 uid: 0,
5088 gid: 0,
5089 }
5090}
5091
5092fn proc_filetype(node: &ProcNode) -> u8 {
5093 match node {
5094 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
5095 FILETYPE_DIRECTORY
5096 }
5097 ProcNode::SelfLink { .. } | ProcNode::PidCwdLink { .. } | ProcNode::PidFdLink { .. } => {
5098 FILETYPE_SYMBOLIC_LINK
5099 }
5100 ProcNode::MountsFile
5101 | ProcNode::CpuInfoFile
5102 | ProcNode::MemInfoFile
5103 | ProcNode::LoadAvgFile
5104 | ProcNode::UptimeFile
5105 | ProcNode::VersionFile
5106 | ProcNode::PidCmdline { .. }
5107 | ProcNode::PidEnviron { .. }
5108 | ProcNode::PidStatFile { .. }
5109 | ProcNode::PidStatusFile { .. } => FILETYPE_REGULAR_FILE,
5110 }
5111}
5112
5113fn proc_inode(node: &ProcNode) -> u64 {
5114 match node {
5115 ProcNode::RootDir => 0xfffe_0001,
5116 ProcNode::MountsFile => 0xfffe_0002,
5117 ProcNode::CpuInfoFile => 0xfffe_0003,
5118 ProcNode::MemInfoFile => 0xfffe_0004,
5119 ProcNode::LoadAvgFile => 0xfffe_0005,
5120 ProcNode::UptimeFile => 0xfffe_0006,
5121 ProcNode::VersionFile => 0xfffe_0007,
5122 ProcNode::SelfLink { pid } => 0xfffe_1000 + u64::from(*pid),
5123 ProcNode::PidDir { pid } => 0xfffe_2000 + u64::from(*pid),
5124 ProcNode::PidFdDir { pid } => 0xfffe_3000 + u64::from(*pid),
5125 ProcNode::PidCmdline { pid } => 0xfffe_4000 + u64::from(*pid),
5126 ProcNode::PidEnviron { pid } => 0xfffe_5000 + u64::from(*pid),
5127 ProcNode::PidCwdLink { pid } => 0xfffe_6000 + u64::from(*pid),
5128 ProcNode::PidStatFile { pid } => 0xfffe_7000 + u64::from(*pid),
5129 ProcNode::PidStatusFile { pid } => 0xfffe_8000 + u64::from(*pid),
5130 ProcNode::PidFdLink { pid, fd } => 0xffff_0000 + ((u64::from(*pid)) << 8) + u64::from(*fd),
5131 }
5132}
5133
5134fn null_separated_bytes(parts: Vec<String>) -> Vec<u8> {
5135 if parts.is_empty() {
5136 return Vec::new();
5137 }
5138
5139 let mut bytes = parts.join("\0").into_bytes();
5140 bytes.push(0);
5141 bytes
5142}
5143
5144fn proc_not_found_error(path: &str) -> KernelError {
5145 KernelError::new(
5146 "ENOENT",
5147 format!("no such file or directory, stat '{path}'"),
5148 )
5149}
5150
5151fn read_only_filesystem_error(path: &str) -> KernelError {
5152 KernelError::new("EROFS", format!("read-only filesystem: {path}"))
5153}
5154
5155fn now_ms() -> u64 {
5156 SystemTime::now()
5157 .duration_since(UNIX_EPOCH)
5158 .unwrap_or_default()
5159 .as_millis() as u64
5160}
5161
5162impl<F> Drop for KernelVm<F> {
5163 fn drop(&mut self) {
5164 if !self.terminated {
5165 dispose_kernel_vm_resources(self);
5166 }
5167 }
5168}
5169
5170#[cfg(test)]
5171mod tests {
5172 use super::*;
5173 use crate::vfs::MemoryFileSystem;
5174 use std::panic::{catch_unwind, AssertUnwindSafe};
5175 use std::thread;
5176
5177 struct RetainedKernelResources {
5178 process: KernelProcessHandle,
5179 fd_tables: Arc<Mutex<FdTableManager>>,
5180 pipes: PipeManager,
5181 ptys: PtyManager,
5182 sockets: SocketTable,
5183 driver_pids: Arc<Mutex<BTreeMap<String, BTreeSet<u32>>>>,
5184 }
5185
5186 fn kernel_with_live_resources() -> (KernelVm<MemoryFileSystem>, RetainedKernelResources) {
5187 let mut config = KernelVmConfig::new("vm-drop-resources");
5188 config.permissions = Permissions::allow_all();
5189 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
5190 kernel
5191 .register_driver(CommandDriver::new("shell", ["sh"]))
5192 .expect("register shell");
5193
5194 let process = kernel
5195 .spawn_process(
5196 "sh",
5197 Vec::new(),
5198 SpawnOptions {
5199 requester_driver: Some(String::from("shell")),
5200 ..SpawnOptions::default()
5201 },
5202 )
5203 .expect("spawn shell");
5204 let _ = kernel.open_pipe("shell", process.pid()).expect("open pipe");
5205 let _ = kernel.open_pty("shell", process.pid()).expect("open pty");
5206 let socket = kernel
5207 .socket_create("shell", process.pid(), SocketSpec::tcp())
5208 .expect("create socket");
5209 kernel
5210 .socket_set_state("shell", process.pid(), socket, SocketState::Listening)
5211 .expect("mark listener");
5212
5213 let retained = RetainedKernelResources {
5214 process: process.clone(),
5215 fd_tables: Arc::clone(&kernel.fd_tables),
5216 pipes: kernel.pipes.clone(),
5217 ptys: kernel.ptys.clone(),
5218 sockets: kernel.sockets.clone(),
5219 driver_pids: Arc::clone(&kernel.driver_pids),
5220 };
5221
5222 assert_eq!(lock_or_recover(retained.fd_tables.as_ref()).len(), 1);
5223 assert_eq!(retained.pipes.pipe_count(), 1);
5224 assert_eq!(retained.ptys.pty_count(), 1);
5225 assert_eq!(retained.sockets.snapshot().sockets, 1);
5226
5227 (kernel, retained)
5228 }
5229
5230 fn recursive_fs_kernel() -> KernelVm<MemoryFileSystem> {
5231 let mut config = KernelVmConfig::new("vm-recursive-fs");
5232 config.permissions = Permissions::allow_all();
5233 KernelVm::new(MemoryFileSystem::new(), config)
5234 }
5235
5236 #[test]
5237 fn recursive_copy_preserves_tree_metadata_and_symlinks() {
5238 let mut kernel = recursive_fs_kernel();
5239 kernel
5240 .mkdir("/src/nested", true)
5241 .expect("create source dirs");
5242 kernel
5243 .write_file("/src/nested/file.txt", b"hello".to_vec())
5244 .expect("write source file");
5245 kernel
5246 .chmod("/src/nested/file.txt", 0o640)
5247 .expect("chmod source file");
5248 kernel
5249 .chown("/src/nested/file.txt", 42, 43)
5250 .expect("chown source file");
5251 kernel
5252 .symlink("../nested/file.txt", "/src/link")
5253 .expect("create source symlink");
5254
5255 kernel
5256 .copy_path("/src", "/dst", true)
5257 .expect("recursive copy");
5258
5259 assert_eq!(
5260 kernel
5261 .read_file("/dst/nested/file.txt")
5262 .expect("read copied"),
5263 b"hello".to_vec()
5264 );
5265 let copied = kernel.lstat("/dst/nested/file.txt").expect("stat copied");
5266 assert_eq!(copied.mode & 0o777, 0o640);
5267 assert_eq!((copied.uid, copied.gid), (42, 43));
5268 let link = kernel.lstat("/dst/link").expect("lstat copied link");
5269 assert!(link.is_symbolic_link);
5270 assert_eq!(
5271 kernel.read_link("/dst/link").expect("read copied link"),
5272 "../nested/file.txt"
5273 );
5274 }
5275
5276 #[test]
5277 fn recursive_remove_deletes_subtree_but_does_not_follow_symlinks() {
5278 let mut kernel = recursive_fs_kernel();
5279 kernel.mkdir("/tree/dir", true).expect("create tree");
5280 kernel
5281 .write_file("/tree/dir/file.txt", b"tree".to_vec())
5282 .expect("write tree file");
5283 kernel
5284 .write_file("/outside.txt", b"outside".to_vec())
5285 .expect("write outside file");
5286 kernel
5287 .symlink("/outside.txt", "/tree/link-out")
5288 .expect("create symlink out of tree");
5289
5290 kernel.remove_path("/tree", true).expect("recursive remove");
5291
5292 assert!(!kernel.exists("/tree").expect("tree existence"));
5293 assert_eq!(
5294 kernel.read_file("/outside.txt").expect("outside survives"),
5295 b"outside".to_vec()
5296 );
5297 }
5298
5299 #[test]
5300 fn read_dir_recursive_respects_user_depth_and_reports_types() {
5301 let mut kernel = recursive_fs_kernel();
5302 kernel.mkdir("/root/a/b", true).expect("create deep tree");
5303 kernel
5304 .write_file("/root/a/file.txt", b"x".to_vec())
5305 .expect("write file");
5306 kernel
5307 .symlink("a/file.txt", "/root/link")
5308 .expect("create link");
5309
5310 let entries = kernel
5311 .read_dir_recursive("/root", Some(0))
5312 .expect("recursive listing");
5313 assert_eq!(entries.len(), 2);
5314 assert!(entries
5315 .iter()
5316 .any(|entry| entry.path == "/root/a" && entry.is_directory));
5317 assert!(entries
5318 .iter()
5319 .any(|entry| entry.path == "/root/link" && entry.is_symbolic_link));
5320 assert!(!entries.iter().any(|entry| entry.path == "/root/a/file.txt"));
5321 }
5322
5323 #[test]
5324 fn recursive_ops_enforce_depth_and_entry_bounds() {
5325 let mut depth_config = KernelVmConfig::new("vm-recursive-depth-limit");
5326 depth_config.permissions = Permissions::allow_all();
5327 depth_config.resources = ResourceLimits {
5328 max_recursive_fs_depth: Some(1),
5329 ..ResourceLimits::default()
5330 };
5331 let mut depth_kernel = KernelVm::new(MemoryFileSystem::new(), depth_config);
5332 depth_kernel
5333 .mkdir("/root/a/b", true)
5334 .expect("create deep tree");
5335
5336 let error = depth_kernel
5337 .copy_path("/root", "/copy", true)
5338 .expect_err("copy should hit depth limit");
5339 assert_eq!(error.code(), "ENOMEM");
5340 assert!(error.to_string().contains("depth 2"));
5341
5342 let mut entry_config = KernelVmConfig::new("vm-recursive-entry-limit");
5343 entry_config.permissions = Permissions::allow_all();
5344 entry_config.resources = ResourceLimits {
5345 max_recursive_fs_entries: Some(2),
5346 ..ResourceLimits::default()
5347 };
5348 let mut entry_kernel = KernelVm::new(MemoryFileSystem::new(), entry_config);
5349 entry_kernel.mkdir("/root", true).expect("create root");
5350 entry_kernel
5351 .write_file("/root/a.txt", b"a".to_vec())
5352 .expect("write a");
5353 entry_kernel
5354 .write_file("/root/b.txt", b"b".to_vec())
5355 .expect("write b");
5356 entry_kernel
5357 .write_file("/root/c.txt", b"c".to_vec())
5358 .expect("write c");
5359
5360 let error = entry_kernel
5361 .read_dir_recursive("/root", None)
5362 .expect_err("listing should hit entry limit");
5363 assert_eq!(error.code(), "ENOMEM");
5364 assert!(error.to_string().contains("3 entries"));
5365 }
5366
5367 fn assert_kernel_drop_released_resources(retained: &RetainedKernelResources) {
5368 assert_eq!(retained.process.wait(Duration::from_millis(50)), Some(143));
5369 assert_eq!(retained.process.kill_signals(), vec![15]);
5370 assert!(
5371 lock_or_recover(retained.fd_tables.as_ref()).is_empty(),
5372 "kernel drop should remove fd tables"
5373 );
5374 assert_eq!(
5375 retained.pipes.pipe_count(),
5376 0,
5377 "kernel drop should close pipes"
5378 );
5379 assert_eq!(
5380 retained.ptys.pty_count(),
5381 0,
5382 "kernel drop should close PTYs"
5383 );
5384 assert_eq!(
5385 retained.sockets.snapshot().sockets,
5386 0,
5387 "kernel drop should reclaim sockets"
5388 );
5389 assert!(
5390 lock_or_recover(retained.driver_pids.as_ref()).is_empty(),
5391 "kernel drop should clear driver-owned pid tracking"
5392 );
5393 }
5394
5395 #[test]
5396 fn setpgid_rejects_joining_a_process_group_owned_by_another_driver() {
5397 let kernel = KernelVm::new(MemoryFileSystem::new(), KernelVmConfig::new("vm-setpgid"));
5398
5399 let leader_pid = kernel.processes.allocate_pid().expect("allocate pid");
5400 kernel.processes.register(
5401 leader_pid,
5402 String::from("driver-a"),
5403 String::from("sh"),
5404 Vec::new(),
5405 ProcessContext {
5406 pid: leader_pid,
5407 ppid: 0,
5408 env: BTreeMap::new(),
5409 cwd: String::from("/"),
5410 umask: DEFAULT_PROCESS_UMASK,
5411 fds: Default::default(),
5412 identity: ProcessIdentity::default(),
5413 blocked_signals: SignalSet::empty(),
5414 pending_signals: SignalSet::empty(),
5415 },
5416 Arc::new(StubDriverProcess::default()),
5417 );
5418
5419 let peer_pid = kernel.processes.allocate_pid().expect("allocate pid");
5420 kernel.processes.register(
5421 peer_pid,
5422 String::from("driver-b"),
5423 String::from("sh"),
5424 Vec::new(),
5425 ProcessContext {
5426 pid: peer_pid,
5427 ppid: leader_pid,
5428 env: BTreeMap::new(),
5429 cwd: String::from("/"),
5430 umask: DEFAULT_PROCESS_UMASK,
5431 fds: Default::default(),
5432 identity: ProcessIdentity::default(),
5433 blocked_signals: SignalSet::empty(),
5434 pending_signals: SignalSet::empty(),
5435 },
5436 Arc::new(StubDriverProcess::default()),
5437 );
5438
5439 lock_or_recover(&kernel.driver_pids)
5440 .entry(String::from("driver-a"))
5441 .or_default()
5442 .insert(leader_pid);
5443 lock_or_recover(&kernel.driver_pids)
5444 .entry(String::from("driver-b"))
5445 .or_default()
5446 .insert(peer_pid);
5447
5448 let error = kernel
5449 .setpgid("driver-b", peer_pid, leader_pid)
5450 .expect_err("cross-driver process-group join should be denied");
5451 assert_eq!(error.code(), "EPERM");
5452 }
5453
5454 #[test]
5455 fn sigprocmask_and_sigpending_require_process_ownership() {
5456 let mut kernel = KernelVm::new(MemoryFileSystem::new(), KernelVmConfig::new("vm-sigmask"));
5457 let process = kernel
5458 .register_process(
5459 String::from("driver-a"),
5460 String::from("sleep"),
5461 Vec::new(),
5462 ProcessContext {
5463 pid: 0,
5464 ppid: 0,
5465 env: BTreeMap::new(),
5466 cwd: String::from("/"),
5467 umask: DEFAULT_PROCESS_UMASK,
5468 fds: Default::default(),
5469 identity: ProcessIdentity::default(),
5470 blocked_signals: SignalSet::empty(),
5471 pending_signals: SignalSet::empty(),
5472 },
5473 None,
5474 )
5475 .expect("create virtual process");
5476 let mask =
5477 SignalSet::from_signal(crate::process_table::SIGCHLD).expect("SIGCHLD should be valid");
5478
5479 let previous = kernel
5480 .sigprocmask("driver-a", process.pid(), SigmaskHow::Block, mask)
5481 .expect("owner should update signal mask");
5482 assert_eq!(previous, SignalSet::empty());
5483 assert_eq!(
5484 kernel
5485 .sigpending("driver-a", process.pid())
5486 .expect("owner should read pending signals"),
5487 SignalSet::empty()
5488 );
5489
5490 let error = kernel
5491 .sigprocmask("driver-b", process.pid(), SigmaskHow::Block, mask)
5492 .expect_err("foreign driver should be rejected");
5493 assert_eq!(error.code(), "EPERM");
5494 let error = kernel
5495 .sigpending("driver-b", process.pid())
5496 .expect_err("foreign driver should be rejected");
5497 assert_eq!(error.code(), "EPERM");
5498 }
5499
5500 #[test]
5501 fn cleanup_process_resources_blocks_concurrent_dup2_until_pipe_cleanup_finishes() {
5502 let fd_tables = Arc::new(Mutex::new(FdTableManager::new()));
5503 let file_locks = FileLockManager::new();
5504 let pipes = PipeManager::new();
5505 let ptys = PtyManager::new();
5506 let sockets = SocketTable::new();
5507 let driver_pids = Arc::new(Mutex::new(BTreeMap::from([(
5508 String::from("driver"),
5509 BTreeSet::from([41]),
5510 )])));
5511 let pipe = pipes.create_pipe();
5512
5513 {
5514 let mut tables = lock_or_recover(fd_tables.as_ref());
5515 let table = tables.create(41);
5516 table
5517 .open_with(
5518 Arc::clone(&pipe.read.description),
5519 pipe.read.filetype,
5520 Some(10),
5521 )
5522 .expect("open pipe read end");
5523 table
5524 .open_with(
5525 Arc::clone(&pipe.write.description),
5526 pipe.write.filetype,
5527 Some(11),
5528 )
5529 .expect("open pipe write end");
5530 }
5531
5532 let hook_state = Arc::new((Mutex::new((false, false)), Condvar::new()));
5533 let hook_state_for_cleanup = Arc::clone(&hook_state);
5534 set_cleanup_process_resources_test_hook(Some(Arc::new(move || {
5535 let (state, wake) = &*hook_state_for_cleanup;
5536 let mut state = lock_or_recover(state);
5537 state.0 = true;
5538 wake.notify_all();
5539 while !state.1 {
5540 state = wake.wait(state).expect("wait for cleanup release");
5541 }
5542 })));
5543
5544 let fd_tables_for_cleanup = Arc::clone(&fd_tables);
5545 let pipes_for_cleanup = pipes.clone();
5546 let driver_pids_for_cleanup = Arc::clone(&driver_pids);
5547 let cleanup_thread = thread::spawn(move || {
5548 cleanup_process_resources(
5549 fd_tables_for_cleanup.as_ref(),
5550 &file_locks,
5551 &pipes_for_cleanup,
5552 &ptys,
5553 &sockets,
5554 driver_pids_for_cleanup.as_ref(),
5555 41,
5556 );
5557 });
5558
5559 {
5560 let (state, wake) = &*hook_state;
5561 let mut state = lock_or_recover(state);
5562 while !state.0 {
5563 state = wake.wait(state).expect("wait for cleanup hook");
5564 }
5565 }
5566
5567 let fd_tables_for_dup = Arc::clone(&fd_tables);
5568 let dup_thread = thread::spawn(move || {
5569 let mut tables = lock_or_recover(fd_tables_for_dup.as_ref());
5570 let Some(table) = tables.get_mut(41) else {
5571 return Err(String::from("ESRCH"));
5572 };
5573 table.dup2(10, 12).map_err(|error| error.code().to_string())
5574 });
5575
5576 {
5577 let (state, wake) = &*hook_state;
5578 let mut state = lock_or_recover(state);
5579 state.1 = true;
5580 wake.notify_all();
5581 }
5582
5583 cleanup_thread.join().expect("cleanup thread should finish");
5584 let dup_result = dup_thread.join().expect("dup thread should finish");
5585 set_cleanup_process_resources_test_hook(None);
5586
5587 assert_eq!(dup_result, Err(String::from("ESRCH")));
5588 assert!(
5589 lock_or_recover(fd_tables.as_ref()).get(41).is_none(),
5590 "cleanup should remove the process FD table"
5591 );
5592 assert_eq!(pipes.pipe_count(), 0, "pipe cleanup should not leak");
5593 assert!(
5594 lock_or_recover(driver_pids.as_ref())
5595 .get("driver")
5596 .is_none_or(|pids| pids.is_empty()),
5597 "driver ownership should be cleared"
5598 );
5599 }
5600
5601 #[test]
5602 fn drop_disposes_live_kernel_vm_resources() {
5603 let (kernel, retained) = kernel_with_live_resources();
5604 drop(kernel);
5605 assert_kernel_drop_released_resources(&retained);
5606 }
5607
5608 #[test]
5609 fn drop_during_panic_still_disposes_live_kernel_vm_resources() {
5610 let retained = Arc::new(Mutex::new(None::<RetainedKernelResources>));
5611 let retained_for_panic = Arc::clone(&retained);
5612
5613 let panic_result = catch_unwind(AssertUnwindSafe(move || {
5614 let (kernel, resources) = kernel_with_live_resources();
5615 *lock_or_recover(retained_for_panic.as_ref()) = Some(resources);
5616 let _kernel = kernel;
5617 panic!("intentional panic to exercise KernelVm::drop");
5618 }));
5619
5620 assert!(panic_result.is_err(), "panic should be observed");
5621 let retained = lock_or_recover(retained.as_ref())
5622 .take()
5623 .expect("panic path should retain resources for assertions");
5624 assert_kernel_drop_released_resources(&retained);
5625 }
5626}