Skip to main content

agent_os_kernel/
kernel.rs

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