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