2045 lines · 65.0 KB · Rust
RawDownload
use agentos_kernel::command_registry::CommandDriver;use agentos_kernel::fd_table::{    RecordLockType, FD_CLOEXEC, F_DUPFD, F_GETFD, F_GETFL, F_SETFD, F_SETFL, LOCK_EX, LOCK_NB,    LOCK_SH, LOCK_UN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_NONBLOCK, O_RDONLY,    O_RDWR, O_TRUNC,};use agentos_kernel::kernel::{    ExecOptions, KernelVm, KernelVmConfig, OpenShellOptions, SpawnOptions, WaitPidFlags,    WaitPidResult, SEEK_SET,};use agentos_kernel::mount_table::{MountOptions, MountTable};use agentos_kernel::permissions::Permissions;use agentos_kernel::pipe_manager::MAX_PIPE_BUFFER_BYTES;use agentos_kernel::process_table::{ProcessWaitEvent, SIGWINCH};use agentos_kernel::socket_table::SocketType;use agentos_kernel::vfs::{    MemoryFileSystem, VfsResult, VirtualDirEntry, VirtualFileSystem, VirtualStat, MAX_PATH_LENGTH,};use std::cell::{Cell, RefCell}; fn assert_kernel_error_code<T: std::fmt::Debug>(    result: agentos_kernel::kernel::KernelResult<T>,    expected: &str,) {    let error = result.expect_err("operation should fail");    assert_eq!(error.code(), expected);} fn spawn_shell(    kernel: &mut KernelVm<MemoryFileSystem>,) -> agentos_kernel::kernel::KernelProcessHandle {    kernel        .spawn_process(            "sh",            Vec::new(),            SpawnOptions {                requester_driver: Some(String::from("shell")),                ..SpawnOptions::default()            },        )        .expect("spawn shell")} fn spawn_shell_in<F: VirtualFileSystem + 'static>(    kernel: &mut KernelVm<F>,) -> agentos_kernel::kernel::KernelProcessHandle {    kernel        .spawn_process(            "sh",            Vec::new(),            SpawnOptions {                requester_driver: Some(String::from("shell")),                ..SpawnOptions::default()            },        )        .expect("spawn shell")} fn assert_not_trivial_pattern(bytes: &[u8]) {    assert!(bytes.iter().any(|byte| *byte != 0));    assert!(        bytes.windows(2).any(|window| window[0] != window[1]),        "random data should not collapse to a repeated byte"    );} struct AtomicityProbeFileSystem {    inner: RefCell<MemoryFileSystem>,    exclusive_race_pending: Cell<bool>,    append_race_pending: Cell<bool>,    target_path: &'static str,} impl AtomicityProbeFileSystem {    fn new(target_path: &'static str) -> Self {        let mut inner = MemoryFileSystem::new();        inner            .write_file(target_path, Vec::new())            .expect("seed append target");        Self {            inner: RefCell::new(inner),            exclusive_race_pending: Cell::new(false),            append_race_pending: Cell::new(false),            target_path,        }    }     fn trigger_exclusive_race(&self) {        self.inner            .borrow_mut()            .remove_file(self.target_path)            .expect("clear target before exclusive race");        self.exclusive_race_pending.set(true);    }     fn trigger_append_race(&self) {        self.inner            .borrow_mut()            .write_file(self.target_path, Vec::new())            .expect("reset target before append race");        self.append_race_pending.set(true);    }} impl VirtualFileSystem for AtomicityProbeFileSystem {    fn read_file(&mut self, path: &str) -> VfsResult<Vec<u8>> {        self.inner.borrow_mut().read_file(path)    }     fn read_dir(&mut self, path: &str) -> VfsResult<Vec<String>> {        self.inner.borrow_mut().read_dir(path)    }     fn read_dir_limited(&mut self, path: &str, max_entries: usize) -> VfsResult<Vec<String>> {        self.inner.borrow_mut().read_dir_limited(path, max_entries)    }     fn read_dir_with_types(&mut self, path: &str) -> VfsResult<Vec<VirtualDirEntry>> {        self.inner.borrow_mut().read_dir_with_types(path)    }     fn write_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<()> {        let content = content.into();        if path == self.target_path {            if self.exclusive_race_pending.replace(false) {                self.inner                    .borrow_mut()                    .write_file(path, b"winner".to_vec())                    .expect("inject competing exclusive creator");            }            if self.append_race_pending.replace(false) {                self.inner                    .borrow_mut()                    .write_file(path, b"RACE".to_vec())                    .expect("inject competing append writer");            }        }        self.inner.borrow_mut().write_file(path, content)    }     fn create_file_exclusive(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<()> {        if path == self.target_path && self.exclusive_race_pending.replace(false) {            self.inner                .borrow_mut()                .write_file(path, b"winner".to_vec())                .expect("inject competing exclusive creator");            return Err(agentos_kernel::vfs::VfsError::new(                "EEXIST",                format!("file already exists, open '{path}'"),            ));        }        self.inner.borrow_mut().create_file_exclusive(path, content)    }     fn append_file(&mut self, path: &str, content: impl Into<Vec<u8>>) -> VfsResult<u64> {        if path == self.target_path && self.append_race_pending.replace(false) {            self.inner                .borrow_mut()                .append_file(path, b"RACE".to_vec())                .expect("inject competing append writer");        }        self.inner.borrow_mut().append_file(path, content)    }     fn create_dir(&mut self, path: &str) -> VfsResult<()> {        self.inner.borrow_mut().create_dir(path)    }     fn mkdir(&mut self, path: &str, recursive: bool) -> VfsResult<()> {        self.inner.borrow_mut().mkdir(path, recursive)    }     fn exists(&self, path: &str) -> bool {        if path == self.target_path && self.exclusive_race_pending.get() {            return false;        }        self.inner.borrow().exists(path)    }     fn stat(&mut self, path: &str) -> VfsResult<VirtualStat> {        self.inner.borrow_mut().stat(path)    }     fn remove_file(&mut self, path: &str) -> VfsResult<()> {        self.inner.borrow_mut().remove_file(path)    }     fn remove_dir(&mut self, path: &str) -> VfsResult<()> {        self.inner.borrow_mut().remove_dir(path)    }     fn rename(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> {        self.inner.borrow_mut().rename(old_path, new_path)    }     fn realpath(&self, path: &str) -> VfsResult<String> {        self.inner.borrow().realpath(path)    }     fn symlink(&mut self, target: &str, link_path: &str) -> VfsResult<()> {        self.inner.borrow_mut().symlink(target, link_path)    }     fn read_link(&self, path: &str) -> VfsResult<String> {        self.inner.borrow().read_link(path)    }     fn lstat(&self, path: &str) -> VfsResult<VirtualStat> {        self.inner.borrow().lstat(path)    }     fn link(&mut self, old_path: &str, new_path: &str) -> VfsResult<()> {        self.inner.borrow_mut().link(old_path, new_path)    }     fn chmod(&mut self, path: &str, mode: u32) -> VfsResult<()> {        self.inner.borrow_mut().chmod(path, mode)    }     fn chown(&mut self, path: &str, uid: u32, gid: u32) -> VfsResult<()> {        self.inner.borrow_mut().chown(path, uid, gid)    }     fn utimes(&mut self, path: &str, atime_ms: u64, mtime_ms: u64) -> VfsResult<()> {        self.inner.borrow_mut().utimes(path, atime_ms, mtime_ms)    }     fn truncate(&mut self, path: &str, length: u64) -> VfsResult<()> {        self.inner.borrow_mut().truncate(path, length)    }     fn pread(&mut self, path: &str, offset: u64, length: usize) -> VfsResult<Vec<u8>> {        self.inner.borrow_mut().pread(path, offset, length)    }} #[test]fn kernel_fd_surface_supports_open_seek_positional_io_dup_and_dev_fd_views() {    let mut config = KernelVmConfig::new("vm-api-fd");    config.permissions = Permissions::allow_all();    let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);    kernel        .register_driver(CommandDriver::new("shell", ["sh"]))        .expect("register shell");    kernel        .filesystem_mut()        .write_file("/tmp/data.txt", b"hello".to_vec())        .expect("seed file");     let process = spawn_shell(&mut kernel);    let fd = kernel        .fd_open("shell", process.pid(), "/tmp/data.txt", O_RDWR, None)        .expect("open existing file");    let created_fd = kernel        .fd_open(            "shell",            process.pid(),            "/tmp/created.txt",            O_CREAT | O_RDWR,            None,        )        .expect("open created file");    kernel        .fd_write("shell", process.pid(), created_fd, b"created")        .expect("write created file");    kernel        .fd_sync("shell", process.pid(), created_fd)        .expect("sync regular file");    let directory_fd = kernel        .fd_open("shell", process.pid(), "/tmp", 0, None)        .expect("open directory for sync");    kernel        .fd_sync("shell", process.pid(), directory_fd)        .expect("sync directory");    let directory_entries = kernel        .fd_read_dir_with_types("shell", process.pid(), directory_fd)        .expect("read directory through fd");    assert!(directory_entries.iter().any(|entry| {        entry.name == "data.txt" && !entry.is_directory && !entry.is_symbolic_link    }));    assert!(directory_entries.iter().any(|entry| {        entry.name == "created.txt" && !entry.is_directory && !entry.is_symbolic_link    }));    assert_kernel_error_code(        kernel.fd_read_dir_with_types("shell", process.pid(), created_fd),        "ENOTDIR",    );    assert_eq!(        kernel            .filesystem_mut()            .read_file("/tmp/created.txt")            .expect("read created file"),        b"created".to_vec()    );     let entries = kernel        .dev_fd_read_dir("shell", process.pid())        .expect("list /dev/fd");    assert!(entries.contains(&String::from("0")));    assert!(entries.contains(&String::from("1")));    assert!(entries.contains(&fd.to_string()));    assert!(entries.contains(&created_fd.to_string()));     let pread = kernel        .fd_pread("shell", process.pid(), fd, 2, 1)        .expect("pread from offset");    assert_eq!(pread, b"el".to_vec());    assert_eq!(        kernel            .fd_seek("shell", process.pid(), fd, 4, SEEK_SET)            .expect("seek to byte 4"),        4    );     let dup_fd = kernel        .fd_dup("shell", process.pid(), fd)        .expect("duplicate fd");    let dup_read = kernel        .fd_read("shell", process.pid(), dup_fd, 1)        .expect("read through dup");    assert_eq!(dup_read, b"o".to_vec());     kernel        .fd_dup2("shell", process.pid(), fd, 20)        .expect("dup2 onto target fd");    kernel        .fd_seek("shell", process.pid(), 20, 0, SEEK_SET)        .expect("seek dup2 target to start");    let full = kernel        .fd_read("shell", process.pid(), fd, 5)        .expect("read full file");    assert_eq!(full, b"hello".to_vec());     kernel        .fd_pwrite("shell", process.pid(), fd, b"X", 1)        .expect("pwrite at offset");    assert_eq!(        kernel            .filesystem_mut()            .read_file("/tmp/data.txt")            .expect("read updated file"),        b"hXllo".to_vec()    );     let file_stat = kernel        .dev_fd_stat("shell", process.pid(), fd)        .expect("stat regular file fd");    assert_eq!(file_stat.size, 5);    assert_eq!(file_stat.blocks, 1);    // Device ids are unique per filesystem instance; assert the fd stat    // reports the same device as a direct path stat on the same filesystem.    assert_eq!(        file_stat.dev,        kernel            .filesystem_mut()            .stat("/tmp/data.txt")            .expect("stat updated file")            .dev    );    assert_eq!(file_stat.rdev, 0);    assert!(!file_stat.is_directory);     let (read_fd, write_fd) = kernel.open_pipe("shell", process.pid()).expect("open pipe");    assert_kernel_error_code(kernel.fd_sync("shell", process.pid(), read_fd), "EINVAL");    let (socket_fd, peer_fd) = kernel        .fd_socketpair("shell", process.pid(), SocketType::Stream, false, false)        .expect("open socketpair for sync validation");    assert_kernel_error_code(kernel.fd_sync("shell", process.pid(), socket_fd), "EINVAL");    kernel        .fd_close("shell", process.pid(), socket_fd)        .expect("close first socket");    kernel        .fd_close("shell", process.pid(), peer_fd)        .expect("close second socket");    kernel        .fd_write("shell", process.pid(), write_fd, b"pipe-data")        .expect("write pipe");    let dev_dup = kernel        .fd_open(            "shell",            process.pid(),            &format!("/dev/fd/{read_fd}"),            0,            None,        )        .expect("duplicate through /dev/fd");    let pipe_bytes = kernel        .fd_read("shell", process.pid(), dev_dup, 32)        .expect("read duplicated pipe fd");    assert_eq!(pipe_bytes, b"pipe-data".to_vec());     let pipe_stat = kernel        .dev_fd_stat("shell", process.pid(), read_fd)        .expect("stat pipe fd");    let write_pipe_stat = kernel        .dev_fd_stat("shell", process.pid(), write_fd)        .expect("stat pipe write fd");    assert_eq!(pipe_stat.mode, 0o010600);    assert_eq!(pipe_stat.size, 0);    assert_eq!(pipe_stat.blocks, 0);    assert_ne!(pipe_stat.dev, 0);    assert_eq!(pipe_stat.nlink, 1);    assert_eq!(write_pipe_stat.dev, pipe_stat.dev);    assert_eq!(write_pipe_stat.ino, pipe_stat.ino);    assert!(!pipe_stat.is_directory);     kernel        .fd_close("shell", process.pid(), directory_fd)        .expect("close directory fd");    assert_kernel_error_code(        kernel.fd_read_dir_with_types("shell", process.pid(), directory_fd),        "EBADF",    );    kernel        .fd_close("shell", process.pid(), created_fd)        .expect("close synced file fd");    assert_kernel_error_code(kernel.fd_sync("shell", process.pid(), created_fd), "EBADF");     process.finish(0);    kernel.waitpid(process.pid()).expect("wait for shell");} #[test]fn fd_open_directory_truncate_rejects_regular_file_without_truncating_it() {    let mut config = KernelVmConfig::new("vm-open-directory-truncate");    config.permissions = Permissions::allow_all();    let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);    kernel        .register_driver(CommandDriver::new("shell", ["sh"]))        .expect("register shell");    kernel        .write_file("/regular", b"payload")        .expect("seed regular file");    let process = spawn_shell(&mut kernel);     assert_kernel_error_code(        kernel.fd_open(            "shell",            process.pid(),            "/regular",            O_RDONLY | O_DIRECTORY | O_TRUNC,            None,        ),        "ENOTDIR",    );    assert_eq!(        kernel.read_file("/regular").expect("read regular file"),        b"payload"    );} #[test]fn fd_open_nofollow_truncate_rejects_symlink_without_truncating_target() {    let mut config = KernelVmConfig::new("vm-open-nofollow-truncate");    config.permissions = Permissions::allow_all();    let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);    kernel        .register_driver(CommandDriver::new("shell", ["sh"]))        .expect("register shell");    kernel        .write_file("/target", b"payload")        .expect("seed target file");    kernel        .symlink("/target", "/target-link")        .expect("create target symlink");    let process = spawn_shell(&mut kernel);     assert_kernel_error_code(        kernel.fd_open(            "shell",            process.pid(),            "/target-link",            O_RDONLY | O_NOFOLLOW | O_TRUNC,            None,        ),        "ELOOP",    );    assert_eq!(        kernel.read_file("/target").expect("read target file"),        b"payload"    );} #[test]fn fd_open_directory_create_rejects_missing_path_without_creating_it() {    let mut config = KernelVmConfig::new("vm-open-directory-create");    config.permissions = Permissions::allow_all();    let mut kernel = KernelVm::new(MemoryFileSystem::new(), config);    kernel        .register_driver(CommandDriver::new("shell", ["sh"]))        .expect("register shell");    let process = spawn_shell(&mut kernel);     assert_kernel_error_code(        kernel.fd_open(            "shell",            process.pid(),            "/missing",            O_RDONLY | O_DIRECTORY | O_CREAT,            None,

Lines 1–500 of 2045.