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 AnonymousFile, AnonymousFileUsage, FdEntry, FdStat, FdTableError, FdTableManager,
11 FileDescription, FileLockManager, FileLockTarget, FlockOperation, ProcessFdTable, RecordLock,
12 RecordLockType, SharedAnonymousFile, TransferredFd, FD_CLOEXEC, FILETYPE_CHARACTER_DEVICE,
13 FILETYPE_DIRECTORY, FILETYPE_PIPE, FILETYPE_REGULAR_FILE, FILETYPE_SOCKET_DGRAM,
14 FILETYPE_SOCKET_STREAM, FILETYPE_SYMBOLIC_LINK, F_DUPFD, O_APPEND, O_CREAT, O_DIRECT,
15 O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_NONBLOCK, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY,
16};
17use crate::mount_table::{MountEntry, MountOptions, MountTable, MountedFileSystem};
18use crate::network_policy::format_tcp_resource;
19use crate::permissions::{
20 check_command_execution, check_network_access, FsOperation, NetworkOperation, PermissionError,
21 PermissionedFileSystem, Permissions,
22};
23use crate::pipe_manager::{PipeError, PipeManager};
24use crate::poll::{
25 PollEvents, PollFd, PollNotifier, PollResult, PollTarget, PollTargetEntry, PollTargetResult,
26 POLLERR, POLLHUP, POLLIN, POLLNVAL, POLLOUT,
27};
28use crate::process_table::{
29 DriverProcess, ProcessContext, ProcessExitCallback, ProcessInfo, ProcessStatus, ProcessTable,
30 ProcessTableError, ProcessWaitResult, SigmaskHow, SignalSet, DEFAULT_PROCESS_UMASK, SIGCONT,
31 SIGPIPE, SIGSTOP, SIGTSTP, SIGWINCH,
32};
33use crate::pty::{
34 LineDisciplineConfig, PartialTermios, PtyError, PtyManager, PtyWindowSize, Termios,
35};
36use crate::resource_accounting::{
37 measure_filesystem_usage, FileSystemStats, FileSystemUsage, ResourceAccountant, ResourceError,
38 ResourceLimits, ResourceSnapshot, DEFAULT_MAX_OPEN_FDS,
39};
40use crate::root_fs::{
41 encode_snapshot, RootFileSystem, RootFilesystemError, RootFilesystemSnapshot,
42};
43use crate::socket_table::{
44 DatagramSocketOption, InetSocketAddress, OpaqueTransferredRight, ReceivedDatagram, SocketId,
45 SocketMulticastMembership, SocketReadiness, SocketRecord, SocketShutdown, SocketSpec,
46 SocketState, SocketTable, SocketTableError, SocketType, TransferredSocketRight,
47};
48use crate::user::{ProcessIdentity, UserConfig, UserManager};
49use crate::vfs::{
50 normalize_path, VfsError, VfsResult, VirtualDirEntry, VirtualFileSystem, VirtualStat,
51 VirtualTimeSpec, VirtualUtimeSpec, MAX_PATH_LENGTH, RENAME_EXCHANGE, S_IFDIR, S_IFLNK, S_IFREG,
52};
53use hickory_proto::rr::RecordType;
54use std::any::Any;
55use std::collections::{BTreeMap, BTreeSet, VecDeque};
56use std::error::Error;
57use std::fmt;
58#[cfg(test)]
59use std::sync::OnceLock;
60use std::sync::{Arc, Condvar, Mutex, MutexGuard, WaitTimeoutResult};
61use std::time::Duration;
62use web_time::{Instant, SystemTime, UNIX_EPOCH};
63
64pub type KernelResult<T> = Result<T, KernelError>;
65pub use crate::process_table::{ProcessWaitEvent as WaitPidEvent, WaitPidFlags};
66
67pub const SEEK_SET: u8 = 0;
68pub const SEEK_CUR: u8 = 1;
69pub const SEEK_END: u8 = 2;
70const EXECUTABLE_PERMISSION_BITS: u32 = 0o111;
71const SHEBANG_LINE_MAX_BYTES: usize = 256;
72const MAX_EXEC_INTERPRETER_DEPTH: usize = 4;
73const MAX_UNIX_SOCKET_SYMLINKS: usize = 40;
74const UNIX_SOCKET_FILE_TYPE: u32 = 0o140000;
75const UNIX_DAC_WRITE: u32 = 0o2;
76const UNIX_DAC_SEARCH: u32 = 0o1;
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct KernelError {
80 code: &'static str,
81 message: String,
82}
83
84impl KernelError {
85 pub fn code(&self) -> &'static str {
86 self.code
87 }
88
89 fn new(code: &'static str, message: impl Into<String>) -> Self {
90 Self {
91 code,
92 message: message.into(),
93 }
94 }
95
96 fn disposed() -> Self {
97 Self::new("EINVAL", "kernel VM is disposed")
98 }
99
100 fn no_such_process(pid: u32) -> Self {
101 Self::new("ESRCH", format!("no such process {pid}"))
102 }
103
104 fn bad_file_descriptor(fd: u32) -> Self {
105 Self::new("EBADF", format!("bad file descriptor {fd}"))
106 }
107
108 fn permission_denied(message: impl Into<String>) -> Self {
109 Self::new("EPERM", message)
110 }
111
112 fn command_not_found(command: &str) -> Self {
113 Self::new("ENOENT", format!("command not found: {command}"))
114 }
115}
116
117impl fmt::Display for KernelError {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(f, "{}: {}", self.code, self.message)
120 }
121}
122
123impl Error for KernelError {}
124
125fn linux_shebang_interpreter(header: &[u8], path: &str) -> KernelResult<Option<String>> {
126 if !header.starts_with(b"#!") {
127 return Ok(None);
128 }
129
130 let payload = &header[2..];
131 let newline = payload.iter().position(|byte| *byte == b'\n');
132 let line = newline.map_or(payload, |index| &payload[..index]);
133 let line_end = line
134 .iter()
135 .rposition(|byte| !matches!(*byte, b' ' | b'\t'))
136 .map(|index| index + 1)
137 .ok_or_else(|| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
138 let line = &line[..line_end];
139 let interpreter_start = line
140 .iter()
141 .position(|byte| !matches!(*byte, b' ' | b'\t'))
142 .ok_or_else(|| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
143 let interpreter_tail = &line[interpreter_start..];
144 let separator = interpreter_tail
145 .iter()
146 .position(|byte| matches!(*byte, b' ' | b'\t'));
147
148 if newline.is_none() && header.len() >= SHEBANG_LINE_MAX_BYTES && separator.is_none() {
151 return Err(KernelError::new(
152 "ENOEXEC",
153 format!("shebang interpreter path exceeds the Linux header limit: {path}"),
154 ));
155 }
156
157 let interpreter_end = separator.unwrap_or(interpreter_tail.len());
158 let interpreter = std::str::from_utf8(&interpreter_tail[..interpreter_end])
159 .map_err(|_| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
160 if interpreter.is_empty() {
161 return Err(KernelError::new(
162 "ENOEXEC",
163 format!("invalid shebang line: {path}"),
164 ));
165 }
166 Ok(Some(interpreter.to_owned()))
167}
168
169#[derive(Clone)]
170pub struct KernelVmConfig {
171 pub vm_id: String,
172 pub env: BTreeMap<String, String>,
173 pub cwd: String,
174 pub user: UserConfig,
175 pub permissions: Permissions,
176 pub loopback_exempt_ports: BTreeSet<u16>,
177 pub dns: DnsConfig,
178 pub dns_resolver: SharedDnsResolver,
179 pub resources: ResourceLimits,
180 pub zombie_ttl: Duration,
181}
182
183impl KernelVmConfig {
184 pub fn new(vm_id: impl Into<String>) -> Self {
185 Self {
186 vm_id: vm_id.into(),
187 env: BTreeMap::new(),
188 cwd: String::from("/workspace"),
189 user: UserConfig::default(),
190 permissions: Permissions::default(),
191 loopback_exempt_ports: BTreeSet::new(),
192 dns: DnsConfig::default(),
193 dns_resolver: Arc::new(HickoryDnsResolver::default()),
194 resources: ResourceLimits::default(),
195 zombie_ttl: Duration::from_secs(60),
196 }
197 }
198}
199
200#[derive(Debug, Clone, Default)]
201pub struct SpawnOptions {
202 pub requester_driver: Option<String>,
203 pub parent_pid: Option<u32>,
204 pub env: BTreeMap<String, String>,
205 pub cwd: Option<String>,
206}
207
208#[derive(Debug, Clone, Default, PartialEq, Eq)]
209pub struct VirtualProcessOptions {
210 pub parent_pid: Option<u32>,
211 pub env: BTreeMap<String, String>,
212 pub cwd: Option<String>,
213}
214
215#[derive(Debug, Clone, Default, PartialEq, Eq)]
216pub struct ExecOptions {
217 pub requester_driver: Option<String>,
218 pub parent_pid: Option<u32>,
219 pub env: BTreeMap<String, String>,
220 pub cwd: Option<String>,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
224pub struct RecursiveDirEntry {
225 pub path: String,
226 pub is_directory: bool,
227 pub is_symbolic_link: bool,
228 pub size: u64,
229}
230
231#[derive(Debug, Clone, Default, PartialEq, Eq)]
232pub struct OpenShellOptions {
233 pub requester_driver: Option<String>,
234 pub command: Option<String>,
235 pub args: Vec<String>,
236 pub env: BTreeMap<String, String>,
237 pub cwd: Option<String>,
238}
239
240#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct WaitPidResult {
242 pub pid: u32,
243 pub status: i32,
244}
245
246#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct WaitPidEventResult {
248 pub pid: u32,
249 pub status: i32,
250 pub event: WaitPidEvent,
251}
252
253#[derive(Debug)]
254pub struct ReceivedFdMessage {
255 pub payload: Vec<u8>,
256 pub rights: Vec<ReceivedFdRight>,
257 pub payload_truncated: bool,
258 pub control_truncated: bool,
259 pub full_length: usize,
260}
261
262#[derive(Clone)]
263pub enum FdTransferRequest {
264 Fd(u32),
265 Opaque(OpaqueTransferredRight),
266}
267
268impl fmt::Debug for FdTransferRequest {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 match self {
271 Self::Fd(fd) => f.debug_tuple("Fd").field(fd).finish(),
272 Self::Opaque(resource) => f
273 .debug_tuple("Opaque")
274 .field(&(Arc::as_ptr(resource) as *const ()))
275 .finish(),
276 }
277 }
278}
279
280pub enum ReceivedFdRight {
281 Fd(u32),
282 Opaque(OpaqueTransferredRight),
283}
284
285impl fmt::Debug for ReceivedFdRight {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 match self {
288 Self::Fd(fd) => f.debug_tuple("Fd").field(fd).finish(),
289 Self::Opaque(resource) => f
290 .debug_tuple("Opaque")
291 .field(&(Arc::as_ptr(resource) as *const ()))
292 .finish(),
293 }
294 }
295}
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub struct ProcessFdSnapshotEntry {
299 pub fd: u32,
300 pub fd_flags: u32,
301 pub status_flags: u32,
302 pub filetype: u8,
303 pub is_socket: bool,
304 pub is_pipe: bool,
305 pub is_pty: bool,
306}
307
308#[derive(Debug, Clone, PartialEq, Eq)]
309pub struct ProcessFdDirEntry {
310 pub name: String,
311 pub ino: u64,
312 pub is_directory: bool,
313 pub is_symbolic_link: bool,
314}
315
316#[derive(Debug, Clone, PartialEq, Eq)]
322pub struct UnixSocketPathNode {
323 pub canonical_path: String,
324 pub stat: VirtualStat,
325}
326
327struct UnixSocketBindTarget {
328 canonical_path: String,
329 parent: UnixSocketPathNode,
330 identity: ProcessIdentity,
331}
332
333#[derive(Debug, Clone)]
334struct FdSocketEntry {
335 description: Arc<FileDescription>,
336 socket_id: SocketId,
337 mode: u32,
338 uid: u32,
339 gid: u32,
340}
341
342type FdSocketRegistry = Arc<Mutex<BTreeMap<u64, FdSocketEntry>>>;
343
344enum OpenFileRemovalBacking {
345 Anonymous {
346 descriptions: Vec<Arc<FileDescription>>,
347 backing: SharedAnonymousFile,
348 },
349 LinkedAlias {
350 descriptions: Vec<Arc<FileDescription>>,
351 live_path: String,
352 },
353}
354
355#[derive(Debug, Clone)]
356struct ResolvedSpawnCommand {
357 command: String,
358 args: Vec<String>,
359 driver: CommandDriver,
360}
361
362#[derive(Debug, Clone)]
363struct ShebangCommand {
364 interpreter: String,
365 args: Vec<String>,
366}
367
368#[derive(Clone)]
369pub struct KernelProcessHandle {
370 pid: u32,
371 driver: String,
372 process: Arc<StubDriverProcess>,
373}
374
375impl fmt::Debug for KernelProcessHandle {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 f.debug_struct("KernelProcessHandle")
378 .field("pid", &self.pid)
379 .field("driver", &self.driver)
380 .finish_non_exhaustive()
381 }
382}
383
384impl KernelProcessHandle {
385 pub fn pid(&self) -> u32 {
386 self.pid
387 }
388
389 pub fn driver(&self) -> &str {
390 &self.driver
391 }
392
393 pub fn finish(&self, exit_code: i32) {
394 self.process.finish(exit_code);
395 }
396
397 pub fn kill(&self, signal: i32) {
398 self.process.kill(signal);
399 }
400
401 pub fn wait(&self, timeout: Duration) -> Option<i32> {
402 self.process.wait(timeout)
403 }
404
405 pub fn kill_signals(&self) -> Vec<i32> {
406 self.process.kill_signals()
407 }
408}
409
410#[derive(Debug, Clone)]
411pub struct OpenShellHandle {
412 process: KernelProcessHandle,
413 master_fd: u32,
414 slave_fd: u32,
415 pty_path: String,
416}
417
418impl OpenShellHandle {
419 pub fn process(&self) -> &KernelProcessHandle {
420 &self.process
421 }
422
423 pub fn pid(&self) -> u32 {
424 self.process.pid()
425 }
426
427 pub fn master_fd(&self) -> u32 {
428 self.master_fd
429 }
430
431 pub fn slave_fd(&self) -> u32 {
432 self.slave_fd
433 }
434
435 pub fn pty_path(&self) -> &str {
436 &self.pty_path
437 }
438}
439
440pub struct KernelVm<F> {
441 vm_id: String,
442 boot_time_ms: u64,
443 boot_instant: Instant,
444 filesystem: PermissionedFileSystem<DeviceLayer<F>>,
445 permissions: Permissions,
446 loopback_exempt_ports: BTreeSet<u16>,
447 dns: DnsConfig,
448 dns_resolver: SharedDnsResolver,
449 env: BTreeMap<String, String>,
450 cwd: String,
451 commands: CommandRegistry,
452 fd_tables: Arc<Mutex<FdTableManager>>,
453 processes: ProcessTable,
454 pipes: PipeManager,
455 ptys: PtyManager,
456 sockets: SocketTable,
457 fd_sockets: FdSocketRegistry,
458 poll_notifier: PollNotifier,
459 users: UserManager,
460 resources: ResourceAccountant,
461 filesystem_usage_cache: Option<FileSystemUsage>,
462 no_posix_acl_cache: BTreeSet<(u64, u64, u64, u32, u32, u32, u32)>,
463 anonymous_file_usage: Arc<AnonymousFileUsage>,
464 file_locks: FileLockManager,
465 unnamed_files: BTreeMap<u64, UnnamedFile>,
466 next_unnamed_file_id: u64,
467 driver_pids: Arc<Mutex<BTreeMap<String, BTreeSet<u32>>>>,
468 terminated: bool,
469}
470
471const UNNAMED_FILE_PREFIX: &str = ".agentos-tmpfile-";
472
473#[derive(Debug, Clone)]
474struct UnnamedFile {
475 path: String,
476 linkable: bool,
477}
478
479pub fn is_internal_unnamed_file_name(name: &str) -> bool {
480 name.starts_with(UNNAMED_FILE_PREFIX)
481}
482
483#[allow(clippy::too_many_arguments)]
486fn cleanup_process_resources(
487 fd_tables: &Mutex<FdTableManager>,
488 file_locks: &FileLockManager,
489 pipes: &PipeManager,
490 ptys: &PtyManager,
491 sockets: &SocketTable,
492 fd_sockets: &FdSocketRegistry,
493 driver_pids: &Mutex<BTreeMap<String, BTreeSet<u32>>>,
494 pid: u32,
495) {
496 let mut cleanup = Vec::new();
497 {
498 let mut tables = lock_or_recover(fd_tables);
499 let descriptors = tables
500 .get(pid)
501 .map(|table| {
502 table
503 .iter()
504 .map(|entry| (entry.fd, Arc::clone(&entry.description), entry.filetype))
505 .collect::<Vec<_>>()
506 })
507 .unwrap_or_default();
508
509 cleanup_process_resources_test_hook();
510
511 if let Some(table) = tables.get_mut(pid) {
512 for (fd, description, filetype) in &descriptors {
513 table.close(*fd);
514 cleanup.push((Arc::clone(description), *filetype));
515 }
516 }
517 tables.remove(pid);
518 }
519
520 for (description, filetype) in cleanup {
521 close_special_resource_if_needed(
522 file_locks,
523 pipes,
524 ptys,
525 sockets,
526 fd_sockets,
527 &description,
528 filetype,
529 );
530 }
531 file_locks.release_process(pid);
532
533 sockets.remove_all_for_pid(pid);
534
535 let mut owners = lock_or_recover(driver_pids);
536 for pids in owners.values_mut() {
537 pids.remove(&pid);
538 }
539}
540
541fn dispose_kernel_vm_resources<F>(kernel: &mut KernelVm<F>) {
542 kernel.processes.terminate_all();
543 let pids = lock_or_recover(&kernel.fd_tables).pids();
544 for pid in pids {
545 cleanup_process_resources(
546 kernel.fd_tables.as_ref(),
547 &kernel.file_locks,
548 &kernel.pipes,
549 &kernel.ptys,
550 &kernel.sockets,
551 &kernel.fd_sockets,
552 kernel.driver_pids.as_ref(),
553 pid,
554 );
555 }
556 lock_or_recover(&kernel.driver_pids).clear();
557 kernel.terminated = true;
558}
559
560#[cfg(test)]
561type CleanupProcessResourcesHook = Arc<dyn Fn() + Send + Sync + 'static>;
562
563#[cfg(test)]
564fn cleanup_process_resources_test_hook() {
565 let hook = lock_or_recover(cleanup_process_resources_test_hook_slot()).clone();
566 if let Some(hook) = hook {
567 hook();
568 }
569}
570
571#[cfg(not(test))]
572fn cleanup_process_resources_test_hook() {}
573
574#[cfg(test)]
575fn cleanup_process_resources_test_hook_slot() -> &'static Mutex<Option<CleanupProcessResourcesHook>>
576{
577 static HOOK: OnceLock<Mutex<Option<CleanupProcessResourcesHook>>> = OnceLock::new();
578 HOOK.get_or_init(|| Mutex::new(None))
579}
580
581#[cfg(test)]
582fn set_cleanup_process_resources_test_hook(hook: Option<CleanupProcessResourcesHook>) {
583 *lock_or_recover(cleanup_process_resources_test_hook_slot()) = hook;
584}
585
586fn close_special_resource_if_needed(
587 file_locks: &FileLockManager,
588 pipes: &PipeManager,
589 ptys: &PtyManager,
590 sockets: &SocketTable,
591 fd_sockets: &FdSocketRegistry,
592 description: &Arc<FileDescription>,
593 filetype: u8,
594) {
595 if description.ref_count() != 0 {
596 return;
597 }
598
599 file_locks.release_owner(description.id());
600
601 if filetype == FILETYPE_PIPE && pipes.is_pipe(description.id()) {
602 pipes.close(description.id());
603 }
604
605 if ptys.is_pty(description.id()) {
606 ptys.close(description.id());
607 }
608
609 prune_fd_sockets(sockets, fd_sockets);
610}
611
612fn prune_fd_sockets(sockets: &SocketTable, fd_sockets: &FdSocketRegistry) {
613 loop {
614 let socket_ids = {
615 let mut registry = lock_or_recover(fd_sockets);
616 let closed = registry
617 .iter()
618 .filter_map(|(description_id, entry)| {
619 (entry.description.ref_count() == 0)
620 .then_some((*description_id, entry.socket_id))
621 })
622 .collect::<Vec<_>>();
623 for (description_id, _) in &closed {
624 registry.remove(description_id);
625 }
626 closed
627 .into_iter()
628 .map(|(_, socket_id)| socket_id)
629 .collect::<Vec<_>>()
630 };
631 if socket_ids.is_empty() {
632 return;
633 }
634 for socket_id in socket_ids {
635 if let Err(error) = sockets.remove(socket_id) {
636 eprintln!(
637 "[agentos] failed to remove closed descriptor-owned socket {socket_id}: {error}"
638 );
639 }
640 }
641 }
642}
643
644#[derive(Debug, Clone, PartialEq, Eq)]
645enum ProcNode {
646 RootDir,
647 MountsFile,
648 CpuInfoFile,
649 MemInfoFile,
650 LoadAvgFile,
651 UptimeFile,
652 VersionFile,
653 SelfLink { pid: u32 },
654 PidDir { pid: u32 },
655 PidFdDir { pid: u32 },
656 PidCmdline { pid: u32 },
657 PidEnviron { pid: u32 },
658 PidCwdLink { pid: u32 },
659 PidStatFile { pid: u32 },
660 PidStatusFile { pid: u32 },
661 PidFdLink { pid: u32, fd: u32 },
662}
663
664impl<F: VirtualFileSystem + 'static> KernelVm<F> {
665 pub fn new(filesystem: F, config: KernelVmConfig) -> Self {
666 let vm_id = config.vm_id;
667 let boot_time_ms = now_ms();
668 let boot_instant = Instant::now();
669 let permissions = config.permissions.clone();
670 let users = UserManager::from_config(config.user);
671 let process_table = ProcessTable::with_zombie_ttl(config.zombie_ttl);
672 let process_table_for_pty = process_table.clone();
673 let max_open_fds = config
674 .resources
675 .max_open_fds
676 .unwrap_or(DEFAULT_MAX_OPEN_FDS);
677 let fd_tables = Arc::new(Mutex::new(FdTableManager::with_max_fds(max_open_fds)));
678 let file_locks =
682 FileLockManager::with_record_lock_limit(max_open_fds.saturating_mul(16).max(16));
683 let driver_pids = Arc::new(Mutex::new(BTreeMap::new()));
684 let poll_notifier = PollNotifier::default();
685 let pipes = PipeManager::with_notifier(poll_notifier.clone());
686 let ptys = PtyManager::with_signal_handler_and_notifier(
687 Arc::new(move |pgid, signal| {
688 let _ = process_table_for_pty.kill(-(pgid as i32), signal);
689 }),
690 poll_notifier.clone(),
691 );
692 let sockets = SocketTable::new();
693 let fd_sockets = Arc::new(Mutex::new(BTreeMap::new()));
694
695 let fd_tables_for_exit = Arc::clone(&fd_tables);
696 let file_locks_for_exit = file_locks.clone();
697 let driver_pids_for_exit = Arc::clone(&driver_pids);
698 let pipes_for_exit = pipes.clone();
699 let ptys_for_exit = ptys.clone();
700 let sockets_for_exit = sockets.clone();
701 let fd_sockets_for_exit = Arc::clone(&fd_sockets);
702 process_table.set_on_process_exit(Some(Arc::new(move |pid| {
703 cleanup_process_resources(
704 fd_tables_for_exit.as_ref(),
705 &file_locks_for_exit,
706 &pipes_for_exit,
707 &ptys_for_exit,
708 &sockets_for_exit,
709 &fd_sockets_for_exit,
710 driver_pids_for_exit.as_ref(),
711 pid,
712 );
713 })));
714
715 let filesystem = PermissionedFileSystem::new(
716 create_device_layer(filesystem),
717 vm_id.clone(),
718 permissions.clone(),
719 );
720 let filesystem_usage_cache = None;
724 let anonymous_file_usage = Arc::new(AnonymousFileUsage::default());
725
726 Self {
727 vm_id: vm_id.clone(),
728 boot_time_ms,
729 boot_instant,
730 filesystem,
731 permissions,
732 loopback_exempt_ports: config.loopback_exempt_ports,
733 dns: config.dns,
734 dns_resolver: config.dns_resolver,
735 env: config.env,
736 cwd: config.cwd,
737 commands: CommandRegistry::new(),
738 fd_tables,
739 processes: process_table,
740 pipes,
741 ptys,
742 sockets,
743 fd_sockets,
744 poll_notifier,
745 users,
746 resources: ResourceAccountant::new(config.resources),
747 filesystem_usage_cache,
748 no_posix_acl_cache: BTreeSet::new(),
749 anonymous_file_usage,
750 file_locks,
751 unnamed_files: BTreeMap::new(),
752 next_unnamed_file_id: 0,
753 driver_pids,
754 terminated: false,
755 }
756 }
757
758 pub fn vm_id(&self) -> &str {
759 &self.vm_id
760 }
761
762 pub fn state(&self) -> LifecycleState {
763 if self.terminated {
764 LifecycleState::Terminated
765 } else if self.processes.running_count() > 0 {
766 LifecycleState::Busy
767 } else {
768 LifecycleState::Ready
769 }
770 }
771
772 pub fn commands(&self) -> BTreeMap<String, String> {
773 self.commands.list()
774 }
775
776 pub fn filesystem(&self) -> &PermissionedFileSystem<DeviceLayer<F>> {
777 &self.filesystem
778 }
779
780 pub fn filesystem_mut(&mut self) -> &mut PermissionedFileSystem<DeviceLayer<F>> {
781 &mut self.filesystem
782 }
783
784 pub fn user_manager(&self) -> &UserManager {
785 &self.users
786 }
787
788 pub fn environment(&self) -> &BTreeMap<String, String> {
789 &self.env
790 }
791
792 pub fn process_identity(
793 &self,
794 requester_driver: &str,
795 pid: u32,
796 ) -> KernelResult<ProcessIdentity> {
797 self.assert_driver_owns(requester_driver, pid)?;
798 Ok(self
799 .processes
800 .get(pid)
801 .ok_or_else(|| KernelError::no_such_process(pid))?
802 .identity)
803 }
804
805 pub fn user_profile(&self) -> UserManager {
806 self.users.clone()
807 }
808
809 pub fn getuid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
810 Ok(self.process_identity(requester_driver, pid)?.uid)
811 }
812
813 pub fn getgid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
814 Ok(self.process_identity(requester_driver, pid)?.gid)
815 }
816
817 pub fn geteuid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
818 Ok(self.process_identity(requester_driver, pid)?.euid)
819 }
820
821 pub fn getegid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
822 Ok(self.process_identity(requester_driver, pid)?.egid)
823 }
824
825 pub fn getgroups(&self, requester_driver: &str, pid: u32) -> KernelResult<Vec<u32>> {
826 Ok(self
827 .process_identity(requester_driver, pid)?
828 .supplementary_gids)
829 }
830
831 pub fn getresuid(&self, requester_driver: &str, pid: u32) -> KernelResult<(u32, u32, u32)> {
832 let identity = self.process_identity(requester_driver, pid)?;
833 Ok((identity.uid, identity.euid, identity.suid))
834 }
835
836 pub fn getresgid(&self, requester_driver: &str, pid: u32) -> KernelResult<(u32, u32, u32)> {
837 let identity = self.process_identity(requester_driver, pid)?;
838 Ok((identity.gid, identity.egid, identity.sgid))
839 }
840
841 pub fn setuid(&self, requester_driver: &str, pid: u32, uid: u32) -> KernelResult<()> {
842 let current = self.process_identity(requester_driver, pid)?;
843 if current.euid == 0 {
844 self.setresuid(requester_driver, pid, Some(uid), Some(uid), Some(uid))
845 } else if uid == current.uid || uid == current.suid {
846 self.setresuid(requester_driver, pid, None, Some(uid), None)
847 } else {
848 Err(credential_transition_denied("setuid", uid))
849 }
850 }
851
852 pub fn seteuid(&self, requester_driver: &str, pid: u32, euid: u32) -> KernelResult<()> {
853 self.setresuid(requester_driver, pid, None, Some(euid), None)
854 }
855
856 pub fn setreuid(
857 &self,
858 requester_driver: &str,
859 pid: u32,
860 uid: Option<u32>,
861 euid: Option<u32>,
862 ) -> KernelResult<()> {
863 let current = self.process_identity(requester_driver, pid)?;
864 let next_uid = uid.unwrap_or(current.uid);
865 let next_euid = euid.unwrap_or(current.euid);
866 let update_saved = uid.is_some() || (euid.is_some() && next_euid != current.uid);
867 self.setresuid(
868 requester_driver,
869 pid,
870 uid,
871 euid,
872 update_saved.then_some(next_euid),
873 )?;
874 debug_assert_eq!(self.process_identity(requester_driver, pid)?.uid, next_uid);
875 Ok(())
876 }
877
878 pub fn setresuid(
879 &self,
880 requester_driver: &str,
881 pid: u32,
882 uid: Option<u32>,
883 euid: Option<u32>,
884 suid: Option<u32>,
885 ) -> KernelResult<()> {
886 let mut identity = self.process_identity(requester_driver, pid)?;
887 if identity.euid != 0 {
888 let allowed = [identity.uid, identity.euid, identity.suid];
889 for requested in [uid, euid, suid].into_iter().flatten() {
890 if !allowed.contains(&requested) {
891 return Err(credential_transition_denied("setresuid", requested));
892 }
893 }
894 }
895 if let Some(uid) = uid {
896 identity.uid = uid;
897 }
898 if let Some(euid) = euid {
899 identity.euid = euid;
900 }
901 if let Some(suid) = suid {
902 identity.suid = suid;
903 }
904 self.processes.set_identity(pid, identity)?;
905 Ok(())
906 }
907
908 pub fn setgid(&self, requester_driver: &str, pid: u32, gid: u32) -> KernelResult<()> {
909 let current = self.process_identity(requester_driver, pid)?;
910 if current.euid == 0 {
911 self.setresgid(requester_driver, pid, Some(gid), Some(gid), Some(gid))
912 } else if gid == current.gid || gid == current.sgid {
913 self.setresgid(requester_driver, pid, None, Some(gid), None)
914 } else {
915 Err(credential_transition_denied("setgid", gid))
916 }
917 }
918
919 pub fn setegid(&self, requester_driver: &str, pid: u32, egid: u32) -> KernelResult<()> {
920 self.setresgid(requester_driver, pid, None, Some(egid), None)
921 }
922
923 pub fn setregid(
924 &self,
925 requester_driver: &str,
926 pid: u32,
927 gid: Option<u32>,
928 egid: Option<u32>,
929 ) -> KernelResult<()> {
930 let current = self.process_identity(requester_driver, pid)?;
931 let next_egid = egid.unwrap_or(current.egid);
932 let update_saved = gid.is_some() || (egid.is_some() && next_egid != current.gid);
933 self.setresgid(
934 requester_driver,
935 pid,
936 gid,
937 egid,
938 update_saved.then_some(next_egid),
939 )
940 }
941
942 pub fn setresgid(
943 &self,
944 requester_driver: &str,
945 pid: u32,
946 gid: Option<u32>,
947 egid: Option<u32>,
948 sgid: Option<u32>,
949 ) -> KernelResult<()> {
950 let mut identity = self.process_identity(requester_driver, pid)?;
951 if identity.euid != 0 {
952 let allowed = [identity.gid, identity.egid, identity.sgid];
953 for requested in [gid, egid, sgid].into_iter().flatten() {
954 if !allowed.contains(&requested) {
955 return Err(credential_transition_denied("setresgid", requested));
956 }
957 }
958 }
959 if let Some(gid) = gid {
960 identity.gid = gid;
961 }
962 if let Some(egid) = egid {
963 identity.egid = egid;
964 }
965 if let Some(sgid) = sgid {
966 identity.sgid = sgid;
967 }
968 self.processes.set_identity(pid, identity)?;
969 Ok(())
970 }
971
972 pub fn setgroups(
973 &self,
974 requester_driver: &str,
975 pid: u32,
976 groups: Vec<u32>,
977 ) -> KernelResult<()> {
978 const MAX_SUPPLEMENTARY_GROUPS: usize = 64;
979 let mut identity = self.process_identity(requester_driver, pid)?;
980 if identity.euid != 0 {
981 return Err(KernelError::new(
982 "EPERM",
983 "setgroups requires effective uid 0",
984 ));
985 }
986 if groups.len() > MAX_SUPPLEMENTARY_GROUPS {
987 return Err(KernelError::new(
988 "EINVAL",
989 format!(
990 "setgroups count {} exceeds limit {MAX_SUPPLEMENTARY_GROUPS}",
991 groups.len()
992 ),
993 ));
994 }
995 let mut normalized = Vec::with_capacity(groups.len());
996 for gid in groups {
997 if !normalized.contains(&gid) {
998 normalized.push(gid);
999 }
1000 }
1001 identity.supplementary_gids = normalized;
1002 self.processes.set_identity(pid, identity)?;
1003 Ok(())
1004 }
1005
1006 pub fn switch_user(&self, requester_driver: &str, pid: u32, uid: u32) -> KernelResult<()> {
1007 let current = self.process_identity(requester_driver, pid)?;
1008 if current.euid != 0 {
1009 return Err(KernelError::new(
1010 "EPERM",
1011 "switch_user requires effective uid 0",
1012 ));
1013 }
1014 let account = self
1015 .users
1016 .account(uid)
1017 .cloned()
1018 .ok_or_else(|| KernelError::new("ENOENT", format!("unknown uid {uid}")))?;
1019 let identity = ProcessIdentity {
1020 uid: account.uid,
1021 gid: account.gid,
1022 euid: account.uid,
1023 egid: account.gid,
1024 suid: account.uid,
1025 sgid: account.gid,
1026 supplementary_gids: account.supplementary_gids,
1027 };
1028 self.processes.set_identity(pid, identity)?;
1029 Ok(())
1030 }
1031
1032 pub fn getpwuid(&self, uid: u32) -> KernelResult<String> {
1033 self.users
1034 .getpwuid(uid)
1035 .ok_or_else(|| KernelError::new("ENOENT", format!("unknown uid {uid}")))
1036 }
1037
1038 pub fn getpwnam(&self, username: &str) -> KernelResult<String> {
1039 self.users
1040 .getpwnam(username)
1041 .ok_or_else(|| KernelError::new("ENOENT", format!("unknown user {username}")))
1042 }
1043
1044 pub fn getpwent(&self, index: usize) -> KernelResult<String> {
1045 self.users
1046 .passwd_entries()
1047 .get(index)
1048 .cloned()
1049 .ok_or_else(|| KernelError::new("ENOENT", "end of passwd database"))
1050 }
1051
1052 pub fn getgrgid(&self, gid: u32) -> KernelResult<String> {
1053 self.users
1054 .getgrgid(gid)
1055 .ok_or_else(|| KernelError::new("ENOENT", format!("unknown gid {gid}")))
1056 }
1057
1058 pub fn getgrnam(&self, name: &str) -> KernelResult<String> {
1059 self.users
1060 .getgrnam(name)
1061 .ok_or_else(|| KernelError::new("ENOENT", format!("unknown group {name}")))
1062 }
1063
1064 pub fn getgrent(&self, index: usize) -> KernelResult<String> {
1065 self.users
1066 .group_entries()
1067 .get(index)
1068 .cloned()
1069 .ok_or_else(|| KernelError::new("ENOENT", "end of group database"))
1070 }
1071
1072 pub fn resource_snapshot(&self) -> ResourceSnapshot {
1073 let fd_tables = lock_or_recover(&self.fd_tables);
1074 self.resources.snapshot(
1075 &self.processes,
1076 &fd_tables,
1077 &self.pipes,
1078 &self.ptys,
1079 &self.sockets,
1080 )
1081 }
1082
1083 pub fn resource_limits(&self) -> &ResourceLimits {
1084 self.resources.limits()
1085 }
1086
1087 pub fn set_permissions(&mut self, permissions: Permissions) {
1088 self.filesystem.set_permissions(permissions.clone());
1089 self.permissions = permissions;
1090 }
1091
1092 pub fn set_loopback_exempt_ports(&mut self, ports: BTreeSet<u16>) {
1093 self.loopback_exempt_ports = ports;
1094 }
1095
1096 pub fn extend_loopback_exempt_ports(&mut self, ports: impl IntoIterator<Item = u16>) {
1097 self.loopback_exempt_ports.extend(ports);
1098 }
1099
1100 pub fn resolve_dns(
1101 &self,
1102 hostname: &str,
1103 policy: DnsLookupPolicy,
1104 ) -> KernelResult<DnsResolution> {
1105 self.assert_not_terminated()?;
1106 if matches!(policy, DnsLookupPolicy::CheckPermissions) {
1107 let resource = format_dns_resource(hostname).map_err(map_dns_resolver_error)?;
1108 check_network_access(
1109 &self.vm_id,
1110 &self.permissions,
1111 NetworkOperation::Dns,
1112 &resource,
1113 )?;
1114 }
1115
1116 resolve_dns(&self.dns, self.dns_resolver.as_ref(), hostname).map_err(map_dns_resolver_error)
1117 }
1118
1119 pub fn resolve_dns_records(
1120 &self,
1121 hostname: &str,
1122 record_type: RecordType,
1123 policy: DnsLookupPolicy,
1124 ) -> KernelResult<DnsRecordResolution> {
1125 self.assert_not_terminated()?;
1126 if matches!(policy, DnsLookupPolicy::CheckPermissions) {
1127 let resource = format_dns_resource(hostname).map_err(map_dns_resolver_error)?;
1128 check_network_access(
1129 &self.vm_id,
1130 &self.permissions,
1131 NetworkOperation::Dns,
1132 &resource,
1133 )?;
1134 }
1135
1136 resolve_dns_records(&self.dns, self.dns_resolver.as_ref(), hostname, record_type)
1137 .map_err(map_dns_resolver_error)
1138 }
1139
1140 pub fn register_driver(&mut self, driver: CommandDriver) -> KernelResult<()> {
1141 self.assert_not_terminated()?;
1142 let driver_name = driver.name().to_owned();
1143 let populate_driver = driver.clone();
1144 self.commands.register(driver)?;
1145 lock_or_recover(&self.driver_pids)
1146 .entry(driver_name)
1147 .or_default();
1148 self.commands
1149 .populate_driver_bin(&mut self.filesystem, &populate_driver)?;
1150 Ok(())
1151 }
1152
1153 pub fn exec(
1154 &mut self,
1155 command: &str,
1156 options: ExecOptions,
1157 ) -> KernelResult<KernelProcessHandle> {
1158 self.spawn_process(
1159 "sh",
1160 vec![String::from("-c"), String::from(command)],
1161 SpawnOptions {
1162 requester_driver: options.requester_driver,
1163 parent_pid: options.parent_pid,
1164 env: options.env,
1165 cwd: options.cwd,
1166 },
1167 )
1168 }
1169
1170 pub fn open_shell(&mut self, options: OpenShellOptions) -> KernelResult<OpenShellHandle> {
1171 let command = options.command.unwrap_or_else(|| String::from("sh"));
1172 let requester_driver = options.requester_driver.clone();
1173 let process = self.spawn_process(
1174 &command,
1175 options.args,
1176 SpawnOptions {
1177 requester_driver: requester_driver.clone(),
1178 parent_pid: None,
1179 env: options.env,
1180 cwd: options.cwd,
1181 },
1182 )?;
1183 let owner = requester_driver.as_deref().unwrap_or(process.driver());
1184 let (master_fd, slave_fd, pty_path) = self.open_pty(owner, process.pid())?;
1185 self.setpgid(owner, process.pid(), process.pid())?;
1186 self.pty_set_foreground_pgid(owner, process.pid(), master_fd, process.pid())?;
1187 Ok(OpenShellHandle {
1188 process,
1189 master_fd,
1190 slave_fd,
1191 pty_path,
1192 })
1193 }
1194
1195 pub fn read_file(&mut self, path: &str) -> KernelResult<Vec<u8>> {
1196 self.assert_not_terminated()?;
1197 self.read_file_internal(None, path)
1198 }
1199
1200 pub fn pread_file(&mut self, path: &str, offset: u64, length: usize) -> KernelResult<Vec<u8>> {
1201 self.assert_not_terminated()?;
1202 self.reject_unix_socket_data_path(path, "ENXIO")?;
1203 self.resources.check_pread_length(length)?;
1204 Ok(VirtualFileSystem::pread(
1205 &mut self.filesystem,
1206 path,
1207 offset,
1208 length,
1209 )?)
1210 }
1211
1212 pub fn pread_file_for_process(
1213 &mut self,
1214 requester_driver: &str,
1215 pid: u32,
1216 path: &str,
1217 offset: u64,
1218 length: usize,
1219 ) -> KernelResult<Vec<u8>> {
1220 self.assert_not_terminated()?;
1221 self.assert_driver_owns(requester_driver, pid)?;
1222 self.check_dac_access(pid, path, DAC_READ)?;
1223 self.reject_unix_socket_data_path(path, "ENXIO")?;
1224 self.resources.check_pread_length(length)?;
1225 Ok(VirtualFileSystem::pread(
1226 &mut self.filesystem,
1227 path,
1228 offset,
1229 length,
1230 )?)
1231 }
1232
1233 pub fn read_file_for_process(
1234 &mut self,
1235 requester_driver: &str,
1236 pid: u32,
1237 path: &str,
1238 ) -> KernelResult<Vec<u8>> {
1239 self.assert_not_terminated()?;
1240 self.assert_driver_owns(requester_driver, pid)?;
1241 self.check_dac_access(pid, path, DAC_READ)?;
1242 self.read_file_internal(Some(pid), path)
1243 }
1244
1245 pub fn write_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> KernelResult<()> {
1246 self.assert_not_terminated()?;
1247 self.reject_read_only_resolved_write_path(path)?;
1248 self.reject_unix_socket_data_path(path, "ENXIO")?;
1249 let content = content.into();
1250 let new_size = content.len() as u64;
1251 let existing = self.storage_stat(path)?;
1252 self.check_write_file_limits_with_existing(path, existing.as_ref(), new_size)?;
1253 self.filesystem.write_file(path, content)?;
1254 self.update_filesystem_usage_cache_for_write(path, existing.as_ref(), new_size);
1255 Ok(())
1256 }
1257
1258 pub fn resolve_unix_socket_bind_target_for_process(
1265 &mut self,
1266 requester_driver: &str,
1267 pid: u32,
1268 cwd: &str,
1269 path: &str,
1270 ) -> KernelResult<String> {
1271 Ok(self
1272 .resolve_unix_socket_bind_target(requester_driver, pid, cwd, path)?
1273 .canonical_path)
1274 }
1275
1276 fn resolve_unix_socket_bind_target(
1277 &mut self,
1278 requester_driver: &str,
1279 pid: u32,
1280 cwd: &str,
1281 path: &str,
1282 ) -> KernelResult<UnixSocketBindTarget> {
1283 self.assert_not_terminated()?;
1284 let identity = self.process_identity(requester_driver, pid)?;
1285 let (absolute_path, mut components, trailing_slash) =
1286 unix_socket_absolute_components(cwd, path)?;
1287
1288 let Some(basename) = components.pop_back() else {
1289 return Err(unix_socket_address_in_use(&absolute_path));
1290 };
1291
1292 if trailing_slash || matches!(basename.as_str(), "." | "..") {
1296 let (_, full_components, _) = unix_socket_absolute_components(cwd, path)?;
1297 resolve_unix_socket_components(
1298 self.raw_filesystem_mut(),
1299 &identity,
1300 full_components,
1301 false,
1302 false,
1303 )?;
1304 return Err(unix_socket_address_in_use(&absolute_path));
1305 }
1306
1307 let parent = resolve_unix_socket_components(
1308 self.raw_filesystem_mut(),
1309 &identity,
1310 components,
1311 true,
1312 true,
1313 )?;
1314 check_unix_dac(
1315 &identity,
1316 &parent.stat,
1317 UNIX_DAC_WRITE | UNIX_DAC_SEARCH,
1318 "bind",
1319 &parent.canonical_path,
1320 )?;
1321
1322 let canonical_path = join_absolute_path(&parent.canonical_path, &basename);
1323 self.reject_read_only_entry_write_path(&canonical_path)?;
1324 self.filesystem
1325 .check_virtual_path(FsOperation::Write, &canonical_path)
1326 .map_err(KernelError::from)?;
1327
1328 match self.raw_filesystem_mut().lstat(&canonical_path) {
1329 Ok(_) => return Err(unix_socket_address_in_use(&canonical_path)),
1330 Err(error) if error.code() == "ENOENT" => {}
1331 Err(error) => return Err(error.into()),
1332 }
1333
1334 Ok(UnixSocketBindTarget {
1335 canonical_path,
1336 parent,
1337 identity,
1338 })
1339 }
1340
1341 pub fn bind_unix_socket_path_for_process(
1350 &mut self,
1351 requester_driver: &str,
1352 pid: u32,
1353 cwd: &str,
1354 path: &str,
1355 ) -> KernelResult<UnixSocketPathNode> {
1356 let target = self.resolve_unix_socket_bind_target(requester_driver, pid, cwd, path)?;
1357 let UnixSocketBindTarget {
1358 canonical_path,
1359 parent,
1360 identity,
1361 } = target;
1362 let umask = self.processes.get_umask(pid)?;
1363
1364 self.check_write_file_limits(&canonical_path, 0)?;
1365 if let Err(error) = VirtualFileSystem::create_file_exclusive(
1366 &mut self.filesystem,
1367 &canonical_path,
1368 Vec::new(),
1369 ) {
1370 return if error.code() == "EEXIST" {
1371 Err(unix_socket_address_in_use(&canonical_path))
1372 } else {
1373 Err(error.into())
1374 };
1375 }
1376
1377 let mode = UNIX_SOCKET_FILE_TYPE | (0o777 & !(umask & 0o777));
1378 let gid = if parent.stat.mode & 0o2000 != 0 {
1379 parent.stat.gid
1380 } else {
1381 identity.egid
1382 };
1383 let metadata_result = (|| -> VfsResult<VirtualStat> {
1384 let filesystem = self.raw_filesystem_mut();
1385 filesystem.chown(&canonical_path, identity.euid, gid)?;
1386 filesystem.chmod(&canonical_path, mode)?;
1387 filesystem.lstat(&canonical_path)
1388 })();
1389 let stat = match metadata_result {
1390 Ok(stat) => stat,
1391 Err(error) => {
1392 let cleanup = self.raw_filesystem_mut().remove_file(&canonical_path);
1393 return match cleanup {
1394 Ok(()) => Err(error.into()),
1395 Err(cleanup_error) => Err(KernelError::new(
1396 error.code(),
1397 format!(
1398 "failed to initialize Unix socket inode metadata: {error}; \
1399 rollback also failed: {cleanup_error}"
1400 ),
1401 )),
1402 };
1403 }
1404 };
1405
1406 self.update_filesystem_usage_cache_for_inode_create(&canonical_path, 0);
1407 Ok(UnixSocketPathNode {
1408 canonical_path,
1409 stat,
1410 })
1411 }
1412
1413 pub fn resolve_unix_socket_connect_target_for_process(
1418 &mut self,
1419 requester_driver: &str,
1420 pid: u32,
1421 cwd: &str,
1422 path: &str,
1423 ) -> KernelResult<UnixSocketPathNode> {
1424 self.assert_not_terminated()?;
1425 let identity = self.process_identity(requester_driver, pid)?;
1426 let (_, components, trailing_slash) = unix_socket_absolute_components(cwd, path)?;
1427 let target = resolve_unix_socket_components(
1428 self.raw_filesystem_mut(),
1429 &identity,
1430 components,
1431 true,
1432 trailing_slash,
1433 )?;
1434 self.filesystem
1435 .check_virtual_path(FsOperation::Write, &target.canonical_path)
1436 .map_err(KernelError::from)?;
1437 check_unix_dac(
1438 &identity,
1439 &target.stat,
1440 UNIX_DAC_WRITE,
1441 "connect",
1442 &target.canonical_path,
1443 )?;
1444 if target.stat.mode & 0o170000 != UNIX_SOCKET_FILE_TYPE {
1445 return Err(KernelError::new(
1446 "ECONNREFUSED",
1447 format!(
1448 "Unix socket connect target is not a socket: {}",
1449 target.canonical_path
1450 ),
1451 ));
1452 }
1453 Ok(target)
1454 }
1455
1456 pub fn pwrite_file(
1465 &mut self,
1466 path: &str,
1467 offset: u64,
1468 content: impl Into<Vec<u8>>,
1469 ) -> KernelResult<()> {
1470 self.assert_not_terminated()?;
1471 self.reject_read_only_resolved_write_path(path)?;
1472 self.reject_unix_socket_data_path(path, "ENXIO")?;
1473 let content = content.into();
1474 let existing = self.storage_stat(path)?;
1475 let existing_size = existing.as_ref().map(|stat| stat.size).unwrap_or(0);
1476 let end = offset.saturating_add(content.len() as u64);
1477 self.check_write_file_limits_with_existing(
1478 path,
1479 existing.as_ref(),
1480 existing_size.max(end),
1481 )?;
1482 self.filesystem.pwrite(path, content, offset)?;
1483 self.update_filesystem_usage_cache_for_write(
1484 path,
1485 existing.as_ref(),
1486 existing_size.max(end),
1487 );
1488 Ok(())
1489 }
1490
1491 pub fn write_file_for_process(
1492 &mut self,
1493 requester_driver: &str,
1494 pid: u32,
1495 path: &str,
1496 content: impl Into<Vec<u8>>,
1497 mode: Option<u32>,
1498 ) -> KernelResult<()> {
1499 self.assert_not_terminated()?;
1500 self.assert_driver_owns(requester_driver, pid)?;
1501 let existed = self.exists_internal(Some(pid), path)?;
1502 if existed {
1503 self.check_dac_access(pid, path, DAC_WRITE)?;
1504 } else {
1505 self.check_dac_parent_access(pid, path, DAC_WRITE | DAC_EXECUTE)?;
1506 }
1507 let content = content.into();
1508 let new_size = content.len() as u64;
1509 self.reject_read_only_resolved_write_path(path)?;
1510 self.reject_unix_socket_data_path(path, "ENXIO")?;
1511 let existing = self.storage_stat(path)?;
1512 self.check_write_file_limits_with_existing(path, existing.as_ref(), new_size)?;
1513 VirtualFileSystem::write_file_with_mode(&mut self.filesystem, path, content, mode)
1514 .map_err(|error| {
1515 KernelError::new(
1516 error.code(),
1517 format!("create storage write for '{path}' failed: {error}"),
1518 )
1519 })?;
1520 self.update_filesystem_usage_cache_for_write(path, existing.as_ref(), new_size);
1521 if !existed {
1522 let umask = self.processes.get_umask(pid)?;
1523 self.apply_process_creation_metadata(pid, path, mode.unwrap_or(0o666), umask, false)
1524 .map_err(|error| {
1525 KernelError::new(
1526 error.code(),
1527 format!("create metadata for '{path}' failed: {error}"),
1528 )
1529 })?;
1530 } else {
1531 self.clear_setid_after_write(pid, path)?;
1532 }
1533 Ok(())
1534 }
1535
1536 pub fn create_dir(&mut self, path: &str) -> KernelResult<()> {
1537 self.assert_not_terminated()?;
1538 self.reject_read_only_entry_write_path(path)?;
1539 self.check_create_dir_limits(path)?;
1540 self.filesystem.create_dir(path)?;
1541 self.update_filesystem_usage_cache_for_inode_create(path, 0);
1542 Ok(())
1543 }
1544
1545 pub fn create_dir_for_process(
1546 &mut self,
1547 requester_driver: &str,
1548 pid: u32,
1549 path: &str,
1550 mode: Option<u32>,
1551 ) -> KernelResult<()> {
1552 self.assert_not_terminated()?;
1553 self.assert_driver_owns(requester_driver, pid)?;
1554 let existed = self.exists_internal(Some(pid), path)?;
1555 if !existed {
1556 self.check_dac_parent_access(pid, path, DAC_WRITE | DAC_EXECUTE)?;
1557 }
1558 self.reject_read_only_entry_write_path(path)?;
1559 self.check_create_dir_limits(path)?;
1560 VirtualFileSystem::create_dir_with_mode(&mut self.filesystem, path, mode)?;
1561 self.update_filesystem_usage_cache_for_inode_create(path, 0);
1562 if !existed {
1563 let umask = self.processes.get_umask(pid)?;
1564 self.apply_process_creation_metadata(pid, path, mode.unwrap_or(0o777), umask, true)?;
1565 }
1566 Ok(())
1567 }
1568
1569 pub fn mkdir(&mut self, path: &str, recursive: bool) -> KernelResult<()> {
1570 self.assert_not_terminated()?;
1571 self.reject_read_only_entry_write_path(path)?;
1572 let created_paths = self.missing_directory_paths(path, recursive)?;
1573 self.check_mkdir_limits(path, recursive)?;
1574 self.filesystem.mkdir(path, recursive)?;
1575 self.update_filesystem_usage_cache_for_inode_creates(path, created_paths.len());
1576 Ok(())
1577 }
1578
1579 pub fn mkdir_for_process(
1580 &mut self,
1581 requester_driver: &str,
1582 pid: u32,
1583 path: &str,
1584 recursive: bool,
1585 mode: Option<u32>,
1586 ) -> KernelResult<()> {
1587 self.assert_not_terminated()?;
1588 self.assert_driver_owns(requester_driver, pid)?;
1589 let created_paths = self.missing_directory_paths(path, recursive)?;
1590 if let Some(first_created) = created_paths.first() {
1591 self.check_dac_parent_access(pid, first_created, DAC_WRITE | DAC_EXECUTE)?;
1592 } else {
1593 self.check_dac_access(pid, path, DAC_EXECUTE)?;
1594 }
1595 self.reject_read_only_entry_write_path(path)?;
1596 self.check_mkdir_limits(path, recursive)?;
1597 VirtualFileSystem::mkdir_with_mode(&mut self.filesystem, path, recursive, mode)?;
1598 if !created_paths.is_empty() {
1599 let umask = self.processes.get_umask(pid)?;
1600 let mode = mode.unwrap_or(0o777);
1601 for created_path in &created_paths {
1602 self.apply_process_creation_metadata(pid, created_path, mode, umask, true)?;
1603 }
1604 }
1605 self.update_filesystem_usage_cache_for_inode_creates(path, created_paths.len());
1606 Ok(())
1607 }
1608
1609 pub fn mknod_for_process(
1610 &mut self,
1611 requester_driver: &str,
1612 pid: u32,
1613 path: &str,
1614 mode: u32,
1615 rdev: u64,
1616 ) -> KernelResult<()> {
1617 self.assert_not_terminated()?;
1618 self.assert_driver_owns(requester_driver, pid)?;
1619 if !matches!(mode & 0o170000, 0o010000 | 0o020000 | 0o060000) {
1620 return Err(KernelError::new(
1621 "EOPNOTSUPP",
1622 format!("unsupported special inode type for {path}"),
1623 ));
1624 }
1625 self.check_dac_parent_access(pid, path, DAC_WRITE | DAC_EXECUTE)?;
1626 self.reject_read_only_entry_write_path(path)?;
1627 self.check_create_dir_limits(path)?;
1628 self.filesystem.mknod(path, mode, rdev)?;
1629 self.update_filesystem_usage_cache_for_inode_create(path, 0);
1630 let umask = self.processes.get_umask(pid)?;
1631 self.apply_process_creation_metadata(pid, path, mode & 0o7777, umask, false)?;
1632 Ok(())
1633 }
1634
1635 pub fn umask(
1636 &self,
1637 requester_driver: &str,
1638 pid: u32,
1639 new_mask: Option<u32>,
1640 ) -> KernelResult<u32> {
1641 self.assert_driver_owns(requester_driver, pid)?;
1642 match new_mask {
1643 Some(mask) => Ok(self.processes.set_umask(pid, mask)?),
1644 None => Ok(self.processes.get_umask(pid)?),
1645 }
1646 }
1647
1648 pub fn exists(&self, path: &str) -> KernelResult<bool> {
1649 self.assert_not_terminated()?;
1650 self.exists_internal(None, path)
1651 }
1652
1653 pub fn exists_for_process(
1654 &mut self,
1655 requester_driver: &str,
1656 pid: u32,
1657 path: &str,
1658 ) -> KernelResult<bool> {
1659 self.assert_not_terminated()?;
1660 self.assert_driver_owns(requester_driver, pid)?;
1661 if let Err(error) = self.check_dac_traversal(pid, path) {
1662 if matches!(error.code(), "EACCES" | "ENOENT" | "ENOTDIR" | "ELOOP") {
1663 return Ok(false);
1664 }
1665 return Err(error);
1666 }
1667 self.exists_internal(Some(pid), path)
1668 }
1669
1670 pub fn stat(&mut self, path: &str) -> KernelResult<VirtualStat> {
1671 self.assert_not_terminated()?;
1672 self.stat_internal(None, path)
1673 }
1674
1675 pub fn stat_for_process(
1676 &mut self,
1677 requester_driver: &str,
1678 pid: u32,
1679 path: &str,
1680 ) -> KernelResult<VirtualStat> {
1681 self.assert_not_terminated()?;
1682 self.assert_driver_owns(requester_driver, pid)?;
1683 self.check_dac_traversal(pid, path)?;
1684 self.stat_internal(Some(pid), path)
1685 }
1686
1687 pub fn filesystem_stats_for_process(
1688 &mut self,
1689 requester_driver: &str,
1690 pid: u32,
1691 path: &str,
1692 ) -> KernelResult<FileSystemStats> {
1693 self.assert_not_terminated()?;
1694 self.assert_driver_owns(requester_driver, pid)?;
1695 self.check_dac_traversal(pid, path)?;
1696 self.stat_internal(Some(pid), path)?;
1697
1698 let max_bytes = self.resource_limits().max_filesystem_bytes;
1699 let max_inodes = self.resource_limits().max_inode_count;
1700 let filesystem = self.raw_filesystem_mut();
1701 let filesystem_any = filesystem as &mut dyn Any;
1702 if let Some(mount_table) = filesystem_any.downcast_mut::<MountTable>() {
1703 return mount_table
1704 .path_stats(path, max_bytes, max_inodes)
1705 .map_err(KernelError::from);
1706 }
1707
1708 let usage = measure_filesystem_usage(filesystem)?;
1709 let total_bytes = max_bytes
1710 .unwrap_or(usage.total_bytes)
1711 .max(usage.total_bytes);
1712 let total_inodes = max_inodes
1713 .map(|value| value as u64)
1714 .unwrap_or(usage.inode_count as u64)
1715 .max(usage.inode_count as u64);
1716 Ok(FileSystemStats {
1717 total_bytes,
1718 used_bytes: usage.total_bytes,
1719 available_bytes: total_bytes.saturating_sub(usage.total_bytes),
1720 total_inodes,
1721 free_inodes: total_inodes.saturating_sub(usage.inode_count as u64),
1722 })
1723 }
1724
1725 pub fn access_for_process(
1726 &mut self,
1727 requester_driver: &str,
1728 pid: u32,
1729 path: &str,
1730 access: u32,
1731 effective_ids: bool,
1732 ) -> KernelResult<()> {
1733 self.assert_driver_owns(requester_driver, pid)?;
1734 self.check_dac_traversal(pid, path)?;
1735 let mut identity = self.process_identity(requester_driver, pid)?;
1736 if !effective_ids {
1737 identity.euid = identity.uid;
1738 identity.egid = identity.gid;
1739 }
1740 let stat = self.filesystem.stat(path)?;
1741 self.check_dac_mode_with_acl(&identity, &stat, access & 0o7, path)
1742 }
1743
1744 pub fn lstat(&self, path: &str) -> KernelResult<VirtualStat> {
1745 self.assert_not_terminated()?;
1746 self.lstat_internal(None, path)
1747 }
1748
1749 pub fn lstat_for_process(
1750 &mut self,
1751 requester_driver: &str,
1752 pid: u32,
1753 path: &str,
1754 ) -> KernelResult<VirtualStat> {
1755 self.assert_not_terminated()?;
1756 self.assert_driver_owns(requester_driver, pid)?;
1757 self.check_dac_traversal(pid, path)?;
1758 self.lstat_internal(Some(pid), path)
1759 }
1760
1761 pub fn read_link(&self, path: &str) -> KernelResult<String> {
1762 self.assert_not_terminated()?;
1763 self.read_link_internal(None, path)
1764 }
1765
1766 pub fn read_link_for_process(
1767 &mut self,
1768 requester_driver: &str,
1769 pid: u32,
1770 path: &str,
1771 ) -> KernelResult<String> {
1772 self.assert_not_terminated()?;
1773 self.assert_driver_owns(requester_driver, pid)?;
1774 self.check_dac_traversal(pid, path)?;
1775 self.read_link_internal(Some(pid), path)
1776 }
1777
1778 pub fn read_dir(&mut self, path: &str) -> KernelResult<Vec<String>> {
1779 self.assert_not_terminated()?;
1780 let entries = self.read_dir_internal(None, path)?;
1781 self.resources.check_readdir_entries(entries.len())?;
1782 Ok(entries)
1783 }
1784
1785 pub fn read_dir_for_process(
1786 &mut self,
1787 requester_driver: &str,
1788 pid: u32,
1789 path: &str,
1790 ) -> KernelResult<Vec<String>> {
1791 self.assert_not_terminated()?;
1792 self.assert_driver_owns(requester_driver, pid)?;
1793 self.check_dac_access(pid, path, DAC_READ | DAC_EXECUTE)?;
1794 let mut entries = self.read_dir_internal(Some(pid), path)?;
1795 entries.retain(|entry| !is_internal_unnamed_file_name(entry));
1796 self.resources.check_readdir_entries(entries.len())?;
1797 Ok(entries)
1798 }
1799
1800 pub fn read_dir_with_types_for_process(
1801 &mut self,
1802 requester_driver: &str,
1803 pid: u32,
1804 path: &str,
1805 ) -> KernelResult<Vec<VirtualDirEntry>> {
1806 self.assert_not_terminated()?;
1807 self.assert_driver_owns(requester_driver, pid)?;
1808 self.check_dac_access(pid, path, DAC_READ | DAC_EXECUTE)?;
1809 let mut entries = self.read_dir_with_types_internal(Some(pid), path)?;
1810 entries.retain(|entry| !is_internal_unnamed_file_name(&entry.name));
1811 self.resources.check_readdir_entries(entries.len())?;
1812 Ok(entries)
1813 }
1814
1815 pub fn read_dir_with_types(&mut self, path: &str) -> KernelResult<Vec<VirtualDirEntry>> {
1822 self.assert_not_terminated()?;
1823 let names = self.read_dir_internal(None, path)?;
1824 self.resources.check_readdir_entries(names.len())?;
1825 let mut entries = Vec::with_capacity(names.len());
1826 for name in names {
1827 let child = normalize_path(&format!("{path}/{name}"));
1828 let stat = self.lstat_internal(None, &child)?;
1829 entries.push(VirtualDirEntry {
1830 name,
1831 is_directory: stat.is_directory,
1832 is_symbolic_link: stat.is_symbolic_link,
1833 });
1834 }
1835 Ok(entries)
1836 }
1837
1838 pub fn read_dir_recursive(
1839 &mut self,
1840 path: &str,
1841 max_depth: Option<usize>,
1842 ) -> KernelResult<Vec<RecursiveDirEntry>> {
1843 self.assert_not_terminated()?;
1844 let depth_limit = self.effective_recursive_fs_depth(max_depth)?;
1845 let caller_limited = max_depth.is_some();
1846 let mut entries = Vec::new();
1847 let mut queue = VecDeque::from([(normalize_path(path), 0usize)]);
1848
1849 while let Some((dir_path, depth)) = queue.pop_front() {
1850 self.resources.check_recursive_fs_depth(depth)?;
1851 let names = self.read_dir_internal(None, &dir_path)?;
1852 self.resources.check_readdir_entries(names.len())?;
1853
1854 for name in names {
1855 if matches!(name.as_str(), "." | "..") {
1856 continue;
1857 }
1858 let child = join_child_path(&dir_path, &name);
1859 let stat = self.lstat_internal(None, &child)?;
1860 let entry = RecursiveDirEntry {
1861 path: child.clone(),
1862 is_directory: stat.is_directory,
1863 is_symbolic_link: stat.is_symbolic_link,
1864 size: stat.size,
1865 };
1866 entries.push(entry);
1867 self.resources.check_recursive_fs_entries(entries.len())?;
1868
1869 if stat.is_directory && !stat.is_symbolic_link {
1870 let child_depth = depth.saturating_add(1);
1871 if child_depth <= depth_limit {
1872 queue.push_back((child, child_depth));
1873 } else if !caller_limited {
1874 self.resources.check_recursive_fs_depth(child_depth)?;
1875 }
1876 }
1877 }
1878 }
1879
1880 Ok(entries)
1881 }
1882
1883 pub fn copy_path(&mut self, from: &str, to: &str, recursive: bool) -> KernelResult<()> {
1884 self.assert_not_terminated()?;
1885 let mut entries = 0usize;
1886 self.copy_path_inner(from, to, recursive, 0, &mut entries)?;
1887 Ok(())
1888 }
1889
1890 pub fn remove_path(&mut self, path: &str, recursive: bool) -> KernelResult<()> {
1891 self.assert_not_terminated()?;
1892 let mut entries = 0usize;
1893 self.remove_path_inner(path, recursive, 0, &mut entries)
1894 }
1895
1896 pub fn move_path(&mut self, from: &str, to: &str) -> KernelResult<()> {
1897 self.assert_not_terminated()?;
1898 match self.rename(from, to) {
1899 Ok(()) => Ok(()),
1900 Err(error) if error.code() == "EXDEV" => {
1901 self.copy_path(from, to, true)?;
1902 self.remove_path(from, true)
1903 }
1904 Err(error) => Err(error),
1905 }
1906 }
1907
1908 pub fn remove_file(&mut self, path: &str) -> KernelResult<()> {
1909 self.assert_not_terminated()?;
1910 self.reject_read_only_entry_write_path(path)?;
1911 let removed = self.storage_lstat(path)?;
1912 let detached = self.prepare_anonymous_file_backing(path, removed.as_ref())?;
1913 self.filesystem.remove_file(path)?;
1914 match detached {
1915 Some(OpenFileRemovalBacking::Anonymous {
1916 descriptions,
1917 backing,
1918 }) => {
1919 for description in descriptions {
1920 description.detach_path(path, Arc::clone(&backing));
1921 }
1922 }
1923 Some(OpenFileRemovalBacking::LinkedAlias {
1924 descriptions,
1925 live_path,
1926 }) => {
1927 for description in descriptions {
1928 description.rebind_deleted_path(path, &live_path);
1929 }
1930 }
1931 None => {}
1932 }
1933 self.update_filesystem_usage_cache_for_remove(path, removed.as_ref());
1934 Ok(())
1935 }
1936
1937 pub fn remove_file_for_process(
1938 &mut self,
1939 requester_driver: &str,
1940 pid: u32,
1941 path: &str,
1942 ) -> KernelResult<()> {
1943 self.assert_driver_owns(requester_driver, pid)?;
1944 self.check_dac_parent_access(pid, path, DAC_WRITE | DAC_EXECUTE)?;
1945 self.check_sticky_directory_removal(pid, path)?;
1946 self.remove_file(path)
1947 }
1948
1949 pub fn remove_dir(&mut self, path: &str) -> KernelResult<()> {
1950 self.assert_not_terminated()?;
1951 self.reject_read_only_entry_write_path(path)?;
1952 let removed = self.storage_lstat(path)?;
1953 let detached = self.prepare_detached_directory_backing(path, removed.as_ref());
1954 self.filesystem.remove_dir(path)?;
1955 if let Some((descriptions, stat)) = detached {
1956 for description in descriptions {
1957 description.detach_directory(path, stat.clone());
1958 }
1959 }
1960 if removed.as_ref().is_some_and(|stat| stat.is_directory) {
1961 self.update_filesystem_usage_cache_for_inode_delete(path, 0);
1962 }
1963 Ok(())
1964 }
1965
1966 pub fn remove_dir_for_process(
1967 &mut self,
1968 requester_driver: &str,
1969 pid: u32,
1970 path: &str,
1971 ) -> KernelResult<()> {
1972 self.assert_driver_owns(requester_driver, pid)?;
1973 self.check_dac_parent_access(pid, path, DAC_WRITE | DAC_EXECUTE)?;
1974 self.check_sticky_directory_removal(pid, path)?;
1975 self.remove_dir(path)
1976 }
1977
1978 pub fn rename(&mut self, old_path: &str, new_path: &str) -> KernelResult<()> {
1979 self.rename_at2(old_path, new_path, 0)
1980 }
1981
1982 pub fn rename_at2(&mut self, old_path: &str, new_path: &str, flags: u32) -> KernelResult<()> {
1983 self.assert_not_terminated()?;
1984 self.reject_read_only_entry_write_path(old_path)?;
1985 self.reject_read_only_entry_write_path(new_path)?;
1986 self.check_rename_copy_up_limits(old_path, new_path)?;
1987 let replaced = self.storage_lstat(new_path)?;
1988 let detached_destination =
1989 self.prepare_anonymous_file_backing(new_path, replaced.as_ref())?;
1990 let detached_directory_destination =
1991 self.prepare_detached_directory_backing(new_path, replaced.as_ref());
1992 self.filesystem.rename_at2(old_path, new_path, flags)?;
1993 if flags == RENAME_EXCHANGE {
1994 let temporary = format!(
1995 "/.agentos-open-rename-exchange-{}",
1996 self.next_unnamed_file_id
1997 );
1998 self.next_unnamed_file_id = self.next_unnamed_file_id.saturating_add(1);
1999 self.rename_open_file_descriptions(old_path, &temporary);
2000 self.rename_open_file_descriptions(new_path, old_path);
2001 self.rename_open_file_descriptions(&temporary, new_path);
2002 self.invalidate_filesystem_usage_cache();
2003 return Ok(());
2004 }
2005 match detached_destination {
2006 Some(OpenFileRemovalBacking::Anonymous {
2007 descriptions,
2008 backing,
2009 }) => {
2010 for description in descriptions {
2011 description.detach_path(new_path, Arc::clone(&backing));
2012 }
2013 }
2014 Some(OpenFileRemovalBacking::LinkedAlias {
2015 descriptions,
2016 live_path,
2017 }) => {
2018 for description in descriptions {
2019 description.rebind_deleted_path(new_path, &live_path);
2020 }
2021 }
2022 None => {}
2023 }
2024 if let Some((descriptions, stat)) = detached_directory_destination {
2025 for description in descriptions {
2026 description.detach_directory(new_path, stat.clone());
2027 }
2028 }
2029 self.rename_open_file_descriptions(old_path, new_path);
2030 self.invalidate_filesystem_usage_cache();
2034 Ok(())
2035 }
2036
2037 pub fn rename_for_process(
2038 &mut self,
2039 requester_driver: &str,
2040 pid: u32,
2041 old_path: &str,
2042 new_path: &str,
2043 ) -> KernelResult<()> {
2044 self.rename_at2_for_process(requester_driver, pid, old_path, new_path, 0)
2045 }
2046
2047 pub fn rename_at2_for_process(
2048 &mut self,
2049 requester_driver: &str,
2050 pid: u32,
2051 old_path: &str,
2052 new_path: &str,
2053 flags: u32,
2054 ) -> KernelResult<()> {
2055 self.assert_driver_owns(requester_driver, pid)?;
2056 self.check_dac_parent_access(pid, old_path, DAC_WRITE | DAC_EXECUTE)?;
2057 self.check_dac_parent_access(pid, new_path, DAC_WRITE | DAC_EXECUTE)?;
2058 self.check_sticky_directory_removal(pid, old_path)?;
2059 if self.exists_internal(Some(pid), new_path)? {
2060 self.check_sticky_directory_removal(pid, new_path)?;
2061 }
2062 self.rename_at2(old_path, new_path, flags)
2063 }
2064
2065 pub fn realpath(&self, path: &str) -> KernelResult<String> {
2066 self.assert_not_terminated()?;
2067 self.realpath_internal(None, path)
2068 }
2069
2070 pub fn realpath_for_process(
2071 &mut self,
2072 requester_driver: &str,
2073 pid: u32,
2074 path: &str,
2075 ) -> KernelResult<String> {
2076 self.assert_not_terminated()?;
2077 self.assert_driver_owns(requester_driver, pid)?;
2078 self.check_dac_traversal(pid, path)?;
2079 self.realpath_internal(Some(pid), path)
2080 }
2081
2082 pub fn symlink(&mut self, target: &str, link_path: &str) -> KernelResult<()> {
2083 self.assert_not_terminated()?;
2084 if is_proc_path(target) {
2085 self.filesystem
2086 .check_virtual_path(FsOperation::Write, link_path)
2087 .map_err(KernelError::from)?;
2088 return Err(read_only_filesystem_error(link_path));
2089 }
2090 self.reject_read_only_entry_write_path(link_path)?;
2091 self.check_symlink_limits(target, link_path)?;
2092 self.filesystem.symlink(target, link_path)?;
2093 self.update_filesystem_usage_cache_for_inode_create(link_path, target.len() as u64);
2094 Ok(())
2095 }
2096
2097 pub fn symlink_for_process(
2098 &mut self,
2099 requester_driver: &str,
2100 pid: u32,
2101 target: &str,
2102 link_path: &str,
2103 ) -> KernelResult<()> {
2104 self.assert_driver_owns(requester_driver, pid)?;
2105 self.check_dac_parent_access(pid, link_path, DAC_WRITE | DAC_EXECUTE)?;
2106 self.symlink(target, link_path)
2107 }
2108
2109 pub fn chmod(&mut self, path: &str, mode: u32) -> KernelResult<()> {
2110 self.assert_not_terminated()?;
2111 self.reject_read_only_resolved_write_path(path)?;
2112 self.filesystem.chmod(path, mode)?;
2113 self.sync_access_acl_mode(path, mode)?;
2114 self.no_posix_acl_cache.clear();
2115 Ok(())
2116 }
2117
2118 pub fn chmod_for_process(
2119 &mut self,
2120 requester_driver: &str,
2121 pid: u32,
2122 path: &str,
2123 mut mode: u32,
2124 ) -> KernelResult<()> {
2125 let identity = self.process_identity(requester_driver, pid)?;
2126 self.check_dac_traversal(pid, path)?;
2127 let stat = self.filesystem.stat(path)?;
2128 if identity.euid != 0 && identity.euid != stat.uid {
2129 return Err(KernelError::new(
2130 "EPERM",
2131 format!("chmod requires ownership of {path}"),
2132 ));
2133 }
2134 if identity.euid != 0
2135 && identity.egid != stat.gid
2136 && !identity.supplementary_gids.contains(&stat.gid)
2137 {
2138 mode &= !0o2000;
2139 }
2140 self.chmod(path, mode)
2141 }
2142
2143 pub fn link(&mut self, old_path: &str, new_path: &str) -> KernelResult<()> {
2144 self.assert_not_terminated()?;
2145 if is_proc_path(old_path) {
2146 self.filesystem
2147 .check_virtual_path(FsOperation::Write, new_path)
2148 .map_err(KernelError::from)?;
2149 return Err(read_only_filesystem_error(new_path));
2150 }
2151 self.reject_read_only_resolved_write_path(old_path)?;
2152 self.reject_read_only_entry_write_path(new_path)?;
2153 self.filesystem.link(old_path, new_path)?;
2154 Ok(())
2157 }
2158
2159 pub fn link_for_process(
2160 &mut self,
2161 requester_driver: &str,
2162 pid: u32,
2163 old_path: &str,
2164 new_path: &str,
2165 ) -> KernelResult<()> {
2166 self.assert_driver_owns(requester_driver, pid)?;
2167 self.check_dac_traversal(pid, old_path)?;
2168 self.check_dac_parent_access(pid, new_path, DAC_WRITE | DAC_EXECUTE)?;
2169 self.link(old_path, new_path)
2170 }
2171
2172 pub fn chown(&mut self, path: &str, uid: u32, gid: u32) -> KernelResult<()> {
2173 self.assert_not_terminated()?;
2174 self.reject_read_only_resolved_write_path(path)?;
2175 self.filesystem.chown(path, uid, gid)?;
2176 self.no_posix_acl_cache.clear();
2177 Ok(())
2178 }
2179
2180 pub fn chown_for_process(
2181 &mut self,
2182 requester_driver: &str,
2183 pid: u32,
2184 path: &str,
2185 uid: u32,
2186 gid: u32,
2187 follow_symlinks: bool,
2188 ) -> KernelResult<()> {
2189 self.assert_not_terminated()?;
2190 let identity = self.process_identity(requester_driver, pid)?;
2191 let stat = if follow_symlinks {
2192 self.stat_for_process(requester_driver, pid, path)?
2193 } else {
2194 self.lstat_for_process(requester_driver, pid, path)?
2195 };
2196 let (next_uid, next_gid) = validate_chown_request(&identity, &stat, uid, gid, path)?;
2197
2198 if follow_symlinks {
2199 self.reject_read_only_resolved_write_path(path)?;
2200 } else {
2201 self.reject_read_only_entry_write_path(path)?;
2202 }
2203 self.filesystem
2204 .chown_spec(path, next_uid, next_gid, follow_symlinks)?;
2205 if let Some(mode) = linux_chown_cleared_mode(&stat) {
2206 self.filesystem.chmod(path, mode)?;
2207 }
2208 Ok(())
2209 }
2210
2211 pub fn lchown_for_process(
2212 &mut self,
2213 requester_driver: &str,
2214 pid: u32,
2215 path: &str,
2216 uid: u32,
2217 gid: u32,
2218 ) -> KernelResult<()> {
2219 let identity = self.process_identity(requester_driver, pid)?;
2220 self.check_dac_traversal(pid, path)?;
2221 let stat = self.filesystem.lstat(path)?;
2222 if identity.euid != 0 {
2223 let owns_file = identity.euid == stat.uid;
2224 let keeps_owner = uid == stat.uid;
2225 let allowed_group = gid == identity.egid || identity.supplementary_gids.contains(&gid);
2226 if !owns_file || !keeps_owner || !allowed_group {
2227 return Err(KernelError::new(
2228 "EPERM",
2229 format!("lchown is not permitted for {path}"),
2230 ));
2231 }
2232 }
2233 self.reject_read_only_entry_write_path(path)?;
2234 self.filesystem.lchown(path, uid, gid)?;
2235 Ok(())
2236 }
2237
2238 pub fn get_xattr(
2239 &mut self,
2240 path: &str,
2241 name: &str,
2242 follow_symlinks: bool,
2243 ) -> KernelResult<Vec<u8>> {
2244 self.assert_not_terminated()?;
2245 Ok(self.filesystem.get_xattr(path, name, follow_symlinks)?)
2246 }
2247
2248 pub fn get_xattr_for_process(
2249 &mut self,
2250 requester_driver: &str,
2251 pid: u32,
2252 path: &str,
2253 name: &str,
2254 follow_symlinks: bool,
2255 ) -> KernelResult<Vec<u8>> {
2256 let identity = self.process_identity(requester_driver, pid)?;
2257 self.check_dac_traversal(pid, path)?;
2258 check_xattr_namespace(&identity, name, false, path)?;
2259 let stat = if follow_symlinks {
2260 self.filesystem.stat(path)?
2261 } else {
2262 self.filesystem.lstat(path)?
2263 };
2264 self.check_dac_mode_with_acl(&identity, &stat, DAC_READ, path)?;
2265 self.get_xattr(path, name, follow_symlinks)
2266 }
2267
2268 pub fn list_xattrs(&mut self, path: &str, follow_symlinks: bool) -> KernelResult<Vec<String>> {
2269 self.assert_not_terminated()?;
2270 Ok(self.filesystem.list_xattrs(path, follow_symlinks)?)
2271 }
2272
2273 pub fn list_xattrs_for_process(
2274 &mut self,
2275 requester_driver: &str,
2276 pid: u32,
2277 path: &str,
2278 follow_symlinks: bool,
2279 ) -> KernelResult<Vec<String>> {
2280 let identity = self.process_identity(requester_driver, pid)?;
2281 self.check_dac_traversal(pid, path)?;
2282 let stat = if follow_symlinks {
2283 self.filesystem.stat(path)?
2284 } else {
2285 self.filesystem.lstat(path)?
2286 };
2287 self.check_dac_mode_with_acl(&identity, &stat, DAC_READ, path)?;
2288 let mut names = self.list_xattrs(path, follow_symlinks)?;
2289 if identity.euid != 0 {
2290 names.retain(|name| !name.starts_with("trusted.") && !name.starts_with("security."));
2291 }
2292 Ok(names)
2293 }
2294
2295 pub fn set_xattr(
2296 &mut self,
2297 path: &str,
2298 name: &str,
2299 value: Vec<u8>,
2300 flags: u32,
2301 follow_symlinks: bool,
2302 ) -> KernelResult<()> {
2303 self.assert_not_terminated()?;
2304 if follow_symlinks {
2305 self.reject_read_only_resolved_write_path(path)?;
2306 } else {
2307 self.reject_read_only_entry_write_path(path)?;
2308 }
2309 self.filesystem
2310 .set_xattr(path, name, value, flags, follow_symlinks)?;
2311 self.no_posix_acl_cache.clear();
2312 Ok(())
2313 }
2314
2315 #[allow(clippy::too_many_arguments)]
2316 pub fn set_xattr_for_process(
2317 &mut self,
2318 requester_driver: &str,
2319 pid: u32,
2320 path: &str,
2321 name: &str,
2322 value: Vec<u8>,
2323 flags: u32,
2324 follow_symlinks: bool,
2325 ) -> KernelResult<()> {
2326 let identity = self.process_identity(requester_driver, pid)?;
2327 self.check_dac_traversal(pid, path)?;
2328 check_xattr_namespace(&identity, name, true, path)?;
2329 let stat = if follow_symlinks {
2330 self.filesystem.stat(path)?
2331 } else {
2332 self.filesystem.lstat(path)?
2333 };
2334 check_xattr_inode_write_policy(&stat, name, path)?;
2335 if name.starts_with("system.posix_acl_") {
2336 if identity.euid != 0 && identity.euid != stat.uid {
2337 return Err(KernelError::new(
2338 "EPERM",
2339 format!("setting {name} requires ownership of {path}"),
2340 ));
2341 }
2342 } else {
2343 self.check_dac_mode_with_acl(&identity, &stat, DAC_WRITE, path)?;
2344 }
2345 let acl = if name == POSIX_ACL_ACCESS || name == POSIX_ACL_DEFAULT {
2346 let acl = PosixAcl::parse(&value, path)?;
2347 if name == POSIX_ACL_DEFAULT && !stat.is_directory {
2348 return Err(KernelError::new(
2349 "EACCES",
2350 format!("default ACL requires a directory: {path}"),
2351 ));
2352 }
2353 Some(acl)
2354 } else {
2355 None
2356 };
2357 self.set_xattr(path, name, value, flags, follow_symlinks)?;
2358 if name == POSIX_ACL_ACCESS {
2359 let mode = acl.expect("access ACL was parsed").mode(stat.mode);
2360 self.filesystem.chmod(path, mode)?;
2361 }
2362 Ok(())
2363 }
2364
2365 pub fn remove_xattr(
2366 &mut self,
2367 path: &str,
2368 name: &str,
2369 follow_symlinks: bool,
2370 ) -> KernelResult<()> {
2371 self.assert_not_terminated()?;
2372 if follow_symlinks {
2373 self.reject_read_only_resolved_write_path(path)?;
2374 } else {
2375 self.reject_read_only_entry_write_path(path)?;
2376 }
2377 self.filesystem.remove_xattr(path, name, follow_symlinks)?;
2378 self.no_posix_acl_cache.clear();
2379 Ok(())
2380 }
2381
2382 pub fn remove_xattr_for_process(
2383 &mut self,
2384 requester_driver: &str,
2385 pid: u32,
2386 path: &str,
2387 name: &str,
2388 follow_symlinks: bool,
2389 ) -> KernelResult<()> {
2390 let identity = self.process_identity(requester_driver, pid)?;
2391 self.check_dac_traversal(pid, path)?;
2392 check_xattr_namespace(&identity, name, true, path)?;
2393 let stat = if follow_symlinks {
2394 self.filesystem.stat(path)?
2395 } else {
2396 self.filesystem.lstat(path)?
2397 };
2398 check_xattr_inode_write_policy(&stat, name, path)?;
2399 if name.starts_with("system.posix_acl_") {
2400 if identity.euid != 0 && identity.euid != stat.uid {
2401 return Err(KernelError::new(
2402 "EPERM",
2403 format!("removing {name} requires ownership of {path}"),
2404 ));
2405 }
2406 } else {
2407 self.check_dac_mode_with_acl(&identity, &stat, DAC_WRITE, path)?;
2408 }
2409 self.remove_xattr(path, name, follow_symlinks)
2410 }
2411
2412 pub fn utimes(&mut self, path: &str, atime_ms: u64, mtime_ms: u64) -> KernelResult<()> {
2413 self.utimes_spec(
2414 path,
2415 VirtualUtimeSpec::Set(VirtualTimeSpec::from_millis(atime_ms)),
2416 VirtualUtimeSpec::Set(VirtualTimeSpec::from_millis(mtime_ms)),
2417 )
2418 }
2419
2420 pub fn utimes_spec(
2421 &mut self,
2422 path: &str,
2423 atime: VirtualUtimeSpec,
2424 mtime: VirtualUtimeSpec,
2425 ) -> KernelResult<()> {
2426 self.assert_not_terminated()?;
2427 self.reject_read_only_resolved_write_path(path)?;
2428 Ok(self.filesystem.utimes_spec(path, atime, mtime, true)?)
2429 }
2430
2431 pub fn utimes_spec_for_process(
2432 &mut self,
2433 requester_driver: &str,
2434 pid: u32,
2435 path: &str,
2436 atime: VirtualUtimeSpec,
2437 mtime: VirtualUtimeSpec,
2438 follow_symlinks: bool,
2439 ) -> KernelResult<()> {
2440 let identity = self.process_identity(requester_driver, pid)?;
2441 self.check_dac_traversal(pid, path)?;
2442 let stat = if follow_symlinks {
2443 self.filesystem.stat(path)?
2444 } else {
2445 self.filesystem.lstat(path)?
2446 };
2447 let owns_file = identity.euid == 0 || identity.euid == stat.uid;
2448 let sets_both_to_now =
2449 matches!(atime, VirtualUtimeSpec::Now) && matches!(mtime, VirtualUtimeSpec::Now);
2450 let omits_both =
2451 matches!(atime, VirtualUtimeSpec::Omit) && matches!(mtime, VirtualUtimeSpec::Omit);
2452 if !owns_file && sets_both_to_now {
2453 self.check_dac_mode_with_acl(&identity, &stat, DAC_WRITE, path)?;
2454 } else if !owns_file && !omits_both {
2455 return Err(KernelError::new(
2456 "EPERM",
2457 format!("changing timestamps requires ownership of {path}"),
2458 ));
2459 }
2460 if follow_symlinks {
2461 self.utimes_spec(path, atime, mtime)
2462 } else {
2463 self.lutimes(path, atime, mtime)
2464 }
2465 }
2466
2467 pub fn lutimes(
2468 &mut self,
2469 path: &str,
2470 atime: VirtualUtimeSpec,
2471 mtime: VirtualUtimeSpec,
2472 ) -> KernelResult<()> {
2473 self.assert_not_terminated()?;
2474 self.reject_read_only_entry_write_path(path)?;
2475 Ok(self.filesystem.utimes_spec(path, atime, mtime, false)?)
2476 }
2477
2478 pub fn futimes(
2479 &mut self,
2480 requester_driver: &str,
2481 pid: u32,
2482 fd: u32,
2483 atime: VirtualUtimeSpec,
2484 mtime: VirtualUtimeSpec,
2485 ) -> KernelResult<()> {
2486 self.assert_not_terminated()?;
2487 let path = self
2488 .description_for_fd(requester_driver, pid, fd)?
2489 .path()
2490 .to_owned();
2491 self.reject_read_only_resolved_write_path(&path)?;
2492 Ok(self.filesystem.utimes_spec(&path, atime, mtime, true)?)
2493 }
2494
2495 pub fn truncate(&mut self, path: &str, length: u64) -> KernelResult<()> {
2496 self.assert_not_terminated()?;
2497 self.reject_read_only_resolved_write_path(path)?;
2498 self.reject_unix_socket_data_path(path, "EINVAL")?;
2499 let existing = self.storage_stat(path)?;
2500 self.check_truncate_limits_with_existing(path, existing.as_ref(), length)?;
2501 self.filesystem.truncate(path, length)?;
2502 self.update_filesystem_usage_cache_for_write(path, existing.as_ref(), length);
2503 Ok(())
2504 }
2505
2506 pub fn truncate_for_process(
2507 &mut self,
2508 requester_driver: &str,
2509 pid: u32,
2510 path: &str,
2511 length: u64,
2512 ) -> KernelResult<()> {
2513 self.assert_driver_owns(requester_driver, pid)?;
2514 self.check_dac_access(pid, path, DAC_WRITE)?;
2515 self.truncate(path, length)?;
2516 self.clear_setid_after_write(pid, path)
2517 }
2518
2519 pub fn fd_truncate(
2520 &mut self,
2521 requester_driver: &str,
2522 pid: u32,
2523 fd: u32,
2524 length: u64,
2525 ) -> KernelResult<()> {
2526 self.assert_driver_owns(requester_driver, pid)?;
2527 let entry = {
2528 let tables = lock_or_recover(&self.fd_tables);
2529 tables
2530 .get(pid)
2531 .and_then(|table| table.get(fd))
2532 .cloned()
2533 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2534 };
2535 if let Some(stat) = entry.description.anonymous_stat() {
2536 self.check_path_resize_limits_with_existing(stat.size, length)?;
2537 entry
2538 .description
2539 .anonymous_truncate(length)
2540 .expect("anonymous stat and backing must agree")?;
2541 return Ok(());
2542 }
2543 if entry.description.flags() & 0b11 == crate::fd_table::O_RDONLY {
2544 return Err(KernelError::bad_file_descriptor(fd));
2545 }
2546 let path = entry.description.path().to_owned();
2547 self.truncate(&path, length)?;
2548 self.clear_setid_after_write(pid, &path)
2549 }
2550
2551 pub fn fd_allocate(
2552 &mut self,
2553 requester_driver: &str,
2554 pid: u32,
2555 fd: u32,
2556 offset: u64,
2557 length: u64,
2558 ) -> KernelResult<()> {
2559 self.assert_driver_owns(requester_driver, pid)?;
2560 let entry = {
2561 let tables = lock_or_recover(&self.fd_tables);
2562 tables
2563 .get(pid)
2564 .and_then(|table| table.get(fd))
2565 .cloned()
2566 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2567 };
2568 if entry.description.flags() & 0b11 == crate::fd_table::O_RDONLY {
2569 return Err(KernelError::bad_file_descriptor(fd));
2570 }
2571 let end = offset
2572 .checked_add(length)
2573 .ok_or_else(|| KernelError::new("EINVAL", "allocation range overflows"))?;
2574 let path = entry.description.path().to_owned();
2575 self.reject_read_only_resolved_write_path(&path)?;
2576 if length == 0 {
2577 return Ok(());
2578 }
2579 let existing = self.storage_stat(&path)?;
2580 let new_size = existing.as_ref().map_or(end, |stat| stat.size.max(end));
2581 self.check_truncate_limits_with_existing(&path, existing.as_ref(), new_size)?;
2582 self.filesystem.allocate(&path, offset, length)?;
2583 self.update_filesystem_usage_cache_for_write(&path, existing.as_ref(), new_size);
2584 self.clear_setid_after_write(pid, &path)
2585 }
2586
2587 pub fn fd_punch_hole(
2588 &mut self,
2589 requester_driver: &str,
2590 pid: u32,
2591 fd: u32,
2592 offset: u64,
2593 length: u64,
2594 ) -> KernelResult<()> {
2595 self.assert_driver_owns(requester_driver, pid)?;
2596 let entry = {
2597 let tables = lock_or_recover(&self.fd_tables);
2598 tables
2599 .get(pid)
2600 .and_then(|table| table.get(fd))
2601 .cloned()
2602 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2603 };
2604 if entry.description.flags() & 0b11 == crate::fd_table::O_RDONLY {
2605 return Err(KernelError::bad_file_descriptor(fd));
2606 }
2607 offset
2608 .checked_add(length)
2609 .ok_or_else(|| KernelError::new("EINVAL", "hole-punch range overflows"))?;
2610 let path = entry.description.path().to_owned();
2611 self.reject_read_only_resolved_write_path(&path)?;
2612 self.filesystem.punch_hole(&path, offset, length)?;
2613 self.clear_setid_after_write(pid, &path)
2614 }
2615
2616 pub fn fd_zero_range(
2617 &mut self,
2618 requester_driver: &str,
2619 pid: u32,
2620 fd: u32,
2621 offset: u64,
2622 length: u64,
2623 keep_size: bool,
2624 ) -> KernelResult<()> {
2625 self.assert_driver_owns(requester_driver, pid)?;
2626 let entry = {
2627 let tables = lock_or_recover(&self.fd_tables);
2628 tables
2629 .get(pid)
2630 .and_then(|table| table.get(fd))
2631 .cloned()
2632 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2633 };
2634 if entry.description.flags() & 0b11 == crate::fd_table::O_RDONLY {
2635 return Err(KernelError::bad_file_descriptor(fd));
2636 }
2637 let end = offset
2638 .checked_add(length)
2639 .ok_or_else(|| KernelError::new("EINVAL", "zero range overflows"))?;
2640 let path = entry.description.path().to_owned();
2641 self.reject_read_only_resolved_write_path(&path)?;
2642 let existing = self.storage_stat(&path)?;
2643 let old_size = existing
2644 .as_ref()
2645 .ok_or_else(|| KernelError::new("ENOENT", format!("no such file: {path}")))?
2646 .size;
2647 let new_size = if keep_size {
2648 old_size
2649 } else {
2650 old_size.max(end)
2651 };
2652 self.check_truncate_limits_with_existing(&path, existing.as_ref(), new_size)?;
2653 self.filesystem
2654 .zero_range(&path, offset, length, keep_size)?;
2655 self.update_filesystem_usage_cache_for_write(&path, existing.as_ref(), new_size);
2656 self.clear_setid_after_write(pid, &path)
2657 }
2658
2659 pub fn fd_insert_range(
2660 &mut self,
2661 requester_driver: &str,
2662 pid: u32,
2663 fd: u32,
2664 offset: u64,
2665 length: u64,
2666 ) -> KernelResult<()> {
2667 self.assert_driver_owns(requester_driver, pid)?;
2668 let entry = {
2669 let tables = lock_or_recover(&self.fd_tables);
2670 tables
2671 .get(pid)
2672 .and_then(|table| table.get(fd))
2673 .cloned()
2674 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2675 };
2676 if entry.description.flags() & 0b11 == crate::fd_table::O_RDONLY {
2677 return Err(KernelError::bad_file_descriptor(fd));
2678 }
2679 let path = entry.description.path().to_owned();
2680 self.reject_read_only_resolved_write_path(&path)?;
2681 let existing = self.storage_stat(&path)?;
2682 let old_size = existing
2683 .as_ref()
2684 .ok_or_else(|| KernelError::new("ENOENT", format!("no such file: {path}")))?
2685 .size;
2686 let new_size = old_size
2687 .checked_add(length)
2688 .ok_or_else(|| KernelError::new("EINVAL", "insert range size overflows"))?;
2689 self.check_truncate_limits_with_existing(&path, existing.as_ref(), new_size)?;
2690 self.filesystem.insert_range(&path, offset, length)?;
2691 self.update_filesystem_usage_cache_for_write(&path, existing.as_ref(), new_size);
2692 self.clear_setid_after_write(pid, &path)
2693 }
2694
2695 pub fn fd_collapse_range(
2696 &mut self,
2697 requester_driver: &str,
2698 pid: u32,
2699 fd: u32,
2700 offset: u64,
2701 length: u64,
2702 ) -> KernelResult<()> {
2703 self.assert_driver_owns(requester_driver, pid)?;
2704 let entry = {
2705 let tables = lock_or_recover(&self.fd_tables);
2706 tables
2707 .get(pid)
2708 .and_then(|table| table.get(fd))
2709 .cloned()
2710 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2711 };
2712 if entry.description.flags() & 0b11 == crate::fd_table::O_RDONLY {
2713 return Err(KernelError::bad_file_descriptor(fd));
2714 }
2715 let path = entry.description.path().to_owned();
2716 self.reject_read_only_resolved_write_path(&path)?;
2717 let existing = self.storage_stat(&path)?;
2718 let old_size = existing
2719 .as_ref()
2720 .ok_or_else(|| KernelError::new("ENOENT", format!("no such file: {path}")))?
2721 .size;
2722 self.filesystem.collapse_range(&path, offset, length)?;
2723 let new_size = old_size.saturating_sub(length);
2724 self.update_filesystem_usage_cache_for_write(&path, existing.as_ref(), new_size);
2725 self.clear_setid_after_write(pid, &path)
2726 }
2727
2728 pub fn fd_allocated_ranges(
2729 &mut self,
2730 requester_driver: &str,
2731 pid: u32,
2732 fd: u32,
2733 ) -> KernelResult<Vec<(u64, u64)>> {
2734 self.assert_driver_owns(requester_driver, pid)?;
2735 let path = {
2736 let tables = lock_or_recover(&self.fd_tables);
2737 tables
2738 .get(pid)
2739 .and_then(|table| table.get(fd))
2740 .map(|entry| entry.description.path().to_owned())
2741 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2742 };
2743 Ok(self.filesystem.allocated_ranges(&path)?)
2744 }
2745
2746 pub fn fd_unwritten_ranges(
2747 &mut self,
2748 requester_driver: &str,
2749 pid: u32,
2750 fd: u32,
2751 ) -> KernelResult<Vec<(u64, u64)>> {
2752 self.assert_driver_owns(requester_driver, pid)?;
2753 let path = {
2754 let tables = lock_or_recover(&self.fd_tables);
2755 tables
2756 .get(pid)
2757 .and_then(|table| table.get(fd))
2758 .map(|entry| entry.description.path().to_owned())
2759 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
2760 };
2761 Ok(self.filesystem.unwritten_ranges(&path)?)
2762 }
2763
2764 pub fn check_execute_for_process(
2765 &mut self,
2766 requester_driver: &str,
2767 pid: u32,
2768 path: &str,
2769 ) -> KernelResult<()> {
2770 self.assert_driver_owns(requester_driver, pid)?;
2771 let stat = self.filesystem.stat(path)?;
2772 if stat.is_directory {
2773 return Err(KernelError::new(
2774 "EACCES",
2775 format!("permission denied, execute '{path}'"),
2776 ));
2777 }
2778 self.check_dac_access(pid, path, DAC_EXECUTE)
2779 }
2780
2781 pub fn list_processes(&self) -> BTreeMap<u32, ProcessInfo> {
2782 self.processes.list_processes()
2783 }
2784
2785 pub fn zombie_timer_count(&self) -> usize {
2786 self.processes.zombie_timer_count()
2787 }
2788
2789 pub fn reap_due_zombies(&self) {
2790 self.processes.reap_due_zombies();
2791 }
2792
2793 pub fn next_zombie_reap_deadline(&self) -> Option<std::time::Instant> {
2794 self.processes.next_zombie_reap_deadline()
2795 }
2796
2797 pub fn spawn_process(
2798 &mut self,
2799 command: &str,
2800 args: Vec<String>,
2801 options: SpawnOptions,
2802 ) -> KernelResult<KernelProcessHandle> {
2803 self.spawn_process_with_process_group(command, args, options, None)
2804 }
2805
2806 pub fn spawn_process_with_process_group(
2807 &mut self,
2808 command: &str,
2809 args: Vec<String>,
2810 options: SpawnOptions,
2811 requested_pgid: Option<u32>,
2812 ) -> KernelResult<KernelProcessHandle> {
2813 self.spawn_process_with_process_group_and_cloexec(
2814 command,
2815 args,
2816 options,
2817 requested_pgid,
2818 false,
2819 )
2820 }
2821
2822 pub fn spawn_process_with_process_group_preserving_cloexec(
2829 &mut self,
2830 command: &str,
2831 args: Vec<String>,
2832 options: SpawnOptions,
2833 requested_pgid: Option<u32>,
2834 ) -> KernelResult<KernelProcessHandle> {
2835 self.spawn_process_with_process_group_and_cloexec(
2836 command,
2837 args,
2838 options,
2839 requested_pgid,
2840 true,
2841 )
2842 }
2843
2844 fn spawn_process_with_process_group_and_cloexec(
2845 &mut self,
2846 command: &str,
2847 args: Vec<String>,
2848 options: SpawnOptions,
2849 requested_pgid: Option<u32>,
2850 preserve_cloexec: bool,
2851 ) -> KernelResult<KernelProcessHandle> {
2852 self.assert_not_terminated()?;
2853 if let (Some(requester), Some(parent_pid)) =
2854 (options.requester_driver.as_deref(), options.parent_pid)
2855 {
2856 self.assert_driver_owns(requester, parent_pid)?;
2857 }
2858
2859 let parent_context = options
2860 .parent_pid
2861 .map(|pid| self.processes.inherited_context(pid))
2862 .transpose()?;
2863 let cwd = options.cwd.clone().unwrap_or_else(|| {
2864 parent_context
2865 .as_ref()
2866 .map(|context| context.cwd.clone())
2867 .unwrap_or_else(|| self.cwd.clone())
2868 });
2869 let resolved = self.resolve_spawn_command(command, &args, &cwd, options.parent_pid)?;
2870
2871 self.resources
2872 .check_process_argv_bytes(&resolved.command, &resolved.args)?;
2873 self.resources
2874 .check_process_env_bytes(&self.env, &options.env)?;
2875
2876 let mut env = parent_context
2877 .as_ref()
2878 .map(|context| context.env.clone())
2879 .unwrap_or_else(|| self.env.clone());
2880 env.extend(options.env.clone());
2881 check_command_execution(
2882 &self.vm_id,
2883 &self.permissions,
2884 &resolved.command,
2885 &resolved.args,
2886 Some(&cwd),
2887 &env,
2888 )?;
2889
2890 let inherited_fds = {
2891 let tables = lock_or_recover(&self.fd_tables);
2892 options
2893 .parent_pid
2894 .and_then(|pid| tables.get(pid).map(ProcessFdTable::len_for_exec))
2895 .unwrap_or(3)
2896 };
2897 self.resources
2898 .check_process_spawn(&self.resource_snapshot(), inherited_fds)?;
2899 let process_umask = match options.parent_pid {
2900 Some(parent_pid) => self.processes.get_umask(parent_pid)?,
2901 None => DEFAULT_PROCESS_UMASK,
2902 };
2903
2904 let mut context = parent_context.unwrap_or_else(|| ProcessContext {
2905 identity: self.users.identity(),
2906 ..ProcessContext::default()
2907 });
2908 context.ppid = options.parent_pid.unwrap_or(0);
2909 context.env = env;
2910 context.cwd = cwd;
2911 context.umask = process_umask;
2912
2913 self.register_process(
2914 resolved.driver.name().to_owned(),
2915 resolved.command,
2916 resolved.args,
2917 context,
2918 options.requester_driver.as_deref(),
2919 requested_pgid,
2920 preserve_cloexec,
2921 )
2922 }
2923
2924 pub fn exec_process(
2929 &mut self,
2930 requester_driver: &str,
2931 pid: u32,
2932 command: &str,
2933 args: Vec<String>,
2934 env: BTreeMap<String, String>,
2935 cwd: String,
2936 ) -> KernelResult<()> {
2937 self.exec_process_retaining_internal_fds(
2938 requester_driver,
2939 pid,
2940 command,
2941 args,
2942 env,
2943 cwd,
2944 &[],
2945 &[],
2946 None,
2947 )
2948 }
2949
2950 pub fn validate_executable_path(&mut self, path: &str, cwd: &str) -> KernelResult<String> {
2955 self.assert_not_terminated()?;
2956 self.resolve_executable_path(path, cwd, None)?
2957 .ok_or_else(|| KernelError::command_not_found(path))
2958 }
2959
2960 pub fn validate_wasm_exec_image(&mut self, path: &str, cwd: &str) -> KernelResult<()> {
2967 self.assert_not_terminated()?;
2968 self.validate_wasm_exec_image_inner(path, cwd, 0)
2969 }
2970
2971 #[allow(clippy::too_many_arguments)]
2976 pub fn exec_process_retaining_internal_fds(
2977 &mut self,
2978 requester_driver: &str,
2979 pid: u32,
2980 command: &str,
2981 args: Vec<String>,
2982 env: BTreeMap<String, String>,
2983 _cwd: String,
2984 retained_internal_fds: &[u32],
2985 additional_cloexec_fds: &[u32],
2986 image_command: Option<&str>,
2987 ) -> KernelResult<()> {
2988 self.assert_not_terminated()?;
2989 self.assert_driver_owns(requester_driver, pid)?;
2990 let cwd = self
2994 .processes
2995 .get(pid)
2996 .ok_or_else(|| KernelError::no_such_process(pid))?
2997 .cwd;
2998 if let Some(image_command) = image_command {
2999 self.validate_executable_path(image_command, &cwd)?;
3004 }
3005 let resolved = self.resolve_spawn_command(command, &args, &cwd, Some(pid))?;
3006 let image_command = image_command.unwrap_or(&resolved.command);
3007 let (committed_argv0, committed_args) = resolved
3008 .args
3009 .split_first()
3010 .map(|(argv0, args)| (argv0.clone(), args.to_vec()))
3011 .unwrap_or_else(|| (String::new(), Vec::new()));
3012 self.resources
3013 .check_process_argv_bytes(&committed_argv0, &committed_args)?;
3014 self.resources
3015 .check_process_env_bytes(&BTreeMap::new(), &env)?;
3016 check_command_execution(
3017 &self.vm_id,
3018 &self.permissions,
3019 image_command,
3020 &committed_args,
3021 Some(&cwd),
3022 &env,
3023 )?;
3024
3025 let closed_entries = {
3031 let mut tables = lock_or_recover(&self.fd_tables);
3032 let table = tables
3033 .get_mut(pid)
3034 .ok_or_else(|| KernelError::no_such_process(pid))?;
3035 let mut fds = table
3036 .close_on_exec_fds()
3037 .into_iter()
3038 .collect::<BTreeSet<_>>();
3039 fds.extend(
3043 additional_cloexec_fds
3044 .iter()
3045 .copied()
3046 .filter(|fd| table.get(*fd).is_some()),
3047 );
3048
3049 self.processes.exec(
3050 pid,
3051 resolved.driver.name().to_owned(),
3052 committed_argv0,
3053 committed_args,
3054 env,
3055 cwd,
3056 )?;
3057
3058 let mut closed_entries = Vec::with_capacity(fds.len());
3059 for fd in fds {
3060 if retained_internal_fds.contains(&fd) {
3061 continue;
3062 }
3063 if let Some(entry) = table.get(fd).cloned() {
3064 let closed = table.close(fd);
3067 debug_assert!(closed);
3068 closed_entries.push((entry.description, entry.filetype));
3069 }
3070 }
3071 closed_entries
3072 };
3073 for (description, filetype) in closed_entries {
3074 if let Some(target) = description.lock_target() {
3075 self.file_locks.release_process_target(pid, target);
3076 }
3077 self.close_special_resource_if_needed(&description, filetype);
3078 }
3079 Ok(())
3080 }
3081
3082 pub fn create_virtual_process(
3083 &mut self,
3084 requester_driver: &str,
3085 driver: &str,
3086 command: &str,
3087 args: Vec<String>,
3088 options: VirtualProcessOptions,
3089 ) -> KernelResult<KernelProcessHandle> {
3090 self.create_virtual_process_with_process_group(
3091 requester_driver,
3092 driver,
3093 command,
3094 args,
3095 options,
3096 None,
3097 )
3098 }
3099
3100 pub fn create_virtual_process_with_process_group(
3101 &mut self,
3102 requester_driver: &str,
3103 driver: &str,
3104 command: &str,
3105 args: Vec<String>,
3106 options: VirtualProcessOptions,
3107 requested_pgid: Option<u32>,
3108 ) -> KernelResult<KernelProcessHandle> {
3109 self.assert_not_terminated()?;
3110 if let Some(parent_pid) = options.parent_pid {
3111 self.assert_driver_owns(requester_driver, parent_pid)?;
3112 }
3113
3114 let parent_context = options
3115 .parent_pid
3116 .map(|pid| self.processes.inherited_context(pid))
3117 .transpose()?;
3118 let cwd = options.cwd.clone().unwrap_or_else(|| {
3119 parent_context
3120 .as_ref()
3121 .map(|context| context.cwd.clone())
3122 .unwrap_or_else(|| self.cwd.clone())
3123 });
3124 self.resources.check_process_argv_bytes(command, &args)?;
3125 self.resources
3126 .check_process_env_bytes(&self.env, &options.env)?;
3127
3128 let mut env = parent_context
3129 .as_ref()
3130 .map(|context| context.env.clone())
3131 .unwrap_or_else(|| self.env.clone());
3132 env.extend(options.env.clone());
3133 check_command_execution(
3134 &self.vm_id,
3135 &self.permissions,
3136 command,
3137 &args,
3138 Some(&cwd),
3139 &env,
3140 )?;
3141
3142 let inherited_fds = {
3143 let tables = lock_or_recover(&self.fd_tables);
3144 options
3145 .parent_pid
3146 .and_then(|pid| tables.get(pid).map(ProcessFdTable::len))
3147 .unwrap_or(3)
3148 };
3149 self.resources
3150 .check_process_spawn(&self.resource_snapshot(), inherited_fds)?;
3151 let process_umask = match options.parent_pid {
3152 Some(parent_pid) => self.processes.get_umask(parent_pid)?,
3153 None => DEFAULT_PROCESS_UMASK,
3154 };
3155
3156 let mut context = parent_context.unwrap_or_else(|| ProcessContext {
3157 identity: self.users.identity(),
3158 ..ProcessContext::default()
3159 });
3160 context.ppid = options.parent_pid.unwrap_or(0);
3161 context.env = env;
3162 context.cwd = cwd;
3163 context.umask = process_umask;
3164
3165 self.register_process(
3166 String::from(driver),
3167 String::from(command),
3168 args,
3169 context,
3170 Some(requester_driver),
3171 requested_pgid,
3172 false,
3173 )
3174 }
3175
3176 pub fn read_process_stdin(
3177 &mut self,
3178 requester_driver: &str,
3179 pid: u32,
3180 length: usize,
3181 timeout: Option<Duration>,
3182 ) -> KernelResult<Option<Vec<u8>>> {
3183 self.fd_read_with_timeout_result(requester_driver, pid, 0, length, timeout)
3184 }
3185
3186 pub fn write_process_stdout(
3187 &mut self,
3188 requester_driver: &str,
3189 pid: u32,
3190 data: &[u8],
3191 ) -> KernelResult<usize> {
3192 self.fd_write(requester_driver, pid, 1, data)
3193 }
3194
3195 pub fn write_process_stderr(
3196 &mut self,
3197 requester_driver: &str,
3198 pid: u32,
3199 data: &[u8],
3200 ) -> KernelResult<usize> {
3201 self.fd_write(requester_driver, pid, 2, data)
3202 }
3203
3204 pub fn exit_process(
3205 &mut self,
3206 requester_driver: &str,
3207 pid: u32,
3208 exit_code: i32,
3209 ) -> KernelResult<()> {
3210 self.assert_driver_owns(requester_driver, pid)?;
3211 self.processes.mark_exited(pid, exit_code);
3212 Ok(())
3213 }
3214
3215 #[allow(clippy::too_many_arguments)]
3216 fn register_process(
3217 &mut self,
3218 driver_name: String,
3219 command: String,
3220 args: Vec<String>,
3221 mut ctx: ProcessContext,
3222 requester_driver: Option<&str>,
3223 requested_pgid: Option<u32>,
3224 preserve_cloexec: bool,
3225 ) -> KernelResult<KernelProcessHandle> {
3226 let pid = self.processes.allocate_pid()?;
3227 ctx.pid = pid;
3228
3229 if let (Some(requester), Some(target_pgid)) = (requester_driver, requested_pgid) {
3230 if target_pgid != 0 && target_pgid != pid {
3231 if let Some(group_owner) =
3232 self.processes
3233 .list_processes()
3234 .into_values()
3235 .find(|process| {
3236 process.pgid == target_pgid && process.status == ProcessStatus::Running
3237 })
3238 {
3239 if group_owner.driver != requester {
3240 return Err(KernelError::permission_denied(format!(
3241 "driver \"{requester}\" cannot join process group {target_pgid} owned by \"{}\"",
3242 group_owner.driver
3243 )));
3244 }
3245 }
3246 }
3247 }
3248
3249 let process = Arc::new(StubDriverProcess::default());
3250 self.processes.register_with_process_group(
3251 pid,
3252 driver_name.clone(),
3253 command,
3254 args,
3255 ctx.clone(),
3256 process.clone(),
3257 requested_pgid,
3258 )?;
3259
3260 {
3261 let mut tables = lock_or_recover(&self.fd_tables);
3262 if ctx.ppid != 0 {
3263 let parent_pid = ctx.ppid;
3264 if preserve_cloexec {
3265 tables.fork_preserving_cloexec(parent_pid, pid);
3266 } else {
3267 tables.fork(parent_pid, pid);
3268 }
3269 } else {
3270 tables.create(pid);
3271 }
3272 }
3273
3274 let mut owners = lock_or_recover(&self.driver_pids);
3275 owners.entry(driver_name.clone()).or_default().insert(pid);
3276 if let Some(requester) = requester_driver {
3277 owners
3278 .entry(String::from(requester))
3279 .or_default()
3280 .insert(pid);
3281 }
3282
3283 Ok(KernelProcessHandle {
3284 pid,
3285 driver: driver_name,
3286 process,
3287 })
3288 }
3289
3290 pub fn waitpid(&mut self, pid: u32) -> KernelResult<WaitPidResult> {
3291 let (pid, status) = self.processes.waitpid(pid)?;
3292 self.cleanup_process_resources(pid);
3293 Ok(WaitPidResult { pid, status })
3294 }
3295
3296 pub fn waitpid_with_options(
3297 &mut self,
3298 requester_driver: &str,
3299 waiter_pid: u32,
3300 pid: i32,
3301 flags: WaitPidFlags,
3302 ) -> KernelResult<Option<WaitPidEventResult>> {
3303 self.assert_driver_owns(requester_driver, waiter_pid)?;
3304 let result = self.processes.waitpid_for(waiter_pid, pid, flags)?;
3305 Ok(result.map(|result| self.finish_waitpid_event(result)))
3306 }
3307
3308 pub fn take_nonterminal_wait_event(
3309 &self,
3310 requester_driver: &str,
3311 waiter_pid: u32,
3312 pid: i32,
3313 flags: WaitPidFlags,
3314 ) -> KernelResult<Option<WaitPidEventResult>> {
3315 self.assert_driver_owns(requester_driver, waiter_pid)?;
3316 let result = self
3317 .processes
3318 .take_nonterminal_wait_event_for(waiter_pid, pid, flags)?;
3319 Ok(result.map(|result| WaitPidEventResult {
3320 pid: result.pid,
3321 status: result.status,
3322 event: result.event,
3323 }))
3324 }
3325
3326 pub fn wait_and_reap(&mut self, pid: u32) -> KernelResult<(u32, i32)> {
3327 let result = self.waitpid(pid)?;
3328 Ok((result.pid, result.status))
3329 }
3330
3331 pub fn open_pipe(&mut self, requester_driver: &str, pid: u32) -> KernelResult<(u32, u32)> {
3332 self.assert_not_terminated()?;
3333 self.assert_driver_owns(requester_driver, pid)?;
3334 let identity = self.process_identity(requester_driver, pid)?;
3335 self.resources
3336 .check_pipe_allocation(&self.resource_snapshot())?;
3337 let (read_fd, write_fd, read_description_id) = {
3338 let mut tables = lock_or_recover(&self.fd_tables);
3339 let table = tables
3340 .get_mut(pid)
3341 .ok_or_else(|| KernelError::no_such_process(pid))?;
3342 let (read_fd, write_fd) = self.pipes.create_pipe_fds(table)?;
3343 let read_description_id = table
3344 .get(read_fd)
3345 .expect("new pipe read descriptor must exist")
3346 .description
3347 .id();
3348 (read_fd, write_fd, read_description_id)
3349 };
3350 self.pipes
3351 .set_owner(read_description_id, identity.euid, identity.egid)?;
3352 Ok((read_fd, write_fd))
3353 }
3354
3355 pub fn fd_pipe_has_reader_in_other_process(
3356 &self,
3357 requester_driver: &str,
3358 pid: u32,
3359 fd: u32,
3360 ) -> KernelResult<bool> {
3361 self.assert_driver_owns(requester_driver, pid)?;
3362 let tables = lock_or_recover(&self.fd_tables);
3363 let write_description_id = tables
3364 .get(pid)
3365 .and_then(|table| table.get(fd))
3366 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
3367 .description
3368 .id();
3369 Ok(tables.pids().into_iter().any(|other_pid| {
3370 other_pid != pid
3371 && tables.get(other_pid).is_some_and(|table| {
3372 table.values().any(|entry| {
3373 self.pipes
3374 .is_write_to_read_pair(write_description_id, entry.description.id())
3375 })
3376 })
3377 }))
3378 }
3379
3380 pub fn fd_snapshot(
3381 &self,
3382 requester_driver: &str,
3383 pid: u32,
3384 ) -> KernelResult<Vec<ProcessFdSnapshotEntry>> {
3385 self.assert_driver_owns(requester_driver, pid)?;
3386 let tables = lock_or_recover(&self.fd_tables);
3387 let table = tables
3388 .get(pid)
3389 .ok_or_else(|| KernelError::no_such_process(pid))?;
3390 Ok(table
3391 .values()
3392 .map(|entry| ProcessFdSnapshotEntry {
3393 fd: entry.fd,
3394 fd_flags: entry.fd_flags,
3395 status_flags: entry.status_flags | entry.description.flags(),
3396 filetype: entry.filetype,
3397 is_socket: self.fd_socket_id(&entry.description).is_some(),
3398 is_pipe: self.pipes.is_pipe(entry.description.id()),
3399 is_pty: self.ptys.is_pty(entry.description.id()),
3400 })
3401 .collect())
3402 }
3403
3404 pub fn fd_socketpair(
3409 &mut self,
3410 requester_driver: &str,
3411 pid: u32,
3412 socket_type: SocketType,
3413 nonblocking: bool,
3414 close_on_exec: bool,
3415 ) -> KernelResult<(u32, u32)> {
3416 self.assert_not_terminated()?;
3417 self.assert_driver_owns(requester_driver, pid)?;
3418 let identity = self.process_identity(requester_driver, pid)?;
3419 let spec = match socket_type {
3420 SocketType::Stream => SocketSpec::unix_stream(),
3421 SocketType::Datagram => SocketSpec::unix_datagram(),
3422 SocketType::SeqPacket => SocketSpec::unix_seqpacket(),
3423 };
3424
3425 let mut snapshot = self.resource_snapshot();
3426 self.resources.check_fd_allocation(&snapshot, 2)?;
3427 for _ in 0..2 {
3428 self.resources.check_socket_allocation(&snapshot)?;
3429 snapshot.sockets = snapshot.sockets.saturating_add(1);
3430 self.resources.check_socket_state_transition(
3431 &snapshot,
3432 SocketState::Created,
3433 SocketState::Connected,
3434 )?;
3435 snapshot.socket_connections = snapshot.socket_connections.saturating_add(1);
3436 }
3437
3438 let status_flags = if nonblocking { O_NONBLOCK } else { 0 };
3439 let fd_flags = if close_on_exec { FD_CLOEXEC } else { 0 };
3440 let filetype = if socket_type == SocketType::Datagram {
3441 FILETYPE_SOCKET_DGRAM
3442 } else {
3443 FILETYPE_SOCKET_STREAM
3444 };
3445 let (first_fd, second_fd, first_description, second_description) = {
3446 let mut tables = lock_or_recover(&self.fd_tables);
3447 let table = tables
3448 .get_mut(pid)
3449 .ok_or_else(|| KernelError::no_such_process(pid))?;
3450 table.open_pair_with_details(
3451 "socketpair:first",
3452 "socketpair:second",
3453 status_flags,
3454 fd_flags,
3455 filetype,
3456 )?
3457 };
3458
3459 let first_socket = match self.sockets.allocate(0, spec) {
3463 Ok(socket) => socket.id(),
3464 Err(error) => {
3465 let mut tables = lock_or_recover(&self.fd_tables);
3466 if let Some(table) = tables.get_mut(pid) {
3467 table.close(first_fd);
3468 table.close(second_fd);
3469 }
3470 return Err(error.into());
3471 }
3472 };
3473 let second_socket = match self.sockets.allocate(0, spec) {
3474 Ok(socket) => socket.id(),
3475 Err(error) => {
3476 if let Err(cleanup_error) = self.sockets.remove(first_socket) {
3477 eprintln!(
3478 "[agentos] failed to roll back first socketpair socket {first_socket}: {cleanup_error}"
3479 );
3480 }
3481 let mut tables = lock_or_recover(&self.fd_tables);
3482 if let Some(table) = tables.get_mut(pid) {
3483 table.close(first_fd);
3484 table.close(second_fd);
3485 }
3486 return Err(error.into());
3487 }
3488 };
3489 if let Err(error) = self.sockets.connect_pair(first_socket, second_socket) {
3490 for socket_id in [first_socket, second_socket] {
3491 if let Err(cleanup_error) = self.sockets.remove(socket_id) {
3492 eprintln!(
3493 "[agentos] failed to roll back socketpair socket {socket_id}: {cleanup_error}"
3494 );
3495 }
3496 }
3497 let mut tables = lock_or_recover(&self.fd_tables);
3498 if let Some(table) = tables.get_mut(pid) {
3499 table.close(first_fd);
3500 table.close(second_fd);
3501 }
3502 return Err(error.into());
3503 }
3504
3505 {
3506 let mut registry = lock_or_recover(&self.fd_sockets);
3507 registry.insert(
3508 first_description.id(),
3509 FdSocketEntry {
3510 description: first_description,
3511 socket_id: first_socket,
3512 mode: 0o777,
3513 uid: identity.euid,
3514 gid: identity.egid,
3515 },
3516 );
3517 registry.insert(
3518 second_description.id(),
3519 FdSocketEntry {
3520 description: second_description,
3521 socket_id: second_socket,
3522 mode: 0o777,
3523 uid: identity.euid,
3524 gid: identity.egid,
3525 },
3526 );
3527 }
3528 self.poll_notifier.notify();
3529 Ok((first_fd, second_fd))
3530 }
3531
3532 pub fn fd_adopt_socket(
3537 &mut self,
3538 requester_driver: &str,
3539 pid: u32,
3540 socket_id: SocketId,
3541 status_flags: u32,
3542 ) -> KernelResult<u32> {
3543 self.assert_not_terminated()?;
3544 self.assert_driver_owns(requester_driver, pid)?;
3545 let identity = self.process_identity(requester_driver, pid)?;
3546 let socket = self
3547 .sockets
3548 .get(socket_id)
3549 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
3550 if socket.owner_pid() != pid && socket.owner_pid() != 0 {
3551 return Err(KernelError::permission_denied(format!(
3552 "process {pid} does not own socket {socket_id}"
3553 )));
3554 }
3555 self.resources
3556 .check_fd_allocation(&self.resource_snapshot(), 1)?;
3557 let filetype = if socket.spec().socket_type == SocketType::Datagram {
3558 FILETYPE_SOCKET_DGRAM
3559 } else {
3560 FILETYPE_SOCKET_STREAM
3561 };
3562 let (fd, description) = {
3563 let mut tables = lock_or_recover(&self.fd_tables);
3564 let table = tables
3565 .get_mut(pid)
3566 .ok_or_else(|| KernelError::no_such_process(pid))?;
3567 let fd = table.open_with_details(
3568 &format!("socket:{socket_id}"),
3569 status_flags,
3570 filetype,
3571 None,
3572 )?;
3573 let description = Arc::clone(
3574 &table
3575 .get(fd)
3576 .expect("newly adopted socket fd must exist")
3577 .description,
3578 );
3579 (fd, description)
3580 };
3581 self.sockets.reassign_owner(socket_id, 0)?;
3582 lock_or_recover(&self.fd_sockets).insert(
3583 description.id(),
3584 FdSocketEntry {
3585 description,
3586 socket_id,
3587 mode: 0o777,
3588 uid: identity.euid,
3589 gid: identity.egid,
3590 },
3591 );
3592 Ok(fd)
3593 }
3594
3595 pub fn fd_adopt_socket_transfer(
3600 &mut self,
3601 requester_driver: &str,
3602 pid: u32,
3603 socket_id: SocketId,
3604 status_flags: u32,
3605 ) -> KernelResult<TransferredFd> {
3606 self.assert_not_terminated()?;
3607 self.assert_driver_owns(requester_driver, pid)?;
3608 let identity = self.process_identity(requester_driver, pid)?;
3609 let socket = self
3610 .sockets
3611 .get(socket_id)
3612 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
3613 if socket.owner_pid() != pid && socket.owner_pid() != 0 {
3614 return Err(KernelError::permission_denied(format!(
3615 "process {pid} does not own socket {socket_id}"
3616 )));
3617 }
3618 let filetype = if socket.spec().socket_type == SocketType::Datagram {
3619 FILETYPE_SOCKET_DGRAM
3620 } else {
3621 FILETYPE_SOCKET_STREAM
3622 };
3623 let transfer = {
3624 let tables = lock_or_recover(&self.fd_tables);
3625 let table = tables
3626 .get(pid)
3627 .ok_or_else(|| KernelError::no_such_process(pid))?;
3628 table.create_transfer(&format!("socket:{socket_id}"), status_flags, filetype)
3629 };
3630 self.sockets.reassign_owner(socket_id, 0)?;
3631 lock_or_recover(&self.fd_sockets).insert(
3632 transfer.description_id(),
3633 FdSocketEntry {
3634 description: transfer.description(),
3635 socket_id,
3636 mode: 0o777,
3637 uid: identity.euid,
3638 gid: identity.egid,
3639 },
3640 );
3641 Ok(transfer)
3642 }
3643
3644 pub fn fd_transfer(
3645 &self,
3646 requester_driver: &str,
3647 pid: u32,
3648 fd: u32,
3649 ) -> KernelResult<TransferredFd> {
3650 self.assert_driver_owns(requester_driver, pid)?;
3651 let tables = lock_or_recover(&self.fd_tables);
3652 let table = tables
3653 .get(pid)
3654 .ok_or_else(|| KernelError::no_such_process(pid))?;
3655 Ok(table.transfer(fd)?)
3656 }
3657
3658 pub fn fd_install_transfer_at(
3663 &mut self,
3664 requester_driver: &str,
3665 pid: u32,
3666 fd: u32,
3667 fd_flags: u32,
3668 transfer: &TransferredFd,
3669 ) -> KernelResult<()> {
3670 self.assert_not_terminated()?;
3671 self.assert_driver_owns(requester_driver, pid)?;
3672 let replaced = {
3673 let mut tables = lock_or_recover(&self.fd_tables);
3674 let table = tables
3675 .get_mut(pid)
3676 .ok_or_else(|| KernelError::no_such_process(pid))?;
3677 let replaced = table
3678 .get(fd)
3679 .map(|entry| (Arc::clone(&entry.description), entry.filetype));
3680 table.install_transferred_at(transfer, fd, fd_flags)?;
3681 replaced
3682 };
3683 if let Some((description, filetype)) = replaced {
3684 self.close_special_resource_if_needed(&description, filetype);
3685 }
3686 Ok(())
3687 }
3688
3689 pub fn fd_socket_sendmsg(
3690 &mut self,
3691 requester_driver: &str,
3692 pid: u32,
3693 socket_fd: u32,
3694 data: &[u8],
3695 rights_fds: &[u32],
3696 ) -> KernelResult<usize> {
3697 let rights = rights_fds
3698 .iter()
3699 .copied()
3700 .map(FdTransferRequest::Fd)
3701 .collect::<Vec<_>>();
3702 self.fd_socket_sendmsg_transfers(requester_driver, pid, socket_fd, data, &rights)
3703 }
3704
3705 pub fn fd_socket_sendmsg_transfers(
3706 &mut self,
3707 requester_driver: &str,
3708 pid: u32,
3709 socket_fd: u32,
3710 data: &[u8],
3711 transfer_requests: &[FdTransferRequest],
3712 ) -> KernelResult<usize> {
3713 self.assert_not_terminated()?;
3714 self.assert_driver_owns(requester_driver, pid)?;
3715 let (socket_id, rights) = {
3716 let tables = lock_or_recover(&self.fd_tables);
3717 let table = tables
3718 .get(pid)
3719 .ok_or_else(|| KernelError::no_such_process(pid))?;
3720 let socket_entry = table
3721 .get(socket_fd)
3722 .ok_or_else(|| KernelError::bad_file_descriptor(socket_fd))?;
3723 let socket_id = self
3724 .fd_socket_id(&socket_entry.description)
3725 .ok_or_else(|| KernelError::new("ENOTSOCK", "descriptor is not a socket"))?;
3726 let rights = transfer_requests
3727 .iter()
3728 .map(|request| match request {
3729 FdTransferRequest::Fd(fd) => table
3730 .transfer(*fd)
3731 .map(TransferredSocketRight::Fd)
3732 .map_err(KernelError::from),
3733 FdTransferRequest::Opaque(resource) => {
3734 Ok(TransferredSocketRight::Opaque(Arc::clone(resource)))
3735 }
3736 })
3737 .collect::<KernelResult<Vec<_>>>()?;
3738 (socket_id, rights)
3739 };
3740
3741 let socket = self
3742 .sockets
3743 .get(socket_id)
3744 .ok_or_else(|| KernelError::bad_file_descriptor(socket_fd))?;
3745 self.sockets.check_write(socket_id)?;
3746 let snapshot = self.resource_snapshot();
3747 if socket.spec().socket_type == SocketType::Stream {
3748 self.resources
3749 .check_socket_buffer_growth(&snapshot, data.len())?;
3750 } else {
3751 self.resources
3752 .check_socket_datagram_enqueue(&snapshot, data.len())?;
3753 }
3754 let written = self.sockets.send_message(socket_id, data, rights)?;
3755 if written > 0 || socket.spec().socket_type != SocketType::Stream {
3756 self.poll_notifier.notify();
3757 }
3758 Ok(written)
3759 }
3760
3761 #[allow(clippy::too_many_arguments)]
3764 pub fn fd_socket_recvmsg(
3765 &mut self,
3766 requester_driver: &str,
3767 pid: u32,
3768 socket_fd: u32,
3769 max_bytes: usize,
3770 max_rights: usize,
3771 close_on_exec: bool,
3772 peek: bool,
3773 dontwait: bool,
3774 waitall: bool,
3775 ) -> KernelResult<Option<ReceivedFdMessage>> {
3776 self.assert_not_terminated()?;
3777 self.assert_driver_owns(requester_driver, pid)?;
3778 let (socket_id, available_fds, nonblocking, socket_type) = {
3779 let tables = lock_or_recover(&self.fd_tables);
3780 let table = tables
3781 .get(pid)
3782 .ok_or_else(|| KernelError::no_such_process(pid))?;
3783 let socket_entry = table
3784 .get(socket_fd)
3785 .ok_or_else(|| KernelError::bad_file_descriptor(socket_fd))?;
3786 let socket_id = self
3787 .fd_socket_id(&socket_entry.description)
3788 .ok_or_else(|| KernelError::new("ENOTSOCK", "descriptor is not a socket"))?;
3789 (
3790 socket_id,
3791 table.available_fd_capacity(),
3792 (socket_entry.description.flags() | socket_entry.status_flags) & O_NONBLOCK != 0,
3793 self.sockets
3794 .get(socket_id)
3795 .ok_or_else(|| KernelError::bad_file_descriptor(socket_fd))?
3796 .spec()
3797 .socket_type,
3798 )
3799 };
3800
3801 let deadline = (!nonblocking && !dontwait)
3802 .then(|| {
3803 self.blocking_read_timeout()
3804 .map(|wait| Instant::now() + wait)
3805 })
3806 .flatten();
3807 let mut message = loop {
3808 let generation = self.poll_notifier.snapshot();
3809 let wait_for_full = waitall
3810 && socket_type == SocketType::Stream
3811 && !nonblocking
3812 && !dontwait
3813 && max_bytes > 0;
3814 match self
3815 .sockets
3816 .recv_message(socket_id, max_bytes, peek || wait_for_full)
3817 {
3818 Ok(Some(message)) => {
3819 let peer_closed = self
3820 .sockets
3821 .poll(socket_id, POLLHUP)
3822 .map(|events| events.intersects(POLLHUP))
3823 .unwrap_or(false);
3824 if wait_for_full && message.full_length < max_bytes && !peer_closed {
3825 drop(message);
3826 } else if wait_for_full && !peek {
3827 break self.sockets.recv_message(socket_id, max_bytes, false)?;
3828 } else {
3829 break Some(message);
3830 }
3831 }
3832 Ok(None) => break None,
3833 Err(error) if error.code() == "EAGAIN" && !nonblocking && !dontwait => {
3834 let remaining =
3835 deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
3836 if matches!(remaining, Some(duration) if duration.is_zero())
3837 || !self.poll_notifier.wait_for_change(generation, remaining)
3838 {
3839 return Err(KernelError::new(
3840 "EAGAIN",
3841 "blocking socket receive timed out; raise limits.resources.maxBlockingReadMs",
3842 ));
3843 }
3844 }
3845 Err(error) => return Err(error.into()),
3846 }
3847 };
3848 let Some(mut message) = message.take() else {
3849 return Ok(None);
3850 };
3851 let mut kernel_fd_count = 0usize;
3852 let mut install_count = 0usize;
3853 for right in message.rights.iter().take(max_rights) {
3854 if matches!(right, TransferredSocketRight::Fd(_)) {
3855 if kernel_fd_count >= available_fds {
3856 break;
3857 }
3858 kernel_fd_count += 1;
3859 }
3860 install_count += 1;
3861 }
3862 let discarded = message.rights.split_off(install_count);
3863 let control_truncated = !discarded.is_empty();
3864 let mut fd_transfers = Vec::with_capacity(kernel_fd_count);
3865 for right in &message.rights {
3866 if let TransferredSocketRight::Fd(fd) = right {
3867 fd_transfers.push(fd.clone());
3868 }
3869 }
3870 let installed_fds = if fd_transfers.is_empty() {
3871 Vec::new()
3872 } else {
3873 let mut tables = lock_or_recover(&self.fd_tables);
3874 let table = tables
3875 .get_mut(pid)
3876 .ok_or_else(|| KernelError::no_such_process(pid))?;
3877 table.install_transferred(&fd_transfers, close_on_exec)?
3878 };
3879 let mut installed_fds = installed_fds.into_iter();
3880 let rights = message
3881 .rights
3882 .drain(..)
3883 .map(|right| match right {
3884 TransferredSocketRight::Fd(_) => ReceivedFdRight::Fd(
3885 installed_fds
3886 .next()
3887 .expect("every retained fd transfer must be installed"),
3888 ),
3889 TransferredSocketRight::Opaque(resource) => ReceivedFdRight::Opaque(resource),
3890 })
3891 .collect();
3892 debug_assert!(installed_fds.next().is_none());
3893 drop(discarded);
3894 drop(fd_transfers);
3895 prune_fd_sockets(&self.sockets, &self.fd_sockets);
3896 self.poll_notifier.notify();
3897 Ok(Some(ReceivedFdMessage {
3898 payload: message.payload,
3899 rights,
3900 payload_truncated: message.truncated,
3901 control_truncated,
3902 full_length: message.full_length,
3903 }))
3904 }
3905
3906 pub fn fd_socket_shutdown(
3907 &mut self,
3908 requester_driver: &str,
3909 pid: u32,
3910 socket_fd: u32,
3911 how: SocketShutdown,
3912 ) -> KernelResult<()> {
3913 self.assert_not_terminated()?;
3914 self.assert_driver_owns(requester_driver, pid)?;
3915 let socket_id = self.fd_socket_id_for_fd(pid, socket_fd)?;
3916 self.sockets.shutdown(socket_id, how)?;
3917 prune_fd_sockets(&self.sockets, &self.fd_sockets);
3918 self.poll_notifier.notify();
3919 Ok(())
3920 }
3921
3922 pub fn open_pty(
3923 &mut self,
3924 requester_driver: &str,
3925 pid: u32,
3926 ) -> KernelResult<(u32, u32, String)> {
3927 self.assert_not_terminated()?;
3928 self.assert_driver_owns(requester_driver, pid)?;
3929 self.resources
3930 .check_pty_allocation(&self.resource_snapshot())?;
3931 let mut tables = lock_or_recover(&self.fd_tables);
3932 let table = tables
3933 .get_mut(pid)
3934 .ok_or_else(|| KernelError::no_such_process(pid))?;
3935 Ok(self.ptys.create_pty_fds(table)?)
3936 }
3937
3938 pub fn socket_create(
3939 &mut self,
3940 requester_driver: &str,
3941 pid: u32,
3942 spec: SocketSpec,
3943 ) -> KernelResult<SocketId> {
3944 self.assert_not_terminated()?;
3945 self.assert_driver_owns(requester_driver, pid)?;
3946 if !self.sockets.has_resource_ledger() {
3951 self.resources
3952 .check_socket_allocation(&self.resource_snapshot())?;
3953 }
3954 Ok(self.sockets.allocate(pid, spec)?.id())
3955 }
3956
3957 #[cfg(not(target_arch = "wasm32"))]
3958 pub fn set_socket_resource_ledger(
3959 &mut self,
3960 resources: Arc<agentos_runtime::accounting::ResourceLedger>,
3961 ) -> KernelResult<()> {
3962 self.sockets.set_resource_ledger(resources)?;
3963 Ok(())
3964 }
3965
3966 pub fn set_socket_readiness_sink<S>(&mut self, sink: Option<S>)
3967 where
3968 S: Fn(SocketReadiness) + Send + Sync + 'static,
3969 {
3970 self.sockets.set_readiness_sink(sink);
3971 }
3972
3973 pub fn socket_get(&self, socket_id: SocketId) -> Option<SocketRecord> {
3974 self.sockets.get(socket_id)
3975 }
3976
3977 pub fn socket_records_for_pid(&self, pid: u32) -> Vec<SocketRecord> {
3978 self.sockets.records_for_owner(pid)
3979 }
3980
3981 pub fn socket_bind_inet(
3982 &mut self,
3983 requester_driver: &str,
3984 pid: u32,
3985 socket_id: SocketId,
3986 address: InetSocketAddress,
3987 ) -> KernelResult<()> {
3988 self.assert_not_terminated()?;
3989 self.assert_driver_owns(requester_driver, pid)?;
3990 let existing = self
3991 .sockets
3992 .get(socket_id)
3993 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
3994 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
3995 return Err(KernelError::permission_denied(format!(
3996 "process {pid} does not own socket {socket_id}"
3997 )));
3998 }
3999 check_network_access(
4000 &self.vm_id,
4001 &self.permissions,
4002 NetworkOperation::Listen,
4003 &format_tcp_resource(address.host(), address.port()),
4004 )?;
4005
4006 self.sockets.bind_inet(socket_id, address)?;
4007 self.poll_notifier.notify();
4008 Ok(())
4009 }
4010
4011 pub fn socket_bind_unix(
4012 &mut self,
4013 requester_driver: &str,
4014 pid: u32,
4015 socket_id: SocketId,
4016 path: impl Into<String>,
4017 ) -> KernelResult<()> {
4018 self.assert_not_terminated()?;
4019 self.assert_driver_owns(requester_driver, pid)?;
4020 let existing = self
4021 .sockets
4022 .get(socket_id)
4023 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4024 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4025 return Err(KernelError::permission_denied(format!(
4026 "process {pid} does not own socket {socket_id}"
4027 )));
4028 }
4029
4030 self.sockets
4031 .bind_unix(socket_id, normalize_path(&path.into()))?;
4032 self.poll_notifier.notify();
4033 Ok(())
4034 }
4035
4036 pub fn socket_listen(
4037 &mut self,
4038 requester_driver: &str,
4039 pid: u32,
4040 socket_id: SocketId,
4041 backlog: usize,
4042 ) -> KernelResult<()> {
4043 self.assert_not_terminated()?;
4044 self.assert_driver_owns(requester_driver, pid)?;
4045 let existing = self
4046 .sockets
4047 .get(socket_id)
4048 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4049 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4050 return Err(KernelError::permission_denied(format!(
4051 "process {pid} does not own socket {socket_id}"
4052 )));
4053 }
4054 if let Some(address) = existing.local_address() {
4055 check_network_access(
4056 &self.vm_id,
4057 &self.permissions,
4058 NetworkOperation::Listen,
4059 &format_tcp_resource(address.host(), address.port()),
4060 )?;
4061 }
4062
4063 self.sockets.listen(socket_id, backlog)?;
4064 self.poll_notifier.notify();
4065 Ok(())
4066 }
4067
4068 pub fn socket_queue_incoming_tcp_connection(
4069 &mut self,
4070 requester_driver: &str,
4071 pid: u32,
4072 listener_socket_id: SocketId,
4073 peer_address: InetSocketAddress,
4074 ) -> KernelResult<()> {
4075 self.assert_not_terminated()?;
4076 self.assert_driver_owns(requester_driver, pid)?;
4077 let existing = self.sockets.get(listener_socket_id).ok_or_else(|| {
4078 KernelError::new("ENOENT", format!("no such socket {listener_socket_id}"))
4079 })?;
4080 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4081 return Err(KernelError::permission_denied(format!(
4082 "process {pid} does not own socket {listener_socket_id}"
4083 )));
4084 }
4085
4086 self.sockets
4087 .enqueue_incoming_tcp_connection(listener_socket_id, peer_address)?;
4088 self.poll_notifier.notify();
4089 Ok(())
4090 }
4091
4092 pub fn socket_accept(
4093 &mut self,
4094 requester_driver: &str,
4095 pid: u32,
4096 listener_socket_id: SocketId,
4097 ) -> KernelResult<SocketId> {
4098 self.assert_not_terminated()?;
4099 self.assert_driver_owns(requester_driver, pid)?;
4100 let existing = self.sockets.get(listener_socket_id).ok_or_else(|| {
4101 KernelError::new("ENOENT", format!("no such socket {listener_socket_id}"))
4102 })?;
4103 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4104 return Err(KernelError::permission_denied(format!(
4105 "process {pid} does not own socket {listener_socket_id}"
4106 )));
4107 }
4108
4109 if !self.sockets.has_resource_ledger() {
4110 let snapshot = self.resource_snapshot();
4111 self.resources.check_socket_allocation(&snapshot)?;
4112 self.resources.check_socket_state_transition(
4113 &snapshot,
4114 SocketState::Created,
4115 SocketState::Connected,
4116 )?;
4117 }
4118
4119 let socket_id = self.sockets.accept(listener_socket_id)?.id();
4120 self.poll_notifier.notify();
4121 Ok(socket_id)
4122 }
4123
4124 pub fn socket_connect_pair(
4125 &mut self,
4126 requester_driver: &str,
4127 pid: u32,
4128 socket_id: SocketId,
4129 peer_socket_id: SocketId,
4130 ) -> KernelResult<()> {
4131 self.assert_not_terminated()?;
4132 self.assert_driver_owns(requester_driver, pid)?;
4133 let existing = self
4134 .sockets
4135 .get(socket_id)
4136 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4137 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4138 return Err(KernelError::permission_denied(format!(
4139 "process {pid} does not own socket {socket_id}"
4140 )));
4141 }
4142
4143 let peer = self.sockets.get(peer_socket_id).ok_or_else(|| {
4144 KernelError::new("ENOENT", format!("no such socket {peer_socket_id}"))
4145 })?;
4146 self.assert_driver_owns(requester_driver, peer.owner_pid())?;
4147
4148 if !self.sockets.has_resource_ledger() {
4149 let mut snapshot = self.resource_snapshot();
4150 for current_state in [existing.state(), peer.state()] {
4151 self.resources.check_socket_state_transition(
4152 &snapshot,
4153 current_state,
4154 SocketState::Connected,
4155 )?;
4156 if !current_state.counts_as_connection() {
4157 snapshot.socket_connections = snapshot.socket_connections.saturating_add(1);
4158 }
4159 }
4160 }
4161
4162 self.sockets.connect_pair(socket_id, peer_socket_id)?;
4163 self.poll_notifier.notify();
4164 Ok(())
4165 }
4166
4167 pub fn socket_connect_unix(
4168 &mut self,
4169 requester_driver: &str,
4170 pid: u32,
4171 socket_id: SocketId,
4172 target_path: impl Into<String>,
4173 ) -> KernelResult<()> {
4174 self.assert_not_terminated()?;
4175 self.assert_driver_owns(requester_driver, pid)?;
4176 let existing = self
4177 .sockets
4178 .get(socket_id)
4179 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4180 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4181 return Err(KernelError::permission_denied(format!(
4182 "process {pid} does not own socket {socket_id}"
4183 )));
4184 }
4185
4186 let target_path = normalize_path(&target_path.into());
4187 self.sockets
4188 .find_bound_unix_socket(&target_path)
4189 .ok_or_else(|| {
4190 KernelError::new(
4191 "ECONNREFUSED",
4192 format!("no listening socket bound at path {target_path}"),
4193 )
4194 })?;
4195
4196 if !self.sockets.has_resource_ledger() {
4197 let mut snapshot = self.resource_snapshot();
4198 self.resources.check_socket_allocation(&snapshot)?;
4199 for current_state in [existing.state(), SocketState::Created] {
4200 self.resources.check_socket_state_transition(
4201 &snapshot,
4202 current_state,
4203 SocketState::Connected,
4204 )?;
4205 if !current_state.counts_as_connection() {
4206 snapshot.socket_connections = snapshot.socket_connections.saturating_add(1);
4207 }
4208 }
4209 }
4210
4211 self.sockets
4212 .connect_to_bound_unix_stream(socket_id, target_path)?;
4213 self.poll_notifier.notify();
4214 Ok(())
4215 }
4216
4217 pub fn socket_connect_inet_loopback(
4218 &mut self,
4219 requester_driver: &str,
4220 pid: u32,
4221 socket_id: SocketId,
4222 target_address: InetSocketAddress,
4223 ) -> KernelResult<()> {
4224 self.assert_not_terminated()?;
4225 self.assert_driver_owns(requester_driver, pid)?;
4226 let existing = self
4227 .sockets
4228 .get(socket_id)
4229 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4230 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4231 return Err(KernelError::permission_denied(format!(
4232 "process {pid} does not own socket {socket_id}"
4233 )));
4234 }
4235 check_network_access(
4236 &self.vm_id,
4237 &self.permissions,
4238 NetworkOperation::Http,
4239 &format_tcp_resource(target_address.host(), target_address.port()),
4240 )?;
4241 self.check_loopback_port_allowed(
4242 SocketSpec::tcp(),
4243 &target_address,
4244 "TCP loopback connect",
4245 )?;
4246
4247 self.sockets
4248 .find_bound_inet_socket(SocketSpec::tcp(), &target_address)
4249 .ok_or_else(|| {
4250 KernelError::new(
4251 "ECONNREFUSED",
4252 format!(
4253 "no listening socket bound at {}:{}",
4254 target_address.host(),
4255 target_address.port()
4256 ),
4257 )
4258 })?;
4259
4260 if !self.sockets.has_resource_ledger() {
4261 let mut snapshot = self.resource_snapshot();
4262 self.resources.check_socket_allocation(&snapshot)?;
4263 for current_state in [existing.state(), SocketState::Created] {
4264 self.resources.check_socket_state_transition(
4265 &snapshot,
4266 current_state,
4267 SocketState::Connected,
4268 )?;
4269 if !current_state.counts_as_connection() {
4270 snapshot.socket_connections = snapshot.socket_connections.saturating_add(1);
4271 }
4272 }
4273 }
4274
4275 self.sockets
4276 .connect_to_bound_inet_stream(socket_id, target_address)?;
4277 self.poll_notifier.notify();
4278 Ok(())
4279 }
4280
4281 pub fn socket_send_to_inet_loopback(
4282 &mut self,
4283 requester_driver: &str,
4284 pid: u32,
4285 socket_id: SocketId,
4286 target_address: InetSocketAddress,
4287 data: &[u8],
4288 ) -> KernelResult<usize> {
4289 self.assert_not_terminated()?;
4290 self.assert_driver_owns(requester_driver, pid)?;
4291 let existing = self
4292 .sockets
4293 .get(socket_id)
4294 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4295 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4296 return Err(KernelError::permission_denied(format!(
4297 "process {pid} does not own socket {socket_id}"
4298 )));
4299 }
4300 if existing.spec() != SocketSpec::udp()
4301 || existing.state() != SocketState::Bound
4302 || existing.local_address().is_none()
4303 {
4304 self.sockets
4305 .check_send_to_bound_udp_socket(socket_id, target_address.clone())?;
4306 }
4307 check_network_access(
4308 &self.vm_id,
4309 &self.permissions,
4310 NetworkOperation::Http,
4311 &format_tcp_resource(target_address.host(), target_address.port()),
4312 )?;
4313 self.check_loopback_port_allowed(SocketSpec::udp(), &target_address, "UDP loopback send")?;
4314
4315 self.sockets
4316 .check_send_to_bound_udp_socket(socket_id, target_address.clone())?;
4317 if !self.sockets.has_resource_ledger() {
4318 self.resources
4319 .check_socket_datagram_enqueue(&self.resource_snapshot(), data.len())?;
4320 }
4321 let written = self
4322 .sockets
4323 .send_to_bound_udp_socket(socket_id, target_address, data)?;
4324 if written > 0 {
4325 self.poll_notifier.notify();
4326 }
4327 Ok(written)
4328 }
4329
4330 pub fn socket_connect_udp_loopback(
4331 &mut self,
4332 requester_driver: &str,
4333 pid: u32,
4334 socket_id: SocketId,
4335 target_address: InetSocketAddress,
4336 ) -> KernelResult<()> {
4337 self.assert_not_terminated()?;
4338 self.assert_driver_owns(requester_driver, pid)?;
4339 let existing = self
4340 .sockets
4341 .get(socket_id)
4342 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4343 if existing.owner_pid() != pid {
4344 return Err(KernelError::permission_denied(format!(
4345 "process {pid} does not own socket {socket_id}"
4346 )));
4347 }
4348 if existing.spec().socket_type != SocketType::Datagram
4349 || existing.state() != SocketState::Bound
4350 || existing.local_address().is_none()
4351 {
4352 return Err(KernelError::new(
4353 "EINVAL",
4354 format!("UDP socket {socket_id} must be bound before connect"),
4355 ));
4356 }
4357 check_network_access(
4358 &self.vm_id,
4359 &self.permissions,
4360 NetworkOperation::Http,
4361 &format_tcp_resource(target_address.host(), target_address.port()),
4362 )?;
4363 self.check_loopback_port_allowed(existing.spec(), &target_address, "UDP loopback connect")?;
4364 self.sockets
4365 .connect_bound_udp_socket(socket_id, target_address)?;
4366 Ok(())
4367 }
4368
4369 pub fn socket_disconnect_udp(
4370 &mut self,
4371 requester_driver: &str,
4372 pid: u32,
4373 socket_id: SocketId,
4374 ) -> KernelResult<()> {
4375 self.assert_not_terminated()?;
4376 self.assert_driver_owns(requester_driver, pid)?;
4377 let existing = self
4378 .sockets
4379 .get(socket_id)
4380 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4381 if existing.owner_pid() != pid {
4382 return Err(KernelError::permission_denied(format!(
4383 "process {pid} does not own socket {socket_id}"
4384 )));
4385 }
4386 self.sockets.disconnect_bound_udp_socket(socket_id)?;
4387 Ok(())
4388 }
4389
4390 fn check_loopback_port_allowed(
4391 &self,
4392 spec: SocketSpec,
4393 target_address: &InetSocketAddress,
4394 operation: &str,
4395 ) -> KernelResult<()> {
4396 if self
4397 .sockets
4398 .find_bound_inet_socket(spec, target_address)
4399 .is_some()
4400 || self.loopback_exempt_ports.contains(&target_address.port())
4401 {
4402 return Ok(());
4403 }
4404
4405 Err(KernelError::permission_denied(format!(
4406 "{operation} to {}:{} is not owned by this VM and is not loopback-exempt",
4407 target_address.host(),
4408 target_address.port()
4409 )))
4410 }
4411
4412 pub fn socket_recv_datagram(
4413 &mut self,
4414 requester_driver: &str,
4415 pid: u32,
4416 socket_id: SocketId,
4417 max_bytes: usize,
4418 ) -> KernelResult<Option<ReceivedDatagram>> {
4419 self.assert_not_terminated()?;
4420 self.assert_driver_owns(requester_driver, pid)?;
4421 let existing = self
4422 .sockets
4423 .get(socket_id)
4424 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4425 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4426 return Err(KernelError::permission_denied(format!(
4427 "process {pid} does not own socket {socket_id}"
4428 )));
4429 }
4430
4431 let result = self.sockets.recv_datagram(socket_id, max_bytes)?;
4432 if result.is_some() {
4433 self.poll_notifier.notify();
4434 }
4435 Ok(result)
4436 }
4437
4438 #[cfg(not(target_arch = "wasm32"))]
4439 pub fn socket_recv_datagram_charged(
4440 &mut self,
4441 requester_driver: &str,
4442 pid: u32,
4443 socket_id: SocketId,
4444 max_bytes: usize,
4445 ) -> KernelResult<Option<crate::socket_table::ChargedReceivedDatagram>> {
4446 self.assert_not_terminated()?;
4447 self.assert_driver_owns(requester_driver, pid)?;
4448 let existing = self
4449 .sockets
4450 .get(socket_id)
4451 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4452 if existing.owner_pid() != pid {
4453 return Err(KernelError::permission_denied(format!(
4454 "process {pid} does not own socket {socket_id}"
4455 )));
4456 }
4457 let result = self.sockets.recv_datagram_charged(socket_id, max_bytes)?;
4458 if result.is_some() {
4459 self.poll_notifier.notify();
4460 }
4461 Ok(result)
4462 }
4463
4464 pub fn socket_set_datagram_option(
4465 &mut self,
4466 requester_driver: &str,
4467 pid: u32,
4468 socket_id: SocketId,
4469 option: DatagramSocketOption,
4470 enabled: bool,
4471 ) -> KernelResult<()> {
4472 self.assert_not_terminated()?;
4473 self.assert_driver_owns(requester_driver, pid)?;
4474 let existing = self
4475 .sockets
4476 .get(socket_id)
4477 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4478 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4479 return Err(KernelError::permission_denied(format!(
4480 "process {pid} does not own socket {socket_id}"
4481 )));
4482 }
4483
4484 self.sockets
4485 .set_datagram_socket_option(socket_id, option, enabled)?;
4486 self.poll_notifier.notify();
4487 Ok(())
4488 }
4489
4490 pub fn socket_add_membership(
4491 &mut self,
4492 requester_driver: &str,
4493 pid: u32,
4494 socket_id: SocketId,
4495 membership: SocketMulticastMembership,
4496 ) -> KernelResult<()> {
4497 self.assert_not_terminated()?;
4498 self.assert_driver_owns(requester_driver, pid)?;
4499 let existing = self
4500 .sockets
4501 .get(socket_id)
4502 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4503 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4504 return Err(KernelError::permission_denied(format!(
4505 "process {pid} does not own socket {socket_id}"
4506 )));
4507 }
4508
4509 self.sockets
4510 .add_multicast_membership(socket_id, membership)?;
4511 self.poll_notifier.notify();
4512 Ok(())
4513 }
4514
4515 pub fn socket_drop_membership(
4516 &mut self,
4517 requester_driver: &str,
4518 pid: u32,
4519 socket_id: SocketId,
4520 membership: SocketMulticastMembership,
4521 ) -> KernelResult<()> {
4522 self.assert_not_terminated()?;
4523 self.assert_driver_owns(requester_driver, pid)?;
4524 let existing = self
4525 .sockets
4526 .get(socket_id)
4527 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4528 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4529 return Err(KernelError::permission_denied(format!(
4530 "process {pid} does not own socket {socket_id}"
4531 )));
4532 }
4533
4534 self.sockets
4535 .drop_multicast_membership(socket_id, membership)?;
4536 self.poll_notifier.notify();
4537 Ok(())
4538 }
4539
4540 pub fn socket_set_state(
4541 &mut self,
4542 requester_driver: &str,
4543 pid: u32,
4544 socket_id: SocketId,
4545 state: SocketState,
4546 ) -> KernelResult<()> {
4547 self.assert_not_terminated()?;
4548 self.assert_driver_owns(requester_driver, pid)?;
4549 let existing = self
4550 .sockets
4551 .get(socket_id)
4552 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4553 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4554 return Err(KernelError::permission_denied(format!(
4555 "process {pid} does not own socket {socket_id}"
4556 )));
4557 }
4558
4559 if !self.sockets.has_resource_ledger() {
4560 self.resources.check_socket_state_transition(
4561 &self.resource_snapshot(),
4562 existing.state(),
4563 state,
4564 )?;
4565 }
4566 self.sockets.update_state(socket_id, state)?;
4567 self.poll_notifier.notify();
4568 Ok(())
4569 }
4570
4571 pub fn socket_write(
4572 &mut self,
4573 requester_driver: &str,
4574 pid: u32,
4575 socket_id: SocketId,
4576 data: &[u8],
4577 ) -> KernelResult<usize> {
4578 self.assert_not_terminated()?;
4579 self.assert_driver_owns(requester_driver, pid)?;
4580 let existing = self
4581 .sockets
4582 .get(socket_id)
4583 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4584 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4585 return Err(KernelError::permission_denied(format!(
4586 "process {pid} does not own socket {socket_id}"
4587 )));
4588 }
4589
4590 self.sockets.check_write(socket_id)?;
4591 if !self.sockets.has_resource_ledger() {
4592 self.resources
4593 .check_socket_buffer_growth(&self.resource_snapshot(), data.len())?;
4594 }
4595 let written = self.sockets.write(socket_id, data)?;
4596 if written > 0 {
4597 self.poll_notifier.notify();
4598 }
4599 Ok(written)
4600 }
4601
4602 pub fn socket_read(
4603 &mut self,
4604 requester_driver: &str,
4605 pid: u32,
4606 socket_id: SocketId,
4607 max_bytes: usize,
4608 ) -> KernelResult<Option<Vec<u8>>> {
4609 self.assert_not_terminated()?;
4610 self.assert_driver_owns(requester_driver, pid)?;
4611 let existing = self
4612 .sockets
4613 .get(socket_id)
4614 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4615 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4616 return Err(KernelError::permission_denied(format!(
4617 "process {pid} does not own socket {socket_id}"
4618 )));
4619 }
4620
4621 let result = self.sockets.read(socket_id, max_bytes)?;
4622 if result.is_some() {
4623 self.poll_notifier.notify();
4624 }
4625 Ok(result)
4626 }
4627
4628 pub fn socket_shutdown(
4629 &mut self,
4630 requester_driver: &str,
4631 pid: u32,
4632 socket_id: SocketId,
4633 how: SocketShutdown,
4634 ) -> KernelResult<()> {
4635 self.assert_not_terminated()?;
4636 self.assert_driver_owns(requester_driver, pid)?;
4637 let existing = self
4638 .sockets
4639 .get(socket_id)
4640 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4641 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4642 return Err(KernelError::permission_denied(format!(
4643 "process {pid} does not own socket {socket_id}"
4644 )));
4645 }
4646
4647 self.sockets.shutdown(socket_id, how)?;
4648 self.poll_notifier.notify();
4649 Ok(())
4650 }
4651
4652 pub fn socket_close(
4653 &mut self,
4654 requester_driver: &str,
4655 pid: u32,
4656 socket_id: SocketId,
4657 ) -> KernelResult<()> {
4658 self.assert_not_terminated()?;
4659 self.assert_driver_owns(requester_driver, pid)?;
4660 let existing = self
4661 .sockets
4662 .get(socket_id)
4663 .ok_or_else(|| KernelError::new("ENOENT", format!("no such socket {socket_id}")))?;
4664 if existing.owner_pid() != pid && existing.owner_pid() != 0 {
4665 return Err(KernelError::permission_denied(format!(
4666 "process {pid} does not own socket {socket_id}"
4667 )));
4668 }
4669
4670 self.sockets.remove(socket_id)?;
4671 self.poll_notifier.notify();
4672 Ok(())
4673 }
4674
4675 pub fn fd_open(
4676 &mut self,
4677 requester_driver: &str,
4678 pid: u32,
4679 path: &str,
4680 flags: u32,
4681 mode: Option<u32>,
4682 ) -> KernelResult<u32> {
4683 self.assert_not_terminated()?;
4684 self.assert_driver_owns(requester_driver, pid)?;
4685 self.validate_fd_open_flags(pid, path, flags)?;
4686 if let Some(existing_fd) = parse_dev_fd_path(path)? {
4687 {
4688 let tables = lock_or_recover(&self.fd_tables);
4689 let table = tables
4690 .get(pid)
4691 .ok_or_else(|| KernelError::no_such_process(pid))?;
4692 table
4693 .get(existing_fd)
4694 .ok_or_else(|| KernelError::bad_file_descriptor(existing_fd))?;
4695 }
4696 self.resources
4697 .check_fd_allocation(&self.resource_snapshot(), 1)?;
4698 let mut tables = lock_or_recover(&self.fd_tables);
4699 let table = tables
4700 .get_mut(pid)
4701 .ok_or_else(|| KernelError::no_such_process(pid))?;
4702 let entry = table
4703 .get(existing_fd)
4704 .cloned()
4705 .ok_or_else(|| KernelError::bad_file_descriptor(existing_fd))?;
4706 return Ok(table.dup_with_status_flags(
4707 existing_fd,
4708 Some(entry.status_flags | (flags & O_NONBLOCK)),
4709 )?);
4710 }
4711
4712 if let Some(proc_node) = self.resolve_proc_node(path, Some(pid))? {
4713 if open_requires_write_access(flags) {
4714 self.filesystem
4715 .check_virtual_path(FsOperation::Write, path)
4716 .map_err(KernelError::from)?;
4717 return Err(read_only_filesystem_error(path));
4718 }
4719
4720 if matches!(
4721 proc_node,
4722 ProcNode::SelfLink { .. }
4723 | ProcNode::PidCwdLink { .. }
4724 | ProcNode::PidFdLink { .. }
4725 ) {
4726 let target = self.proc_symlink_target(&proc_node)?;
4727 return self.fd_open(requester_driver, pid, &target, flags, mode);
4728 }
4729
4730 self.filesystem
4731 .check_virtual_path(FsOperation::Read, path)
4732 .map_err(KernelError::from)?;
4733 self.resources
4734 .check_fd_allocation(&self.resource_snapshot(), 1)?;
4735 let mut tables = lock_or_recover(&self.fd_tables);
4736 let table = tables
4737 .get_mut(pid)
4738 .ok_or_else(|| KernelError::no_such_process(pid))?;
4739 return Ok(table.open_with_details(
4740 &self.proc_canonical_path(&proc_node),
4741 flags,
4742 proc_filetype(&proc_node),
4743 None,
4744 )?);
4745 }
4746
4747 if open_requires_write_access(flags) {
4748 self.reject_read_only_resolved_write_path(path)?;
4749 }
4750 let existed = self.exists_internal(Some(pid), path)?;
4751 if existed {
4752 let mut access = 0;
4753 if flags & (O_WRONLY | O_RDWR) != O_WRONLY {
4754 access |= DAC_READ;
4755 }
4756 if flags & (O_WRONLY | O_RDWR) != 0 || flags & O_TRUNC != 0 {
4757 access |= DAC_WRITE;
4758 }
4759 self.check_dac_access(pid, path, access)?;
4760 } else if flags & O_CREAT != 0 {
4761 self.check_dac_parent_access(pid, path, DAC_WRITE | DAC_EXECUTE)?;
4762 }
4763 if existed {
4764 let stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
4765 if stat.mode & 0o170000 == 0o010000 {
4766 if flags & O_CREAT != 0 && flags & O_EXCL != 0 {
4767 return Err(KernelError::new(
4768 "EEXIST",
4769 format!("file already exists, open '{path}'"),
4770 ));
4771 }
4772 let key = (stat.dev, stat.ino);
4773 if !self.pipes.has_named_pipe(key) {
4774 self.resources
4775 .check_pipe_allocation(&self.resource_snapshot())?;
4776 }
4777 self.resources
4778 .check_fd_allocation(&self.resource_snapshot(), 1)?;
4779 let timeout = self.blocking_read_timeout();
4780 let pipe = self.pipes.open_named_pipe(key, path, flags, timeout)?;
4781 let mut tables = lock_or_recover(&self.fd_tables);
4782 let table = tables
4783 .get_mut(pid)
4784 .ok_or_else(|| KernelError::no_such_process(pid))?;
4785 return match table.open_with(Arc::clone(&pipe.description), FILETYPE_PIPE, None) {
4786 Ok(fd) => Ok(fd),
4787 Err(error) => {
4788 self.pipes.close(pipe.description.id());
4789 Err(error.into())
4790 }
4791 };
4792 }
4793 }
4794 let (filetype, lock_target) = self.prepare_fd_open(path, flags, mode)?;
4795 let description_path = if filetype == FILETYPE_DIRECTORY {
4796 self.realpath_internal(Some(pid), path)?
4797 } else {
4798 path.to_owned()
4799 };
4800 if flags & O_CREAT != 0 && !existed {
4801 let umask = self.processes.get_umask(pid)?;
4802 self.apply_process_creation_metadata(pid, path, mode.unwrap_or(0o666), umask, false)?;
4803 } else if flags & O_TRUNC != 0 {
4804 self.clear_setid_after_write(pid, path)?;
4805 }
4806 self.resources
4807 .check_fd_allocation(&self.resource_snapshot(), 1)?;
4808 let mut tables = lock_or_recover(&self.fd_tables);
4809 let table = tables
4810 .get_mut(pid)
4811 .ok_or_else(|| KernelError::no_such_process(pid))?;
4812 Ok(table.open_with_details(&description_path, flags, filetype, lock_target)?)
4813 }
4814
4815 pub fn fd_open_tmpfile(
4816 &mut self,
4817 requester_driver: &str,
4818 pid: u32,
4819 directory: &str,
4820 flags: u32,
4821 mode: u32,
4822 linkable: bool,
4823 ) -> KernelResult<u32> {
4824 self.assert_not_terminated()?;
4825 self.assert_driver_owns(requester_driver, pid)?;
4826 if flags & 0b11 == crate::fd_table::O_RDONLY {
4827 return Err(KernelError::new(
4828 "EINVAL",
4829 "O_TMPFILE requires write access",
4830 ));
4831 }
4832 let directory_stat = self.stat_for_process(requester_driver, pid, directory)?;
4833 if !directory_stat.is_directory {
4834 return Err(KernelError::new(
4835 "ENOTDIR",
4836 format!("O_TMPFILE target is not a directory: {directory}"),
4837 ));
4838 }
4839 self.check_dac_access(pid, directory, DAC_WRITE | DAC_EXECUTE)?;
4840 self.reject_read_only_resolved_write_path(directory)?;
4841
4842 let hidden_path = loop {
4843 let id = self.next_unnamed_file_id;
4844 self.next_unnamed_file_id =
4845 self.next_unnamed_file_id.checked_add(1).ok_or_else(|| {
4846 KernelError::new("EMFILE", "unnamed-file identifier space exhausted")
4847 })?;
4848 let candidate = normalize_path(&format!("{directory}/{UNNAMED_FILE_PREFIX}{pid}-{id}"));
4849 if !self.exists_internal(Some(pid), &candidate)? {
4850 break candidate;
4851 }
4852 };
4853
4854 let fd = self.fd_open(
4855 requester_driver,
4856 pid,
4857 &hidden_path,
4858 (flags & !O_TRUNC) | O_CREAT | O_EXCL,
4859 Some(mode),
4860 )?;
4861 let description_id = self.description_for_fd(requester_driver, pid, fd)?.id();
4862 self.unnamed_files.insert(
4863 description_id,
4864 UnnamedFile {
4865 path: hidden_path,
4866 linkable,
4867 },
4868 );
4869 Ok(fd)
4870 }
4871
4872 pub fn fd_link_tmpfile_for_process(
4873 &mut self,
4874 requester_driver: &str,
4875 pid: u32,
4876 fd: u32,
4877 destination: &str,
4878 ) -> KernelResult<()> {
4879 self.assert_not_terminated()?;
4880 self.assert_driver_owns(requester_driver, pid)?;
4881 let description = self.description_for_fd(requester_driver, pid, fd)?;
4882 let source = if let Some(unnamed) = self.unnamed_files.get(&description.id()) {
4883 if !unnamed.linkable {
4884 return Err(KernelError::new(
4885 "ENOENT",
4886 "O_EXCL unnamed files cannot be linked",
4887 ));
4888 }
4889 unnamed.path.clone()
4890 } else {
4891 description.path().to_string()
4892 };
4893 self.check_dac_parent_access(pid, destination, DAC_WRITE | DAC_EXECUTE)?;
4894 self.link(&source, destination)
4895 }
4896
4897 pub fn fd_read(
4898 &mut self,
4899 requester_driver: &str,
4900 pid: u32,
4901 fd: u32,
4902 length: usize,
4903 ) -> KernelResult<Vec<u8>> {
4904 Ok(self
4905 .fd_read_with_timeout_result(requester_driver, pid, fd, length, None)?
4906 .unwrap_or_default())
4907 }
4908
4909 pub fn fd_read_with_timeout_result(
4910 &mut self,
4911 requester_driver: &str,
4912 pid: u32,
4913 fd: u32,
4914 length: usize,
4915 timeout: Option<Duration>,
4916 ) -> KernelResult<Option<Vec<u8>>> {
4917 self.assert_driver_owns(requester_driver, pid)?;
4918 let entry = {
4919 let tables = lock_or_recover(&self.fd_tables);
4920 tables
4921 .get(pid)
4922 .and_then(|table| table.get(fd))
4923 .cloned()
4924 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
4925 };
4926 if entry.description.flags() & 0b11 == O_WRONLY {
4927 return Err(KernelError::bad_file_descriptor(fd));
4928 }
4929
4930 if let Some(socket_id) = self.fd_socket_id(&entry.description) {
4931 self.resources.check_pread_length(length)?;
4932 let nonblocking = (entry.description.flags() | entry.status_flags) & O_NONBLOCK != 0;
4933 let wait = if nonblocking {
4934 Some(Duration::ZERO)
4935 } else {
4936 timeout.or_else(|| self.blocking_read_timeout())
4937 };
4938 let deadline = wait.map(|wait| Instant::now() + wait);
4939 let result = loop {
4940 let generation = self.poll_notifier.snapshot();
4941 match self.sockets.read(socket_id, length) {
4942 Ok(result) => break result,
4943 Err(error) if error.code() == "EAGAIN" && !nonblocking => {
4944 let remaining = deadline
4945 .map(|deadline| deadline.saturating_duration_since(Instant::now()));
4946 if matches!(remaining, Some(duration) if duration.is_zero())
4947 || !self.poll_notifier.wait_for_change(generation, remaining)
4948 {
4949 return Err(KernelError::new(
4950 "EAGAIN",
4951 "blocking socket read timed out; raise limits.resources.maxBlockingReadMs",
4952 ));
4953 }
4954 }
4955 Err(error) => return Err(error.into()),
4956 }
4957 };
4958 prune_fd_sockets(&self.sockets, &self.fd_sockets);
4959 if result.is_some() {
4960 self.poll_notifier.notify();
4961 }
4962 return Ok(result);
4963 }
4964
4965 if self.pipes.is_pipe(entry.description.id()) {
4966 let result = self.pipes.read_with_timeout(
4967 entry.description.id(),
4968 length,
4969 if (entry.description.flags() | entry.status_flags) & O_NONBLOCK != 0 {
4970 Some(Duration::ZERO)
4971 } else {
4972 timeout.or_else(|| self.blocking_read_timeout())
4973 },
4974 )?;
4975 return Ok(result);
4976 }
4977
4978 if self.ptys.is_pty(entry.description.id()) {
4979 return Ok(self.ptys.read_with_timeout(
4980 entry.description.id(),
4981 length,
4982 if (entry.description.flags() | entry.status_flags) & O_NONBLOCK != 0 {
4983 Some(Duration::ZERO)
4984 } else {
4985 timeout.or_else(|| self.blocking_read_timeout())
4986 },
4987 )?);
4988 }
4989
4990 self.resources.check_pread_length(length)?;
4991
4992 let path = entry.description.path();
4993 if is_proc_path(&path) {
4994 let bytes = self.proc_read_file_from_open_path(Some(pid), &path)?;
4995 let start = entry.description.cursor() as usize;
4996 let end = start.saturating_add(length).min(bytes.len());
4997 let chunk = if start >= bytes.len() {
4998 Vec::new()
4999 } else {
5000 bytes[start..end].to_vec()
5001 };
5002 entry.description.set_cursor(
5003 entry
5004 .description
5005 .cursor()
5006 .saturating_add(chunk.len() as u64),
5007 );
5008 return Ok(Some(chunk));
5009 }
5010
5011 let cursor = entry.description.cursor();
5012 let bytes = if let Some(bytes) = entry.description.anonymous_pread(cursor, length) {
5013 bytes
5014 } else {
5015 VirtualFileSystem::pread(&mut self.filesystem, &path, cursor, length)?
5016 };
5017 entry
5018 .description
5019 .set_cursor(cursor.saturating_add(bytes.len() as u64));
5020 Ok(Some(bytes))
5021 }
5022
5023 pub fn fd_write(
5024 &mut self,
5025 requester_driver: &str,
5026 pid: u32,
5027 fd: u32,
5028 data: &[u8],
5029 ) -> KernelResult<usize> {
5030 self.fd_write_with_mode(requester_driver, pid, fd, data, false)
5031 }
5032
5033 pub fn fd_write_nonblocking(
5039 &mut self,
5040 requester_driver: &str,
5041 pid: u32,
5042 fd: u32,
5043 data: &[u8],
5044 ) -> KernelResult<usize> {
5045 self.fd_write_with_mode(requester_driver, pid, fd, data, true)
5046 }
5047
5048 fn fd_write_with_mode(
5049 &mut self,
5050 requester_driver: &str,
5051 pid: u32,
5052 fd: u32,
5053 data: &[u8],
5054 force_nonblocking: bool,
5055 ) -> KernelResult<usize> {
5056 self.assert_driver_owns(requester_driver, pid)?;
5057 self.resources.check_fd_write_size(data.len())?;
5058 let entry = {
5059 let tables = lock_or_recover(&self.fd_tables);
5060 tables
5061 .get(pid)
5062 .and_then(|table| table.get(fd))
5063 .cloned()
5064 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5065 };
5066 if let Some(socket_id) = self.fd_socket_id(&entry.description) {
5067 let socket = self
5068 .sockets
5069 .get(socket_id)
5070 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
5071 self.sockets.check_write(socket_id)?;
5072 let snapshot = self.resource_snapshot();
5073 if socket.spec().socket_type == SocketType::Stream {
5074 self.resources
5075 .check_socket_buffer_growth(&snapshot, data.len())?;
5076 } else {
5077 self.resources
5078 .check_socket_datagram_enqueue(&snapshot, data.len())?;
5079 }
5080 let written = self.sockets.write(socket_id, data)?;
5081 if written > 0 || socket.spec().socket_type != SocketType::Stream {
5082 self.poll_notifier.notify();
5083 }
5084 return Ok(written);
5085 }
5086
5087 if self.pipes.is_pipe(entry.description.id()) {
5088 return match self.pipes.write_with_mode(
5089 entry.description.id(),
5090 data,
5091 force_nonblocking
5092 || (entry.description.flags() | entry.status_flags) & O_NONBLOCK != 0,
5093 ) {
5094 Ok(bytes) => Ok(bytes),
5095 Err(error) => {
5096 if error.code() == "EPIPE" {
5097 self.processes.kill(pid as i32, SIGPIPE)?;
5098 }
5099 Err(error.into())
5100 }
5101 };
5102 }
5103
5104 if self.ptys.is_pty(entry.description.id()) {
5105 return Ok(self.ptys.write(entry.description.id(), data)?);
5106 }
5107
5108 let path = entry.description.path();
5109 if let Some(stat) = entry.description.anonymous_stat() {
5110 if entry.description.flags() & 0b11 == O_RDONLY {
5111 return Err(KernelError::bad_file_descriptor(fd));
5112 }
5113 let cursor = if entry.description.flags() & O_APPEND != 0 {
5114 stat.size
5115 } else {
5116 entry.description.cursor()
5117 };
5118 let required_size = stat.size.max(checked_write_end(cursor, data.len())?);
5119 self.check_path_resize_limits_with_existing(stat.size, required_size)?;
5120 let new_size = entry
5121 .description
5122 .anonymous_pwrite(cursor, data)
5123 .expect("anonymous stat and backing must agree")?;
5124 debug_assert_eq!(new_size, required_size);
5125 entry
5126 .description
5127 .set_cursor(cursor.saturating_add(data.len() as u64));
5128 return Ok(data.len());
5129 }
5130 self.reject_read_only_resolved_write_path(&path)?;
5131 if entry.description.flags() & 0b11 == O_RDONLY {
5132 return Err(KernelError::bad_file_descriptor(fd));
5133 }
5134 if is_virtual_device_storage_path(&path) {
5135 VirtualFileSystem::write_file(&mut self.filesystem, &path, data.to_vec())?;
5136 let cursor = entry.description.cursor();
5137 entry
5138 .description
5139 .set_cursor(cursor.saturating_add(data.len() as u64));
5140 return Ok(data.len());
5141 }
5142 let current_size = self.current_storage_file_size(&path)?;
5143 let cursor = entry.description.cursor();
5144 if entry.description.flags() & O_APPEND != 0 {
5145 check_direct_io_alignment(entry.description.flags(), current_size, data.len())?;
5146 let required_size = current_size.max(checked_write_end(current_size, data.len())?);
5147 self.check_path_resize_limits_with_existing(current_size, required_size)?;
5148 let new_len = VirtualFileSystem::append_file(&mut self.filesystem, &path, data)?;
5149 self.update_filesystem_usage_cache_for_resize(&path, current_size, new_len);
5150 self.clear_setid_after_write(pid, &path)?;
5151 entry.description.set_cursor(new_len);
5152 return Ok(data.len());
5153 }
5154
5155 check_direct_io_alignment(entry.description.flags(), cursor, data.len())?;
5156 let required_size = current_size.max(checked_write_end(cursor, data.len())?);
5157 self.check_path_resize_limits_with_existing(current_size, required_size)?;
5158 VirtualFileSystem::pwrite(&mut self.filesystem, &path, data, cursor)?;
5159 self.update_filesystem_usage_cache_for_resize(&path, current_size, required_size);
5160 self.clear_setid_after_write(pid, &path)?;
5161 entry
5162 .description
5163 .set_cursor(cursor.saturating_add(data.len() as u64));
5164 Ok(data.len())
5165 }
5166
5167 pub fn fd_write_nonblocking_pipe(
5170 &mut self,
5171 requester_driver: &str,
5172 pid: u32,
5173 fd: u32,
5174 data: &[u8],
5175 ) -> KernelResult<usize> {
5176 self.assert_driver_owns(requester_driver, pid)?;
5177 self.resources.check_fd_write_size(data.len())?;
5178 let entry = {
5179 let tables = lock_or_recover(&self.fd_tables);
5180 tables
5181 .get(pid)
5182 .and_then(|table| table.get(fd))
5183 .cloned()
5184 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5185 };
5186 if !self.pipes.is_pipe(entry.description.id()) {
5187 return self.fd_write(requester_driver, pid, fd, data);
5188 }
5189 match self
5190 .pipes
5191 .write_with_mode(entry.description.id(), data, true)
5192 {
5193 Ok(bytes) => Ok(bytes),
5194 Err(error) => {
5195 if error.code() == "EPIPE" {
5196 self.processes.kill(pid as i32, SIGPIPE)?;
5197 }
5198 Err(error.into())
5199 }
5200 }
5201 }
5202
5203 pub fn poll_fds(
5204 &self,
5205 requester_driver: &str,
5206 pid: u32,
5207 fds: Vec<PollFd>,
5208 timeout_ms: i32,
5209 ) -> KernelResult<PollResult> {
5210 let targets = fds
5211 .into_iter()
5212 .map(|poll_fd| PollTargetEntry::fd(poll_fd.fd, poll_fd.events))
5213 .collect::<Vec<_>>();
5214 let result = self.poll_targets(requester_driver, pid, targets, timeout_ms)?;
5215 Ok(PollResult {
5216 ready_count: result.ready_count,
5217 fds: result
5218 .targets
5219 .into_iter()
5220 .map(|target| match target.target {
5221 PollTarget::Fd(fd) => PollFd {
5222 fd,
5223 events: target.events,
5224 revents: target.revents,
5225 },
5226 PollTarget::Socket(_) => unreachable!("fd poll should only include fd targets"),
5227 })
5228 .collect(),
5229 })
5230 }
5231
5232 pub fn poll_wait_handle(&self) -> crate::poll::PollWaitHandle {
5236 crate::poll::PollWaitHandle::new(self.poll_notifier.clone())
5237 }
5238
5239 pub fn poll_targets(
5240 &self,
5241 requester_driver: &str,
5242 pid: u32,
5243 mut targets: Vec<PollTargetEntry>,
5244 timeout_ms: i32,
5245 ) -> KernelResult<PollTargetResult> {
5246 self.assert_driver_owns(requester_driver, pid)?;
5247 if timeout_ms < -1 {
5248 return Err(KernelError::new(
5249 "EINVAL",
5250 format!("invalid poll timeout {timeout_ms}"),
5251 ));
5252 }
5253
5254 let timeout = if timeout_ms < 0 {
5255 None
5256 } else {
5257 Some(Duration::from_millis(timeout_ms as u64))
5258 };
5259 let deadline = timeout.map(|duration| Instant::now() + duration);
5260
5261 loop {
5262 let observed_generation = self.poll_notifier.snapshot();
5263 let ready_count = self.populate_poll_target_revents(pid, &mut targets)?;
5264 if ready_count > 0 || matches!(timeout, Some(duration) if duration.is_zero()) {
5265 return Ok(PollTargetResult {
5266 ready_count,
5267 targets,
5268 });
5269 }
5270
5271 let remaining = deadline.map(|target| target.saturating_duration_since(Instant::now()));
5272 if matches!(remaining, Some(duration) if duration.is_zero()) {
5273 return Ok(PollTargetResult {
5274 ready_count,
5275 targets,
5276 });
5277 }
5278
5279 if !self
5280 .poll_notifier
5281 .wait_for_change(observed_generation, remaining)
5282 {
5283 return Ok(PollTargetResult {
5284 ready_count,
5285 targets,
5286 });
5287 }
5288 }
5289 }
5290
5291 pub fn fd_seek(
5292 &mut self,
5293 requester_driver: &str,
5294 pid: u32,
5295 fd: u32,
5296 offset: i64,
5297 whence: u8,
5298 ) -> KernelResult<u64> {
5299 self.assert_driver_owns(requester_driver, pid)?;
5300 let entry = {
5301 let tables = lock_or_recover(&self.fd_tables);
5302 tables
5303 .get(pid)
5304 .and_then(|table| table.get(fd))
5305 .cloned()
5306 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5307 };
5308
5309 if self.pipes.is_pipe(entry.description.id())
5310 || self.ptys.is_pty(entry.description.id())
5311 || self.fd_socket_id(&entry.description).is_some()
5312 {
5313 return Err(KernelError::new("ESPIPE", "illegal seek"));
5314 }
5315
5316 let base = match whence {
5317 SEEK_SET => 0_i128,
5318 SEEK_CUR => i128::from(entry.description.cursor()),
5319 SEEK_END => {
5320 let path = entry.description.path();
5321 let size = if let Some(stat) = entry.description.anonymous_stat() {
5322 stat.size
5323 } else if is_proc_path(&path) {
5324 self.proc_stat_from_open_path(Some(pid), &path)?.size
5325 } else {
5326 self.filesystem.stat(&path)?.size
5327 };
5328 i128::from(size)
5329 }
5330 _ => {
5331 return Err(KernelError::new(
5332 "EINVAL",
5333 format!("invalid whence {whence}"),
5334 ));
5335 }
5336 };
5337 let next = base + i128::from(offset);
5338 if next < 0 {
5339 return Err(KernelError::new("EINVAL", "negative seek position"));
5340 }
5341 let next = u64::try_from(next)
5342 .map_err(|_| KernelError::new("EINVAL", "seek position out of range"))?;
5343 entry.description.set_cursor(next);
5344 Ok(next)
5345 }
5346
5347 pub fn fd_pread(
5348 &mut self,
5349 requester_driver: &str,
5350 pid: u32,
5351 fd: u32,
5352 length: usize,
5353 offset: u64,
5354 ) -> KernelResult<Vec<u8>> {
5355 self.assert_driver_owns(requester_driver, pid)?;
5356 self.resources.check_pread_length(length)?;
5357 let entry = {
5358 let tables = lock_or_recover(&self.fd_tables);
5359 tables
5360 .get(pid)
5361 .and_then(|table| table.get(fd))
5362 .cloned()
5363 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5364 };
5365 if entry.description.flags() & 0b11 == O_WRONLY {
5366 return Err(KernelError::bad_file_descriptor(fd));
5367 }
5368
5369 if self.pipes.is_pipe(entry.description.id())
5370 || self.ptys.is_pty(entry.description.id())
5371 || self.fd_socket_id(&entry.description).is_some()
5372 {
5373 return Err(KernelError::new("ESPIPE", "illegal seek"));
5374 }
5375
5376 let path = entry.description.path();
5377 if is_proc_path(&path) {
5378 let bytes = self.proc_read_file_from_open_path(Some(pid), &path)?;
5379 let start = usize::try_from(offset)
5380 .map_err(|_| KernelError::new("EINVAL", "pread offset out of range"))?;
5381 let end = start.saturating_add(length).min(bytes.len());
5382 return Ok(if start >= bytes.len() {
5383 Vec::new()
5384 } else {
5385 bytes[start..end].to_vec()
5386 });
5387 }
5388
5389 if let Some(bytes) = entry.description.anonymous_pread(offset, length) {
5390 return Ok(bytes);
5391 }
5392 Ok(VirtualFileSystem::pread(
5393 &mut self.filesystem,
5394 &path,
5395 offset,
5396 length,
5397 )?)
5398 }
5399
5400 pub fn fd_pwrite(
5401 &mut self,
5402 requester_driver: &str,
5403 pid: u32,
5404 fd: u32,
5405 data: &[u8],
5406 offset: u64,
5407 ) -> KernelResult<usize> {
5408 self.assert_driver_owns(requester_driver, pid)?;
5409 self.resources.check_fd_write_size(data.len())?;
5410 let entry = {
5411 let tables = lock_or_recover(&self.fd_tables);
5412 tables
5413 .get(pid)
5414 .and_then(|table| table.get(fd))
5415 .cloned()
5416 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5417 };
5418 if self.pipes.is_pipe(entry.description.id())
5419 || self.ptys.is_pty(entry.description.id())
5420 || self.fd_socket_id(&entry.description).is_some()
5421 {
5422 return Err(KernelError::new("ESPIPE", "illegal seek"));
5423 }
5424
5425 let path = entry.description.path();
5426 if let Some(stat) = entry.description.anonymous_stat() {
5427 let required_size = stat.size.max(checked_write_end(offset, data.len())?);
5428 self.check_path_resize_limits_with_existing(stat.size, required_size)?;
5429 if entry.description.flags() & 0b11 == O_RDONLY {
5430 return Err(KernelError::bad_file_descriptor(fd));
5431 }
5432 let new_size = entry
5433 .description
5434 .anonymous_pwrite(offset, data)
5435 .expect("anonymous stat and backing must agree")?;
5436 debug_assert_eq!(new_size, required_size);
5437 return Ok(data.len());
5438 }
5439 self.reject_read_only_resolved_write_path(&path)?;
5440
5441 let current_size = self.current_storage_file_size(&path)?;
5442 let required_size = current_size.max(checked_write_end(offset, data.len())?);
5443 self.check_path_resize_limits_with_existing(current_size, required_size)?;
5444 if entry.description.flags() & 0b11 == O_RDONLY {
5445 return Err(KernelError::bad_file_descriptor(fd));
5446 }
5447 VirtualFileSystem::pwrite(&mut self.filesystem, &path, data.to_vec(), offset)?;
5448 self.update_filesystem_usage_cache_for_resize(&path, current_size, required_size);
5449 Ok(data.len())
5450 }
5451
5452 pub fn fd_chmod(
5453 &mut self,
5454 requester_driver: &str,
5455 pid: u32,
5456 fd: u32,
5457 mode: u32,
5458 ) -> KernelResult<()> {
5459 let description = self.description_for_fd(requester_driver, pid, fd)?;
5460 if description.detached_chmod(mode) {
5461 return Ok(());
5462 }
5463 if self.pipes.is_pipe(description.id()) {
5464 self.pipes.chmod(description.id(), mode)?;
5465 return Ok(());
5466 }
5467 if let Some(socket) = lock_or_recover(&self.fd_sockets).get_mut(&description.id()) {
5468 socket.mode = mode & 0o7777;
5469 return Ok(());
5470 }
5471 let path = description.path();
5472 self.chmod(&path, mode)
5473 }
5474
5475 pub fn fd_chmod_for_process(
5476 &mut self,
5477 requester_driver: &str,
5478 pid: u32,
5479 fd: u32,
5480 mode: u32,
5481 ) -> KernelResult<()> {
5482 let identity = self.process_identity(requester_driver, pid)?;
5483 let stat = self.dev_fd_stat(requester_driver, pid, fd)?;
5484 if identity.euid != 0 && identity.euid != stat.uid {
5485 return Err(KernelError::new(
5486 "EPERM",
5487 format!("operation not permitted, process does not own fd {fd}"),
5488 ));
5489 }
5490 self.fd_chmod(requester_driver, pid, fd, mode)
5491 }
5492
5493 pub fn fd_chown_for_process(
5494 &mut self,
5495 requester_driver: &str,
5496 pid: u32,
5497 fd: u32,
5498 uid: u32,
5499 gid: u32,
5500 ) -> KernelResult<()> {
5501 let description = self.description_for_fd(requester_driver, pid, fd)?;
5502 let identity = self.process_identity(requester_driver, pid)?;
5503 let stat = self.dev_fd_stat(requester_driver, pid, fd)?;
5504 let (next_uid, next_gid) =
5505 validate_chown_request(&identity, &stat, uid, gid, &format!("fd {fd}"))?;
5506 let changed_mode = linux_chown_cleared_mode(&stat);
5507 if description.detached_chown(next_uid, next_gid, changed_mode) {
5508 return Ok(());
5509 }
5510 if self.pipes.is_pipe(description.id()) {
5511 self.pipes.set_owner(description.id(), next_uid, next_gid)?;
5512 return Ok(());
5513 }
5514 if let Some(socket) = lock_or_recover(&self.fd_sockets).get_mut(&description.id()) {
5515 socket.uid = next_uid;
5516 socket.gid = next_gid;
5517 return Ok(());
5518 }
5519
5520 let fd_type = self.fd_stat(requester_driver, pid, fd)?.filetype;
5521 if !matches!(fd_type, FILETYPE_REGULAR_FILE | FILETYPE_DIRECTORY) {
5522 return Ok(());
5526 }
5527 let path = description.path();
5528 self.reject_read_only_resolved_write_path(&path)?;
5529 self.filesystem.chown(&path, next_uid, next_gid)?;
5530 if let Some(mode) = changed_mode {
5531 self.filesystem.chmod(&path, mode)?;
5532 }
5533 Ok(())
5534 }
5535
5536 pub fn fd_dup(&mut self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<u32> {
5537 self.assert_driver_owns(requester_driver, pid)?;
5538 {
5539 let tables = lock_or_recover(&self.fd_tables);
5540 let table = tables
5541 .get(pid)
5542 .ok_or_else(|| KernelError::no_such_process(pid))?;
5543 table
5544 .get(fd)
5545 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
5546 }
5547 self.resources
5548 .check_fd_allocation(&self.resource_snapshot(), 1)?;
5549 let mut tables = lock_or_recover(&self.fd_tables);
5550 let table = tables
5551 .get_mut(pid)
5552 .ok_or_else(|| KernelError::no_such_process(pid))?;
5553 Ok(table.dup(fd)?)
5554 }
5555
5556 pub fn fd_dup2(
5557 &mut self,
5558 requester_driver: &str,
5559 pid: u32,
5560 old_fd: u32,
5561 new_fd: u32,
5562 ) -> KernelResult<()> {
5563 self.assert_driver_owns(requester_driver, pid)?;
5564 let (replaced, needs_fd_growth) = {
5565 let tables = lock_or_recover(&self.fd_tables);
5566 let table = tables
5567 .get(pid)
5568 .ok_or_else(|| KernelError::no_such_process(pid))?;
5569 table
5570 .get(old_fd)
5571 .ok_or_else(|| KernelError::bad_file_descriptor(old_fd))?;
5572 let replaced = if old_fd == new_fd {
5573 None
5574 } else {
5575 table.get(new_fd).cloned()
5576 };
5577 if new_fd as usize >= table.max_fds() {
5578 return Err(KernelError::bad_file_descriptor(new_fd));
5579 }
5580 let needs_fd_growth = old_fd != new_fd && replaced.is_none();
5581 (replaced, needs_fd_growth)
5582 };
5583 if needs_fd_growth {
5584 self.resources
5585 .check_fd_allocation(&self.resource_snapshot(), 1)?;
5586 }
5587 {
5588 let mut tables = lock_or_recover(&self.fd_tables);
5589 let table = tables
5590 .get_mut(pid)
5591 .ok_or_else(|| KernelError::no_such_process(pid))?;
5592 table.dup2(old_fd, new_fd)?;
5593 }
5594
5595 if let Some(entry) = replaced {
5596 if let Some(target) = entry.description.lock_target() {
5597 self.file_locks.release_process_target(pid, target);
5598 }
5599 self.close_special_resource_if_needed(&entry.description, entry.filetype);
5600 }
5601 Ok(())
5602 }
5603
5604 pub fn fd_close(&mut self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<()> {
5605 self.assert_driver_owns(requester_driver, pid)?;
5606 let (description, filetype, lock_target) = {
5607 let mut tables = lock_or_recover(&self.fd_tables);
5608 let table = tables
5609 .get_mut(pid)
5610 .ok_or_else(|| KernelError::no_such_process(pid))?;
5611 let entry = table
5612 .get(fd)
5613 .cloned()
5614 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
5615 table.close(fd);
5616 let lock_target = entry.description.lock_target();
5617 (entry.description, entry.filetype, lock_target)
5618 };
5619 if let Some(target) = lock_target {
5620 self.file_locks.release_process_target(pid, target);
5621 }
5622 self.close_special_resource_if_needed(&description, filetype);
5623 self.cleanup_unnamed_file_if_closed(&description)?;
5624 Ok(())
5625 }
5626
5627 pub fn close_process_cloexec_fds(
5630 &mut self,
5631 requester_driver: &str,
5632 pid: u32,
5633 ) -> KernelResult<()> {
5634 self.assert_driver_owns(requester_driver, pid)?;
5635 let closed_entries = {
5636 let mut tables = lock_or_recover(&self.fd_tables);
5637 let table = tables
5638 .get_mut(pid)
5639 .ok_or_else(|| KernelError::no_such_process(pid))?;
5640 let fds = table.close_on_exec_fds();
5641 let mut closed_entries = Vec::with_capacity(fds.len());
5642 for fd in fds {
5643 let entry = table
5644 .get(fd)
5645 .cloned()
5646 .expect("close-on-exec snapshot must reference an open descriptor");
5647 let closed = table.close(fd);
5648 debug_assert!(closed);
5649 closed_entries.push((entry.description, entry.filetype));
5650 }
5651 closed_entries
5652 };
5653 for (description, filetype) in closed_entries {
5654 if let Some(target) = description.lock_target() {
5655 self.file_locks.release_process_target(pid, target);
5656 }
5657 self.close_special_resource_if_needed(&description, filetype);
5658 }
5659 Ok(())
5660 }
5661
5662 pub fn fd_fcntl(
5663 &mut self,
5664 requester_driver: &str,
5665 pid: u32,
5666 fd: u32,
5667 command: u32,
5668 arg: u32,
5669 ) -> KernelResult<u32> {
5670 self.assert_driver_owns(requester_driver, pid)?;
5671 if command == F_DUPFD {
5672 {
5673 let tables = lock_or_recover(&self.fd_tables);
5674 let table = tables
5675 .get(pid)
5676 .ok_or_else(|| KernelError::no_such_process(pid))?;
5677 table
5678 .get(fd)
5679 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
5680 if arg as usize >= table.max_fds() {
5681 return Err(KernelError::new(
5682 "EINVAL",
5683 format!("fd {arg} exceeds process fd limit"),
5684 ));
5685 }
5686 }
5687 self.resources
5688 .check_fd_allocation(&self.resource_snapshot(), 1)?;
5689 }
5690 let mut tables = lock_or_recover(&self.fd_tables);
5691 let table = tables
5692 .get_mut(pid)
5693 .ok_or_else(|| KernelError::no_such_process(pid))?;
5694 let result = table.fcntl(fd, command, arg)?;
5695 if command == F_DUPFD {
5696 self.poll_notifier.notify();
5697 }
5698 Ok(result)
5699 }
5700
5701 pub fn fd_named_pipe_peer_ready(
5702 &self,
5703 requester_driver: &str,
5704 pid: u32,
5705 fd: u32,
5706 ) -> KernelResult<bool> {
5707 self.assert_driver_owns(requester_driver, pid)?;
5708 let entry = lock_or_recover(&self.fd_tables)
5709 .get(pid)
5710 .and_then(|table| table.get(fd))
5711 .cloned()
5712 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?;
5713 if entry.filetype != FILETYPE_PIPE {
5714 return Err(KernelError::new(
5715 "EINVAL",
5716 format!("fd {fd} is not a named pipe"),
5717 ));
5718 }
5719 self.pipes
5720 .named_pipe_peer_ready(entry.description.id())?
5721 .ok_or_else(|| KernelError::new("EINVAL", format!("fd {fd} is an anonymous pipe")))
5722 }
5723
5724 pub fn fd_flock(
5725 &self,
5726 requester_driver: &str,
5727 pid: u32,
5728 fd: u32,
5729 operation: u32,
5730 ) -> KernelResult<()> {
5731 self.assert_driver_owns(requester_driver, pid)?;
5732 let entry = {
5733 let tables = lock_or_recover(&self.fd_tables);
5734 tables
5735 .get(pid)
5736 .and_then(|table| table.get(fd))
5737 .cloned()
5738 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5739 };
5740
5741 if !matches!(entry.filetype, FILETYPE_REGULAR_FILE | FILETYPE_DIRECTORY) {
5742 return Err(KernelError::new(
5743 "EBADF",
5744 format!("file descriptor {fd} does not support advisory locking"),
5745 ));
5746 }
5747
5748 let target = entry.description.lock_target().ok_or_else(|| {
5749 KernelError::new(
5750 "EBADF",
5751 format!("file descriptor {fd} is missing advisory lock metadata"),
5752 )
5753 })?;
5754 let operation = FlockOperation::from_bits(operation)?;
5755 self.file_locks
5756 .apply(entry.description.id(), target, operation)?;
5757 Ok(())
5758 }
5759
5760 #[allow(clippy::too_many_arguments)]
5762 pub fn fd_record_lock(
5763 &self,
5764 requester_driver: &str,
5765 pid: u32,
5766 fd: u32,
5767 lock_type: RecordLockType,
5768 start: u64,
5769 length: u64,
5770 query: bool,
5771 ) -> KernelResult<Option<RecordLock>> {
5772 self.fd_record_lock_impl(
5773 requester_driver,
5774 pid,
5775 fd,
5776 lock_type,
5777 start,
5778 length,
5779 query,
5780 false,
5781 )
5782 }
5783
5784 pub fn fd_record_lock_wait(
5785 &self,
5786 requester_driver: &str,
5787 pid: u32,
5788 fd: u32,
5789 lock_type: RecordLockType,
5790 start: u64,
5791 length: u64,
5792 ) -> KernelResult<()> {
5793 self.fd_record_lock_impl(
5794 requester_driver,
5795 pid,
5796 fd,
5797 lock_type,
5798 start,
5799 length,
5800 false,
5801 true,
5802 )
5803 .map(|_| ())
5804 }
5805
5806 pub fn fd_record_lock_cancel(&self, requester_driver: &str, pid: u32) -> KernelResult<()> {
5807 self.assert_driver_owns(requester_driver, pid)?;
5808 self.file_locks.cancel_record_lock_wait(pid);
5809 Ok(())
5810 }
5811
5812 #[allow(clippy::too_many_arguments)]
5813 fn fd_record_lock_impl(
5814 &self,
5815 requester_driver: &str,
5816 pid: u32,
5817 fd: u32,
5818 lock_type: RecordLockType,
5819 start: u64,
5820 length: u64,
5821 query: bool,
5822 blocking: bool,
5823 ) -> KernelResult<Option<RecordLock>> {
5824 self.assert_driver_owns(requester_driver, pid)?;
5825 let entry = {
5826 let tables = lock_or_recover(&self.fd_tables);
5827 tables
5828 .get(pid)
5829 .and_then(|table| table.get(fd))
5830 .cloned()
5831 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5832 };
5833 if !matches!(entry.filetype, FILETYPE_REGULAR_FILE | FILETYPE_DIRECTORY) {
5834 return Err(KernelError::new(
5835 "EBADF",
5836 format!("file descriptor {fd} does not support POSIX record locks"),
5837 ));
5838 }
5839 if query && lock_type == RecordLockType::Unlock {
5840 return Err(KernelError::new(
5841 "EINVAL",
5842 "F_GETLK requires a read or write lock type",
5843 ));
5844 }
5845 if !query {
5846 let access_mode = entry.description.flags() & 0o3;
5847 if (lock_type == RecordLockType::Read && access_mode == O_WRONLY)
5848 || (lock_type == RecordLockType::Write && access_mode == O_RDONLY)
5849 {
5850 return Err(KernelError::new(
5851 "EBADF",
5852 format!("file descriptor {fd} access mode is incompatible with record lock"),
5853 ));
5854 }
5855 }
5856 let target = entry.description.lock_target().ok_or_else(|| {
5857 KernelError::new(
5858 "EBADF",
5859 format!("file descriptor {fd} is missing record lock metadata"),
5860 )
5861 })?;
5862 let request = RecordLock::new(lock_type, start, length, pid)?;
5863 if query {
5864 Ok(self.file_locks.query_record_lock(target, request))
5865 } else if blocking {
5866 self.file_locks.set_blocking_record_lock(target, request)?;
5867 Ok(None)
5868 } else {
5869 self.file_locks.set_record_lock(target, request)?;
5870 Ok(None)
5871 }
5872 }
5873
5874 pub fn fd_stat(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<FdStat> {
5875 self.assert_driver_owns(requester_driver, pid)?;
5876 let tables = lock_or_recover(&self.fd_tables);
5877 Ok(tables
5878 .get(pid)
5879 .ok_or_else(|| KernelError::no_such_process(pid))?
5880 .stat(fd)?)
5881 }
5882
5883 pub fn fd_sync(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<()> {
5888 let stat = self.fd_stat(requester_driver, pid, fd)?;
5889 match stat.filetype {
5890 FILETYPE_REGULAR_FILE | FILETYPE_DIRECTORY => Ok(()),
5891 _ => Err(KernelError::new(
5892 "EINVAL",
5893 format!("file descriptor {fd} cannot be synchronized"),
5894 )),
5895 }
5896 }
5897
5898 pub fn fd_read_dir_with_types(
5899 &mut self,
5900 requester_driver: &str,
5901 pid: u32,
5902 fd: u32,
5903 ) -> KernelResult<Vec<ProcessFdDirEntry>> {
5904 let stat = self.fd_stat(requester_driver, pid, fd)?;
5905 if stat.filetype != FILETYPE_DIRECTORY {
5906 return Err(KernelError::new(
5907 "ENOTDIR",
5908 format!("file descriptor {fd} is not a directory"),
5909 ));
5910 }
5911 let description = self.description_for_fd(requester_driver, pid, fd)?;
5912 if description.detached_directory_stat().is_some() {
5913 return Ok(Vec::new());
5917 }
5918 let path = self.fd_path(requester_driver, pid, fd)?;
5919 let children = self.read_dir_with_types_for_process(requester_driver, pid, &path)?;
5920 self.resources
5921 .check_readdir_entries(children.len().saturating_add(2))?;
5922
5923 let current_stat = self.stat_internal(Some(pid), &path)?;
5927 let parent = parent_path(&path);
5928 let parent_stat = self.stat_internal(Some(pid), &parent)?;
5929 let mut entries = Vec::with_capacity(children.len().saturating_add(2));
5930 entries.push(ProcessFdDirEntry {
5931 name: String::from("."),
5932 ino: required_dirent_ino(&path, current_stat.ino)?,
5933 is_directory: true,
5934 is_symbolic_link: false,
5935 });
5936 entries.push(ProcessFdDirEntry {
5937 name: String::from(".."),
5938 ino: required_dirent_ino(&parent, parent_stat.ino)?,
5939 is_directory: true,
5940 is_symbolic_link: false,
5941 });
5942 for child in children {
5943 let child_path = join_child_path(&path, &child.name);
5944 let child_stat = self.lstat_internal(Some(pid), &child_path)?;
5945 entries.push(ProcessFdDirEntry {
5946 name: child.name,
5947 ino: required_dirent_ino(&child_path, child_stat.ino)?,
5948 is_directory: child.is_directory,
5949 is_symbolic_link: child.is_symbolic_link,
5950 });
5951 }
5952 Ok(entries)
5953 }
5954
5955 pub fn fd_path(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<String> {
5956 let description = self.description_for_fd(requester_driver, pid, fd)?;
5957 Ok(description.path())
5958 }
5959
5960 pub fn isatty(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<bool> {
5961 self.assert_driver_owns(requester_driver, pid)?;
5962 let entry = {
5963 let tables = lock_or_recover(&self.fd_tables);
5964 tables
5965 .get(pid)
5966 .and_then(|table| table.get(fd))
5967 .cloned()
5968 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
5969 };
5970 Ok(self.ptys.is_slave(entry.description.id()))
5971 }
5972
5973 pub fn pty_window_size(
5974 &self,
5975 requester_driver: &str,
5976 pid: u32,
5977 fd: u32,
5978 ) -> KernelResult<PtyWindowSize> {
5979 let description = self.description_for_fd(requester_driver, pid, fd)?;
5980 Ok(self.ptys.window_size(description.id())?)
5981 }
5982
5983 pub fn pty_set_discipline(
5984 &self,
5985 requester_driver: &str,
5986 pid: u32,
5987 fd: u32,
5988 config: LineDisciplineConfig,
5989 ) -> KernelResult<()> {
5990 let description = self.description_for_fd(requester_driver, pid, fd)?;
5991 self.ptys.set_discipline(description.id(), config)?;
5992 Ok(())
5993 }
5994
5995 pub fn pty_set_raw_mode(
6000 &self,
6001 requester_driver: &str,
6002 pid: u32,
6003 fd: u32,
6004 enabled: bool,
6005 ) -> KernelResult<Option<u64>> {
6006 let description = self.description_for_fd(requester_driver, pid, fd)?;
6007 let foreground_pgid = self.ptys.get_foreground_pgid(description.id())?;
6008 let process_pgid = self.processes.getpgid(pid)?;
6009 let lease_owner =
6010 (!enabled || foreground_pgid == 0 || foreground_pgid == process_pgid).then_some(pid);
6011 Ok(self
6012 .ptys
6013 .set_raw_mode(description.id(), lease_owner, enabled)?)
6014 }
6015
6016 pub fn pty_release_raw_mode(
6020 &self,
6021 requester_driver: &str,
6022 descriptor_owner_pid: u32,
6023 fd: u32,
6024 raw_mode_owner_pid: u32,
6025 generation: u64,
6026 ) -> KernelResult<bool> {
6027 self.assert_driver_owns(requester_driver, raw_mode_owner_pid)?;
6028 let description = self.description_for_fd(requester_driver, descriptor_owner_pid, fd)?;
6029 Ok(self
6030 .ptys
6031 .release_raw_mode(description.id(), raw_mode_owner_pid, generation)?)
6032 }
6033
6034 pub fn pty_set_foreground_pgid(
6035 &self,
6036 requester_driver: &str,
6037 pid: u32,
6038 fd: u32,
6039 pgid: u32,
6040 ) -> KernelResult<()> {
6041 let description = self.description_for_fd(requester_driver, pid, fd)?;
6042 let requester_sid = self.processes.getsid(pid)?;
6043 let group = self
6044 .processes
6045 .list_processes()
6046 .into_values()
6047 .find(|process| process.pgid == pgid && process.status != ProcessStatus::Exited)
6048 .ok_or_else(|| KernelError::new("ESRCH", format!("no such process group {pgid}")))?;
6049 if group.sid != requester_sid {
6050 return Err(KernelError::permission_denied(
6051 "cannot set foreground process group in different session",
6052 ));
6053 }
6054 self.ptys.set_foreground_pgid(description.id(), pgid)?;
6055 Ok(())
6056 }
6057
6058 pub fn tcgetattr(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<Termios> {
6059 let description = self.description_for_fd(requester_driver, pid, fd)?;
6060 Ok(self.ptys.get_termios(description.id())?)
6061 }
6062
6063 pub fn tcsetattr(
6064 &self,
6065 requester_driver: &str,
6066 pid: u32,
6067 fd: u32,
6068 termios: PartialTermios,
6069 ) -> KernelResult<()> {
6070 let description = self.description_for_fd(requester_driver, pid, fd)?;
6071 self.ptys.set_termios(description.id(), termios)?;
6072 Ok(())
6073 }
6074
6075 pub fn tcgetpgrp(&self, requester_driver: &str, pid: u32, fd: u32) -> KernelResult<u32> {
6076 let description = self.description_for_fd(requester_driver, pid, fd)?;
6077 Ok(self.ptys.get_foreground_pgid(description.id())?)
6078 }
6079
6080 pub fn pty_resize(
6081 &self,
6082 requester_driver: &str,
6083 pid: u32,
6084 fd: u32,
6085 cols: u16,
6086 rows: u16,
6087 ) -> KernelResult<()> {
6088 let description = self.description_for_fd(requester_driver, pid, fd)?;
6089 let target_pgid = self.ptys.resize(description.id(), cols, rows)?;
6090 if let Some(pgid) = target_pgid {
6091 match self.processes.kill(-(pgid as i32), SIGWINCH) {
6092 Ok(()) => {}
6093 Err(error) if error.code() == "ESRCH" => {}
6094 Err(error) => return Err(error.into()),
6095 }
6096 }
6097 Ok(())
6098 }
6099
6100 pub fn signal_process(
6101 &self,
6102 requester_driver: &str,
6103 pid: i32,
6104 signal: i32,
6105 ) -> KernelResult<()> {
6106 if pid < 0 {
6107 let pgid = pid.unsigned_abs();
6108 let members = self
6109 .processes
6110 .list_processes()
6111 .into_values()
6112 .filter(|process| process.pgid == pgid && process.status != ProcessStatus::Exited)
6113 .collect::<Vec<_>>();
6114 if members.is_empty() {
6115 self.processes.kill(pid, signal)?;
6116 return Ok(());
6117 }
6118 if let Some(process) = members
6119 .iter()
6120 .find(|process| process.driver != requester_driver)
6121 {
6122 return Err(KernelError::permission_denied(format!(
6123 "driver \"{requester_driver}\" does not own process group {pgid} containing PID {}",
6124 process.pid
6125 )));
6126 }
6127 self.processes.kill(pid, signal)?;
6128 return Ok(());
6129 }
6130
6131 let pid = u32::try_from(pid)
6132 .map_err(|_| KernelError::new("EINVAL", format!("invalid pid {pid}")))?;
6133 self.assert_driver_owns(requester_driver, pid)?;
6134 self.processes.kill(pid as i32, signal)?;
6135 Ok(())
6136 }
6137
6138 pub fn kill_process(&self, requester_driver: &str, pid: u32, signal: i32) -> KernelResult<()> {
6139 let pid = i32::try_from(pid)
6140 .map_err(|_| KernelError::new("EINVAL", format!("pid {pid} exceeds i32::MAX")))?;
6141 self.signal_process(requester_driver, pid, signal)
6142 }
6143
6144 pub fn setpgid(&self, requester_driver: &str, pid: u32, pgid: u32) -> KernelResult<()> {
6145 self.assert_driver_owns(requester_driver, pid)?;
6146 let target_pgid = if pgid == 0 { pid } else { pgid };
6147 if target_pgid != pid {
6148 if let Some(group_owner) =
6149 self.processes
6150 .list_processes()
6151 .into_values()
6152 .find(|process| {
6153 process.pgid == target_pgid && process.status == ProcessStatus::Running
6154 })
6155 {
6156 if group_owner.driver != requester_driver {
6157 return Err(KernelError::permission_denied(format!(
6158 "driver \"{requester_driver}\" cannot join process group {target_pgid} owned by \"{}\"",
6159 group_owner.driver
6160 )));
6161 }
6162 }
6163 }
6164 self.processes.setpgid(pid, pgid)?;
6165 Ok(())
6166 }
6167
6168 pub fn getpgid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
6169 self.assert_driver_owns(requester_driver, pid)?;
6170 Ok(self.processes.getpgid(pid)?)
6171 }
6172
6173 pub fn getpid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
6174 self.assert_driver_owns(requester_driver, pid)?;
6175 Ok(pid)
6176 }
6177
6178 pub fn sigprocmask(
6179 &self,
6180 requester_driver: &str,
6181 pid: u32,
6182 how: SigmaskHow,
6183 set: SignalSet,
6184 ) -> KernelResult<SignalSet> {
6185 self.assert_driver_owns(requester_driver, pid)?;
6186 Ok(self.processes.sigprocmask(pid, how, set)?)
6187 }
6188
6189 pub fn sigpending(&self, requester_driver: &str, pid: u32) -> KernelResult<SignalSet> {
6190 self.assert_driver_owns(requester_driver, pid)?;
6191 Ok(self.processes.sigpending(pid)?)
6192 }
6193
6194 pub fn getppid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
6195 self.assert_driver_owns(requester_driver, pid)?;
6196 Ok(self.processes.getppid(pid)?)
6197 }
6198
6199 pub fn setsid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
6200 self.assert_driver_owns(requester_driver, pid)?;
6201 Ok(self.processes.setsid(pid)?)
6202 }
6203
6204 pub fn getsid(&self, requester_driver: &str, pid: u32) -> KernelResult<u32> {
6205 self.assert_driver_owns(requester_driver, pid)?;
6206 Ok(self.processes.getsid(pid)?)
6207 }
6208
6209 pub fn dev_fd_read_dir(&self, requester_driver: &str, pid: u32) -> KernelResult<Vec<String>> {
6210 self.assert_driver_owns(requester_driver, pid)?;
6211 let tables = lock_or_recover(&self.fd_tables);
6212 let table = tables
6213 .get(pid)
6214 .ok_or_else(|| KernelError::no_such_process(pid))?;
6215 let entry_count = table.len();
6216 self.resources.check_readdir_entries(entry_count)?;
6217 Ok(table.iter().map(|entry| entry.fd.to_string()).collect())
6218 }
6219
6220 pub fn dev_fd_stat(
6221 &mut self,
6222 requester_driver: &str,
6223 pid: u32,
6224 fd: u32,
6225 ) -> KernelResult<VirtualStat> {
6226 self.assert_driver_owns(requester_driver, pid)?;
6227 let entry = {
6228 let tables = lock_or_recover(&self.fd_tables);
6229 tables
6230 .get(pid)
6231 .and_then(|table| table.get(fd))
6232 .cloned()
6233 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
6234 };
6235
6236 if let Some(pipe_id) = self.pipes.pipe_id_for(entry.description.id()) {
6237 let metadata = self
6238 .pipes
6239 .metadata(entry.description.id())
6240 .expect("live pipe description must retain inode metadata");
6241 let mut stat = synthetic_special_file_stat(pipe_id, 0o010000 | metadata.mode, 13);
6242 stat.uid = metadata.uid;
6243 stat.gid = metadata.gid;
6244 return Ok(stat);
6245 }
6246
6247 if let Some(socket) = lock_or_recover(&self.fd_sockets).get(&entry.description.id()) {
6248 let mut stat =
6249 synthetic_special_file_stat(entry.description.id(), 0o140000 | socket.mode, 9);
6250 stat.uid = socket.uid;
6251 stat.gid = socket.gid;
6252 return Ok(stat);
6253 }
6254
6255 if self.ptys.is_pty(entry.description.id()) {
6256 return Ok(synthetic_character_device_stat(entry.description.id()));
6257 }
6258
6259 if let Some(stat) = entry.description.anonymous_stat() {
6260 return Ok(stat);
6261 }
6262 if let Some(stat) = entry.description.detached_directory_stat() {
6263 return Ok(stat);
6264 }
6265 let path = entry.description.path();
6266 if is_proc_path(&path) {
6267 return self.proc_stat_from_open_path(Some(pid), &path);
6268 }
6269
6270 Ok(self.filesystem.stat(&path)?)
6271 }
6272
6273 pub fn dispose(&mut self) -> KernelResult<()> {
6274 if self.terminated {
6275 return Ok(());
6276 }
6277
6278 dispose_kernel_vm_resources(self);
6279 Ok(())
6280 }
6281
6282 fn prepare_fd_open(
6283 &mut self,
6284 path: &str,
6285 flags: u32,
6286 mode: Option<u32>,
6287 ) -> KernelResult<(u8, Option<FileLockTarget>)> {
6288 if flags & O_CREAT != 0 && flags & O_EXCL != 0 {
6289 self.check_write_file_limits(path, 0)?;
6290 VirtualFileSystem::create_file_exclusive_with_mode(
6291 &mut self.filesystem,
6292 path,
6293 Vec::new(),
6294 mode,
6295 )?;
6296 self.update_filesystem_usage_cache_for_inode_create(path, 0);
6297 let stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
6298 return Ok((
6299 filetype_for_path(path, &stat),
6300 Some(FileLockTarget::new(stat.dev, stat.ino)),
6301 ));
6302 }
6303
6304 let exists = self.filesystem.exists(path)?;
6305 if exists {
6306 let existing_stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
6307 if existing_stat.mode & 0o170000 == 0o140000 {
6308 return Err(KernelError::new(
6309 "ENXIO",
6310 format!("cannot open Unix socket pathname '{path}'"),
6311 ));
6312 }
6313 if flags & O_TRUNC != 0 {
6314 let existing_size = self.current_storage_file_size(path)?;
6315 self.check_path_resize_limits_with_existing(existing_size, 0)?;
6316 VirtualFileSystem::truncate(&mut self.filesystem, path, 0)?;
6317 self.update_filesystem_usage_cache_for_resize(path, existing_size, 0);
6318 }
6319 } else if flags & O_CREAT != 0 {
6320 self.check_write_file_limits(path, 0)?;
6321 VirtualFileSystem::write_file_with_mode(&mut self.filesystem, path, Vec::new(), mode)?;
6322 self.update_filesystem_usage_cache_for_inode_create(path, 0);
6323 } else {
6324 let _ = VirtualFileSystem::stat(&mut self.filesystem, path)?;
6325 unreachable!("stat should return an error when opening a missing path");
6326 }
6327
6328 let stat = VirtualFileSystem::stat(&mut self.filesystem, path)?;
6329 Ok((
6330 filetype_for_path(path, &stat),
6331 Some(FileLockTarget::new(stat.dev, stat.ino)),
6332 ))
6333 }
6334
6335 fn validate_fd_open_flags(&mut self, pid: u32, path: &str, flags: u32) -> KernelResult<()> {
6336 if flags & O_DIRECTORY != 0 && flags & O_CREAT != 0 {
6337 return Err(KernelError::new(
6338 "EINVAL",
6339 format!("O_DIRECTORY and O_CREAT cannot be combined for '{path}'"),
6340 ));
6341 }
6342
6343 if let Some(existing_fd) = parse_dev_fd_path(path)? {
6344 let filetype = lock_or_recover(&self.fd_tables)
6345 .get(pid)
6346 .and_then(|table| table.get(existing_fd))
6347 .map(|entry| entry.filetype)
6348 .ok_or_else(|| {
6349 KernelError::new(
6350 "ENOENT",
6351 format!("no such file or directory, open '{path}'"),
6352 )
6353 })?;
6354 if flags & O_CREAT != 0 && flags & O_EXCL != 0 {
6355 return Err(KernelError::new(
6356 "EEXIST",
6357 format!("file already exists, open '{path}'"),
6358 ));
6359 }
6360 if flags & O_NOFOLLOW != 0 {
6361 return Err(KernelError::new(
6362 "ELOOP",
6363 format!("symbolic link not followed, open '{path}'"),
6364 ));
6365 }
6366 if flags & O_DIRECTORY != 0 && filetype != FILETYPE_DIRECTORY {
6367 return Err(KernelError::new(
6368 "ENOTDIR",
6369 format!("not a directory, open '{path}'"),
6370 ));
6371 }
6372 return Ok(());
6373 }
6374
6375 if flags & O_DIRECTORY != 0 {
6376 let stat = if flags & O_NOFOLLOW != 0 {
6377 self.lstat_internal(Some(pid), path)?
6378 } else {
6379 self.stat_internal(Some(pid), path)?
6380 };
6381 if !stat.is_directory || stat.is_symbolic_link {
6382 return Err(KernelError::new(
6383 "ENOTDIR",
6384 format!("not a directory, open '{path}'"),
6385 ));
6386 }
6387 } else if flags & O_NOFOLLOW != 0 && flags & (O_CREAT | O_EXCL) != (O_CREAT | O_EXCL) {
6388 match self.lstat_internal(Some(pid), path) {
6389 Ok(stat) if stat.is_symbolic_link => {
6390 return Err(KernelError::new(
6391 "ELOOP",
6392 format!("symbolic link not followed, open '{path}'"),
6393 ));
6394 }
6395 Ok(_) => {}
6396 Err(error) if error.code() == "ENOENT" && flags & O_CREAT != 0 => {}
6397 Err(error) => return Err(error),
6398 }
6399 }
6400
6401 Ok(())
6402 }
6403
6404 fn reject_unix_socket_data_path(&mut self, path: &str, code: &'static str) -> KernelResult<()> {
6405 if self
6406 .storage_stat(path)?
6407 .is_some_and(|stat| stat.mode & 0o170000 == 0o140000)
6408 {
6409 return Err(KernelError::new(
6410 code,
6411 format!("Unix socket pathname does not support file data I/O, '{path}'"),
6412 ));
6413 }
6414 Ok(())
6415 }
6416
6417 fn reject_read_only_write_path(&mut self, path: &str) -> KernelResult<()> {
6418 if is_proc_path(path) {
6419 self.filesystem
6420 .check_virtual_path(FsOperation::Write, path)
6421 .map_err(KernelError::from)?;
6422 return Err(read_only_filesystem_error(path));
6423 }
6424
6425 if is_agentos_path(path) {
6426 return Err(read_only_filesystem_error(path));
6427 }
6428
6429 Ok(())
6430 }
6431
6432 fn reject_read_only_resolved_write_path(&mut self, path: &str) -> KernelResult<()> {
6433 self.reject_read_only_write_path(path)?;
6434
6435 if let Some(resolved) = self.resolve_write_guard_path(path, true)? {
6436 if is_agentos_path(&resolved) {
6437 return Err(read_only_filesystem_error(&resolved));
6438 }
6439 if self.has_agentos_hardlink_alias(&resolved)? {
6440 return Err(read_only_filesystem_error(&resolved));
6441 }
6442 }
6443 if self.has_agentos_hardlink_alias(path)? {
6444 return Err(read_only_filesystem_error(path));
6445 }
6446
6447 Ok(())
6448 }
6449
6450 fn reject_read_only_entry_write_path(&mut self, path: &str) -> KernelResult<()> {
6451 self.reject_read_only_write_path(path)?;
6452
6453 if let Some(resolved) = self.resolve_write_guard_path(path, false)? {
6454 if is_agentos_path(&resolved) {
6455 return Err(read_only_filesystem_error(&resolved));
6456 }
6457 if self.has_agentos_hardlink_alias(&resolved)? {
6458 return Err(read_only_filesystem_error(&resolved));
6459 }
6460 }
6461 if self.has_agentos_hardlink_alias(path)? {
6462 return Err(read_only_filesystem_error(path));
6463 }
6464
6465 Ok(())
6466 }
6467
6468 fn has_agentos_hardlink_alias(&mut self, path: &str) -> KernelResult<bool> {
6469 let Some(target) = self.storage_lstat(path)? else {
6470 return Ok(false);
6471 };
6472 if target.is_directory || target.is_symbolic_link {
6473 return Ok(false);
6474 }
6475
6476 self.agentos_subtree_contains_inode("/etc/agentos", target.dev, target.ino)
6477 }
6478
6479 fn agentos_subtree_contains_inode(
6480 &mut self,
6481 path: &str,
6482 target_dev: u64,
6483 target_ino: u64,
6484 ) -> KernelResult<bool> {
6485 let Some(stat) = self.storage_lstat(path)? else {
6486 return Ok(false);
6487 };
6488 if !stat.is_directory && !stat.is_symbolic_link {
6489 return Ok(stat.dev == target_dev && stat.ino == target_ino);
6490 }
6491 if !stat.is_directory {
6492 return Ok(false);
6493 }
6494
6495 let children = self.raw_filesystem_mut().read_dir_with_types(path)?;
6496 for child in children {
6497 if child.name == "." || child.name == ".." {
6498 continue;
6499 }
6500 let child_path = join_absolute_path(path, &child.name);
6501 if self.agentos_subtree_contains_inode(&child_path, target_dev, target_ino)? {
6502 return Ok(true);
6503 }
6504 }
6505
6506 Ok(false)
6507 }
6508
6509 fn resolve_write_guard_path(
6510 &mut self,
6511 path: &str,
6512 follow_final_symlink: bool,
6513 ) -> KernelResult<Option<String>> {
6514 let normalized = normalize_path(path);
6515 if normalized == "/" {
6516 return Ok(Some(normalized));
6517 }
6518
6519 if follow_final_symlink {
6520 if let Ok(resolved) = self.filesystem.realpath(&normalized) {
6521 return Ok(Some(resolved));
6522 }
6523 }
6524
6525 let components: Vec<&str> = normalized
6526 .split('/')
6527 .filter(|component| !component.is_empty())
6528 .collect();
6529 let mut resolved_prefix = String::from("/");
6530 let mut raw_prefix = String::from("/");
6531
6532 for (index, component) in components.iter().enumerate() {
6533 let is_final = index + 1 == components.len();
6534 if is_final && !follow_final_symlink {
6535 return Ok(Some(join_absolute_path(&resolved_prefix, component)));
6536 }
6537
6538 raw_prefix = join_absolute_path(&raw_prefix, component);
6539 match self.filesystem.realpath(&raw_prefix) {
6540 Ok(resolved) => {
6541 resolved_prefix = resolved;
6542 }
6543 Err(error) if error.code() == "ENOENT" => {
6544 let mut resolved = resolved_prefix;
6545 for remaining in &components[index..] {
6546 resolved = join_absolute_path(&resolved, remaining);
6547 }
6548 return Ok(Some(resolved));
6549 }
6550 Err(error) => return Err(error.into()),
6551 }
6552 }
6553
6554 Ok(Some(resolved_prefix))
6555 }
6556
6557 fn populate_poll_target_revents(
6558 &self,
6559 pid: u32,
6560 targets: &mut [PollTargetEntry],
6561 ) -> KernelResult<usize> {
6562 let mut ready_count = 0;
6563 for target in targets.iter_mut() {
6564 target.revents = self.poll_target_entry(pid, target.target, target.events)?;
6565 if !target.revents.is_empty() {
6566 ready_count += 1;
6567 }
6568 }
6569
6570 Ok(ready_count)
6571 }
6572
6573 fn poll_target_entry(
6574 &self,
6575 pid: u32,
6576 target: PollTarget,
6577 requested: PollEvents,
6578 ) -> KernelResult<PollEvents> {
6579 match target {
6580 PollTarget::Fd(fd) => {
6581 let entry = {
6582 let tables = lock_or_recover(&self.fd_tables);
6583 tables
6584 .get(pid)
6585 .ok_or_else(|| KernelError::no_such_process(pid))?
6586 .get(fd)
6587 .cloned()
6588 };
6589 if let Some(entry) = entry {
6590 self.poll_entry(&entry, requested)
6591 } else {
6592 Ok(POLLNVAL)
6593 }
6594 }
6595 PollTarget::Socket(socket_id) => {
6596 let socket = self.sockets.get(socket_id);
6597 if let Some(socket) = socket {
6598 if socket.owner_pid() != pid && socket.owner_pid() != 0 {
6599 return Err(KernelError::permission_denied(format!(
6600 "process {pid} does not own socket {socket_id}"
6601 )));
6602 }
6603 let mut events = self.sockets.poll(socket_id, requested)?;
6604 if events.intersects(POLLOUT)
6605 && !self.socket_pollout_has_resource_capacity(&socket)
6606 {
6607 events = PollEvents::from_bits(events.bits() & !POLLOUT.bits());
6608 }
6609 Ok(events)
6610 } else {
6611 Ok(POLLNVAL)
6612 }
6613 }
6614 }
6615 }
6616
6617 fn socket_pollout_has_resource_capacity(&self, socket: &SocketRecord) -> bool {
6618 #[cfg(not(target_arch = "wasm32"))]
6619 if self.sockets.has_resource_ledger() {
6620 return self.sockets.buffered_byte_capacity_available()
6621 && (socket.spec().socket_type != SocketType::Datagram
6622 || self.sockets.datagram_capacity_available());
6623 }
6624
6625 let snapshot = self.resource_snapshot();
6626 if self
6627 .resources
6628 .limits()
6629 .max_socket_buffered_bytes
6630 .is_some_and(|limit| snapshot.socket_buffered_bytes >= limit)
6631 {
6632 return false;
6633 }
6634
6635 if socket.spec().socket_type == SocketType::Datagram
6636 && self
6637 .resources
6638 .limits()
6639 .max_socket_datagram_queue_len
6640 .is_some_and(|limit| snapshot.socket_datagram_queue_len >= limit)
6641 {
6642 return false;
6643 }
6644
6645 true
6646 }
6647
6648 fn poll_entry(
6649 &self,
6650 entry: &crate::fd_table::FdEntry,
6651 requested: PollEvents,
6652 ) -> KernelResult<PollEvents> {
6653 if let Some(socket_id) = self.fd_socket_id(&entry.description) {
6654 let socket = self
6655 .sockets
6656 .get(socket_id)
6657 .ok_or_else(|| KernelError::bad_file_descriptor(entry.fd))?;
6658 let mut events = self.sockets.poll(socket_id, requested)?;
6659 if events.intersects(POLLOUT) && !self.socket_pollout_has_resource_capacity(&socket) {
6660 events = PollEvents::from_bits(events.bits() & !POLLOUT.bits());
6661 }
6662 return Ok(events);
6663 }
6664
6665 if self.pipes.is_pipe(entry.description.id()) {
6666 return Ok(self.pipes.poll(entry.description.id(), requested)?);
6667 }
6668
6669 if self.ptys.is_pty(entry.description.id()) {
6670 return Ok(self.ptys.poll(entry.description.id(), requested)?);
6671 }
6672
6673 let access_mode = entry.description.flags() & 0b11;
6674 let mut events = PollEvents::empty();
6675 if requested.intersects(POLLIN) && access_mode != crate::fd_table::O_WRONLY {
6676 events |= POLLIN;
6677 }
6678 if requested.intersects(POLLOUT) && access_mode != crate::fd_table::O_RDONLY {
6679 events |= POLLOUT;
6680 }
6681 if entry.filetype == FILETYPE_DIRECTORY && requested.intersects(POLLOUT) {
6682 events |= POLLERR;
6683 }
6684 if self.terminated {
6685 events |= POLLHUP;
6686 }
6687 Ok(events)
6688 }
6689
6690 fn description_for_fd(
6691 &self,
6692 requester_driver: &str,
6693 pid: u32,
6694 fd: u32,
6695 ) -> KernelResult<Arc<FileDescription>> {
6696 self.assert_driver_owns(requester_driver, pid)?;
6697 lock_or_recover(&self.fd_tables)
6698 .get(pid)
6699 .and_then(|table| table.get(fd))
6700 .map(|entry| Arc::clone(&entry.description))
6701 .ok_or_else(|| KernelError::bad_file_descriptor(fd))
6702 }
6703
6704 fn fd_socket_id(&self, description: &Arc<FileDescription>) -> Option<SocketId> {
6705 lock_or_recover(&self.fd_sockets)
6706 .get(&description.id())
6707 .filter(|entry| Arc::ptr_eq(&entry.description, description))
6708 .map(|entry| entry.socket_id)
6709 }
6710
6711 fn fd_socket_id_for_fd(&self, pid: u32, fd: u32) -> KernelResult<SocketId> {
6712 let description = {
6713 let tables = lock_or_recover(&self.fd_tables);
6714 tables
6715 .get(pid)
6716 .and_then(|table| table.get(fd))
6717 .map(|entry| Arc::clone(&entry.description))
6718 .ok_or_else(|| KernelError::bad_file_descriptor(fd))?
6719 };
6720 self.fd_socket_id(&description)
6721 .ok_or_else(|| KernelError::new("ENOTSOCK", "descriptor is not a socket"))
6722 }
6723
6724 fn open_file_descriptions(&self) -> Vec<Arc<FileDescription>> {
6725 let tables = lock_or_recover(&self.fd_tables);
6726 let mut descriptions = BTreeMap::new();
6727 for pid in tables.pids() {
6728 let Some(table) = tables.get(pid) else {
6729 continue;
6730 };
6731 for entry in table.values() {
6732 descriptions
6733 .entry(entry.description.id())
6734 .or_insert_with(|| Arc::clone(&entry.description));
6735 }
6736 }
6737 descriptions.into_values().collect()
6738 }
6739
6740 fn prepare_anonymous_file_backing(
6741 &mut self,
6742 path: &str,
6743 stat: Option<&VirtualStat>,
6744 ) -> KernelResult<Option<OpenFileRemovalBacking>> {
6745 let descriptions = self
6746 .open_file_descriptions()
6747 .into_iter()
6748 .filter(|description| description.is_path_backed_by(path))
6749 .collect::<Vec<_>>();
6750 if descriptions.is_empty() {
6751 return Ok(None);
6752 }
6753 let mut stat = match stat {
6754 Some(stat) if !stat.is_directory && !stat.is_symbolic_link => stat.clone(),
6755 _ => return Ok(None),
6756 };
6757 if stat.nlink > 1 {
6758 if let Some(live_path) = self.find_surviving_hard_link(path, &stat)? {
6759 return Ok(Some(OpenFileRemovalBacking::LinkedAlias {
6760 descriptions,
6761 live_path,
6762 }));
6763 }
6764 }
6765 stat.nlink = 0;
6766 let data = self.filesystem.read_file(path)?;
6767 let backing: SharedAnonymousFile = Arc::new(Mutex::new(AnonymousFile::new(
6768 data,
6769 stat,
6770 Arc::clone(&self.anonymous_file_usage),
6771 )));
6772 Ok(Some(OpenFileRemovalBacking::Anonymous {
6773 descriptions,
6774 backing,
6775 }))
6776 }
6777
6778 fn find_surviving_hard_link(
6779 &mut self,
6780 removed_path: &str,
6781 target: &VirtualStat,
6782 ) -> KernelResult<Option<String>> {
6783 let removed_path = normalize_path(removed_path);
6784 let mut queue = VecDeque::from([(String::from("/"), 0usize)]);
6785 let mut entries = 0usize;
6786 let per_directory_limit = self.resources.max_readdir_entries().unwrap_or(usize::MAX);
6787
6788 while let Some((directory, depth)) = queue.pop_front() {
6789 self.resources.check_recursive_fs_depth(depth)?;
6790 let names = self
6791 .raw_filesystem_mut()
6792 .read_dir_limited(&directory, per_directory_limit)?;
6793 self.resources.check_readdir_entries(names.len())?;
6794 for name in names {
6795 if matches!(name.as_str(), "." | "..") {
6796 continue;
6797 }
6798 entries = entries.saturating_add(1);
6799 self.resources.check_recursive_fs_entries(entries)?;
6800 let path = join_child_path(&directory, &name);
6801 let stat = match self.raw_filesystem_mut().lstat(&path) {
6802 Ok(stat) => stat,
6803 Err(error) if error.code() == "ENOENT" => continue,
6804 Err(error) => return Err(error.into()),
6805 };
6806 if path != removed_path && stat.dev == target.dev && stat.ino == target.ino {
6807 return Ok(Some(path));
6808 }
6809 if stat.is_directory && !stat.is_symbolic_link {
6810 queue.push_back((path, depth.saturating_add(1)));
6811 }
6812 }
6813 }
6814 Ok(None)
6815 }
6816
6817 fn prepare_detached_directory_backing(
6818 &self,
6819 path: &str,
6820 stat: Option<&VirtualStat>,
6821 ) -> Option<(Vec<Arc<FileDescription>>, VirtualStat)> {
6822 let mut stat = stat.filter(|stat| stat.is_directory)?.clone();
6823 stat.nlink = 0;
6824 let descriptions = self
6825 .open_file_descriptions()
6826 .into_iter()
6827 .filter(|description| {
6828 description.is_path_backed_by(path)
6829 || description
6830 .lock_target()
6831 .is_some_and(|target| target.ino() == stat.ino)
6832 })
6833 .collect::<Vec<_>>();
6834 (!descriptions.is_empty()).then_some((descriptions, stat))
6835 }
6836
6837 fn rename_open_file_descriptions(&self, old_path: &str, new_path: &str) {
6838 for description in self.open_file_descriptions() {
6839 description.rename_path_prefix(old_path, new_path);
6840 }
6841 }
6842
6843 fn assert_not_terminated(&self) -> KernelResult<()> {
6844 if self.terminated {
6845 Err(KernelError::disposed())
6846 } else {
6847 Ok(())
6848 }
6849 }
6850
6851 fn assert_driver_owns(&self, requester_driver: &str, pid: u32) -> KernelResult<()> {
6852 let driver_pids = lock_or_recover(&self.driver_pids);
6853 if driver_pids
6854 .get(requester_driver)
6855 .map(|pids| pids.contains(&pid))
6856 .unwrap_or(false)
6857 {
6858 return Ok(());
6859 }
6860
6861 if driver_pids.values().any(|pids| pids.contains(&pid)) {
6862 return Err(KernelError::permission_denied(format!(
6863 "driver \"{requester_driver}\" does not own PID {pid}"
6864 )));
6865 }
6866
6867 Err(KernelError::no_such_process(pid))
6868 }
6869
6870 fn cleanup_process_resources(&self, pid: u32) {
6871 cleanup_process_resources(
6872 self.fd_tables.as_ref(),
6873 &self.file_locks,
6874 &self.pipes,
6875 &self.ptys,
6876 &self.sockets,
6877 &self.fd_sockets,
6878 self.driver_pids.as_ref(),
6879 pid,
6880 );
6881 }
6882
6883 fn resolve_spawn_command(
6884 &mut self,
6885 command: &str,
6886 args: &[String],
6887 cwd: &str,
6888 parent_pid: Option<u32>,
6889 ) -> KernelResult<ResolvedSpawnCommand> {
6890 if let Some(driver) = self.commands.resolve(command).cloned() {
6891 return Ok(ResolvedSpawnCommand {
6892 command: command.to_owned(),
6893 args: args.to_vec(),
6894 driver,
6895 });
6896 }
6897
6898 let Some(path) = self.resolve_executable_path(command, cwd, parent_pid)? else {
6899 return Err(KernelError::command_not_found(command));
6900 };
6901
6902 if let Some(registered_command) = self.resolve_registered_command_path(&path) {
6903 let driver = self
6904 .commands
6905 .resolve(®istered_command)
6906 .cloned()
6907 .ok_or_else(|| KernelError::command_not_found(®istered_command))?;
6908 return Ok(ResolvedSpawnCommand {
6909 command: registered_command,
6910 args: args.to_vec(),
6911 driver,
6912 });
6913 }
6914
6915 let shebang = self
6916 .parse_shebang_command(&path)?
6917 .ok_or_else(|| KernelError::new("ENOEXEC", format!("exec format error: {path}")))?;
6918 self.resolve_shebang_command(&path, args, shebang)
6919 }
6920
6921 fn resolve_executable_path(
6922 &mut self,
6923 command: &str,
6924 cwd: &str,
6925 parent_pid: Option<u32>,
6926 ) -> KernelResult<Option<String>> {
6927 if !command.contains('/') {
6928 return Ok(None);
6929 }
6930
6931 let path = if command.starts_with('/') {
6932 normalize_path(command)
6933 } else {
6934 normalize_path(&format!("{cwd}/{command}"))
6935 };
6936 let path = self.filesystem.realpath(&path).unwrap_or(path);
6942 let stat = self.filesystem.stat(&path)?;
6943 if stat.is_directory {
6944 return Err(KernelError::new(
6945 "EACCES",
6946 format!("permission denied, execute '{path}'"),
6947 ));
6948 }
6949 let registered = self.resolve_registered_command_path(&path).is_some();
6953 if let Some(pid) = parent_pid {
6954 if !registered {
6955 self.check_dac_access(pid, &path, DAC_EXECUTE)?;
6956 }
6957 } else if stat.mode & EXECUTABLE_PERMISSION_BITS == 0 && !registered {
6958 return Err(KernelError::new(
6959 "EACCES",
6960 format!("permission denied, execute '{path}'"),
6961 ));
6962 }
6963 Ok(Some(path))
6964 }
6965
6966 fn validate_wasm_exec_image_inner(
6967 &mut self,
6968 path: &str,
6969 cwd: &str,
6970 interpreter_depth: usize,
6971 ) -> KernelResult<()> {
6972 let resolved = self.validate_executable_path(path, cwd)?;
6973 if self.resolve_registered_command_path(&resolved).is_some() {
6980 return Ok(());
6981 }
6982 let header = self
6983 .filesystem
6984 .pread(&resolved, 0, SHEBANG_LINE_MAX_BYTES)?;
6985 if header.starts_with(b"\0asm") {
6986 return Ok(());
6987 }
6988
6989 let Some(interpreter) = linux_shebang_interpreter(&header, &resolved)? else {
6990 return Err(KernelError::new(
6991 "ENOEXEC",
6992 format!("exec format error: {resolved}"),
6993 ));
6994 };
6995 if interpreter_depth >= MAX_EXEC_INTERPRETER_DEPTH {
6996 return Err(KernelError::new(
6997 "ELOOP",
6998 format!("too many levels of symbolic links or interpreters: {resolved}"),
6999 ));
7000 }
7001
7002 self.validate_wasm_exec_image_inner(&interpreter, cwd, interpreter_depth + 1)
7003 }
7004
7005 fn resolve_registered_command_path(&self, path: &str) -> Option<String> {
7006 let normalized = normalize_path(path);
7007 for prefix in ["/bin/", "/usr/bin/", "/usr/local/bin/"] {
7008 let Some(name) = normalized.strip_prefix(prefix) else {
7009 continue;
7010 };
7011 if !name.is_empty() && !name.contains('/') && self.commands.resolve(name).is_some() {
7012 return Some(name.to_owned());
7013 }
7014 }
7015
7016 if let Some(name) = normalized
7017 .strip_prefix("/__secure_exec/commands/")
7018 .and_then(|suffix| suffix.rsplit('/').next())
7019 {
7020 if !name.is_empty() && !name.contains('/') && self.commands.resolve(name).is_some() {
7021 return Some(name.to_owned());
7022 }
7023 }
7024
7025 None
7026 }
7027
7028 fn parse_shebang_command(&mut self, path: &str) -> KernelResult<Option<ShebangCommand>> {
7029 let header = self.filesystem.pread(path, 0, SHEBANG_LINE_MAX_BYTES + 1)?;
7030 if !header.starts_with(b"#!") {
7031 return Ok(None);
7032 }
7033
7034 let line_end = match header.iter().position(|byte| *byte == b'\n') {
7035 Some(index) => index,
7036 None if header.len() <= SHEBANG_LINE_MAX_BYTES => header.len(),
7037 None => {
7038 return Err(KernelError::new(
7039 "ENOEXEC",
7040 format!("shebang line exceeds {SHEBANG_LINE_MAX_BYTES} bytes: {path}"),
7041 ));
7042 }
7043 };
7044 let line = header[2..line_end]
7045 .strip_suffix(b"\r")
7046 .unwrap_or(&header[2..line_end]);
7047 let text = std::str::from_utf8(line)
7048 .map_err(|_| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
7049 let mut parts = text.split_ascii_whitespace();
7050 let interpreter = parts
7051 .next()
7052 .ok_or_else(|| KernelError::new("ENOEXEC", format!("invalid shebang line: {path}")))?;
7053 Ok(Some(ShebangCommand {
7054 interpreter: interpreter.to_owned(),
7055 args: parts.map(ToOwned::to_owned).collect(),
7056 }))
7057 }
7058
7059 fn resolve_shebang_command(
7060 &self,
7061 path: &str,
7062 args: &[String],
7063 shebang: ShebangCommand,
7064 ) -> KernelResult<ResolvedSpawnCommand> {
7065 let mut interpreter_args = shebang.args;
7066 let interpreter = normalize_path(&shebang.interpreter);
7067 let command = if interpreter == "/usr/bin/env" || interpreter == "/bin/env" {
7068 if interpreter_args.is_empty() {
7069 return Err(KernelError::new(
7070 "ENOENT",
7071 format!("missing interpreter after /usr/bin/env in shebang: {path}"),
7072 ));
7073 }
7074 interpreter_args.remove(0)
7075 } else if let Some(command) = self.resolve_registered_command_path(&interpreter) {
7076 command
7077 } else if self.commands.resolve(&shebang.interpreter).is_some() {
7078 shebang.interpreter
7079 } else {
7080 return Err(KernelError::command_not_found(&shebang.interpreter));
7081 };
7082
7083 let driver = self
7084 .commands
7085 .resolve(&command)
7086 .cloned()
7087 .ok_or_else(|| KernelError::command_not_found(&command))?;
7088 let mut resolved_args = interpreter_args;
7089 resolved_args.push(path.to_owned());
7090 resolved_args.extend(args.iter().cloned());
7091 Ok(ResolvedSpawnCommand {
7092 command,
7093 args: resolved_args,
7094 driver,
7095 })
7096 }
7097
7098 fn finish_waitpid_event(&mut self, result: ProcessWaitResult) -> WaitPidEventResult {
7099 if result.event == WaitPidEvent::Exited {
7100 self.cleanup_process_resources(result.pid);
7101 }
7102 WaitPidEventResult {
7103 pid: result.pid,
7104 status: result.status,
7105 event: result.event,
7106 }
7107 }
7108
7109 fn raw_filesystem_mut(&mut self) -> &mut F {
7110 self.filesystem.inner_mut().inner_mut()
7111 }
7112
7113 fn read_file_internal(
7114 &mut self,
7115 current_pid: Option<u32>,
7116 path: &str,
7117 ) -> KernelResult<Vec<u8>> {
7118 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
7119 self.filesystem
7120 .check_virtual_path(FsOperation::Read, path)
7121 .map_err(KernelError::from)?;
7122 return self.proc_read_file(current_pid, &proc_node);
7123 }
7124
7125 self.reject_unix_socket_data_path(path, "ENXIO")?;
7126 Ok(self.filesystem.read_file(path)?)
7127 }
7128
7129 fn effective_recursive_fs_depth(
7130 &self,
7131 requested_max_depth: Option<usize>,
7132 ) -> KernelResult<usize> {
7133 match (requested_max_depth, self.resources.max_recursive_fs_depth()) {
7134 (Some(requested), Some(limit)) if requested > limit => Err(KernelError::new(
7135 "EINVAL",
7136 format!(
7137 "requested recursive filesystem max depth {requested} exceeds configured limit {limit}"
7138 ),
7139 )),
7140 (Some(requested), _) => Ok(requested),
7141 (None, Some(limit)) => Ok(limit),
7142 (None, None) => Ok(usize::MAX),
7143 }
7144 }
7145
7146 fn copy_path_inner(
7147 &mut self,
7148 from: &str,
7149 to: &str,
7150 recursive: bool,
7151 depth: usize,
7152 entries: &mut usize,
7153 ) -> KernelResult<()> {
7154 self.resources.check_recursive_fs_depth(depth)?;
7155 *entries = entries.saturating_add(1);
7156 self.resources.check_recursive_fs_entries(*entries)?;
7157 let source_stat = self.lstat_internal(None, from)?;
7158
7159 if source_stat.is_symbolic_link {
7160 let target = self.read_link_internal(None, from)?;
7161 self.symlink(&target, to)?;
7162 return Ok(());
7163 }
7164
7165 if source_stat.is_directory {
7166 if !recursive {
7167 return Err(KernelError::new(
7168 "EISDIR",
7169 format!("illegal operation on a directory, copy '{from}'"),
7170 ));
7171 }
7172
7173 let source_root = normalize_path(from);
7174 let destination_root = normalize_path(to);
7175 if destination_root.starts_with(&(source_root.clone() + "/")) {
7176 return Err(KernelError::new(
7177 "EINVAL",
7178 format!("cannot copy '{from}' into its own descendant '{to}'"),
7179 ));
7180 }
7181
7182 self.mkdir(&parent_path(&destination_root), true)?;
7183 if !self.exists_internal(None, &destination_root)? {
7184 self.create_dir(&destination_root)?;
7185 }
7186 self.chmod(&destination_root, source_stat.mode)?;
7187 self.chown(&destination_root, source_stat.uid, source_stat.gid)?;
7188
7189 let names = self.read_dir_internal(None, from)?;
7190 self.resources.check_readdir_entries(names.len())?;
7191 for name in names {
7192 if matches!(name.as_str(), "." | "..") {
7193 continue;
7194 }
7195 let child_from = join_child_path(from, &name);
7196 let child_to = join_child_path(to, &name);
7197 self.copy_path_inner(
7198 &child_from,
7199 &child_to,
7200 true,
7201 depth.saturating_add(1),
7202 entries,
7203 )?;
7204 }
7205 return Ok(());
7206 }
7207
7208 let content = self.read_file_internal(None, from)?;
7209 self.write_file(to, content)?;
7210 self.chmod(to, source_stat.mode)?;
7211 self.chown(to, source_stat.uid, source_stat.gid)
7212 }
7213
7214 fn remove_path_inner(
7215 &mut self,
7216 path: &str,
7217 recursive: bool,
7218 depth: usize,
7219 entries: &mut usize,
7220 ) -> KernelResult<()> {
7221 self.resources.check_recursive_fs_depth(depth)?;
7222 *entries = entries.saturating_add(1);
7223 self.resources.check_recursive_fs_entries(*entries)?;
7224 let stat = self.lstat_internal(None, path)?;
7225 if stat.is_directory && !stat.is_symbolic_link {
7226 if recursive {
7227 let names = self.read_dir_internal(None, path)?;
7228 self.resources.check_readdir_entries(names.len())?;
7229 for name in names {
7230 if matches!(name.as_str(), "." | "..") {
7231 continue;
7232 }
7233 let child = join_child_path(path, &name);
7234 self.remove_path_inner(&child, true, depth.saturating_add(1), entries)?;
7235 }
7236 }
7237 return self.remove_dir(path);
7238 }
7239
7240 self.remove_file(path)
7241 }
7242
7243 fn exists_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<bool> {
7244 match self.resolve_proc_node(path, current_pid) {
7245 Ok(Some(_)) => {
7246 self.filesystem
7247 .check_virtual_path(FsOperation::Read, path)
7248 .map_err(KernelError::from)?;
7249 Ok(true)
7250 }
7251 Ok(None) => Ok(self.filesystem.exists(path)?),
7252 Err(error) if error.code() == "ENOENT" => Ok(false),
7253 Err(error) => Err(error),
7254 }
7255 }
7256
7257 fn stat_internal(&mut self, current_pid: Option<u32>, path: &str) -> KernelResult<VirtualStat> {
7258 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
7259 self.filesystem
7260 .check_virtual_path(FsOperation::Read, path)
7261 .map_err(KernelError::from)?;
7262 return self.proc_stat(current_pid, &proc_node);
7263 }
7264
7265 Ok(self.filesystem.stat(path)?)
7266 }
7267
7268 fn lstat_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<VirtualStat> {
7269 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
7270 self.filesystem
7271 .check_virtual_path(FsOperation::Read, path)
7272 .map_err(KernelError::from)?;
7273 return self.proc_lstat(&proc_node);
7274 }
7275
7276 Ok(self.filesystem.lstat(path)?)
7277 }
7278
7279 fn read_link_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<String> {
7280 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
7281 self.filesystem
7282 .check_virtual_path(FsOperation::Read, path)
7283 .map_err(KernelError::from)?;
7284 return self.proc_read_link(&proc_node);
7285 }
7286
7287 Ok(self.filesystem.read_link(path)?)
7288 }
7289
7290 fn read_dir_internal(
7291 &mut self,
7292 current_pid: Option<u32>,
7293 path: &str,
7294 ) -> KernelResult<Vec<String>> {
7295 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
7296 self.filesystem
7297 .check_virtual_path(FsOperation::Read, path)
7298 .map_err(KernelError::from)?;
7299 return self.proc_read_dir(current_pid, &proc_node);
7300 }
7301
7302 if let Some(limit) = self.resources.max_readdir_entries() {
7303 Ok(self.filesystem.read_dir_limited(path, limit)?)
7304 } else {
7305 Ok(self.filesystem.read_dir(path)?)
7306 }
7307 }
7308
7309 fn read_dir_with_types_internal(
7310 &mut self,
7311 current_pid: Option<u32>,
7312 path: &str,
7313 ) -> KernelResult<Vec<VirtualDirEntry>> {
7314 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
7315 self.filesystem
7316 .check_virtual_path(FsOperation::Read, path)
7317 .map_err(KernelError::from)?;
7318 return Ok(self
7319 .proc_read_dir(current_pid, &proc_node)?
7320 .into_iter()
7321 .map(|name| VirtualDirEntry {
7322 name,
7323 is_directory: false,
7324 is_symbolic_link: false,
7325 })
7326 .collect());
7327 }
7328
7329 Ok(self.filesystem.read_dir_with_types(path)?)
7330 }
7331
7332 fn realpath_internal(&self, current_pid: Option<u32>, path: &str) -> KernelResult<String> {
7333 if let Some(proc_node) = self.resolve_proc_node(path, current_pid)? {
7334 self.filesystem
7335 .check_virtual_path(FsOperation::Read, path)
7336 .map_err(KernelError::from)?;
7337 return self.proc_realpath(current_pid, &proc_node);
7338 }
7339
7340 Ok(self.filesystem.realpath(path)?)
7341 }
7342
7343 fn resolve_proc_node(
7344 &self,
7345 path: &str,
7346 current_pid: Option<u32>,
7347 ) -> KernelResult<Option<ProcNode>> {
7348 let normalized = normalize_path(path);
7349 if !is_proc_path(&normalized) {
7350 return Ok(None);
7351 }
7352
7353 if normalized == "/proc" {
7354 return Ok(Some(ProcNode::RootDir));
7355 }
7356
7357 let suffix = normalized
7358 .strip_prefix("/proc/")
7359 .expect("proc path should have /proc prefix");
7360 let parts = suffix.split('/').collect::<Vec<_>>();
7361 if parts.is_empty() {
7362 return Ok(Some(ProcNode::RootDir));
7363 }
7364
7365 let root_node = match parts.as_slice() {
7366 ["mounts"] => Some(ProcNode::MountsFile),
7367 ["cpuinfo"] => Some(ProcNode::CpuInfoFile),
7368 ["meminfo"] => Some(ProcNode::MemInfoFile),
7369 ["loadavg"] => Some(ProcNode::LoadAvgFile),
7370 ["uptime"] => Some(ProcNode::UptimeFile),
7371 ["version"] => Some(ProcNode::VersionFile),
7372 _ => None,
7373 };
7374 if let Some(node) = root_node {
7375 return Ok(Some(node));
7376 }
7377
7378 let pid = match parts[0] {
7379 "self" => current_pid.ok_or_else(|| proc_not_found_error(&normalized))?,
7380 raw => raw
7381 .parse::<u32>()
7382 .map_err(|_| proc_not_found_error(&normalized))?,
7383 };
7384 self.proc_entry(pid)?;
7385
7386 let node = match parts.as_slice() {
7387 ["self"] => ProcNode::SelfLink { pid },
7388 [_pid] => ProcNode::PidDir { pid },
7389 [_pid, "fd"] => ProcNode::PidFdDir { pid },
7390 [_pid, "cmdline"] => ProcNode::PidCmdline { pid },
7391 [_pid, "environ"] => ProcNode::PidEnviron { pid },
7392 [_pid, "cwd"] => ProcNode::PidCwdLink { pid },
7393 [_pid, "stat"] => ProcNode::PidStatFile { pid },
7394 [_pid, "status"] => ProcNode::PidStatusFile { pid },
7395 [_pid, "fd", fd] => {
7396 let fd = fd
7397 .parse::<u32>()
7398 .map_err(|_| proc_not_found_error(&normalized))?;
7399 self.proc_fd_entry(pid, fd)?;
7400 ProcNode::PidFdLink { pid, fd }
7401 }
7402 _ => return Err(proc_not_found_error(&normalized)),
7403 };
7404
7405 Ok(Some(node))
7406 }
7407
7408 fn proc_entry(&self, pid: u32) -> KernelResult<crate::process_table::ProcessEntry> {
7409 self.processes
7410 .get(pid)
7411 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}")))
7412 }
7413
7414 fn proc_fd_entry(&self, pid: u32, fd: u32) -> KernelResult<FdEntry> {
7415 lock_or_recover(&self.fd_tables)
7416 .get(pid)
7417 .and_then(|table| table.get(fd))
7418 .cloned()
7419 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}/fd/{fd}")))
7420 }
7421
7422 fn proc_read_file(
7423 &mut self,
7424 current_pid: Option<u32>,
7425 node: &ProcNode,
7426 ) -> KernelResult<Vec<u8>> {
7427 match node {
7428 ProcNode::SelfLink { .. }
7429 | ProcNode::PidCwdLink { .. }
7430 | ProcNode::PidFdLink { .. } => {
7431 let target = self.proc_symlink_target(node)?;
7432 self.read_file_internal(current_pid, &target)
7433 }
7434 ProcNode::MountsFile => Ok(self.proc_mounts_bytes()),
7435 ProcNode::CpuInfoFile => Ok(self.proc_cpuinfo_bytes()),
7436 ProcNode::MemInfoFile => Ok(self.proc_meminfo_bytes()),
7437 ProcNode::LoadAvgFile => Ok(self.proc_loadavg_bytes()),
7438 ProcNode::UptimeFile => Ok(self.proc_uptime_bytes()),
7439 ProcNode::VersionFile => Ok(self.proc_version_bytes()),
7440 ProcNode::PidCmdline { pid } => Ok(self.proc_cmdline_bytes(*pid)),
7441 ProcNode::PidEnviron { pid } => Ok(self.proc_environ_bytes(*pid)),
7442 ProcNode::PidStatFile { pid } => Ok(self.proc_stat_bytes(*pid)),
7443 ProcNode::PidStatusFile { pid } => Ok(self.proc_status_bytes(*pid)),
7444 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
7445 Err(KernelError::new(
7446 "EISDIR",
7447 format!(
7448 "illegal operation on a directory, read '{}'",
7449 self.proc_canonical_path(node)
7450 ),
7451 ))
7452 }
7453 }
7454 }
7455
7456 fn proc_stat(
7457 &mut self,
7458 current_pid: Option<u32>,
7459 node: &ProcNode,
7460 ) -> KernelResult<VirtualStat> {
7461 match node {
7462 ProcNode::SelfLink { .. }
7463 | ProcNode::PidCwdLink { .. }
7464 | ProcNode::PidFdLink { .. } => {
7465 let target = self.proc_symlink_target(node)?;
7466 self.stat_internal(current_pid, &target)
7467 }
7468 _ => self.proc_lstat(node),
7469 }
7470 }
7471
7472 fn proc_lstat(&self, node: &ProcNode) -> KernelResult<VirtualStat> {
7473 match node {
7474 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
7475 Ok(proc_dir_stat(proc_inode(node)))
7476 }
7477 ProcNode::MountsFile => Ok(proc_file_stat(
7478 proc_inode(node),
7479 self.proc_mounts_bytes().len() as u64,
7480 )),
7481 ProcNode::CpuInfoFile => Ok(proc_file_stat(
7482 proc_inode(node),
7483 self.proc_cpuinfo_bytes().len() as u64,
7484 )),
7485 ProcNode::MemInfoFile => Ok(proc_file_stat(
7486 proc_inode(node),
7487 self.proc_meminfo_bytes().len() as u64,
7488 )),
7489 ProcNode::LoadAvgFile => Ok(proc_file_stat(
7490 proc_inode(node),
7491 self.proc_loadavg_bytes().len() as u64,
7492 )),
7493 ProcNode::UptimeFile => Ok(proc_file_stat(
7494 proc_inode(node),
7495 self.proc_uptime_bytes().len() as u64,
7496 )),
7497 ProcNode::VersionFile => Ok(proc_file_stat(
7498 proc_inode(node),
7499 self.proc_version_bytes().len() as u64,
7500 )),
7501 ProcNode::PidCmdline { pid } => Ok(proc_file_stat(
7502 proc_inode(node),
7503 self.proc_cmdline_bytes(*pid).len() as u64,
7504 )),
7505 ProcNode::PidEnviron { pid } => Ok(proc_file_stat(
7506 proc_inode(node),
7507 self.proc_environ_bytes(*pid).len() as u64,
7508 )),
7509 ProcNode::PidStatFile { pid } => Ok(proc_file_stat(
7510 proc_inode(node),
7511 self.proc_stat_bytes(*pid).len() as u64,
7512 )),
7513 ProcNode::PidStatusFile { pid } => Ok(proc_file_stat(
7514 proc_inode(node),
7515 self.proc_status_bytes(*pid).len() as u64,
7516 )),
7517 ProcNode::SelfLink { .. }
7518 | ProcNode::PidCwdLink { .. }
7519 | ProcNode::PidFdLink { .. } => Ok(proc_symlink_stat(
7520 proc_inode(node),
7521 self.proc_read_link(node)?.len() as u64,
7522 )),
7523 }
7524 }
7525
7526 fn proc_read_link(&self, node: &ProcNode) -> KernelResult<String> {
7527 match node {
7528 ProcNode::SelfLink { .. }
7529 | ProcNode::PidCwdLink { .. }
7530 | ProcNode::PidFdLink { .. } => self.proc_symlink_target(node),
7531 _ => Err(KernelError::new(
7532 "EINVAL",
7533 format!(
7534 "invalid argument, readlink '{}'",
7535 self.proc_canonical_path(node)
7536 ),
7537 )),
7538 }
7539 }
7540
7541 fn proc_read_dir(
7542 &mut self,
7543 current_pid: Option<u32>,
7544 node: &ProcNode,
7545 ) -> KernelResult<Vec<String>> {
7546 match node {
7547 ProcNode::SelfLink { .. }
7548 | ProcNode::PidCwdLink { .. }
7549 | ProcNode::PidFdLink { .. } => {
7550 let target = self.proc_symlink_target(node)?;
7551 self.read_dir_internal(current_pid, &target)
7552 }
7553 ProcNode::RootDir => {
7554 let mut entries = self
7555 .processes
7556 .list_processes()
7557 .keys()
7558 .map(|pid| pid.to_string())
7559 .collect::<Vec<_>>();
7560 entries.push(String::from("cpuinfo"));
7561 entries.push(String::from("loadavg"));
7562 entries.push(String::from("meminfo"));
7563 entries.push(String::from("mounts"));
7564 entries.push(String::from("self"));
7565 entries.push(String::from("uptime"));
7566 entries.push(String::from("version"));
7567 entries.sort();
7568 Ok(entries)
7569 }
7570 ProcNode::PidDir { .. } => Ok(vec![
7571 String::from("cmdline"),
7572 String::from("cwd"),
7573 String::from("environ"),
7574 String::from("fd"),
7575 String::from("stat"),
7576 String::from("status"),
7577 ]),
7578 ProcNode::PidFdDir { pid } => {
7579 let tables = lock_or_recover(&self.fd_tables);
7580 let table = tables
7581 .get(*pid)
7582 .ok_or_else(|| proc_not_found_error(&format!("/proc/{pid}/fd")))?;
7583 Ok(table.iter().map(|entry| entry.fd.to_string()).collect())
7584 }
7585 _ => Err(KernelError::new(
7586 "ENOTDIR",
7587 format!(
7588 "not a directory, scandir '{}'",
7589 self.proc_canonical_path(node)
7590 ),
7591 )),
7592 }
7593 }
7594
7595 fn proc_realpath(&self, current_pid: Option<u32>, node: &ProcNode) -> KernelResult<String> {
7596 match node {
7597 ProcNode::SelfLink { .. }
7598 | ProcNode::PidCwdLink { .. }
7599 | ProcNode::PidFdLink { .. } => {
7600 let target = self.proc_symlink_target(node)?;
7601 self.realpath_internal(current_pid, &target)
7602 }
7603 _ => Ok(self.proc_canonical_path(node)),
7604 }
7605 }
7606
7607 fn proc_symlink_target(&self, node: &ProcNode) -> KernelResult<String> {
7608 match node {
7609 ProcNode::SelfLink { pid } => Ok(format!("/proc/{pid}")),
7610 ProcNode::PidCwdLink { pid } => Ok(self.proc_entry(*pid)?.cwd),
7611 ProcNode::PidFdLink { pid, fd } => Ok(self
7612 .proc_fd_entry(*pid, *fd)?
7613 .description
7614 .proc_display_path()),
7615 _ => Err(KernelError::new(
7616 "EINVAL",
7617 format!(
7618 "'{}' is not a symbolic link",
7619 self.proc_canonical_path(node)
7620 ),
7621 )),
7622 }
7623 }
7624
7625 fn proc_canonical_path(&self, node: &ProcNode) -> String {
7626 match node {
7627 ProcNode::RootDir => String::from("/proc"),
7628 ProcNode::MountsFile => String::from("/proc/mounts"),
7629 ProcNode::CpuInfoFile => String::from("/proc/cpuinfo"),
7630 ProcNode::MemInfoFile => String::from("/proc/meminfo"),
7631 ProcNode::LoadAvgFile => String::from("/proc/loadavg"),
7632 ProcNode::UptimeFile => String::from("/proc/uptime"),
7633 ProcNode::VersionFile => String::from("/proc/version"),
7634 ProcNode::SelfLink { pid } => format!("/proc/{pid}"),
7635 ProcNode::PidDir { pid } => format!("/proc/{pid}"),
7636 ProcNode::PidFdDir { pid } => format!("/proc/{pid}/fd"),
7637 ProcNode::PidCmdline { pid } => format!("/proc/{pid}/cmdline"),
7638 ProcNode::PidEnviron { pid } => format!("/proc/{pid}/environ"),
7639 ProcNode::PidCwdLink { pid } => format!("/proc/{pid}/cwd"),
7640 ProcNode::PidStatFile { pid } => format!("/proc/{pid}/stat"),
7641 ProcNode::PidStatusFile { pid } => format!("/proc/{pid}/status"),
7642 ProcNode::PidFdLink { pid, fd } => format!("/proc/{pid}/fd/{fd}"),
7643 }
7644 }
7645
7646 fn proc_cmdline_bytes(&self, pid: u32) -> Vec<u8> {
7647 let entry = self
7648 .processes
7649 .get(pid)
7650 .expect("process must exist while procfs path is resolved");
7651 let mut argv = vec![entry.command];
7652 argv.extend(entry.args);
7653 null_separated_bytes(argv)
7654 }
7655
7656 fn proc_environ_bytes(&self, pid: u32) -> Vec<u8> {
7657 let entry = self
7658 .processes
7659 .get(pid)
7660 .expect("process must exist while procfs path is resolved");
7661 null_separated_bytes(
7662 entry
7663 .env
7664 .into_iter()
7665 .map(|(key, value)| format!("{key}={value}"))
7666 .collect(),
7667 )
7668 }
7669
7670 fn proc_stat_bytes(&self, pid: u32) -> Vec<u8> {
7671 let entry = self
7672 .processes
7673 .get(pid)
7674 .expect("process must exist while procfs path is resolved");
7675 let command = entry.command.replace(')', "]");
7676 let state = match entry.status {
7677 ProcessStatus::Running => 'R',
7678 ProcessStatus::Stopped => 'T',
7679 ProcessStatus::Exited => 'Z',
7680 };
7681 format!(
7682 "{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",
7683 ppid = entry.ppid,
7684 pgid = entry.pgid,
7685 sid = entry.sid,
7686 )
7687 .into_bytes()
7688 }
7689
7690 fn proc_mounts_bytes(&self) -> Vec<u8> {
7691 let mounts = if let Some(table) =
7692 (self.filesystem.inner().inner() as &dyn Any).downcast_ref::<MountTable>()
7693 {
7694 table.get_mounts()
7695 } else {
7696 vec![MountEntry {
7697 path: String::from("/"),
7698 plugin_id: String::from("root"),
7699 guest_source: String::from("root"),
7700 guest_fstype: String::from("root"),
7701 read_only: false,
7702 access_time: crate::mount_table::AccessTimePolicy::Relatime,
7703 no_dir_atime: false,
7704 }]
7705 };
7706
7707 mounts
7708 .into_iter()
7709 .map(|mount| {
7710 let options = mount.option_string();
7711 format!(
7712 "{source} {target} {fstype} {options} 0 0\n",
7713 source = mount.guest_source,
7714 target = mount.path,
7715 fstype = mount.guest_fstype,
7716 )
7717 })
7718 .collect::<String>()
7719 .into_bytes()
7720 }
7721
7722 fn proc_cpu_count(&self) -> usize {
7723 self.resource_limits().virtual_cpu_count.unwrap_or(1)
7724 }
7725
7726 fn proc_cpuinfo_bytes(&self) -> Vec<u8> {
7727 let mut body = String::new();
7728 for processor in 0..self.proc_cpu_count() {
7729 body.push_str(&format!(
7730 "processor\t: {processor}\nmodel name\t: secure-exec Virtual CPU\ncpu MHz\t\t: 1000.000\nsiblings\t: 1\ncpu cores\t: 1\n\n"
7731 ));
7732 }
7733 body.into_bytes()
7734 }
7735
7736 fn proc_mem_total_bytes(&self) -> u64 {
7737 self.resource_limits()
7738 .max_wasm_memory_bytes
7739 .or(self.resource_limits().max_filesystem_bytes)
7740 .unwrap_or(DEFAULT_MAX_OPEN_FDS as u64 * 1024 * 1024)
7741 }
7742
7743 fn proc_meminfo_bytes(&self) -> Vec<u8> {
7744 let total_kb = self.proc_mem_total_bytes().div_ceil(1024);
7745 let zero_kb = 0;
7746 format!(
7747 "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"
7748 )
7749 .into_bytes()
7750 }
7751
7752 fn proc_loadavg_bytes(&self) -> Vec<u8> {
7753 let processes = self.processes.list_processes();
7754 let running = processes
7755 .values()
7756 .filter(|process| process.status == ProcessStatus::Running)
7757 .count();
7758 let total = processes.len().max(1);
7759 let last_pid = processes.keys().next_back().copied().unwrap_or(0);
7760 format!("0.00 0.00 0.00 {running}/{total} {last_pid}\n").into_bytes()
7761 }
7762
7763 fn proc_uptime_bytes(&self) -> Vec<u8> {
7764 let uptime = self.boot_instant.elapsed().as_secs_f64();
7765 format!("{uptime:.2} {uptime:.2}\n").into_bytes()
7766 }
7767
7768 fn proc_version_bytes(&self) -> Vec<u8> {
7769 format!(
7770 "Linux version 6.8.0-agentos (agentos@localhost) #1 SMP boot={}\n",
7771 self.boot_time_ms
7772 )
7773 .into_bytes()
7774 }
7775
7776 fn proc_status_bytes(&self, pid: u32) -> Vec<u8> {
7777 let entry = self
7778 .processes
7779 .get(pid)
7780 .expect("process must exist while procfs path is resolved");
7781 let (state_code, state_name) = match entry.status {
7782 ProcessStatus::Running => ('R', "running"),
7783 ProcessStatus::Stopped => ('T', "stopped"),
7784 ProcessStatus::Exited => ('Z', "zombie"),
7785 };
7786 format!(
7787 "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",
7788 0,
7789 0,
7790 name = entry.command,
7791 ppid = entry.ppid,
7792 uid = entry.identity.uid,
7793 euid = entry.identity.euid,
7794 gid = entry.identity.gid,
7795 egid = entry.identity.egid,
7796 )
7797 .into_bytes()
7798 }
7799
7800 fn proc_read_file_from_open_path(
7801 &mut self,
7802 current_pid: Option<u32>,
7803 path: &str,
7804 ) -> KernelResult<Vec<u8>> {
7805 let node = self
7806 .resolve_proc_node(path, current_pid)?
7807 .ok_or_else(|| proc_not_found_error(path))?;
7808 self.proc_read_file(current_pid, &node)
7809 }
7810
7811 fn proc_stat_from_open_path(
7812 &mut self,
7813 current_pid: Option<u32>,
7814 path: &str,
7815 ) -> KernelResult<VirtualStat> {
7816 let node = self
7817 .resolve_proc_node(path, current_pid)?
7818 .ok_or_else(|| proc_not_found_error(path))?;
7819 self.proc_stat(current_pid, &node)
7820 }
7821
7822 fn filesystem_usage(&mut self) -> KernelResult<FileSystemUsage> {
7823 if let Some(linked_usage) = self.filesystem_usage_cache.clone() {
7824 return Ok(FileSystemUsage {
7825 total_bytes: linked_usage
7826 .total_bytes
7827 .saturating_add(self.anonymous_file_usage.bytes()),
7828 inode_count: linked_usage
7829 .inode_count
7830 .saturating_add(self.anonymous_file_usage.inodes()),
7831 });
7832 }
7833 let filesystem = self.raw_filesystem_mut();
7834 let filesystem_any = filesystem as &mut dyn Any;
7835 let linked_usage = if let Some(mount_table) = filesystem_any.downcast_mut::<MountTable>() {
7836 mount_table.root_usage()?
7837 } else {
7838 measure_filesystem_usage(filesystem)?
7839 };
7840 self.filesystem_usage_cache = Some(linked_usage.clone());
7841 Ok(FileSystemUsage {
7842 total_bytes: linked_usage
7843 .total_bytes
7844 .saturating_add(self.anonymous_file_usage.bytes()),
7845 inode_count: linked_usage
7846 .inode_count
7847 .saturating_add(self.anonymous_file_usage.inodes()),
7848 })
7849 }
7850
7851 fn invalidate_filesystem_usage_cache(&mut self) {
7852 self.filesystem_usage_cache = None;
7853 }
7854
7855 fn path_uses_root_filesystem(&mut self, path: &str) -> bool {
7856 let filesystem = self.raw_filesystem_mut();
7857 let filesystem_any = filesystem as &mut dyn Any;
7858 filesystem_any
7859 .downcast_mut::<MountTable>()
7860 .is_none_or(|mount_table| mount_table.path_uses_root_filesystem(path))
7861 }
7862
7863 fn update_filesystem_usage_cache_for_resize(
7864 &mut self,
7865 path: &str,
7866 old_size: u64,
7867 new_size: u64,
7868 ) {
7869 if !self.path_uses_root_filesystem(path) {
7870 return;
7871 }
7872 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
7873 usage.total_bytes = usage
7874 .total_bytes
7875 .saturating_sub(old_size)
7876 .saturating_add(new_size);
7877 }
7878 }
7879
7880 fn update_filesystem_usage_cache_for_write(
7881 &mut self,
7882 path: &str,
7883 existing: Option<&VirtualStat>,
7884 new_size: u64,
7885 ) {
7886 if is_storage_directory(existing) {
7887 return;
7888 }
7889
7890 if let Some(stat) = existing {
7891 self.update_filesystem_usage_cache_for_resize(path, stat.size, new_size);
7892 } else {
7893 self.update_filesystem_usage_cache_for_inode_create(path, new_size);
7894 }
7895 }
7896
7897 fn update_filesystem_usage_cache_for_inode_create(&mut self, path: &str, size: u64) {
7898 if !self.path_uses_root_filesystem(path) {
7899 return;
7900 }
7901 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
7902 usage.total_bytes = usage.total_bytes.saturating_add(size);
7903 usage.inode_count = usage.inode_count.saturating_add(1);
7904 }
7905 }
7906
7907 fn update_filesystem_usage_cache_for_inode_creates(&mut self, path: &str, count: usize) {
7908 if count == 0 {
7909 return;
7910 }
7911 if !self.path_uses_root_filesystem(path) {
7912 return;
7913 }
7914 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
7915 usage.inode_count = usage.inode_count.saturating_add(count);
7916 }
7917 }
7918
7919 fn update_filesystem_usage_cache_for_inode_delete(&mut self, path: &str, size: u64) {
7920 if !self.path_uses_root_filesystem(path) {
7921 return;
7922 }
7923 if let Some(usage) = self.filesystem_usage_cache.as_mut() {
7924 usage.total_bytes = usage.total_bytes.saturating_sub(size);
7925 usage.inode_count = usage.inode_count.saturating_sub(1);
7926 }
7927 }
7928
7929 fn update_filesystem_usage_cache_for_remove(
7930 &mut self,
7931 path: &str,
7932 removed: Option<&VirtualStat>,
7933 ) {
7934 let Some(stat) = removed else {
7935 return;
7936 };
7937 if stat.is_directory || stat.nlink > 1 {
7938 return;
7939 }
7940 self.update_filesystem_usage_cache_for_inode_delete(path, stat.size);
7941 }
7942
7943 fn storage_stat(&mut self, path: &str) -> KernelResult<Option<VirtualStat>> {
7944 if is_virtual_device_storage_path(path) {
7945 return Ok(None);
7946 }
7947
7948 match self.raw_filesystem_mut().stat(path) {
7949 Ok(stat) => Ok(Some(stat)),
7950 Err(error) if error.code() == "ENOENT" => Ok(None),
7951 Err(error) => Err(error.into()),
7952 }
7953 }
7954
7955 fn storage_lstat(&mut self, path: &str) -> KernelResult<Option<VirtualStat>> {
7956 if is_virtual_device_storage_path(path) {
7957 return Ok(None);
7958 }
7959
7960 match self.raw_filesystem_mut().lstat(path) {
7961 Ok(stat) => Ok(Some(stat)),
7962 Err(error) if error.code() == "ENOENT" => Ok(None),
7963 Err(error) => Err(error.into()),
7964 }
7965 }
7966
7967 fn current_storage_file_size(&mut self, path: &str) -> KernelResult<u64> {
7968 Ok(self
7969 .storage_stat(path)?
7970 .filter(|stat| !stat.is_directory)
7971 .map(|stat| stat.size)
7972 .unwrap_or(0))
7973 }
7974
7975 fn check_dac_traversal(&mut self, pid: u32, path: &str) -> KernelResult<()> {
7976 if is_proc_path(path) {
7977 return Ok(());
7978 }
7979 let identity = self
7980 .processes
7981 .get(pid)
7982 .ok_or_else(|| KernelError::no_such_process(pid))?
7983 .identity;
7984 let normalized = normalize_path(path);
7985 let components = normalized
7986 .split('/')
7987 .filter(|component| !component.is_empty())
7988 .collect::<Vec<_>>();
7989 let mut current = String::from("/");
7990 for component in components.iter().take(components.len().saturating_sub(1)) {
7991 current = join_child_path(¤t, component);
7992 let stat = self.filesystem.stat(¤t)?;
7993 if !stat.is_directory {
7994 return Err(KernelError::new(
7995 "ENOTDIR",
7996 format!("path component is not a directory: {current}"),
7997 ));
7998 }
7999 self.check_dac_mode_with_acl(&identity, &stat, DAC_EXECUTE, ¤t)?;
8000 }
8001 Ok(())
8002 }
8003
8004 fn check_dac_access(&mut self, pid: u32, path: &str, access: u32) -> KernelResult<()> {
8005 if is_proc_path(path) {
8006 return Ok(());
8007 }
8008 self.check_dac_traversal(pid, path)?;
8009 if access == 0 {
8010 return Ok(());
8011 }
8012 let identity = self
8013 .processes
8014 .get(pid)
8015 .ok_or_else(|| KernelError::no_such_process(pid))?
8016 .identity;
8017 let stat = self.filesystem.stat(path)?;
8018 self.check_dac_mode_with_acl(&identity, &stat, access, path)
8019 }
8020
8021 fn check_dac_mode_with_acl(
8022 &mut self,
8023 identity: &ProcessIdentity,
8024 stat: &VirtualStat,
8025 access: u32,
8026 path: &str,
8027 ) -> KernelResult<()> {
8028 if identity.euid == 0 || identity.euid == stat.uid {
8034 return check_dac_mode(identity, stat, access, path);
8035 }
8036 let cache_key = (
8037 stat.dev,
8038 stat.ino,
8039 stat.ctime_ms,
8040 stat.ctime_nsec,
8041 stat.mode,
8042 stat.uid,
8043 stat.gid,
8044 );
8045 if self.no_posix_acl_cache.contains(&cache_key) {
8046 return check_dac_mode(identity, stat, access, path);
8047 }
8048 match self.read_posix_acl(path, POSIX_ACL_ACCESS)? {
8049 Some(acl) => acl.check_access(identity, stat, access, path),
8050 None => {
8051 if self.no_posix_acl_cache.len() >= 4_096 {
8057 self.no_posix_acl_cache.clear();
8058 }
8059 self.no_posix_acl_cache.insert(cache_key);
8060 check_dac_mode(identity, stat, access, path)
8061 }
8062 }
8063 }
8064
8065 fn read_posix_acl(&mut self, path: &str, name: &str) -> KernelResult<Option<PosixAcl>> {
8066 match self.filesystem.get_xattr(path, name, true) {
8067 Ok(value) => PosixAcl::parse(&value, path).map(Some),
8068 Err(error) if matches!(error.code(), "ENODATA" | "EOPNOTSUPP") => Ok(None),
8069 Err(error) => Err(error.into()),
8070 }
8071 }
8072
8073 fn sync_access_acl_mode(&mut self, path: &str, mode: u32) -> KernelResult<()> {
8074 let Some(mut acl) = self.read_posix_acl(path, POSIX_ACL_ACCESS)? else {
8075 return Ok(());
8076 };
8077 acl.apply_mode(mode);
8078 self.filesystem
8079 .set_xattr(path, POSIX_ACL_ACCESS, acl.encode(), 2, true)?;
8080 Ok(())
8081 }
8082
8083 fn check_dac_parent_access(&mut self, pid: u32, path: &str, access: u32) -> KernelResult<()> {
8084 let mut parent = parent_path(path);
8085 loop {
8086 match self.check_dac_access(pid, &parent, access) {
8087 Err(error) if error.code() == "ENOENT" && parent != "/" => {
8088 parent = parent_path(&parent);
8089 }
8090 result => return result,
8091 }
8092 }
8093 }
8094
8095 fn check_sticky_directory_removal(&mut self, pid: u32, path: &str) -> KernelResult<()> {
8096 let identity = self
8097 .processes
8098 .get(pid)
8099 .ok_or_else(|| KernelError::no_such_process(pid))?
8100 .identity;
8101 if identity.euid == 0 {
8102 return Ok(());
8103 }
8104 let parent = self.filesystem.stat(&parent_path(path))?;
8105 if parent.mode & 0o1000 == 0 || identity.euid == parent.uid {
8106 return Ok(());
8107 }
8108 let target = self.filesystem.lstat(path)?;
8109 if identity.euid == target.uid {
8110 Ok(())
8111 } else {
8112 Err(KernelError::new(
8113 "EPERM",
8114 format!("sticky directory prevents removing {path}"),
8115 ))
8116 }
8117 }
8118
8119 fn apply_process_creation_metadata(
8120 &mut self,
8121 pid: u32,
8122 path: &str,
8123 mode: u32,
8124 umask: u32,
8125 is_directory: bool,
8126 ) -> KernelResult<()> {
8127 let identity = self
8128 .processes
8129 .get(pid)
8130 .ok_or_else(|| KernelError::no_such_process(pid))?
8131 .identity;
8132 let parent_path = parent_path(path);
8133 let parent = self.filesystem.stat(&parent_path).map_err(|error| {
8134 KernelError::new(
8135 error.code(),
8136 format!("creation parent stat for '{parent_path}' failed: {error}"),
8137 )
8138 })?;
8139 let inherit_setgid = parent.mode & 0o2000 != 0;
8140 let gid = if inherit_setgid {
8141 parent.gid
8142 } else {
8143 identity.egid
8144 };
8145 self.filesystem
8146 .chown(path, identity.euid, gid)
8147 .map_err(|error| {
8148 KernelError::new(
8149 error.code(),
8150 format!("creation ownership for '{path}' failed: {error}"),
8151 )
8152 })?;
8153 let inherited_acl = self.read_posix_acl(&parent_path, POSIX_ACL_DEFAULT)?;
8154 let mut masked_mode = if let Some(acl) = inherited_acl.as_ref() {
8155 acl.restrict_to_mode(mode).mode(mode)
8156 } else {
8157 (mode & !0o777) | ((mode & 0o777) & !(umask & 0o777))
8158 };
8159 if is_directory && inherit_setgid {
8160 masked_mode |= 0o2000;
8161 }
8162 self.filesystem.chmod(path, masked_mode).map_err(|error| {
8163 KernelError::new(
8164 error.code(),
8165 format!("creation mode for '{path}' failed: {error}"),
8166 )
8167 })?;
8168 if let Some(default_acl) = inherited_acl {
8169 let access_acl = default_acl.restrict_to_mode(mode);
8170 self.filesystem
8171 .set_xattr(path, POSIX_ACL_ACCESS, access_acl.encode(), 0, true)?;
8172 if is_directory {
8173 self.filesystem.set_xattr(
8174 path,
8175 POSIX_ACL_DEFAULT,
8176 default_acl.encode(),
8177 0,
8178 true,
8179 )?;
8180 }
8181 }
8182 Ok(())
8183 }
8184
8185 fn clear_setid_after_write(&mut self, pid: u32, path: &str) -> KernelResult<()> {
8186 let identity = self
8187 .processes
8188 .get(pid)
8189 .ok_or_else(|| KernelError::no_such_process(pid))?
8190 .identity;
8191 if identity.euid == 0 {
8192 return Ok(());
8193 }
8194 let stat = self.filesystem.stat(path)?;
8195 if stat.mode & 0o6000 != 0 {
8196 self.filesystem.chmod(path, stat.mode & !0o6000)?;
8197 }
8198 Ok(())
8199 }
8200
8201 fn missing_directory_paths(
8202 &mut self,
8203 path: &str,
8204 recursive: bool,
8205 ) -> KernelResult<Vec<String>> {
8206 let normalized = normalize_path(path);
8207 if normalized == "/" {
8208 return Ok(Vec::new());
8209 }
8210
8211 if !recursive {
8212 return Ok(if self.storage_lstat(&normalized)?.is_none() {
8213 vec![normalized]
8214 } else {
8215 Vec::new()
8216 });
8217 }
8218
8219 let mut created = Vec::new();
8220 let mut current = String::from("/");
8221 for component in normalized
8222 .split('/')
8223 .filter(|component| !component.is_empty())
8224 {
8225 current = if current == "/" {
8226 format!("/{component}")
8227 } else {
8228 format!("{current}/{component}")
8229 };
8230 if self.storage_lstat(¤t)?.is_none() {
8231 created.push(current.clone());
8232 }
8233 }
8234 Ok(created)
8235 }
8236
8237 fn check_write_file_limits(&mut self, path: &str, new_size: u64) -> KernelResult<()> {
8238 let existing = self.storage_stat(path)?;
8239 self.check_write_file_limits_with_existing(path, existing.as_ref(), new_size)
8240 }
8241
8242 fn check_write_file_limits_with_existing(
8243 &mut self,
8244 path: &str,
8245 existing: Option<&VirtualStat>,
8246 new_size: u64,
8247 ) -> KernelResult<()> {
8248 if is_virtual_device_storage_path(path) {
8249 return Ok(());
8250 }
8251
8252 if let Some(existing) = existing {
8253 if is_storage_directory(Some(existing)) {
8254 return Ok(());
8255 }
8256 if new_size <= existing.size {
8257 return Ok(());
8258 }
8259
8260 let usage = self.filesystem_usage()?;
8261 self.resources.check_filesystem_usage(
8262 &usage,
8263 usage
8264 .total_bytes
8265 .saturating_sub(existing.size)
8266 .saturating_add(new_size),
8267 usage.inode_count,
8268 )?;
8269 return Ok(());
8270 }
8271
8272 let usage = self.filesystem_usage()?;
8273 self.resources.check_filesystem_usage(
8274 &usage,
8275 usage.total_bytes.saturating_add(new_size),
8276 usage.inode_count.saturating_add(1),
8277 )?;
8278 Ok(())
8279 }
8280
8281 fn check_create_dir_limits(&mut self, path: &str) -> KernelResult<()> {
8282 if is_virtual_device_storage_path(path) || self.storage_lstat(path)?.is_some() {
8283 return Ok(());
8284 }
8285
8286 let parent = parent_path(path);
8287 let Some(parent_stat) = self.storage_stat(&parent)? else {
8288 return Ok(());
8289 };
8290 if !parent_stat.is_directory {
8291 return Ok(());
8292 }
8293
8294 let usage = self.filesystem_usage()?;
8295 self.resources.check_filesystem_usage(
8296 &usage,
8297 usage.total_bytes,
8298 usage.inode_count.saturating_add(1),
8299 )?;
8300 Ok(())
8301 }
8302
8303 fn check_mkdir_limits(&mut self, path: &str, recursive: bool) -> KernelResult<()> {
8304 if is_virtual_device_storage_path(path) {
8305 return Ok(());
8306 }
8307
8308 if !recursive {
8309 return self.check_create_dir_limits(path);
8310 }
8311
8312 let usage = self.filesystem_usage()?;
8313 let new_inodes = count_missing_directory_components(self.raw_filesystem_mut(), path, true)?;
8314 self.resources.check_filesystem_usage(
8315 &usage,
8316 usage.total_bytes,
8317 usage.inode_count.saturating_add(new_inodes),
8318 )?;
8319 Ok(())
8320 }
8321
8322 fn check_symlink_limits(&mut self, target: &str, link_path: &str) -> KernelResult<()> {
8323 if is_virtual_device_storage_path(link_path) || self.storage_lstat(link_path)?.is_some() {
8324 return Ok(());
8325 }
8326
8327 let parent = parent_path(link_path);
8328 let Some(parent_stat) = self.storage_stat(&parent)? else {
8329 return Ok(());
8330 };
8331 if !parent_stat.is_directory {
8332 return Ok(());
8333 }
8334
8335 let usage = self.filesystem_usage()?;
8336 self.resources.check_filesystem_usage(
8337 &usage,
8338 usage.total_bytes.saturating_add(target.len() as u64),
8339 usage.inode_count.saturating_add(1),
8340 )?;
8341 Ok(())
8342 }
8343
8344 fn check_truncate_limits_with_existing(
8345 &mut self,
8346 path: &str,
8347 existing: Option<&VirtualStat>,
8348 length: u64,
8349 ) -> KernelResult<()> {
8350 if is_virtual_device_storage_path(path) {
8351 return Ok(());
8352 }
8353
8354 let Some(existing) = existing else {
8355 return Ok(());
8356 };
8357 if is_storage_directory(Some(existing)) {
8358 return Ok(());
8359 }
8360 self.check_path_resize_limits_with_existing(existing.size, length)
8361 }
8362
8363 fn check_rename_copy_up_limits(&mut self, old_path: &str, new_path: &str) -> KernelResult<()> {
8364 let max_bytes = self.resource_limits().max_filesystem_bytes;
8365 let max_inodes = self.resource_limits().max_inode_count;
8366 let filesystem_any = self.raw_filesystem_mut() as &mut dyn Any;
8367
8368 if let Some(root) = filesystem_any.downcast_mut::<RootFileSystem>() {
8369 root.check_rename_copy_up_limits(old_path, new_path, max_bytes, max_inodes)?;
8370 return Ok(());
8371 }
8372
8373 if let Some(mount_table) = filesystem_any.downcast_mut::<MountTable>() {
8374 mount_table.check_rename_copy_up_limits(old_path, new_path, max_bytes, max_inodes)?;
8375 }
8376
8377 Ok(())
8378 }
8379
8380 fn check_path_resize_limits_with_existing(
8381 &mut self,
8382 existing_size: u64,
8383 new_size: u64,
8384 ) -> KernelResult<()> {
8385 if new_size <= existing_size {
8386 return Ok(());
8387 }
8388
8389 let usage = self.filesystem_usage()?;
8390 self.resources.check_filesystem_usage(
8391 &usage,
8392 usage
8393 .total_bytes
8394 .saturating_sub(existing_size)
8395 .saturating_add(new_size),
8396 usage.inode_count,
8397 )?;
8398 Ok(())
8399 }
8400
8401 fn blocking_read_timeout(&self) -> Option<Duration> {
8402 self.resources
8403 .limits()
8404 .max_blocking_read_ms
8405 .map(Duration::from_millis)
8406 }
8407
8408 fn close_special_resource_if_needed(&self, description: &Arc<FileDescription>, filetype: u8) {
8409 close_special_resource_if_needed(
8410 &self.file_locks,
8411 &self.pipes,
8412 &self.ptys,
8413 &self.sockets,
8414 &self.fd_sockets,
8415 description,
8416 filetype,
8417 );
8418 }
8419
8420 fn cleanup_unnamed_file_if_closed(
8421 &mut self,
8422 description: &Arc<FileDescription>,
8423 ) -> KernelResult<()> {
8424 if description.ref_count() != 0 {
8425 return Ok(());
8426 }
8427 let Some(unnamed) = self.unnamed_files.get(&description.id()).cloned() else {
8428 return Ok(());
8429 };
8430 match self.filesystem.remove_file(&unnamed.path) {
8431 Ok(()) => {
8432 self.unnamed_files.remove(&description.id());
8433 self.invalidate_filesystem_usage_cache();
8434 Ok(())
8435 }
8436 Err(error) if error.code() == "ENOENT" => {
8437 self.unnamed_files.remove(&description.id());
8438 Ok(())
8439 }
8440 Err(error) => Err(error.into()),
8441 }
8442 }
8443}
8444
8445impl KernelVm<MountTable> {
8446 fn check_mount_permissions(&self, path: &str) -> KernelResult<()> {
8447 self.filesystem
8448 .check_path(FsOperation::Write, path)
8449 .map_err(KernelError::from)?;
8450 if is_sensitive_mount_path(path) {
8451 self.filesystem
8452 .check_path(FsOperation::MountSensitive, path)
8453 .map_err(KernelError::from)?;
8454 }
8455 Ok(())
8456 }
8457
8458 pub fn mount_filesystem(
8459 &mut self,
8460 path: &str,
8461 filesystem: impl VirtualFileSystem + 'static,
8462 options: MountOptions,
8463 ) -> KernelResult<()> {
8464 self.assert_not_terminated()?;
8465 self.check_mount_permissions(path)?;
8466 self.filesystem
8467 .inner_mut()
8468 .inner_mut()
8469 .mount(path, filesystem, options)
8470 .map_err(KernelError::from)?;
8471 self.invalidate_filesystem_usage_cache();
8472 Ok(())
8473 }
8474
8475 pub fn mount_boxed_filesystem(
8476 &mut self,
8477 path: &str,
8478 filesystem: Box<dyn MountedFileSystem>,
8479 options: MountOptions,
8480 ) -> KernelResult<()> {
8481 self.assert_not_terminated()?;
8482 self.check_mount_permissions(path)?;
8483 self.filesystem
8484 .inner_mut()
8485 .inner_mut()
8486 .mount_boxed(path, filesystem, options)
8487 .map_err(KernelError::from)?;
8488 self.invalidate_filesystem_usage_cache();
8489 Ok(())
8490 }
8491
8492 pub fn unmount_filesystem(&mut self, path: &str) -> KernelResult<()> {
8493 self.assert_not_terminated()?;
8494 self.check_mount_permissions(path)?;
8495 self.filesystem
8496 .inner_mut()
8497 .inner_mut()
8498 .unmount(path)
8499 .map_err(KernelError::from)?;
8500 self.invalidate_filesystem_usage_cache();
8501 Ok(())
8502 }
8503
8504 pub fn remount_filesystem_for_process(
8505 &mut self,
8506 requester_driver: &str,
8507 pid: u32,
8508 path: &str,
8509 options: &str,
8510 ) -> KernelResult<()> {
8511 self.assert_not_terminated()?;
8512 self.assert_driver_owns(requester_driver, pid)?;
8513 if self.process_identity(requester_driver, pid)?.euid != 0 {
8514 return Err(KernelError::new(
8515 "EPERM",
8516 "remount requires effective uid 0",
8517 ));
8518 }
8519 self.check_mount_permissions(path)?;
8520 self.filesystem
8521 .inner_mut()
8522 .inner_mut()
8523 .remount(path, options)
8524 .map_err(KernelError::from)
8525 }
8526
8527 pub fn mounted_filesystems(&self) -> Vec<MountEntry> {
8528 self.filesystem.inner().inner().get_mounts()
8529 }
8530
8531 pub fn root_filesystem_mut(&mut self) -> Option<&mut RootFileSystem> {
8532 self.filesystem
8533 .inner_mut()
8534 .inner_mut()
8535 .root_virtual_filesystem_mut::<RootFileSystem>()
8536 }
8537
8538 pub fn snapshot_root_filesystem(&mut self) -> KernelResult<RootFilesystemSnapshot> {
8539 let usage = self.filesystem_usage()?;
8540 self.resources
8541 .check_filesystem_usage(&usage, usage.total_bytes, usage.inode_count)?;
8542 let root = self
8543 .root_filesystem_mut()
8544 .ok_or_else(|| KernelError::new("EINVAL", "native root filesystem is not available"))?;
8545 root.snapshot().map_err(KernelError::from)
8546 }
8547
8548 pub fn snapshot_root_filesystem_bounded(
8553 &mut self,
8554 max_bytes: u64,
8555 ) -> KernelResult<RootFilesystemSnapshot> {
8556 if max_bytes == 0 {
8557 return Err(KernelError::new(
8558 "EINVAL",
8559 "maxBytes must be greater than zero",
8560 ));
8561 }
8562 let usage = self.filesystem_usage()?;
8563 self.resources
8564 .check_filesystem_usage(&usage, usage.total_bytes, usage.inode_count)?;
8565 if usage.total_bytes > max_bytes {
8566 return Err(KernelError::new(
8567 "EFBIG",
8568 format!(
8569 "root filesystem export exceeds maxBytes: {} content bytes > {max_bytes}; raise maxBytes",
8570 usage.total_bytes
8571 ),
8572 ));
8573 }
8574 let root = self
8575 .root_filesystem_mut()
8576 .ok_or_else(|| KernelError::new("EINVAL", "native root filesystem is not available"))?;
8577 let snapshot = root.snapshot().map_err(KernelError::from)?;
8578 let encoded_len = encode_snapshot(&snapshot).map_err(KernelError::from)?.len();
8579 if u64::try_from(encoded_len).unwrap_or(u64::MAX) > max_bytes {
8580 return Err(KernelError::new(
8581 "EFBIG",
8582 format!(
8583 "root filesystem export exceeds maxBytes: {encoded_len} encoded bytes > {max_bytes}; raise maxBytes"
8584 ),
8585 ));
8586 }
8587 Ok(snapshot)
8588 }
8589}
8590
8591#[derive(Default)]
8592struct StubDriverState {
8593 exit_code: Option<i32>,
8594 on_exit: Option<ProcessExitCallback>,
8595 kill_signals: Vec<i32>,
8596}
8597
8598#[derive(Default)]
8599struct StubDriverProcess {
8600 state: Mutex<StubDriverState>,
8601 waiters: Condvar,
8602}
8603
8604impl StubDriverProcess {
8605 fn finish(&self, exit_code: i32) {
8606 let callback = {
8607 let mut state = lock_or_recover(&self.state);
8608 if state.exit_code.is_some() {
8609 return;
8610 }
8611 state.exit_code = Some(exit_code);
8612 self.waiters.notify_all();
8613 state.on_exit.clone()
8614 };
8615
8616 if let Some(callback) = callback {
8617 callback(exit_code);
8618 }
8619 }
8620
8621 fn kill_signals(&self) -> Vec<i32> {
8622 lock_or_recover(&self.state).kill_signals.clone()
8623 }
8624}
8625
8626impl DriverProcess for StubDriverProcess {
8627 fn kill(&self, signal: i32) {
8628 {
8629 let mut state = lock_or_recover(&self.state);
8630 state.kill_signals.push(signal);
8631 }
8632 if matches!(
8633 signal,
8634 crate::process_table::SIGCHLD | SIGCONT | SIGSTOP | SIGTSTP | SIGWINCH
8635 ) {
8636 return;
8637 }
8638 self.finish(128 + signal);
8639 }
8640
8641 fn wait(&self, timeout: Duration) -> Option<i32> {
8642 let state = lock_or_recover(&self.state);
8643 if let Some(code) = state.exit_code {
8644 return Some(code);
8645 }
8646
8647 let (state, _) = wait_timeout_or_recover(&self.waiters, state, timeout);
8648 state.exit_code
8649 }
8650
8651 fn set_on_exit(&self, callback: ProcessExitCallback) {
8652 let maybe_exit = {
8653 let mut state = lock_or_recover(&self.state);
8654 state.on_exit = Some(callback.clone());
8655 state.exit_code
8656 };
8657
8658 if let Some(code) = maybe_exit {
8659 callback(code);
8660 }
8661 }
8662}
8663
8664fn unix_socket_absolute_components(
8665 cwd: &str,
8666 path: &str,
8667) -> KernelResult<(String, VecDeque<String>, bool)> {
8668 if path.is_empty() {
8669 return Err(KernelError::new(
8670 "ENOENT",
8671 "Unix socket pathname must not be empty",
8672 ));
8673 }
8674 if path.as_bytes().contains(&0) {
8675 return Err(KernelError::new(
8676 "EINVAL",
8677 "Unix socket pathname contains a NUL byte",
8678 ));
8679 }
8680 if path.len() >= MAX_PATH_LENGTH {
8684 return Err(KernelError::new(
8685 "ENAMETOOLONG",
8686 format!(
8687 "Unix socket pathname is {} bytes; Linux permits at most {}",
8688 path.len(),
8689 MAX_PATH_LENGTH - 1
8690 ),
8691 ));
8692 }
8693 if !path.starts_with('/') && !cwd.starts_with('/') {
8694 return Err(KernelError::new(
8695 "EINVAL",
8696 format!("Unix socket cwd must be absolute: {cwd}"),
8697 ));
8698 }
8699
8700 let absolute = if path.starts_with('/') {
8701 path.to_owned()
8702 } else if cwd == "/" {
8703 format!("/{path}")
8704 } else {
8705 format!("{}/{path}", cwd.trim_end_matches('/'))
8706 };
8707 let trailing_slash = absolute.len() > 1 && absolute.ends_with('/');
8708 let components = absolute
8709 .split('/')
8710 .filter(|component| !component.is_empty())
8711 .map(ToOwned::to_owned)
8712 .collect();
8713 Ok((absolute, components, trailing_slash))
8714}
8715
8716fn resolve_unix_socket_components<F: VirtualFileSystem>(
8717 filesystem: &mut F,
8718 identity: &ProcessIdentity,
8719 mut remaining: VecDeque<String>,
8720 follow_final_symlink: bool,
8721 mut require_final_directory: bool,
8722) -> KernelResult<UnixSocketPathNode> {
8723 let mut resolved = Vec::<String>::new();
8724 let mut followed_symlinks = 0usize;
8725
8726 if remaining.is_empty() {
8727 let stat = filesystem.stat("/")?;
8728 if require_final_directory && !stat.is_directory {
8729 return Err(KernelError::new("ENOTDIR", "root is not a directory"));
8730 }
8731 return Ok(UnixSocketPathNode {
8732 canonical_path: String::from("/"),
8733 stat,
8734 });
8735 }
8736
8737 while let Some(component) = remaining.pop_front() {
8738 let current_path = unix_socket_components_path(&resolved);
8739 let current_stat = filesystem.stat(¤t_path)?;
8740 if !current_stat.is_directory {
8741 return Err(KernelError::new(
8742 "ENOTDIR",
8743 format!("not a directory while resolving Unix socket path: {current_path}"),
8744 ));
8745 }
8746 check_unix_dac(
8747 identity,
8748 ¤t_stat,
8749 UNIX_DAC_SEARCH,
8750 "search",
8751 ¤t_path,
8752 )?;
8753
8754 match component.as_str() {
8755 "." => continue,
8756 ".." => {
8757 resolved.pop();
8758 continue;
8759 }
8760 _ => {}
8761 }
8762
8763 let candidate = join_absolute_path(¤t_path, &component);
8764 let stat = filesystem.lstat(&candidate)?;
8765 let is_final = remaining.is_empty();
8766 if stat.is_symbolic_link && (!is_final || follow_final_symlink) {
8767 followed_symlinks = followed_symlinks.saturating_add(1);
8768 if followed_symlinks > MAX_UNIX_SOCKET_SYMLINKS {
8769 return Err(KernelError::new(
8770 "ELOOP",
8771 format!("too many symbolic links while resolving '{candidate}'"),
8772 ));
8773 }
8774 let target = filesystem.read_link(&candidate)?;
8775 if target.is_empty() {
8776 return Err(KernelError::new(
8777 "ENOENT",
8778 format!("empty symbolic link target while resolving '{candidate}'"),
8779 ));
8780 }
8781 if target.starts_with('/') {
8782 resolved.clear();
8783 }
8784 if target.len() > 1 && target.ends_with('/') && remaining.is_empty() {
8788 require_final_directory = true;
8789 }
8790 let target_components = target
8791 .split('/')
8792 .filter(|target_component| !target_component.is_empty())
8793 .map(ToOwned::to_owned)
8794 .collect::<Vec<_>>();
8795 for target_component in target_components.into_iter().rev() {
8796 remaining.push_front(target_component);
8797 }
8798 continue;
8799 }
8800
8801 if is_final {
8802 if require_final_directory && !stat.is_directory {
8803 return Err(KernelError::new(
8804 "ENOTDIR",
8805 format!("not a directory while resolving Unix socket path: {candidate}"),
8806 ));
8807 }
8808 return Ok(UnixSocketPathNode {
8809 canonical_path: candidate,
8810 stat,
8811 });
8812 }
8813 if !stat.is_directory {
8814 return Err(KernelError::new(
8815 "ENOTDIR",
8816 format!("not a directory while resolving Unix socket path: {candidate}"),
8817 ));
8818 }
8819 resolved.push(component);
8820 }
8821
8822 let canonical_path = unix_socket_components_path(&resolved);
8823 let stat = filesystem.stat(&canonical_path)?;
8824 if require_final_directory && !stat.is_directory {
8825 return Err(KernelError::new(
8826 "ENOTDIR",
8827 format!("not a directory while resolving Unix socket path: {canonical_path}"),
8828 ));
8829 }
8830 Ok(UnixSocketPathNode {
8831 canonical_path,
8832 stat,
8833 })
8834}
8835
8836fn unix_socket_components_path(components: &[String]) -> String {
8837 if components.is_empty() {
8838 String::from("/")
8839 } else {
8840 format!("/{}", components.join("/"))
8841 }
8842}
8843
8844fn check_unix_dac(
8845 identity: &ProcessIdentity,
8846 stat: &VirtualStat,
8847 requested: u32,
8848 operation: &str,
8849 path: &str,
8850) -> KernelResult<()> {
8851 if identity.euid == 0 {
8855 return Ok(());
8856 }
8857 let shift = if identity.euid == stat.uid {
8858 6
8859 } else if identity.egid == stat.gid || identity.supplementary_gids.contains(&stat.gid) {
8860 3
8861 } else {
8862 0
8863 };
8864 let granted = (stat.mode >> shift) & 0o7;
8865 if granted & requested == requested {
8866 Ok(())
8867 } else {
8868 Err(KernelError::new(
8869 "EACCES",
8870 format!("permission denied, {operation} Unix socket path '{path}'"),
8871 ))
8872 }
8873}
8874
8875fn validate_chown_request(
8876 identity: &ProcessIdentity,
8877 stat: &VirtualStat,
8878 requested_uid: u32,
8879 requested_gid: u32,
8880 subject: &str,
8881) -> KernelResult<(u32, u32)> {
8882 const UNCHANGED_ID: u32 = u32::MAX;
8883 let next_uid = if requested_uid == UNCHANGED_ID {
8884 stat.uid
8885 } else {
8886 requested_uid
8887 };
8888 let next_gid = if requested_gid == UNCHANGED_ID {
8889 stat.gid
8890 } else {
8891 requested_gid
8892 };
8893
8894 if identity.euid == 0 || (requested_uid == UNCHANGED_ID && requested_gid == UNCHANGED_ID) {
8895 return Ok((next_uid, next_gid));
8896 }
8897 if identity.euid != stat.uid {
8898 return Err(KernelError::new(
8899 "EPERM",
8900 format!("operation not permitted, process does not own '{subject}'"),
8901 ));
8902 }
8903 if requested_uid != UNCHANGED_ID && requested_uid != stat.uid {
8904 return Err(KernelError::new(
8905 "EPERM",
8906 format!("operation not permitted, cannot change owner of '{subject}'"),
8907 ));
8908 }
8909 if requested_gid != UNCHANGED_ID
8910 && requested_gid != identity.egid
8911 && !identity.supplementary_gids.contains(&requested_gid)
8912 {
8913 return Err(KernelError::new(
8914 "EPERM",
8915 format!(
8916 "operation not permitted, gid {requested_gid} is not a process group for '{subject}'"
8917 ),
8918 ));
8919 }
8920 Ok((next_uid, next_gid))
8921}
8922
8923fn linux_chown_cleared_mode(stat: &VirtualStat) -> Option<u32> {
8927 if stat.mode & 0o170000 != 0o100000 {
8928 return None;
8929 }
8930 let mut mode = stat.mode & !0o4000;
8931 if stat.mode & 0o0010 != 0 {
8932 mode &= !0o2000;
8933 }
8934 (mode != stat.mode).then_some(mode)
8935}
8936
8937fn unix_socket_address_in_use(path: &str) -> KernelError {
8938 KernelError::new(
8939 "EADDRINUSE",
8940 format!("Unix socket pathname is already in use: {path}"),
8941 )
8942}
8943
8944impl From<VfsError> for KernelError {
8945 fn from(error: VfsError) -> Self {
8946 map_error(error.code(), error.to_string())
8947 }
8948}
8949
8950fn lock_or_recover<'a, T>(mutex: &'a Mutex<T>) -> MutexGuard<'a, T> {
8951 match mutex.lock() {
8952 Ok(guard) => guard,
8953 Err(poisoned) => poisoned.into_inner(),
8954 }
8955}
8956
8957fn wait_timeout_or_recover<'a, T>(
8958 condvar: &Condvar,
8959 guard: MutexGuard<'a, T>,
8960 timeout: Duration,
8961) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
8962 match condvar.wait_timeout(guard, timeout) {
8963 Ok(result) => result,
8964 Err(poisoned) => poisoned.into_inner(),
8965 }
8966}
8967
8968fn is_sensitive_mount_path(path: &str) -> bool {
8969 let normalized = crate::vfs::normalize_path(path);
8970 normalized == "/"
8971 || normalized == "/etc"
8972 || normalized.starts_with("/etc/")
8973 || normalized == "/proc"
8974 || normalized.starts_with("/proc/")
8975}
8976
8977impl From<FdTableError> for KernelError {
8978 fn from(error: FdTableError) -> Self {
8979 map_error(error.code(), error.to_string())
8980 }
8981}
8982
8983impl From<PipeError> for KernelError {
8984 fn from(error: PipeError) -> Self {
8985 map_error(error.code(), error.to_string())
8986 }
8987}
8988
8989impl From<PtyError> for KernelError {
8990 fn from(error: PtyError) -> Self {
8991 map_error(error.code(), error.to_string())
8992 }
8993}
8994
8995impl From<ProcessTableError> for KernelError {
8996 fn from(error: ProcessTableError) -> Self {
8997 map_error(error.code(), error.to_string())
8998 }
8999}
9000
9001impl From<PermissionError> for KernelError {
9002 fn from(error: PermissionError) -> Self {
9003 map_error(error.code(), error.to_string())
9004 }
9005}
9006
9007impl From<ResourceError> for KernelError {
9008 fn from(error: ResourceError) -> Self {
9009 map_error(error.code(), error.to_string())
9010 }
9011}
9012
9013impl From<SocketTableError> for KernelError {
9014 fn from(error: SocketTableError) -> Self {
9015 map_error(error.code(), error.to_string())
9016 }
9017}
9018
9019impl From<RootFilesystemError> for KernelError {
9020 fn from(error: RootFilesystemError) -> Self {
9021 map_error("EINVAL", error.to_string())
9022 }
9023}
9024
9025fn map_dns_resolver_error(error: crate::dns::DnsResolverError) -> KernelError {
9026 let code = match error.kind() {
9027 DnsResolverErrorKind::InvalidInput => "EINVAL",
9028 DnsResolverErrorKind::NxDomain => "ENOENT",
9029 DnsResolverErrorKind::NoData => "ENODATA",
9030 DnsResolverErrorKind::LookupFailed => "EHOSTUNREACH",
9031 };
9032 map_error(code, error.to_string())
9033}
9034
9035fn map_error(code: &'static str, message: String) -> KernelError {
9036 let trimmed = strip_error_prefix(code, &message)
9037 .map(ToOwned::to_owned)
9038 .unwrap_or(message);
9039 KernelError::new(code, trimmed)
9040}
9041
9042fn strip_error_prefix<'a>(code: &str, message: &'a str) -> Option<&'a str> {
9043 let prefix = format!("{code}: ");
9044 message.strip_prefix(&prefix)
9045}
9046
9047fn parse_dev_fd_path(path: &str) -> KernelResult<Option<u32>> {
9048 let Some(raw_fd) = path.strip_prefix("/dev/fd/") else {
9049 return Ok(None);
9050 };
9051 if raw_fd.is_empty() {
9052 return Err(KernelError::new(
9053 "EBADF",
9054 format!("bad file descriptor: {path}"),
9055 ));
9056 }
9057 let fd = raw_fd
9058 .parse::<u32>()
9059 .map_err(|_| KernelError::new("EBADF", format!("bad file descriptor: {path}")))?;
9060 Ok(Some(fd))
9061}
9062
9063fn count_missing_directory_components<F: VirtualFileSystem>(
9064 filesystem: &mut F,
9065 path: &str,
9066 include_final: bool,
9067) -> VfsResult<usize> {
9068 let normalized = normalize_path(path);
9069 let parts = normalized
9070 .split('/')
9071 .filter(|part| !part.is_empty())
9072 .collect::<Vec<_>>();
9073 let limit = if include_final {
9074 parts.len()
9075 } else {
9076 parts.len().saturating_sub(1)
9077 };
9078
9079 let mut current = String::from("/");
9080 for (index, part) in parts.iter().take(limit).enumerate() {
9081 let candidate = if current == "/" {
9082 format!("/{}", part)
9083 } else {
9084 format!("{current}/{}", part)
9085 };
9086
9087 match filesystem.stat(&candidate) {
9088 Ok(stat) => {
9089 if !stat.is_directory {
9090 return Err(VfsError::new(
9091 "ENOTDIR",
9092 format!("not a directory, mkdir '{candidate}'"),
9093 ));
9094 }
9095 current = candidate;
9096 }
9097 Err(error) if error.code() == "ENOENT" => {
9098 return Ok(limit.saturating_sub(index));
9099 }
9100 Err(error) => return Err(error),
9101 }
9102 }
9103
9104 Ok(0)
9105}
9106
9107fn parent_path(path: &str) -> String {
9108 let normalized = normalize_path(path);
9109 let Some((head, _)) = normalized.rsplit_once('/') else {
9110 return String::from("/");
9111 };
9112
9113 if head.is_empty() {
9114 String::from("/")
9115 } else {
9116 String::from(head)
9117 }
9118}
9119
9120fn required_dirent_ino(path: &str, ino: u64) -> KernelResult<u64> {
9121 if ino == 0 {
9122 return Err(KernelError::new(
9123 "EIO",
9124 format!("filesystem returned an invalid zero inode for directory entry {path}"),
9125 ));
9126 }
9127 Ok(ino)
9128}
9129
9130fn join_absolute_path(parent: &str, child: &str) -> String {
9131 if parent == "/" {
9132 format!("/{child}")
9133 } else {
9134 format!("{parent}/{child}")
9135 }
9136}
9137
9138fn join_child_path(parent: &str, child: &str) -> String {
9139 normalize_path(&join_absolute_path(parent, child))
9140}
9141
9142fn is_virtual_device_storage_path(path: &str) -> bool {
9143 matches!(
9144 path,
9145 "/dev/null" | "/dev/zero" | "/dev/stdin" | "/dev/stdout" | "/dev/stderr" | "/dev/urandom"
9146 ) || path == "/dev"
9147 || path == "/dev/fd"
9148 || path == "/dev/pts"
9149 || path.starts_with("/dev/fd/")
9150 || path.starts_with("/dev/pts/")
9151}
9152
9153fn is_storage_directory(stat: Option<&VirtualStat>) -> bool {
9154 stat.is_some_and(|stat| stat.is_directory && !stat.is_symbolic_link)
9155}
9156
9157fn is_proc_path(path: &str) -> bool {
9158 let normalized = normalize_path(path);
9159 normalized == "/proc" || normalized.starts_with("/proc/")
9160}
9161
9162fn is_agentos_path(path: &str) -> bool {
9163 let normalized = normalize_path(path);
9164 normalized == "/etc/agentos" || normalized.starts_with("/etc/agentos/")
9165}
9166
9167fn open_requires_write_access(flags: u32) -> bool {
9168 flags & (O_CREAT | O_EXCL | O_TRUNC) != 0 || (flags & 0b11) != crate::fd_table::O_RDONLY
9169}
9170
9171const DAC_EXECUTE: u32 = 0o1;
9172const DAC_WRITE: u32 = 0o2;
9173const DAC_READ: u32 = 0o4;
9174const POSIX_ACL_ACCESS: &str = "system.posix_acl_access";
9175const POSIX_ACL_DEFAULT: &str = "system.posix_acl_default";
9176const POSIX_ACL_XATTR_VERSION: u32 = 2;
9177const POSIX_ACL_ENTRY_LIMIT: usize = 25;
9178const XATTR_NAME_MAX: usize = 255;
9179const ACL_USER_OBJ: u16 = 0x01;
9180const ACL_USER: u16 = 0x02;
9181const ACL_GROUP_OBJ: u16 = 0x04;
9182const ACL_GROUP: u16 = 0x08;
9183const ACL_MASK: u16 = 0x10;
9184const ACL_OTHER: u16 = 0x20;
9185const ACL_UNDEFINED_ID: u32 = u32::MAX;
9186
9187#[derive(Clone, Debug, PartialEq, Eq)]
9188struct PosixAclEntry {
9189 tag: u16,
9190 perm: u16,
9191 id: u32,
9192}
9193
9194#[derive(Clone, Debug, PartialEq, Eq)]
9195struct PosixAcl {
9196 entries: Vec<PosixAclEntry>,
9197}
9198
9199impl PosixAcl {
9200 fn parse(value: &[u8], path: &str) -> KernelResult<Self> {
9201 if value.len() < 4 || !(value.len() - 4).is_multiple_of(8) {
9202 return Err(invalid_acl(path, "invalid xattr length"));
9203 }
9204 let entry_count = (value.len() - 4) / 8;
9205 if entry_count > POSIX_ACL_ENTRY_LIMIT {
9206 return Err(KernelError::new(
9207 "E2BIG",
9208 format!(
9209 "POSIX ACL for {path} has {entry_count} entries; limit is {POSIX_ACL_ENTRY_LIMIT}"
9210 ),
9211 ));
9212 }
9213 let version = u32::from_le_bytes(value[0..4].try_into().expect("four ACL version bytes"));
9214 if version != POSIX_ACL_XATTR_VERSION {
9215 return Err(invalid_acl(path, "unsupported xattr version"));
9216 }
9217 let entries = value[4..]
9218 .chunks_exact(8)
9219 .map(|bytes| PosixAclEntry {
9220 tag: u16::from_le_bytes([bytes[0], bytes[1]]),
9221 perm: u16::from_le_bytes([bytes[2], bytes[3]]),
9222 id: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
9223 })
9224 .collect::<Vec<_>>();
9225 let acl = Self { entries };
9226 acl.validate(path)?;
9227 Ok(acl)
9228 }
9229
9230 fn validate(&self, path: &str) -> KernelResult<()> {
9231 if self.entries.len() < 3 {
9232 return Err(invalid_acl(path, "missing required entries"));
9233 }
9234 for entry in &self.entries {
9235 if entry.perm > 0o7 {
9236 return Err(invalid_acl(path, "permission bits exceed rwx"));
9237 }
9238 let named = matches!(entry.tag, ACL_USER | ACL_GROUP);
9239 if (named && entry.id == ACL_UNDEFINED_ID) || (!named && entry.id != ACL_UNDEFINED_ID) {
9240 return Err(invalid_acl(path, "entry id does not match its tag"));
9241 }
9242 }
9243
9244 let mut index = 0;
9245 if self.entries.get(index).map(|entry| entry.tag) != Some(ACL_USER_OBJ) {
9246 return Err(invalid_acl(path, "ACL must start with user::"));
9247 }
9248 index += 1;
9249 let mut last_id = None;
9250 while self
9251 .entries
9252 .get(index)
9253 .is_some_and(|entry| entry.tag == ACL_USER)
9254 {
9255 let id = self.entries[index].id;
9256 if last_id.is_some_and(|last| id <= last) {
9257 return Err(invalid_acl(path, "named users are not strictly sorted"));
9258 }
9259 last_id = Some(id);
9260 index += 1;
9261 }
9262 if self.entries.get(index).map(|entry| entry.tag) != Some(ACL_GROUP_OBJ) {
9263 return Err(invalid_acl(path, "ACL is missing group::"));
9264 }
9265 index += 1;
9266 last_id = None;
9267 while self
9268 .entries
9269 .get(index)
9270 .is_some_and(|entry| entry.tag == ACL_GROUP)
9271 {
9272 let id = self.entries[index].id;
9273 if last_id.is_some_and(|last| id <= last) {
9274 return Err(invalid_acl(path, "named groups are not strictly sorted"));
9275 }
9276 last_id = Some(id);
9277 index += 1;
9278 }
9279 let has_named = self
9280 .entries
9281 .iter()
9282 .any(|entry| matches!(entry.tag, ACL_USER | ACL_GROUP));
9283 let has_mask = self
9284 .entries
9285 .get(index)
9286 .is_some_and(|entry| entry.tag == ACL_MASK);
9287 if has_mask {
9288 index += 1;
9289 }
9290 if has_named && !has_mask {
9291 return Err(invalid_acl(path, "named entries require a mask"));
9292 }
9293 if self.entries.get(index).map(|entry| entry.tag) != Some(ACL_OTHER)
9294 || index + 1 != self.entries.len()
9295 {
9296 return Err(invalid_acl(path, "ACL must end with other::"));
9297 }
9298 Ok(())
9299 }
9300
9301 fn encode(&self) -> Vec<u8> {
9302 let mut value = Vec::with_capacity(4 + self.entries.len() * 8);
9303 value.extend_from_slice(&POSIX_ACL_XATTR_VERSION.to_le_bytes());
9304 for entry in &self.entries {
9305 value.extend_from_slice(&entry.tag.to_le_bytes());
9306 value.extend_from_slice(&entry.perm.to_le_bytes());
9307 value.extend_from_slice(&entry.id.to_le_bytes());
9308 }
9309 value
9310 }
9311
9312 fn entry(&self, tag: u16) -> &PosixAclEntry {
9313 self.entries
9314 .iter()
9315 .find(|entry| entry.tag == tag)
9316 .expect("validated ACL required entry")
9317 }
9318
9319 fn mask(&self) -> u32 {
9320 self.entries
9321 .iter()
9322 .find(|entry| entry.tag == ACL_MASK)
9323 .map_or(0o7, |entry| u32::from(entry.perm))
9324 }
9325
9326 fn mode(&self, original_mode: u32) -> u32 {
9327 let owner = u32::from(self.entry(ACL_USER_OBJ).perm);
9328 let group = self
9329 .entries
9330 .iter()
9331 .find(|entry| entry.tag == ACL_MASK)
9332 .unwrap_or_else(|| self.entry(ACL_GROUP_OBJ));
9333 let other = u32::from(self.entry(ACL_OTHER).perm);
9334 (original_mode & !0o777) | (owner << 6) | (u32::from(group.perm) << 3) | other
9335 }
9336
9337 fn apply_mode(&mut self, mode: u32) {
9338 let has_mask = self.entries.iter().any(|entry| entry.tag == ACL_MASK);
9339 for entry in &mut self.entries {
9340 let permissions = match entry.tag {
9341 ACL_USER_OBJ => Some((mode >> 6) & 0o7),
9342 ACL_MASK => Some((mode >> 3) & 0o7),
9343 ACL_GROUP_OBJ if !has_mask => Some((mode >> 3) & 0o7),
9344 ACL_OTHER => Some(mode & 0o7),
9345 _ => None,
9346 };
9347 if let Some(permissions) = permissions {
9348 entry.perm = permissions as u16;
9349 }
9350 }
9351 }
9352
9353 fn restrict_to_mode(&self, mode: u32) -> Self {
9354 let mut acl = self.clone();
9355 let has_mask = acl.entries.iter().any(|entry| entry.tag == ACL_MASK);
9356 for entry in &mut acl.entries {
9357 let restriction = match entry.tag {
9358 ACL_USER_OBJ => Some((mode >> 6) & 0o7),
9359 ACL_MASK => Some((mode >> 3) & 0o7),
9360 ACL_GROUP_OBJ if !has_mask => Some((mode >> 3) & 0o7),
9361 ACL_OTHER => Some(mode & 0o7),
9362 _ => None,
9363 };
9364 if let Some(restriction) = restriction {
9365 entry.perm &= restriction as u16;
9366 }
9367 }
9368 acl
9369 }
9370
9371 fn check_access(
9372 &self,
9373 identity: &ProcessIdentity,
9374 stat: &VirtualStat,
9375 access: u32,
9376 path: &str,
9377 ) -> KernelResult<()> {
9378 let mask = self.mask();
9379 let granted = if identity.euid == stat.uid {
9380 u32::from(self.entry(ACL_USER_OBJ).perm)
9381 } else if let Some(entry) = self
9382 .entries
9383 .iter()
9384 .find(|entry| entry.tag == ACL_USER && entry.id == identity.euid)
9385 {
9386 u32::from(entry.perm) & mask
9387 } else {
9388 let in_group = |gid| identity.egid == gid || identity.supplementary_gids.contains(&gid);
9389 let mut matched = false;
9390 let mut group_permissions = 0;
9391 if in_group(stat.gid) {
9392 matched = true;
9393 group_permissions |= u32::from(self.entry(ACL_GROUP_OBJ).perm);
9394 }
9395 for entry in self.entries.iter().filter(|entry| entry.tag == ACL_GROUP) {
9396 if in_group(entry.id) {
9397 matched = true;
9398 group_permissions |= u32::from(entry.perm);
9399 }
9400 }
9401 if matched {
9402 group_permissions & mask
9403 } else {
9404 u32::from(self.entry(ACL_OTHER).perm)
9405 }
9406 };
9407 if granted & access == access {
9408 Ok(())
9409 } else {
9410 Err(KernelError::new(
9411 "EACCES",
9412 format!(
9413 "ACL permission denied: {path} requires {access:o}, granted={granted:o}, euid={}, egid={}",
9414 identity.euid, identity.egid
9415 ),
9416 ))
9417 }
9418 }
9419}
9420
9421fn invalid_acl(path: &str, reason: &str) -> KernelError {
9422 KernelError::new("EINVAL", format!("invalid POSIX ACL for {path}: {reason}"))
9423}
9424
9425fn check_xattr_namespace(
9426 identity: &ProcessIdentity,
9427 name: &str,
9428 write: bool,
9429 path: &str,
9430) -> KernelResult<()> {
9431 if name.is_empty() || name.len() > XATTR_NAME_MAX {
9432 return Err(KernelError::new(
9433 "EINVAL",
9434 format!(
9435 "extended attribute name for {path} is {} bytes; maximum is {XATTR_NAME_MAX}",
9436 name.len()
9437 ),
9438 ));
9439 }
9440 let supported = name.starts_with("user.")
9441 || name.starts_with("trusted.")
9442 || name.starts_with("security.")
9443 || name == "system.posix_acl_access"
9444 || name == "system.posix_acl_default";
9445 if !supported {
9446 return Err(KernelError::new(
9447 "EOPNOTSUPP",
9448 format!("unsupported extended attribute namespace for {name} on {path}"),
9449 ));
9450 }
9451 if identity.euid != 0 && (name.starts_with("trusted.") || name.starts_with("security.")) {
9452 return Err(KernelError::permission_denied(format!(
9453 "{} {name} requires root privileges on {path}",
9454 if write { "modifying" } else { "reading" }
9455 )));
9456 }
9457 Ok(())
9458}
9459
9460fn check_xattr_inode_write_policy(stat: &VirtualStat, name: &str, path: &str) -> KernelResult<()> {
9461 if name.starts_with("user.") && !stat.is_directory && stat.mode & 0o170000 != S_IFREG {
9462 return Err(KernelError::new(
9463 "EPERM",
9464 format!("user extended attributes require a regular file or directory: {path}"),
9465 ));
9466 }
9467 Ok(())
9468}
9469
9470fn check_dac_mode(
9471 identity: &ProcessIdentity,
9472 stat: &VirtualStat,
9473 access: u32,
9474 path: &str,
9475) -> KernelResult<()> {
9476 if identity.euid == 0 {
9477 if access & DAC_EXECUTE != 0 && !stat.is_directory && stat.mode & 0o111 == 0 {
9478 return Err(KernelError::new(
9479 "EACCES",
9480 format!("execute permission denied: {path}"),
9481 ));
9482 }
9483 return Ok(());
9484 }
9485 let shift = if identity.euid == stat.uid {
9486 6
9487 } else if identity.egid == stat.gid || identity.supplementary_gids.contains(&stat.gid) {
9488 3
9489 } else {
9490 0
9491 };
9492 let granted = (stat.mode >> shift) & 0o7;
9493 if granted & access == access {
9494 Ok(())
9495 } else {
9496 Err(KernelError::new(
9497 "EACCES",
9498 format!(
9499 "permission denied: {path} requires {access:o}, mode={:o}, euid={}, egid={}",
9500 stat.mode & 0o7777,
9501 identity.euid,
9502 identity.egid
9503 ),
9504 ))
9505 }
9506}
9507
9508fn credential_transition_denied(operation: &str, id: u32) -> KernelError {
9509 KernelError::new(
9510 "EPERM",
9511 format!("{operation} is not permitted for credential id {id}"),
9512 )
9513}
9514
9515fn checked_write_end(offset: u64, len: usize) -> KernelResult<u64> {
9516 offset
9517 .checked_add(len as u64)
9518 .ok_or_else(|| KernelError::new("EINVAL", "write offset out of range"))
9519}
9520
9521fn check_direct_io_alignment(flags: u32, offset: u64, len: usize) -> KernelResult<()> {
9522 const DIRECT_IO_ALIGNMENT: u64 = 512;
9523 if flags & O_DIRECT == 0 {
9524 return Ok(());
9525 }
9526 if !offset.is_multiple_of(DIRECT_IO_ALIGNMENT)
9527 || !(len as u64).is_multiple_of(DIRECT_IO_ALIGNMENT)
9528 {
9529 return Err(KernelError::new(
9530 "EINVAL",
9531 format!("O_DIRECT I/O requires {DIRECT_IO_ALIGNMENT}-byte aligned offset and length"),
9532 ));
9533 }
9534 Ok(())
9535}
9536
9537fn filetype_for_path(path: &str, stat: &VirtualStat) -> u8 {
9538 if stat.is_directory {
9539 FILETYPE_DIRECTORY
9540 } else if stat.mode & 0o170000 == 0o140000 {
9541 FILETYPE_SOCKET_STREAM
9542 } else if path.starts_with("/dev/") {
9543 FILETYPE_CHARACTER_DEVICE
9544 } else if stat.is_symbolic_link {
9545 FILETYPE_SYMBOLIC_LINK
9546 } else {
9547 FILETYPE_REGULAR_FILE
9548 }
9549}
9550
9551fn synthetic_character_device_stat(ino: u64) -> VirtualStat {
9552 synthetic_special_file_stat(ino, 0o020666, 2)
9553}
9554
9555fn synthetic_special_file_stat(ino: u64, mode: u32, dev: u64) -> VirtualStat {
9556 let now = now_ms();
9557 VirtualStat {
9558 mode,
9559 size: 0,
9560 blocks: 0,
9561 dev,
9562 rdev: 0,
9563 is_directory: false,
9564 is_symbolic_link: false,
9565 atime_ms: now,
9566 atime_nsec: 0,
9567 mtime_ms: now,
9568 mtime_nsec: 0,
9569 ctime_ms: now,
9570 ctime_nsec: 0,
9571 birthtime_ms: now,
9572 ino,
9573 nlink: 1,
9574 uid: 0,
9575 gid: 0,
9576 }
9577}
9578
9579fn proc_dir_stat(ino: u64) -> VirtualStat {
9580 let now = now_ms();
9581 VirtualStat {
9582 mode: S_IFDIR | 0o555,
9583 size: 0,
9584 blocks: 0,
9585 dev: 3,
9586 rdev: 0,
9587 is_directory: true,
9588 is_symbolic_link: false,
9589 atime_ms: now,
9590 atime_nsec: 0,
9591 mtime_ms: now,
9592 mtime_nsec: 0,
9593 ctime_ms: now,
9594 ctime_nsec: 0,
9595 birthtime_ms: now,
9596 ino,
9597 nlink: 2,
9598 uid: 0,
9599 gid: 0,
9600 }
9601}
9602
9603fn proc_file_stat(ino: u64, size: u64) -> VirtualStat {
9604 let now = now_ms();
9605 VirtualStat {
9606 mode: S_IFREG | 0o444,
9607 size,
9608 blocks: if size == 0 { 0 } else { size.div_ceil(512) },
9609 dev: 3,
9610 rdev: 0,
9611 is_directory: false,
9612 is_symbolic_link: false,
9613 atime_ms: now,
9614 atime_nsec: 0,
9615 mtime_ms: now,
9616 mtime_nsec: 0,
9617 ctime_ms: now,
9618 ctime_nsec: 0,
9619 birthtime_ms: now,
9620 ino,
9621 nlink: 1,
9622 uid: 0,
9623 gid: 0,
9624 }
9625}
9626
9627fn proc_symlink_stat(ino: u64, size: u64) -> VirtualStat {
9628 let now = now_ms();
9629 VirtualStat {
9630 mode: S_IFLNK | 0o777,
9631 size,
9632 blocks: if size == 0 { 0 } else { size.div_ceil(512) },
9633 dev: 3,
9634 rdev: 0,
9635 is_directory: false,
9636 is_symbolic_link: true,
9637 atime_ms: now,
9638 atime_nsec: 0,
9639 mtime_ms: now,
9640 mtime_nsec: 0,
9641 ctime_ms: now,
9642 ctime_nsec: 0,
9643 birthtime_ms: now,
9644 ino,
9645 nlink: 1,
9646 uid: 0,
9647 gid: 0,
9648 }
9649}
9650
9651fn proc_filetype(node: &ProcNode) -> u8 {
9652 match node {
9653 ProcNode::RootDir | ProcNode::PidDir { .. } | ProcNode::PidFdDir { .. } => {
9654 FILETYPE_DIRECTORY
9655 }
9656 ProcNode::SelfLink { .. } | ProcNode::PidCwdLink { .. } | ProcNode::PidFdLink { .. } => {
9657 FILETYPE_SYMBOLIC_LINK
9658 }
9659 ProcNode::MountsFile
9660 | ProcNode::CpuInfoFile
9661 | ProcNode::MemInfoFile
9662 | ProcNode::LoadAvgFile
9663 | ProcNode::UptimeFile
9664 | ProcNode::VersionFile
9665 | ProcNode::PidCmdline { .. }
9666 | ProcNode::PidEnviron { .. }
9667 | ProcNode::PidStatFile { .. }
9668 | ProcNode::PidStatusFile { .. } => FILETYPE_REGULAR_FILE,
9669 }
9670}
9671
9672fn proc_inode(node: &ProcNode) -> u64 {
9673 match node {
9674 ProcNode::RootDir => 0xfffe_0001,
9675 ProcNode::MountsFile => 0xfffe_0002,
9676 ProcNode::CpuInfoFile => 0xfffe_0003,
9677 ProcNode::MemInfoFile => 0xfffe_0004,
9678 ProcNode::LoadAvgFile => 0xfffe_0005,
9679 ProcNode::UptimeFile => 0xfffe_0006,
9680 ProcNode::VersionFile => 0xfffe_0007,
9681 ProcNode::SelfLink { pid } => 0xfffe_1000 + u64::from(*pid),
9682 ProcNode::PidDir { pid } => 0xfffe_2000 + u64::from(*pid),
9683 ProcNode::PidFdDir { pid } => 0xfffe_3000 + u64::from(*pid),
9684 ProcNode::PidCmdline { pid } => 0xfffe_4000 + u64::from(*pid),
9685 ProcNode::PidEnviron { pid } => 0xfffe_5000 + u64::from(*pid),
9686 ProcNode::PidCwdLink { pid } => 0xfffe_6000 + u64::from(*pid),
9687 ProcNode::PidStatFile { pid } => 0xfffe_7000 + u64::from(*pid),
9688 ProcNode::PidStatusFile { pid } => 0xfffe_8000 + u64::from(*pid),
9689 ProcNode::PidFdLink { pid, fd } => 0xffff_0000 + ((u64::from(*pid)) << 8) + u64::from(*fd),
9690 }
9691}
9692
9693fn null_separated_bytes(parts: Vec<String>) -> Vec<u8> {
9694 if parts.is_empty() {
9695 return Vec::new();
9696 }
9697
9698 let mut bytes = parts.join("\0").into_bytes();
9699 bytes.push(0);
9700 bytes
9701}
9702
9703fn proc_not_found_error(path: &str) -> KernelError {
9704 KernelError::new(
9705 "ENOENT",
9706 format!("no such file or directory, stat '{path}'"),
9707 )
9708}
9709
9710fn read_only_filesystem_error(path: &str) -> KernelError {
9711 KernelError::new("EROFS", format!("read-only filesystem: {path}"))
9712}
9713
9714fn now_ms() -> u64 {
9715 SystemTime::now()
9716 .duration_since(UNIX_EPOCH)
9717 .unwrap_or_default()
9718 .as_millis() as u64
9719}
9720
9721impl<F> Drop for KernelVm<F> {
9722 fn drop(&mut self) {
9723 if !self.terminated {
9724 dispose_kernel_vm_resources(self);
9725 }
9726 }
9727}
9728
9729#[cfg(test)]
9730mod tests {
9731 use super::*;
9732 use crate::fd_table::{FD_CLOEXEC, F_GETFD, F_SETFD, O_RDONLY};
9733 use crate::process_table::SIGTERM;
9734 use crate::vfs::MemoryFileSystem;
9735 use std::panic::{catch_unwind, AssertUnwindSafe};
9736 use std::thread;
9737
9738 fn kernel_with_process() -> (KernelVm<MemoryFileSystem>, KernelProcessHandle) {
9739 let mut config = KernelVmConfig::new("vm-fd-socket-test");
9740 config.permissions = Permissions::allow_all();
9741 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
9742 kernel
9743 .register_driver(CommandDriver::new("wasm", ["socket-test"]))
9744 .expect("register wasm driver");
9745 let process = kernel
9746 .spawn_process(
9747 "socket-test",
9748 Vec::new(),
9749 SpawnOptions {
9750 requester_driver: Some(String::from("wasm")),
9751 ..SpawnOptions::default()
9752 },
9753 )
9754 .expect("spawn socket test process");
9755 (kernel, process)
9756 }
9757
9758 #[test]
9759 fn fd_socketpair_preserves_messages_and_transfers_descriptions() {
9760 let (mut kernel, process) = kernel_with_process();
9761 let pid = process.pid();
9762 let (left, right) = kernel
9763 .fd_socketpair("wasm", pid, SocketType::Stream, true, false)
9764 .expect("create stream socketpair");
9765 assert_ne!(left, right);
9766 assert_eq!(
9767 kernel
9768 .fd_stat("wasm", pid, left)
9769 .expect("stat socket")
9770 .filetype,
9771 FILETYPE_SOCKET_STREAM
9772 );
9773 assert_ne!(
9774 kernel
9775 .fd_stat("wasm", pid, left)
9776 .expect("stat socket")
9777 .flags
9778 & O_NONBLOCK,
9779 0
9780 );
9781
9782 kernel
9783 .fd_write("wasm", pid, left, b"hello")
9784 .expect("write socketpair");
9785 assert_eq!(
9786 kernel
9787 .fd_read("wasm", pid, right, 32)
9788 .expect("read socketpair"),
9789 b"hello"
9790 );
9791
9792 let (pipe_read, pipe_write) = kernel.open_pipe("wasm", pid).expect("open pipe");
9793 kernel
9794 .fd_socket_sendmsg("wasm", pid, left, b"x", &[pipe_read])
9795 .expect("send pipe description");
9796 let received = kernel
9797 .fd_socket_recvmsg("wasm", pid, right, 1, 1, true, false, false, false)
9798 .expect("receive rights")
9799 .expect("message available");
9800 assert_eq!(received.payload, b"x");
9801 assert!(!received.payload_truncated);
9802 assert!(!received.control_truncated);
9803 assert_eq!(received.rights.len(), 1);
9804 let passed_read = match received.rights[0] {
9805 ReceivedFdRight::Fd(fd) => fd,
9806 ReceivedFdRight::Opaque(_) => panic!("expected transferred pipe fd"),
9807 };
9808 assert_eq!(
9809 kernel
9810 .fd_fcntl("wasm", pid, passed_read, F_GETFD, 0)
9811 .expect("get received fd flags"),
9812 FD_CLOEXEC
9813 );
9814
9815 kernel
9816 .fd_close("wasm", pid, pipe_read)
9817 .expect("close original pipe read end");
9818 kernel
9819 .fd_write("wasm", pid, pipe_write, b"through-rights")
9820 .expect("write pipe");
9821 assert_eq!(
9822 kernel
9823 .fd_read("wasm", pid, passed_read, 64)
9824 .expect("read transferred pipe"),
9825 b"through-rights"
9826 );
9827
9828 for fd in [passed_read, pipe_write, left, right] {
9829 kernel.fd_close("wasm", pid, fd).expect("close fd");
9830 }
9831 assert_eq!(kernel.sockets.snapshot().sockets, 0);
9832 assert!(lock_or_recover(&kernel.fd_sockets).is_empty());
9833 }
9834
9835 #[test]
9836 fn closed_socket_identity_cannot_poison_reused_regular_fd() {
9837 let (mut kernel, process) = kernel_with_process();
9838 let pid = process.pid();
9839 let (left, right) = kernel
9840 .fd_socketpair("wasm", pid, SocketType::Stream, false, false)
9841 .expect("create socketpair");
9842 let stale_socket_entry = {
9843 let tables = lock_or_recover(&kernel.fd_tables);
9844 let description = &tables.get(pid).unwrap().get(left).unwrap().description;
9845 lock_or_recover(&kernel.fd_sockets)
9846 .get(&description.id())
9847 .cloned()
9848 .expect("registered socket description")
9849 };
9850 kernel
9851 .fd_close("wasm", pid, left)
9852 .expect("close left socket");
9853 kernel
9854 .fd_close("wasm", pid, right)
9855 .expect("close right socket");
9856 assert!(lock_or_recover(&kernel.fd_sockets).is_empty());
9857
9858 kernel
9859 .write_file("/regular", b"regular-data".to_vec())
9860 .expect("seed regular file");
9861 let regular_fd = kernel
9862 .fd_open("wasm", pid, "/regular", O_RDONLY, None)
9863 .expect("open regular file after socket close");
9864 let regular_description = {
9865 let tables = lock_or_recover(&kernel.fd_tables);
9866 Arc::clone(
9867 &tables
9868 .get(pid)
9869 .unwrap()
9870 .get(regular_fd)
9871 .unwrap()
9872 .description,
9873 )
9874 };
9875 assert_ne!(
9876 regular_description.id(),
9877 stale_socket_entry.description.id(),
9878 "open-file-description ids must be globally unique"
9879 );
9880
9881 lock_or_recover(&kernel.fd_sockets).insert(regular_description.id(), stale_socket_entry);
9884 assert_eq!(
9885 kernel
9886 .fd_read("wasm", pid, regular_fd, 32)
9887 .expect("read regular file despite stale socket key"),
9888 b"regular-data"
9889 );
9890 kernel
9891 .fd_close("wasm", pid, regular_fd)
9892 .expect("close regular file");
9893 assert!(lock_or_recover(&kernel.fd_sockets).is_empty());
9894 }
9895
9896 #[test]
9897 fn fd_socketpair_datagram_reads_one_truncated_message_at_a_time() {
9898 let (mut kernel, process) = kernel_with_process();
9899 let pid = process.pid();
9900 let (left, right) = kernel
9901 .fd_socketpair("wasm", pid, SocketType::Datagram, false, false)
9902 .expect("create datagram socketpair");
9903 kernel
9904 .fd_write("wasm", pid, left, b"abcd")
9905 .expect("write first datagram");
9906 kernel
9907 .fd_write("wasm", pid, left, b"ef")
9908 .expect("write second datagram");
9909 let truncated = kernel
9910 .fd_socket_recvmsg("wasm", pid, right, 2, 0, false, false, false, false)
9911 .unwrap()
9912 .unwrap();
9913 assert_eq!(truncated.payload, b"ab");
9914 assert!(truncated.payload_truncated);
9915 assert_eq!(truncated.full_length, 4);
9916 assert_eq!(kernel.fd_read("wasm", pid, right, 8).unwrap(), b"ef");
9917 }
9918
9919 #[test]
9920 fn fd_socketpair_peek_duplicates_rights_without_consuming_message() {
9921 let (mut kernel, process) = kernel_with_process();
9922 let pid = process.pid();
9923 let (left, right) = kernel
9924 .fd_socketpair("wasm", pid, SocketType::Stream, false, false)
9925 .unwrap();
9926 let (pipe_read, pipe_write) = kernel.open_pipe("wasm", pid).unwrap();
9927 kernel
9928 .fd_socket_sendmsg("wasm", pid, left, b"hello", &[pipe_read])
9929 .unwrap();
9930
9931 let peeked = kernel
9932 .fd_socket_recvmsg("wasm", pid, right, 2, 1, false, true, false, false)
9933 .unwrap()
9934 .unwrap();
9935 assert_eq!(peeked.payload, b"he");
9936 assert_eq!(peeked.full_length, 2);
9937 let peeked_fd = match peeked.rights[0] {
9938 ReceivedFdRight::Fd(fd) => fd,
9939 ReceivedFdRight::Opaque(_) => panic!("expected fd right"),
9940 };
9941
9942 let consumed = kernel
9943 .fd_socket_recvmsg("wasm", pid, right, 5, 1, false, false, false, true)
9944 .unwrap()
9945 .unwrap();
9946 assert_eq!(consumed.payload, b"hello");
9947 let consumed_fd = match consumed.rights[0] {
9948 ReceivedFdRight::Fd(fd) => fd,
9949 ReceivedFdRight::Opaque(_) => panic!("expected fd right"),
9950 };
9951 kernel.fd_close("wasm", pid, pipe_read).unwrap();
9952 kernel.fd_write("wasm", pid, pipe_write, b"ab").unwrap();
9953 assert_eq!(kernel.fd_read("wasm", pid, peeked_fd, 1).unwrap(), b"a");
9954 assert_eq!(kernel.fd_read("wasm", pid, consumed_fd, 1).unwrap(), b"b");
9955 for fd in [peeked_fd, consumed_fd, pipe_write, left, right] {
9956 kernel.fd_close("wasm", pid, fd).unwrap();
9957 }
9958 }
9959
9960 #[test]
9961 fn exact_fd_transfer_install_preserves_description_identity_and_offset() {
9962 let (mut kernel, parent) = kernel_with_process();
9963 let parent_pid = parent.pid();
9964 kernel.write_file("/shared", b"abc").unwrap();
9965 let source_fd = kernel
9966 .fd_open("wasm", parent_pid, "/shared", O_RDONLY, None)
9967 .unwrap();
9968 kernel.fd_seek("wasm", parent_pid, source_fd, 1, 0).unwrap();
9969 let transfer = kernel.fd_transfer("wasm", parent_pid, source_fd).unwrap();
9970
9971 let child = kernel
9972 .spawn_process(
9973 "socket-test",
9974 Vec::new(),
9975 SpawnOptions {
9976 requester_driver: Some(String::from("wasm")),
9977 parent_pid: Some(parent_pid),
9978 ..SpawnOptions::default()
9979 },
9980 )
9981 .unwrap();
9982 kernel
9983 .fd_install_transfer_at("wasm", child.pid(), 9, 0, &transfer)
9984 .unwrap();
9985 assert_eq!(
9986 kernel
9987 .fd_transfer("wasm", child.pid(), 9)
9988 .unwrap()
9989 .description_id(),
9990 transfer.description_id()
9991 );
9992 assert_eq!(kernel.fd_read("wasm", child.pid(), 9, 1).unwrap(), b"b");
9993 assert_eq!(
9994 kernel.fd_read("wasm", parent_pid, source_fd, 1).unwrap(),
9995 b"c"
9996 );
9997
9998 child.finish(0);
9999 parent.finish(0);
10000 kernel.waitpid(child.pid()).unwrap();
10001 kernel.waitpid(parent_pid).unwrap();
10002 }
10003
10004 #[test]
10005 fn dev_fd_stat_reports_linux_pipe_and_socket_modes() {
10006 let (mut kernel, process) = kernel_with_process();
10007 let pid = process.pid();
10008 let (pipe_read, pipe_write) = kernel.open_pipe("wasm", pid).unwrap();
10009 let (socket_left, socket_right) = kernel
10010 .fd_socketpair("wasm", pid, SocketType::Stream, false, false)
10011 .unwrap();
10012
10013 for fd in [pipe_read, pipe_write] {
10014 let stat = kernel.dev_fd_stat("wasm", pid, fd).unwrap();
10015 assert_eq!(stat.mode, 0o010600);
10016 assert_eq!(stat.nlink, 1);
10017 assert_eq!(stat.size, 0);
10018 }
10019 let read_stat = kernel.dev_fd_stat("wasm", pid, pipe_read).unwrap();
10020 let write_stat = kernel.dev_fd_stat("wasm", pid, pipe_write).unwrap();
10021 assert_eq!(read_stat.dev, write_stat.dev);
10022 assert_eq!(read_stat.ino, write_stat.ino);
10023 for fd in [socket_left, socket_right] {
10024 let stat = kernel.dev_fd_stat("wasm", pid, fd).unwrap();
10025 assert_eq!(stat.mode, 0o140777);
10026 assert_eq!(stat.nlink, 1);
10027 assert_eq!(stat.size, 0);
10028 }
10029 }
10030
10031 #[test]
10032 fn adopted_kernel_socket_lives_while_queued_transfer_guard_exists() {
10033 let (mut kernel, process) = kernel_with_process();
10034 let pid = process.pid();
10035 let socket_id = kernel
10036 .socket_create("wasm", pid, SocketSpec::tcp())
10037 .expect("create transferable socket");
10038 let guard = kernel
10039 .fd_adopt_socket_transfer("wasm", pid, socket_id, O_NONBLOCK)
10040 .expect("retain socket description");
10041 assert_eq!(
10042 kernel.sockets.get(socket_id).unwrap().owner_pid(),
10043 0,
10044 "description-owned sockets must survive sender process cleanup"
10045 );
10046
10047 let (left, right) = kernel
10048 .fd_socketpair("wasm", pid, SocketType::Stream, false, false)
10049 .expect("create rights channel");
10050 kernel
10051 .fd_socket_sendmsg_transfers(
10052 "wasm",
10053 pid,
10054 left,
10055 b"x",
10056 &[FdTransferRequest::Opaque(Arc::new(guard))],
10057 )
10058 .expect("queue socket guard");
10059 kernel.fd_close("wasm", pid, left).unwrap();
10060 kernel.fd_close("wasm", pid, right).unwrap();
10061 assert!(
10062 kernel.sockets.get(socket_id).is_none(),
10063 "discarding the queued right must release and prune the adopted socket"
10064 );
10065 }
10066
10067 #[test]
10068 fn adopting_socket_for_transfer_does_not_require_a_free_sender_fd() {
10069 let mut config = KernelVmConfig::new("vm-full-fd-transfer-test");
10070 config.permissions = Permissions::allow_all();
10071 config.resources.max_open_fds = Some(3);
10072 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
10073 kernel
10074 .register_driver(CommandDriver::new("wasm", ["socket-test"]))
10075 .unwrap();
10076 let process = kernel
10077 .spawn_process(
10078 "socket-test",
10079 Vec::new(),
10080 SpawnOptions {
10081 requester_driver: Some(String::from("wasm")),
10082 ..SpawnOptions::default()
10083 },
10084 )
10085 .unwrap();
10086 let pid = process.pid();
10087 let socket_id = kernel
10088 .socket_create("wasm", pid, SocketSpec::tcp())
10089 .unwrap();
10090
10091 let guard = kernel
10092 .fd_adopt_socket_transfer("wasm", pid, socket_id, 0)
10093 .expect("SCM_RIGHTS must not need a temporary fd in a full sender table");
10094 assert_eq!(kernel.fd_snapshot("wasm", pid).unwrap().len(), 3);
10095 assert_eq!(kernel.sockets.get(socket_id).unwrap().owner_pid(), 0);
10096 drop(guard);
10097 }
10098
10099 struct RetainedKernelResources {
10100 process: KernelProcessHandle,
10101 fd_tables: Arc<Mutex<FdTableManager>>,
10102 pipes: PipeManager,
10103 ptys: PtyManager,
10104 sockets: SocketTable,
10105 driver_pids: Arc<Mutex<BTreeMap<String, BTreeSet<u32>>>>,
10106 }
10107
10108 fn kernel_with_live_resources() -> (KernelVm<MemoryFileSystem>, RetainedKernelResources) {
10109 let mut config = KernelVmConfig::new("vm-drop-resources");
10110 config.permissions = Permissions::allow_all();
10111 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
10112 kernel
10113 .register_driver(CommandDriver::new("shell", ["sh"]))
10114 .expect("register shell");
10115
10116 let process = kernel
10117 .spawn_process(
10118 "sh",
10119 Vec::new(),
10120 SpawnOptions {
10121 requester_driver: Some(String::from("shell")),
10122 ..SpawnOptions::default()
10123 },
10124 )
10125 .expect("spawn shell");
10126 let _ = kernel.open_pipe("shell", process.pid()).expect("open pipe");
10127 let _ = kernel.open_pty("shell", process.pid()).expect("open pty");
10128 let socket = kernel
10129 .socket_create("shell", process.pid(), SocketSpec::tcp())
10130 .expect("create socket");
10131 kernel
10132 .socket_set_state("shell", process.pid(), socket, SocketState::Listening)
10133 .expect("mark listener");
10134
10135 let retained = RetainedKernelResources {
10136 process: process.clone(),
10137 fd_tables: Arc::clone(&kernel.fd_tables),
10138 pipes: kernel.pipes.clone(),
10139 ptys: kernel.ptys.clone(),
10140 sockets: kernel.sockets.clone(),
10141 driver_pids: Arc::clone(&kernel.driver_pids),
10142 };
10143
10144 assert_eq!(lock_or_recover(retained.fd_tables.as_ref()).len(), 1);
10145 assert_eq!(retained.pipes.pipe_count(), 1);
10146 assert_eq!(retained.ptys.pty_count(), 1);
10147 assert_eq!(retained.sockets.snapshot().sockets, 1);
10148
10149 (kernel, retained)
10150 }
10151
10152 fn recursive_fs_kernel() -> KernelVm<MemoryFileSystem> {
10153 let mut config = KernelVmConfig::new("vm-recursive-fs");
10154 config.permissions = Permissions::allow_all();
10155 KernelVm::new(MemoryFileSystem::new(), config)
10156 }
10157
10158 #[test]
10159 fn exec_process_replaces_image_without_replacing_linux_process_state() {
10160 let mut config = KernelVmConfig::new("vm-exec-process-state");
10161 config.permissions = Permissions::allow_all();
10162 config.env = BTreeMap::from([(String::from("INHERITED"), String::from("old"))]);
10163 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
10164 kernel
10165 .register_driver(CommandDriver::new("runtime", ["old", "new"]))
10166 .expect("register runtime commands");
10167
10168 let parent = kernel
10169 .spawn_process(
10170 "old",
10171 Vec::new(),
10172 SpawnOptions {
10173 requester_driver: Some(String::from("runtime")),
10174 ..SpawnOptions::default()
10175 },
10176 )
10177 .expect("spawn parent");
10178 let child = kernel
10179 .spawn_process(
10180 "old",
10181 vec![String::from("old-argv")],
10182 SpawnOptions {
10183 requester_driver: Some(String::from("runtime")),
10184 parent_pid: Some(parent.pid()),
10185 cwd: Some(String::from("/before-exec")),
10186 env: BTreeMap::from([(String::from("STALE"), String::from("value"))]),
10187 },
10188 )
10189 .expect("spawn child");
10190 kernel
10191 .setpgid("runtime", child.pid(), child.pid())
10192 .expect("put child in its own process group");
10193 kernel
10194 .umask("runtime", child.pid(), Some(0o077))
10195 .expect("set process umask");
10196 let blocked = SignalSet::from_signal(SIGTERM).expect("signal set");
10197 kernel
10198 .sigprocmask("runtime", child.pid(), SigmaskHow::Block, blocked)
10199 .expect("block signal");
10200 kernel
10201 .kill_process("runtime", child.pid(), SIGTERM)
10202 .expect("queue blocked signal");
10203
10204 let (preserved_fd, cloexec_fd) = kernel
10205 .open_pipe("runtime", child.pid())
10206 .expect("open process pipe");
10207 kernel
10208 .fd_fcntl("runtime", child.pid(), cloexec_fd, F_SETFD, FD_CLOEXEC)
10209 .expect("mark close-on-exec fd");
10210 let (forwarded_cloexec_fd, forwarded_peer_fd) = kernel
10211 .open_pipe("runtime", child.pid())
10212 .expect("open runner-forwarded process pipe");
10213
10214 let before = kernel.processes.get(child.pid()).expect("child entry");
10215 let replacement_env = BTreeMap::from([(String::from("ONLY"), String::from("new"))]);
10216 kernel
10217 .mkdir("/literal", true)
10218 .expect("create literal executable directory");
10219 kernel
10220 .write_file("/literal/not-executable", b"wasm".to_vec())
10221 .expect("create non-executable replacement");
10222 kernel
10223 .chmod("/literal/not-executable", 0o644)
10224 .expect("clear replacement execute bits");
10225 let error = kernel
10226 .exec_process_retaining_internal_fds(
10227 "runtime",
10228 child.pid(),
10229 "new",
10230 vec![String::new(), String::from("argument")],
10231 replacement_env.clone(),
10232 String::from("/must-not-change-cwd"),
10233 &[],
10234 &[forwarded_cloexec_fd],
10235 Some("/literal/not-executable"),
10236 )
10237 .expect_err("pathname validation must fail before exec commits");
10238 assert_eq!(error.code(), "EACCES");
10239 assert_eq!(
10240 kernel.processes.get(child.pid()).expect("child entry"),
10241 before,
10242 "a pre-commit failure must not change process metadata"
10243 );
10244 kernel
10245 .fd_stat("runtime", child.pid(), cloexec_fd)
10246 .expect("a pre-commit failure must not close CLOEXEC descriptors");
10247 kernel
10248 .fd_stat("runtime", child.pid(), forwarded_cloexec_fd)
10249 .expect("a pre-commit failure must not close forwarded descriptors");
10250 kernel
10251 .write_file("/literal/new", b"wasm".to_vec())
10252 .expect("create executable replacement");
10253 kernel
10254 .chmod("/literal/new", 0o755)
10255 .expect("mark replacement executable");
10256 kernel
10257 .exec_process_retaining_internal_fds(
10258 "runtime",
10259 child.pid(),
10260 "new",
10261 vec![String::new(), String::from("argument")],
10262 replacement_env.clone(),
10263 String::from("/must-not-change-cwd"),
10264 &[],
10265 &[forwarded_cloexec_fd],
10266 Some("/literal/new"),
10267 )
10268 .expect("replace process image");
10269
10270 let after = kernel.processes.get(child.pid()).expect("exec child entry");
10271 assert_eq!(after.pid, before.pid);
10272 assert_eq!(after.ppid, before.ppid);
10273 assert_eq!(after.pgid, before.pgid);
10274 assert_eq!(after.sid, before.sid);
10275 assert_eq!(after.identity, before.identity);
10276 assert_eq!(after.umask, before.umask);
10277 assert_eq!(after.cwd, before.cwd, "execve must preserve cwd");
10278 assert_eq!(after.command, "");
10279 assert_eq!(after.args, vec![String::from("argument")]);
10280 assert_eq!(
10281 kernel
10282 .read_file_for_process(
10283 "runtime",
10284 child.pid(),
10285 &format!("/proc/{}/cmdline", child.pid()),
10286 )
10287 .expect("read post-exec cmdline"),
10288 b"\0argument\0".to_vec(),
10289 "procfs cmdline must contain argv exactly once, including empty argv0"
10290 );
10291 assert_eq!(after.env, replacement_env, "envp must replace, not overlay");
10292 assert_eq!(
10293 kernel
10294 .sigprocmask(
10295 "runtime",
10296 child.pid(),
10297 SigmaskHow::Block,
10298 SignalSet::empty(),
10299 )
10300 .expect("read signal mask"),
10301 blocked,
10302 "blocked signal mask must survive exec"
10303 );
10304 assert!(
10305 kernel
10306 .sigpending("runtime", child.pid())
10307 .expect("read pending signals")
10308 .contains(SIGTERM),
10309 "pending signals must survive exec"
10310 );
10311 kernel
10312 .fd_stat("runtime", child.pid(), preserved_fd)
10313 .expect("non-CLOEXEC fd must survive");
10314 assert_eq!(
10315 kernel
10316 .fd_stat("runtime", child.pid(), cloexec_fd)
10317 .expect_err("CLOEXEC fd must close")
10318 .code(),
10319 "EBADF"
10320 );
10321 assert_eq!(
10322 kernel
10323 .fd_stat("runtime", child.pid(), forwarded_cloexec_fd)
10324 .expect_err("runner-forwarded CLOEXEC fd must close")
10325 .code(),
10326 "EBADF"
10327 );
10328 kernel
10329 .fd_stat("runtime", child.pid(), forwarded_peer_fd)
10330 .expect("unmarked peer of runner-forwarded fd must survive");
10331 }
10332
10333 #[test]
10334 fn validate_executable_path_matches_linux_path_errors_and_symlinks() {
10335 let mut config = KernelVmConfig::new("vm-exec-path-errors");
10336 config.permissions = Permissions::allow_all();
10337 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
10338
10339 assert_eq!(
10340 kernel
10341 .validate_executable_path("/missing", "/")
10342 .expect_err("missing image must fail")
10343 .code(),
10344 "ENOENT"
10345 );
10346
10347 kernel
10348 .write_file("/plain", b"data".to_vec())
10349 .expect("create plain file");
10350 assert_eq!(
10351 kernel
10352 .validate_executable_path("/plain/child", "/")
10353 .expect_err("non-directory path component must fail")
10354 .code(),
10355 "ENOTDIR"
10356 );
10357
10358 kernel.mkdir("/directory", true).expect("create directory");
10359 assert_eq!(
10360 kernel
10361 .validate_executable_path("/directory", "/")
10362 .expect_err("directory image must fail")
10363 .code(),
10364 "EACCES"
10365 );
10366 assert_eq!(
10367 kernel
10368 .validate_executable_path("/plain", "/")
10369 .expect_err("non-executable image must fail")
10370 .code(),
10371 "EACCES"
10372 );
10373
10374 kernel
10375 .chmod("/plain", 0o755)
10376 .expect("mark target executable");
10377 kernel
10378 .symlink("/plain", "/image-link")
10379 .expect("create executable symlink");
10380 assert_eq!(
10381 kernel
10382 .validate_executable_path("/image-link", "/")
10383 .expect("exec must follow final symlink"),
10384 "/plain"
10385 );
10386
10387 kernel
10388 .symlink("/loop-b", "/loop-a")
10389 .expect("create first loop link");
10390 kernel
10391 .symlink("/loop-a", "/loop-b")
10392 .expect("create second loop link");
10393 assert_eq!(
10394 kernel
10395 .validate_executable_path("/loop-a", "/")
10396 .expect_err("symlink loop must fail")
10397 .code(),
10398 "ELOOP"
10399 );
10400 }
10401
10402 #[test]
10403 fn validate_wasm_exec_image_follows_linux_shebang_chain() {
10404 let mut config = KernelVmConfig::new("vm-exec-shebang");
10405 config.permissions = Permissions::allow_all();
10406 let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);
10407
10408 kernel
10409 .register_driver(CommandDriver::new("shell", ["sh"]))
10410 .expect("register projected shell command");
10411 kernel
10412 .mkdir("/bin", true)
10413 .expect("create command directory");
10414 kernel
10415 .write_file("/bin/sh", b"#!/bin/sh\n".to_vec())
10416 .expect("write self-referential registered command stub");
10417 kernel
10418 .write_file("/registered-script", b"#!/bin/sh\n".to_vec())
10419 .expect("write registered-interpreter script");
10420 kernel
10421 .chmod("/registered-script", 0o755)
10422 .expect("mark registered-interpreter script executable");
10423 kernel
10424 .validate_wasm_exec_image("/registered-script", "/")
10425 .expect("registered command stub must resolve as a runtime image");
10426
10427 kernel
10428 .write_file("/interpreter.wasm", b"\0asm\x01\0\0\0".to_vec())
10429 .expect("write WASM interpreter");
10430 kernel
10431 .chmod("/interpreter.wasm", 0o755)
10432 .expect("mark interpreter executable");
10433 kernel
10434 .write_file(
10435 "/script",
10436 b"#!/interpreter.wasm one optional argument\necho ignored\n".to_vec(),
10437 )
10438 .expect("write executable script");
10439 kernel
10440 .chmod("/script", 0o755)
10441 .expect("mark script executable");
10442 kernel
10443 .validate_wasm_exec_image("/script", "/")
10444 .expect("WASM interpreter chain must validate");
10445
10446 kernel
10447 .write_file("/missing-interpreter", b"#!/absent\n".to_vec())
10448 .expect("write missing-interpreter script");
10449 kernel
10450 .chmod("/missing-interpreter", 0o755)
10451 .expect("mark missing-interpreter script executable");
10452 assert_eq!(
10453 kernel
10454 .validate_wasm_exec_image("/missing-interpreter", "/")
10455 .expect_err("missing interpreter must fail")
10456 .code(),
10457 "ENOENT"
10458 );
10459
10460 kernel
10461 .write_file("/not-executable", b"\0asm\x01\0\0\0".to_vec())
10462 .expect("write non-executable interpreter");
10463 kernel
10464 .write_file("/denied-script", b"#!/not-executable\n".to_vec())
10465 .expect("write denied-interpreter script");
10466 kernel
10467 .chmod("/denied-script", 0o755)
10468 .expect("mark denied-interpreter script executable");
10469 assert_eq!(
10470 kernel
10471 .validate_wasm_exec_image("/denied-script", "/")
10472 .expect_err("non-executable interpreter must fail")
10473 .code(),
10474 "EACCES"
10475 );
10476
10477 for depth in 0..=MAX_EXEC_INTERPRETER_DEPTH {
10478 let path = format!("/recursive-{depth}");
10479 let next = format!("/recursive-{}", depth + 1);
10480 kernel
10481 .write_file(&path, format!("#!{next}\n").into_bytes())
10482 .expect("write recursive interpreter");
10483 kernel
10484 .chmod(&path, 0o755)
10485 .expect("mark recursive interpreter executable");
10486 }
10487 assert_eq!(
10488 kernel
10489 .validate_wasm_exec_image("/recursive-0", "/")
10490 .expect_err("interpreter recursion must be bounded")
10491 .code(),
10492 "ELOOP"
10493 );
10494 }
10495
10496 #[test]
10497 fn recursive_copy_preserves_tree_metadata_and_symlinks() {
10498 let mut kernel = recursive_fs_kernel();
10499 kernel
10500 .mkdir("/src/nested", true)
10501 .expect("create source dirs");
10502 kernel
10503 .write_file("/src/nested/file.txt", b"hello".to_vec())
10504 .expect("write source file");
10505 kernel
10506 .chmod("/src/nested/file.txt", 0o640)
10507 .expect("chmod source file");
10508 kernel
10509 .chown("/src/nested/file.txt", 42, 43)
10510 .expect("chown source file");
10511 kernel
10512 .symlink("../nested/file.txt", "/src/link")
10513 .expect("create source symlink");
10514
10515 kernel
10516 .copy_path("/src", "/dst", true)
10517 .expect("recursive copy");
10518
10519 assert_eq!(
10520 kernel
10521 .read_file("/dst/nested/file.txt")
10522 .expect("read copied"),
10523 b"hello".to_vec()
10524 );
10525 let copied = kernel.lstat("/dst/nested/file.txt").expect("stat copied");
10526 assert_eq!(copied.mode & 0o777, 0o640);
10527 assert_eq!((copied.uid, copied.gid), (42, 43));
10528 let link = kernel.lstat("/dst/link").expect("lstat copied link");
10529 assert!(link.is_symbolic_link);
10530 assert_eq!(
10531 kernel.read_link("/dst/link").expect("read copied link"),
10532 "../nested/file.txt"
10533 );
10534 }
10535
10536 #[test]
10537 fn recursive_remove_deletes_subtree_but_does_not_follow_symlinks() {
10538 let mut kernel = recursive_fs_kernel();
10539 kernel.mkdir("/tree/dir", true).expect("create tree");
10540 kernel
10541 .write_file("/tree/dir/file.txt", b"tree".to_vec())
10542 .expect("write tree file");
10543 kernel
10544 .write_file("/outside.txt", b"outside".to_vec())
10545 .expect("write outside file");
10546 kernel
10547 .symlink("/outside.txt", "/tree/link-out")
10548 .expect("create symlink out of tree");
10549
10550 kernel.remove_path("/tree", true).expect("recursive remove");
10551
10552 assert!(!kernel.exists("/tree").expect("tree existence"));
10553 assert_eq!(
10554 kernel.read_file("/outside.txt").expect("outside survives"),
10555 b"outside".to_vec()
10556 );
10557 }
10558
10559 #[test]
10560 fn read_dir_recursive_respects_user_depth_and_reports_types() {
10561 let mut kernel = recursive_fs_kernel();
10562 kernel.mkdir("/root/a/b", true).expect("create deep tree");
10563 kernel
10564 .write_file("/root/a/file.txt", b"x".to_vec())
10565 .expect("write file");
10566 kernel
10567 .symlink("a/file.txt", "/root/link")
10568 .expect("create link");
10569
10570 let entries = kernel
10571 .read_dir_recursive("/root", Some(0))
10572 .expect("recursive listing");
10573 assert_eq!(entries.len(), 2);
10574 assert!(entries
10575 .iter()
10576 .any(|entry| entry.path == "/root/a" && entry.is_directory));
10577 assert!(entries
10578 .iter()
10579 .any(|entry| entry.path == "/root/link" && entry.is_symbolic_link));
10580 assert!(!entries.iter().any(|entry| entry.path == "/root/a/file.txt"));
10581 }
10582
10583 #[test]
10584 fn recursive_ops_enforce_depth_and_entry_bounds() {
10585 let mut depth_config = KernelVmConfig::new("vm-recursive-depth-limit");
10586 depth_config.permissions = Permissions::allow_all();
10587 depth_config.resources = ResourceLimits {
10588 max_recursive_fs_depth: Some(1),
10589 ..ResourceLimits::default()
10590 };
10591 let mut depth_kernel = KernelVm::new(MemoryFileSystem::new(), depth_config);
10592 depth_kernel
10593 .mkdir("/root/a/b", true)
10594 .expect("create deep tree");
10595
10596 let error = depth_kernel
10597 .copy_path("/root", "/copy", true)
10598 .expect_err("copy should hit depth limit");
10599 assert_eq!(error.code(), "ENOMEM");
10600 assert!(error.to_string().contains("depth 2"));
10601
10602 let mut entry_config = KernelVmConfig::new("vm-recursive-entry-limit");
10603 entry_config.permissions = Permissions::allow_all();
10604 entry_config.resources = ResourceLimits {
10605 max_recursive_fs_entries: Some(2),
10606 ..ResourceLimits::default()
10607 };
10608 let mut entry_kernel = KernelVm::new(MemoryFileSystem::new(), entry_config);
10609 entry_kernel.mkdir("/root", true).expect("create root");
10610 entry_kernel
10611 .write_file("/root/a.txt", b"a".to_vec())
10612 .expect("write a");
10613 entry_kernel
10614 .write_file("/root/b.txt", b"b".to_vec())
10615 .expect("write b");
10616 entry_kernel
10617 .write_file("/root/c.txt", b"c".to_vec())
10618 .expect("write c");
10619
10620 let error = entry_kernel
10621 .read_dir_recursive("/root", None)
10622 .expect_err("listing should hit entry limit");
10623 assert_eq!(error.code(), "ENOMEM");
10624 assert!(error.to_string().contains("3 entries"));
10625 }
10626
10627 fn assert_kernel_drop_released_resources(retained: &RetainedKernelResources) {
10628 assert_eq!(retained.process.wait(Duration::from_millis(50)), Some(143));
10629 assert_eq!(retained.process.kill_signals(), vec![15]);
10630 assert!(
10631 lock_or_recover(retained.fd_tables.as_ref()).is_empty(),
10632 "kernel drop should remove fd tables"
10633 );
10634 assert_eq!(
10635 retained.pipes.pipe_count(),
10636 0,
10637 "kernel drop should close pipes"
10638 );
10639 assert_eq!(
10640 retained.ptys.pty_count(),
10641 0,
10642 "kernel drop should close PTYs"
10643 );
10644 assert_eq!(
10645 retained.sockets.snapshot().sockets,
10646 0,
10647 "kernel drop should reclaim sockets"
10648 );
10649 assert!(
10650 lock_or_recover(retained.driver_pids.as_ref()).is_empty(),
10651 "kernel drop should clear driver-owned pid tracking"
10652 );
10653 }
10654
10655 #[test]
10656 fn setpgid_rejects_joining_a_process_group_owned_by_another_driver() {
10657 let kernel = KernelVm::new(MemoryFileSystem::new(), KernelVmConfig::new("vm-setpgid"));
10658
10659 let leader_pid = kernel.processes.allocate_pid().expect("allocate pid");
10660 kernel.processes.register(
10661 leader_pid,
10662 String::from("driver-a"),
10663 String::from("sh"),
10664 Vec::new(),
10665 ProcessContext {
10666 pid: leader_pid,
10667 ppid: 0,
10668 env: BTreeMap::new(),
10669 cwd: String::from("/"),
10670 umask: DEFAULT_PROCESS_UMASK,
10671 fds: Default::default(),
10672 identity: ProcessIdentity::default(),
10673 blocked_signals: SignalSet::empty(),
10674 pending_signals: SignalSet::empty(),
10675 },
10676 Arc::new(StubDriverProcess::default()),
10677 );
10678
10679 let peer_pid = kernel.processes.allocate_pid().expect("allocate pid");
10680 kernel.processes.register(
10681 peer_pid,
10682 String::from("driver-b"),
10683 String::from("sh"),
10684 Vec::new(),
10685 ProcessContext {
10686 pid: peer_pid,
10687 ppid: leader_pid,
10688 env: BTreeMap::new(),
10689 cwd: String::from("/"),
10690 umask: DEFAULT_PROCESS_UMASK,
10691 fds: Default::default(),
10692 identity: ProcessIdentity::default(),
10693 blocked_signals: SignalSet::empty(),
10694 pending_signals: SignalSet::empty(),
10695 },
10696 Arc::new(StubDriverProcess::default()),
10697 );
10698
10699 lock_or_recover(&kernel.driver_pids)
10700 .entry(String::from("driver-a"))
10701 .or_default()
10702 .insert(leader_pid);
10703 lock_or_recover(&kernel.driver_pids)
10704 .entry(String::from("driver-b"))
10705 .or_default()
10706 .insert(peer_pid);
10707
10708 let error = kernel
10709 .setpgid("driver-b", peer_pid, leader_pid)
10710 .expect_err("cross-driver process-group join should be denied");
10711 assert_eq!(error.code(), "EPERM");
10712 }
10713
10714 #[test]
10715 fn sigprocmask_and_sigpending_require_process_ownership() {
10716 let mut kernel = KernelVm::new(MemoryFileSystem::new(), KernelVmConfig::new("vm-sigmask"));
10717 let process = kernel
10718 .register_process(
10719 String::from("driver-a"),
10720 String::from("sleep"),
10721 Vec::new(),
10722 ProcessContext {
10723 pid: 0,
10724 ppid: 0,
10725 env: BTreeMap::new(),
10726 cwd: String::from("/"),
10727 umask: DEFAULT_PROCESS_UMASK,
10728 fds: Default::default(),
10729 identity: ProcessIdentity::default(),
10730 blocked_signals: SignalSet::empty(),
10731 pending_signals: SignalSet::empty(),
10732 },
10733 None,
10734 None,
10735 false,
10736 )
10737 .expect("create virtual process");
10738 let mask =
10739 SignalSet::from_signal(crate::process_table::SIGCHLD).expect("SIGCHLD should be valid");
10740
10741 let previous = kernel
10742 .sigprocmask("driver-a", process.pid(), SigmaskHow::Block, mask)
10743 .expect("owner should update signal mask");
10744 assert_eq!(previous, SignalSet::empty());
10745 assert_eq!(
10746 kernel
10747 .sigpending("driver-a", process.pid())
10748 .expect("owner should read pending signals"),
10749 SignalSet::empty()
10750 );
10751
10752 let error = kernel
10753 .sigprocmask("driver-b", process.pid(), SigmaskHow::Block, mask)
10754 .expect_err("foreign driver should be rejected");
10755 assert_eq!(error.code(), "EPERM");
10756 let error = kernel
10757 .sigpending("driver-b", process.pid())
10758 .expect_err("foreign driver should be rejected");
10759 assert_eq!(error.code(), "EPERM");
10760 }
10761
10762 #[test]
10763 fn cleanup_process_resources_blocks_concurrent_dup2_until_pipe_cleanup_finishes() {
10764 let fd_tables = Arc::new(Mutex::new(FdTableManager::new()));
10765 let file_locks = FileLockManager::new();
10766 let pipes = PipeManager::new();
10767 let ptys = PtyManager::new();
10768 let sockets = SocketTable::new();
10769 let fd_sockets = Arc::new(Mutex::new(BTreeMap::new()));
10770 let driver_pids = Arc::new(Mutex::new(BTreeMap::from([(
10771 String::from("driver"),
10772 BTreeSet::from([41]),
10773 )])));
10774 let pipe = pipes.create_pipe();
10775
10776 {
10777 let mut tables = lock_or_recover(fd_tables.as_ref());
10778 let table = tables.create(41);
10779 table
10780 .open_with(
10781 Arc::clone(&pipe.read.description),
10782 pipe.read.filetype,
10783 Some(10),
10784 )
10785 .expect("open pipe read end");
10786 table
10787 .open_with(
10788 Arc::clone(&pipe.write.description),
10789 pipe.write.filetype,
10790 Some(11),
10791 )
10792 .expect("open pipe write end");
10793 }
10794
10795 let hook_state = Arc::new((Mutex::new((false, false)), Condvar::new()));
10796 let hook_state_for_cleanup = Arc::clone(&hook_state);
10797 set_cleanup_process_resources_test_hook(Some(Arc::new(move || {
10798 let (state, wake) = &*hook_state_for_cleanup;
10799 let mut state = lock_or_recover(state);
10800 state.0 = true;
10801 wake.notify_all();
10802 while !state.1 {
10803 state = wake.wait(state).expect("wait for cleanup release");
10804 }
10805 })));
10806
10807 let fd_tables_for_cleanup = Arc::clone(&fd_tables);
10808 let pipes_for_cleanup = pipes.clone();
10809 let driver_pids_for_cleanup = Arc::clone(&driver_pids);
10810 let cleanup_thread = thread::spawn(move || {
10811 cleanup_process_resources(
10812 fd_tables_for_cleanup.as_ref(),
10813 &file_locks,
10814 &pipes_for_cleanup,
10815 &ptys,
10816 &sockets,
10817 &fd_sockets,
10818 driver_pids_for_cleanup.as_ref(),
10819 41,
10820 );
10821 });
10822
10823 {
10824 let (state, wake) = &*hook_state;
10825 let mut state = lock_or_recover(state);
10826 while !state.0 {
10827 state = wake.wait(state).expect("wait for cleanup hook");
10828 }
10829 }
10830
10831 let fd_tables_for_dup = Arc::clone(&fd_tables);
10832 let dup_thread = thread::spawn(move || {
10833 let mut tables = lock_or_recover(fd_tables_for_dup.as_ref());
10834 let Some(table) = tables.get_mut(41) else {
10835 return Err(String::from("ESRCH"));
10836 };
10837 table.dup2(10, 12).map_err(|error| error.code().to_string())
10838 });
10839
10840 {
10841 let (state, wake) = &*hook_state;
10842 let mut state = lock_or_recover(state);
10843 state.1 = true;
10844 wake.notify_all();
10845 }
10846
10847 cleanup_thread.join().expect("cleanup thread should finish");
10848 let dup_result = dup_thread.join().expect("dup thread should finish");
10849 set_cleanup_process_resources_test_hook(None);
10850
10851 assert_eq!(dup_result, Err(String::from("ESRCH")));
10852 assert!(
10853 lock_or_recover(fd_tables.as_ref()).get(41).is_none(),
10854 "cleanup should remove the process FD table"
10855 );
10856 assert_eq!(pipes.pipe_count(), 0, "pipe cleanup should not leak");
10857 assert!(
10858 lock_or_recover(driver_pids.as_ref())
10859 .get("driver")
10860 .is_none_or(|pids| pids.is_empty()),
10861 "driver ownership should be cleared"
10862 );
10863 }
10864
10865 #[test]
10866 fn drop_disposes_live_kernel_vm_resources() {
10867 let (kernel, retained) = kernel_with_live_resources();
10868 drop(kernel);
10869 assert_kernel_drop_released_resources(&retained);
10870 }
10871
10872 #[test]
10873 fn drop_during_panic_still_disposes_live_kernel_vm_resources() {
10874 let retained = Arc::new(Mutex::new(None::<RetainedKernelResources>));
10875 let retained_for_panic = Arc::clone(&retained);
10876
10877 let panic_result = catch_unwind(AssertUnwindSafe(move || {
10878 let (kernel, resources) = kernel_with_live_resources();
10879 *lock_or_recover(retained_for_panic.as_ref()) = Some(resources);
10880 let _kernel = kernel;
10881 panic!("intentional panic to exercise KernelVm::drop");
10882 }));
10883
10884 assert!(panic_result.is_err(), "panic should be observed");
10885 let retained = lock_or_recover(retained.as_ref())
10886 .take()
10887 .expect("panic path should retain resources for assertions");
10888 assert_kernel_drop_released_resources(&retained);
10889 }
10890}