diff --git a/Cargo.toml b/Cargo.toml index fb88a21136f..de518caf3f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -195,6 +195,8 @@ opt-level = 3 opt-level = 3 [profile.dev.package.sha2] opt-level = 3 +[profile.dev.package.xxhash-rust] +opt-level = 3 [profile.dev.package.digest] opt-level = 3 diff --git a/lib/journal/src/concrete/compacting.rs b/lib/journal/src/concrete/compacting.rs index b6d1a650636..feef78bf6c9 100644 --- a/lib/journal/src/concrete/compacting.rs +++ b/lib/journal/src/concrete/compacting.rs @@ -44,13 +44,19 @@ struct State { snapshots: Vec, // Last tty event thats been set tty: Option, + // The last change directory event + chdir: Option, // Events that create a particular directory - create_directory: HashMap, + create_directory: HashMap, // Events that remove a particular directory remove_directory: HashMap, // When creating and truncating a file we have a special // lookup so that duplicates can be erased create_trunc_file: HashMap, + // When modifying an existing file + modify_file: HashMap, + // Events that unlink a file + unlink_file: HashMap, // Thread events are only maintained while the thread and the // process are still running thread_map: HashMap, @@ -99,6 +105,9 @@ impl State { if let Some(tty) = self.tty.as_ref() { filter.add_event_to_whitelist(*tty); } + if let Some(tty) = self.chdir.as_ref() { + filter.add_event_to_whitelist(*tty); + } for e in self.snapshots.iter() { filter.add_event_to_whitelist(*e); } @@ -108,12 +117,22 @@ impl State { for t in self.thread_map.iter() { filter.add_event_to_whitelist(*t.1); } - for (_, e) in self.create_directory.iter() { + for (_, e) in self.remove_directory.iter() { filter.add_event_to_whitelist(*e); } - for (_, e) in self.remove_directory.iter() { + for (_, e) in self.unlink_file.iter() { filter.add_event_to_whitelist(*e); } + for (_, l) in self.create_directory.iter() { + if let Some(d) = self.descriptors.get(l) { + for e in d.events.iter() { + filter.add_event_to_whitelist(*e); + } + for e in d.write_map.values() { + filter.add_event_to_whitelist(*e); + } + } + } for (_, l) in self .suspect_descriptors .iter() @@ -203,6 +222,7 @@ impl CompactingJournal { inner_tx: tx, inner_rx: rx.as_restarted()?, tty: None, + chdir: None, snapshots: Default::default(), memory_map: Default::default(), thread_map: Default::default(), @@ -211,6 +231,8 @@ impl CompactingJournal { create_directory: Default::default(), remove_directory: Default::default(), create_trunc_file: Default::default(), + modify_file: Default::default(), + unlink_file: Default::default(), suspect_descriptors: Default::default(), keep_descriptors: Default::default(), stdio_descriptors: Default::default(), @@ -367,6 +389,9 @@ impl WritableJournal for CompactingJournalTx { JournalEntry::TtySetV1 { .. } => { state.tty.replace(event_index); } + JournalEntry::ChangeDirectoryV1 { .. } => { + state.chdir.replace(event_index); + } JournalEntry::OpenFileDescriptorV1 { fd, o_flags, path, .. } => { @@ -396,13 +421,42 @@ impl WritableJournal for CompactingJournalTx { // Creating a file and erasing anything that was there before means // the entire create branch that exists before this one can be ignored - if o_flags.contains(wasi::Oflags::CREATE) && o_flags.contains(wasi::Oflags::TRUNC) { - let path = path.to_string(); + let path = path.to_string(); + if o_flags.contains(wasi::Oflags::CREATE) + && (o_flags.contains(wasi::Oflags::TRUNC) + || o_flags.contains(wasi::Oflags::EXCL)) + { if let Some(existing) = state.create_trunc_file.remove(&path) { - state.suspect_descriptors.remove(&existing); - state.keep_descriptors.remove(&existing); + if let Some(remove) = state.suspect_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + if let Some(remove) = state.keep_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + } + if let Some(existing) = state.modify_file.remove(&path) { + if let Some(remove) = state.suspect_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + if let Some(remove) = state.keep_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + } + if let Some(existing) = state.create_trunc_file.insert(path, *fd) { + if let Some(remove) = state.suspect_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + if let Some(remove) = state.keep_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + } + } else if let Some(existing) = state.modify_file.insert(path, *fd) { + if let Some(remove) = state.suspect_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + if let Some(remove) = state.keep_descriptors.remove(&existing) { + state.descriptors.remove(&remove); } - state.create_trunc_file.insert(path, *fd); } } // We keep non-mutable events for file descriptors that are suspect @@ -411,15 +465,21 @@ impl WritableJournal for CompactingJournalTx { // Get the lookup // (if its suspect then it will remove the entry and // thus the entire branch of events it represents is discarded) - let mut skip = false; + let mut erase = false; let lookup = if matches!(&entry, JournalEntry::CloseFileDescriptorV1 { .. }) { if state.open_sockets.remove(fd).is_some() { - skip = true; + erase = true; } if state.open_pipes.remove(fd).is_some() { - skip = true; + erase = true; + } + match state.suspect_descriptors.remove(fd) { + Some(a) => { + erase = true; + Some(a) + } + None => None, } - state.suspect_descriptors.remove(fd) } else { state.suspect_descriptors.get(fd).cloned() }; @@ -427,13 +487,16 @@ impl WritableJournal for CompactingJournalTx { .or_else(|| state.keep_descriptors.get(fd).cloned()) .or_else(|| state.stdio_descriptors.get(fd).cloned()); - if !skip { + // If we are to erase all these events as if they never happened then do so + if erase { if let Some(lookup) = lookup { - let state = state.descriptors.entry(lookup).or_default(); - state.events.push(event_index); - } else { - state.whitelist.insert(event_index); + state.descriptors.remove(&lookup); } + } else if let Some(lookup) = lookup { + let state = state.descriptors.entry(lookup).or_default(); + state.events.push(event_index); + } else { + state.whitelist.insert(event_index); } } // Things that modify a file descriptor mean that it is @@ -514,14 +577,96 @@ impl WritableJournal for CompactingJournalTx { // Creating a new directory only needs to be done once JournalEntry::CreateDirectoryV1 { path, .. } => { let path = path.to_string(); - state.remove_directory.remove(&path); - state.create_directory.entry(path).or_insert(event_index); + + // Newly created directories are stored as a set of . + let lookup = match state.create_directory.get(&path) { + Some(lookup) => *lookup, + None => { + let lookup = DescriptorLookup(state.descriptor_seed); + state.descriptor_seed += 1; + state.create_directory.insert(path, lookup); + lookup + } + }; + + // Add the event that creates the directory + state + .descriptors + .entry(lookup) + .or_default() + .events + .push(event_index); } // Deleting a directory only needs to be done once JournalEntry::RemoveDirectoryV1 { path, .. } => { let path = path.to_string(); state.create_directory.remove(&path); - state.remove_directory.entry(path).or_insert(event_index); + state.remove_directory.insert(path, event_index); + } + // Unlinks the file from the file system + JournalEntry::UnlinkFileV1 { path, .. } => { + let path = path.to_string(); + if let Some(existing) = state + .create_trunc_file + .remove(&path) + .or_else(|| state.modify_file.remove(&path)) + { + if let Some(remove) = state.suspect_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + if let Some(remove) = state.keep_descriptors.remove(&existing) { + state.descriptors.remove(&remove); + } + } + state.unlink_file.insert(path, event_index); + } + // Renames may update some of the tracking functions + JournalEntry::PathRenameV1 { + old_path, new_path, .. + } => { + let old_path = old_path.to_string(); + let new_path = new_path.to_string(); + + if let Some(existing) = state.create_trunc_file.remove(&old_path) { + if let Some(replaces) = state.create_trunc_file.insert(new_path, existing) { + if let Some(remove) = state.suspect_descriptors.remove(&replaces) { + state.descriptors.remove(&remove); + } + if let Some(remove) = state.keep_descriptors.remove(&replaces) { + state.descriptors.remove(&remove); + } + } + } else if let Some(existing) = state.modify_file.remove(&old_path) { + if let Some(replaces) = state.modify_file.insert(new_path, existing) { + if let Some(remove) = state.suspect_descriptors.remove(&replaces) { + state.descriptors.remove(&remove); + } + if let Some(remove) = state.keep_descriptors.remove(&replaces) { + state.descriptors.remove(&remove); + } + } + } else if let Some(existing) = state.create_directory.remove(&old_path) { + if let Some(replaces) = state.create_directory.insert(new_path, existing) { + state.descriptors.remove(&replaces); + } + } else { + state.whitelist.insert(event_index); + } + } + // Update all the directory operations + JournalEntry::PathSetTimesV1 { path, .. } => { + let path = path.to_string(); + let lookup = state.create_directory.get(&path).cloned(); + if let Some(lookup) = lookup { + state + .descriptors + .entry(lookup) + .or_default() + .events + .push(event_index); + } else { + state.whitelist.insert(event_index); + } } // Pipes that remain open at the end will be added JournalEntry::CreatePipeV1 { fd1, fd2, .. } => { diff --git a/lib/journal/src/concrete/printing.rs b/lib/journal/src/concrete/printing.rs index 2c9868b8e03..bea5041c9d8 100644 --- a/lib/journal/src/concrete/printing.rs +++ b/lib/journal/src/concrete/printing.rs @@ -133,6 +133,8 @@ impl<'a> fmt::Display for JournalEntry<'a> { if o_flags.contains(wasi::Oflags::CREATE) { if o_flags.contains(wasi::Oflags::TRUNC) { write!(f, "fd-create-new (fd={}, path={})", fd, path) + } else if o_flags.contains(wasi::Oflags::EXCL) { + write!(f, "fd-create-excl (fd={}, path={})", fd, path) } else { write!(f, "fd-create (fd={}, path={})", fd, path) } diff --git a/lib/virtual-fs/src/overlay_fs.rs b/lib/virtual-fs/src/overlay_fs.rs index e243327f33d..d140762c6e3 100644 --- a/lib/virtual-fs/src/overlay_fs.rs +++ b/lib/virtual-fs/src/overlay_fs.rs @@ -813,7 +813,10 @@ where fn poll_copy_start_and_progress(&mut self, cx: &mut Context) -> Poll> { replace_with_or_abort(&mut self.state, |state| match state { - CowState::ReadOnly(inner) => CowState::SeekingGet(inner), + CowState::ReadOnly(inner) => { + tracing::trace!("COW file touched, starting file clone",); + CowState::SeekingGet(inner) + } state => state, }); self.poll_copy_progress(cx) diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index d3565608cf1..ca227404c86 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -221,7 +221,7 @@ impl VMOwnedMemory { pub fn new_with_file( memory: &MemoryType, style: &MemoryStyle, - backing_file: std::fs::File, + backing_file: std::path::PathBuf, memory_type: MmapType, ) -> Result { unsafe { Self::new_internal(memory, style, None, Some(backing_file), memory_type) } @@ -261,7 +261,7 @@ impl VMOwnedMemory { memory: &MemoryType, style: &MemoryStyle, vm_memory_location: NonNull, - backing_file: Option, + backing_file: Option, memory_type: MmapType, ) -> Result { Self::new_internal( @@ -278,7 +278,7 @@ impl VMOwnedMemory { memory: &MemoryType, style: &MemoryStyle, vm_memory_location: Option>, - backing_file: Option, + backing_file: Option, memory_type: MmapType, ) -> Result { if memory.minimum > Pages::max_value() { @@ -463,7 +463,7 @@ impl VMSharedMemory { pub fn new_with_file( memory: &MemoryType, style: &MemoryStyle, - backing_file: std::fs::File, + backing_file: std::path::PathBuf, memory_type: MmapType, ) -> Result { Ok(VMOwnedMemory::new_with_file(memory, style, backing_file, memory_type)?.to_shared()) @@ -497,7 +497,7 @@ impl VMSharedMemory { memory: &MemoryType, style: &MemoryStyle, vm_memory_location: NonNull, - backing_file: Option, + backing_file: Option, memory_type: MmapType, ) -> Result { Ok(VMOwnedMemory::from_definition_with_file( diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index c6d82f88308..ef301a4f678 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -67,7 +67,7 @@ impl Mmap { pub fn accessible_reserved( mut accessible_size: usize, mapping_size: usize, - mut backing_file: Option, + mut backing_file: Option, memory_type: MmapType, ) -> Result { use std::os::fd::IntoRawFd; @@ -85,17 +85,38 @@ impl Mmap { // If there is a backing file, resize the file so that its at least // `mapping_size` bytes. - if let Some(backing_file) = &mut backing_file { - let len = backing_file.metadata().map_err(|e| e.to_string())?.len() as usize; + let mut memory_fd = -1; + if let Some(backing_file_path) = &mut backing_file { + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&backing_file_path) + .map_err(|e| e.to_string())?; + + let mut backing_file_accessible = backing_file_path.clone(); + backing_file_accessible.set_extension("accessible"); + + let len = file.metadata().map_err(|e| e.to_string())?.len() as usize; if len < mapping_size { - backing_file - .set_len(mapping_size as u64) + std::fs::write(&backing_file_accessible, format!("{}", len).as_bytes()).ok(); + + file.set_len(mapping_size as u64) .map_err(|e| e.to_string())?; } - accessible_size = accessible_size.max(len).min(mapping_size); - } - let memory_fd = backing_file.map_or(-1, |fd| fd.into_raw_fd()); + if backing_file_accessible.exists() { + let accessible = std::fs::read_to_string(&backing_file_accessible) + .map_err(|e| e.to_string())? + .parse::() + .map_err(|e| e.to_string())?; + accessible_size = accessible_size.max(accessible); + } else { + accessible_size = accessible_size.max(len); + } + + accessible_size = accessible_size.min(mapping_size); + memory_fd = file.into_raw_fd(); + } // Compute the flags let mut flags = match memory_fd { @@ -168,7 +189,7 @@ impl Mmap { pub fn accessible_reserved( accessible_size: usize, mapping_size: usize, - _backing_file: Option, + _backing_file: Option, _memory_type: MmapType, ) -> Result { use winapi::um::memoryapi::VirtualAlloc; diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index 24751ceec1e..52b9796d48c 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -58,6 +58,13 @@ use crate::{bin_factory::BinaryPackage, state::PreopenedDir, ALL_RIGHTS}; /// run the libc initialization logic pub const VIRTUAL_ROOT_FD: WasiFd = 3; +/// The root inode and stdio inodes are the first inodes in the +/// file system tree +pub const FS_STDIN_INO: Inode = Inode(10); +pub const FS_STDOUT_INO: Inode = Inode(11); +pub const FS_STDERR_INO: Inode = Inode(12); +pub const FS_ROOT_INO: Inode = Inode(13); + const STDIN_DEFAULT_RIGHTS: Rights = { // This might seem a bit overenineered, but it's the only way I // discovered for getting the values in a const environment @@ -95,6 +102,10 @@ impl Inode { pub fn as_u64(&self) -> u64 { self.0 } + + pub fn from_path(str: &str) -> Self { + Inode(xxhash_rust::xxh64::xxh64(str.as_bytes(), 0)) + } } #[derive(Debug, Clone)] @@ -143,7 +154,6 @@ impl InodeWeakGuard { #[derive(Debug)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] struct WasiInodesProtected { - seed: u64, lookup: HashMap>, } @@ -157,7 +167,6 @@ impl WasiInodes { pub fn new() -> Self { Self { protected: Arc::new(RwLock::new(WasiInodesProtected { - seed: 1, lookup: Default::default(), })), } @@ -166,20 +175,17 @@ impl WasiInodes { /// adds another value to the inodes pub fn add_inode_val(&self, val: InodeVal) -> InodeGuard { let val = Arc::new(val); + let st_ino = { + let guard = val.stat.read().unwrap(); + guard.st_ino + }; let mut guard = self.protected.write().unwrap(); - let ino = Inode(guard.seed); - guard.seed += 1; + let ino = Inode(st_ino); guard.lookup.insert(ino, Arc::downgrade(&val)); - // Set the inode value - { - let mut guard = val.stat.write().unwrap(); - guard.st_ino = ino.0; - } - // every 100 calls we clear out dead weaks - if guard.seed % 100 == 1 { + if guard.lookup.len() % 100 == 1 { guard.lookup.retain(|_, v| Weak::strong_count(v) > 0); } @@ -591,7 +597,7 @@ impl WasiFs { vfs_preopens: &[String], fs_backing: WasiFsRoot, ) -> Result { - let mut wasi_fs = Self::new_init(fs_backing, inodes)?; + let mut wasi_fs = Self::new_init(fs_backing, inodes, FS_ROOT_INO)?; wasi_fs.init_preopens = preopens.to_vec(); wasi_fs.init_vfs_preopens = vfs_preopens.to_vec(); wasi_fs.create_preopens(inodes, false)?; @@ -612,11 +618,16 @@ impl WasiFs { /// Private helper function to init the filesystem, called in `new` and /// `new_with_preopen` - fn new_init(fs_backing: WasiFsRoot, inodes: &WasiInodes) -> Result { + fn new_init( + fs_backing: WasiFsRoot, + inodes: &WasiInodes, + st_ino: Inode, + ) -> Result { debug!("Initializing WASI filesystem"); let stat = Filestat { st_filetype: Filetype::Directory, + st_ino: st_ino.as_u64(), ..Filestat::default() }; let root_kind = Kind::Root { @@ -919,7 +930,7 @@ impl WasiFs { &self, inodes: &WasiInodes, mut cur_inode: InodeGuard, - path: &str, + path_str: &str, mut symlink_count: u32, follow_symlinks: bool, ) -> Result { @@ -927,7 +938,7 @@ impl WasiFs { return Err(Errno::Mlink); } - let path: &Path = Path::new(path); + let path: &Path = Path::new(path_str); let n_components = path.components().count(); // TODO: rights checks @@ -1048,6 +1059,11 @@ impl WasiFs { file.to_string_lossy().to_string().into(), Filestat { st_filetype: file_type, + st_ino: Inode::from_path(path_str).as_u64(), + st_size: metadata.len(), + st_ctim: metadata.created(), + st_mtim: metadata.modified(), + st_atim: metadata.accessed(), ..Filestat::default() }, ); @@ -1546,14 +1562,15 @@ impl WasiFs { _ => {} } - let ret = inodes.add_inode_val(InodeVal { + let st_ino = Inode::from_path(&name); + stat.st_ino = st_ino.as_u64(); + + inodes.add_inode_val(InodeVal { stat: RwLock::new(stat), is_preopened, name, kind: RwLock::new(kind), - }); - stat.st_ino = ret.ino().as_u64(); - ret + }) } pub fn create_fd( @@ -1565,14 +1582,45 @@ impl WasiFs { inode: InodeGuard, ) -> Result { let idx = self.next_fd.next_val(); - self.create_fd_ext(rights, rights_inheriting, flags, open_flags, inode, idx)?; + self.create_fd_ext( + rights, + rights_inheriting, + flags, + open_flags, + inode, + idx, + false, + )?; Ok(idx) } + pub fn with_fd( + &self, + rights: Rights, + rights_inheriting: Rights, + flags: Fdflags, + open_flags: u16, + inode: InodeGuard, + idx: WasiFd, + ) -> Result<(), Errno> { + self.make_max_fd(idx + 1); + self.create_fd_ext( + rights, + rights_inheriting, + flags, + open_flags, + inode, + idx, + true, + )?; + Ok(()) + } + pub fn make_max_fd(&self, fd: u32) { self.next_fd.clip_val(fd); } + #[allow(clippy::too_many_arguments)] pub fn create_fd_ext( &self, rights: Rights, @@ -1581,12 +1629,17 @@ impl WasiFs { open_flags: u16, inode: InodeGuard, idx: WasiFd, + exclusive: bool, ) -> Result<(), Errno> { let is_stdio = matches!( idx, __WASI_STDIN_FILENO | __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO ); - self.fd_map.write().unwrap().insert( + let mut guard = self.fd_map.write().unwrap(); + if exclusive && guard.contains_key(&idx) { + return Err(Errno::Exist); + } + guard.insert( idx, Fd { rights, @@ -1640,6 +1693,7 @@ impl WasiFs { __WASI_STDOUT_FILENO, STDOUT_DEFAULT_RIGHTS, Fdflags::APPEND, + FS_STDOUT_INO, ); } @@ -1651,6 +1705,7 @@ impl WasiFs { __WASI_STDIN_FILENO, STDIN_DEFAULT_RIGHTS, Fdflags::empty(), + FS_STDIN_INO, ); } @@ -1662,6 +1717,7 @@ impl WasiFs { __WASI_STDERR_FILENO, STDERR_DEFAULT_RIGHTS, Fdflags::APPEND, + FS_STDERR_INO, ); } @@ -1873,6 +1929,7 @@ impl WasiFs { Ok(()) } + #[allow(clippy::too_many_arguments)] pub(crate) fn create_std_dev_inner( &self, inodes: &WasiInodes, @@ -1881,10 +1938,12 @@ impl WasiFs { raw_fd: WasiFd, rights: Rights, fd_flags: Fdflags, + st_ino: Inode, ) { let inode = { let stat = Filestat { st_filetype: Filetype::CharacterDevice, + st_ino: st_ino.as_u64(), ..Filestat::default() }; let kind = Kind::File { @@ -1921,6 +1980,7 @@ impl WasiFs { let wf = wf.read().unwrap(); return Ok(Filestat { st_filetype: Filetype::RegularFile, + st_ino: Inode::from_path(path.to_string_lossy().as_ref()).as_u64(), st_size: wf.size(), st_atim: wf.last_accessed(), st_mtim: wf.last_modified(), diff --git a/lib/wasix/src/journal/effector/syscalls/epoll_create.rs b/lib/wasix/src/journal/effector/syscalls/epoll_create.rs index 7fbe2ae227a..c25d9608bfd 100644 --- a/lib/wasix/src/journal/effector/syscalls/epoll_create.rs +++ b/lib/wasix/src/journal/effector/syscalls/epoll_create.rs @@ -6,7 +6,7 @@ impl JournalEffector { } pub fn apply_epoll_create(ctx: &mut FunctionEnvMut<'_, WasiEnv>, fd: Fd) -> anyhow::Result<()> { - let ret_fd = crate::syscalls::epoll_create_internal(ctx) + let ret_fd = crate::syscalls::epoll_create_internal(ctx, Some(fd)) .map_err(|err| { anyhow::format_err!("journal restore error: failed to create epoll - {}", err) })? diff --git a/lib/wasix/src/journal/effector/syscalls/fd_event.rs b/lib/wasix/src/journal/effector/syscalls/fd_event.rs index a30738947a2..9416299cca0 100644 --- a/lib/wasix/src/journal/effector/syscalls/fd_event.rs +++ b/lib/wasix/src/journal/effector/syscalls/fd_event.rs @@ -25,23 +25,13 @@ impl JournalEffector { flags: EventFdFlags, fd: Fd, ) -> anyhow::Result<()> { - let ret_fd = crate::syscalls::fd_event_internal(ctx, initial_val, flags) + crate::syscalls::fd_event_internal(ctx, initial_val, flags, Some(fd)) .map(|r| r.map_err(|err| err.to_string())) .unwrap_or_else(|err| Err(err.to_string())) .map_err(|err| { anyhow::format_err!("journal restore error: failed to create event - {}", err) })?; - let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd, fd); - if ret != Errno::Success { - bail!( - "journal restore error: failed renumber file descriptor after create event (from={}, to={}) - {}", - ret_fd, - fd, - ret - ); - } - Ok(()) } } diff --git a/lib/wasix/src/journal/effector/syscalls/fd_pipe.rs b/lib/wasix/src/journal/effector/syscalls/fd_pipe.rs index 21b32d8a402..b99575477d0 100644 --- a/lib/wasix/src/journal/effector/syscalls/fd_pipe.rs +++ b/lib/wasix/src/journal/effector/syscalls/fd_pipe.rs @@ -14,30 +14,10 @@ impl JournalEffector { fd1: Fd, fd2: Fd, ) -> anyhow::Result<()> { - let (ret_fd1, ret_fd2) = crate::syscalls::fd_pipe_internal(ctx).map_err(|err| { + crate::syscalls::fd_pipe_internal(ctx, Some(fd1), Some(fd2)).map_err(|err| { anyhow::format_err!("journal restore error: failed to create pipe - {}", err) })?; - let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd1, fd1); - if ret != Errno::Success { - bail!( - "journal restore error: failed renumber file descriptor after create pipe (from={}, to={}) - {}", - ret_fd1, - fd1, - ret - ); - } - - let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd2, fd2); - if ret != Errno::Success { - bail!( - "journal restore error: failed renumber file descriptor after create pipe (from={}, to={}) - {}", - ret_fd2, - fd2, - ret - ); - } - Ok(()) } } diff --git a/lib/wasix/src/journal/effector/syscalls/path_open.rs b/lib/wasix/src/journal/effector/syscalls/path_open.rs index f63e303bceb..ccd7cdec98f 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_open.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_open.rs @@ -49,8 +49,9 @@ impl JournalEffector { fs_rights_base, fs_rights_inheriting, fs_flags, + Some(fd), ); - let ret_fd = match res? { + match res? { Ok(fd) => fd, Err(err) => { bail!( @@ -61,17 +62,6 @@ impl JournalEffector { ); } }; - - let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd, fd); - if ret != Errno::Success { - bail!( - "journal restore error: failed renumber file descriptor after open (from={}, to={}) - {}", - ret_fd, - fd, - ret - ); - } - Ok(()) } } diff --git a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs index b66d4407d00..8cef12b838b 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs @@ -89,25 +89,13 @@ impl JournalEffector { } let rights = Rights::all_socket(); - let ret_fd = state - .fs - .create_fd(rights, rights, new_flags, 0, inode) - .map_err(|err| { - anyhow::format_err!( - "journal restore error: failed to create remote accepted socket - {}", - err - ) - })?; - - let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd, fd); - if ret != Errno::Success { - bail!( - "journal restore error: failed renumber file descriptor after accepting socket (from={}, to={}) - {}", - ret_fd, - fd, - ret - ); - } + let ret = state.fs.with_fd(rights, rights, new_flags, 0, inode, fd); + ret.map_err(|err| { + anyhow::format_err!( + "journal restore error: failed to create remote accepted socket - {}", + err + ) + })?; Ok(()) } diff --git a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs index a72763497b6..51829efdc97 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs @@ -70,9 +70,9 @@ impl JournalEffector { .create_inode_with_default_stat(inodes, kind, false, "socket".into()); let rights = Rights::all_socket(); - let ret_fd = state + state .fs - .create_fd(rights, rights, Fdflags::empty(), 0, inode) + .with_fd(rights, rights, Fdflags::empty(), 0, inode, fd) .map_err(|err| { anyhow::format_err!( "journal restore error: failed to create remote connected socket - {}", @@ -80,16 +80,6 @@ impl JournalEffector { ) })?; - let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd, fd); - if ret != Errno::Success { - bail!( - "journal restore error: failed renumber file descriptor after connecting the socket (from={}, to={}) - {}", - ret_fd, - fd, - ret - ); - } - Ok(()) } } diff --git a/lib/wasix/src/journal/effector/syscalls/sock_open.rs b/lib/wasix/src/journal/effector/syscalls/sock_open.rs index eaa07b40919..2b2433608ab 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_open.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_open.rs @@ -20,7 +20,7 @@ impl JournalEffector { pt: SockProto, fd: Fd, ) -> anyhow::Result<()> { - let ret_fd = crate::syscalls::sock_open_internal(ctx, af, ty, pt) + crate::syscalls::sock_open_internal(ctx, af, ty, pt, Some(fd)) .map_err(|err| { anyhow::format_err!( "journal restore error: failed to open socket (af={:?}, ty={:?}, pt={:?}) - {}", @@ -39,16 +39,6 @@ impl JournalEffector { err ) })?; - - let ret = crate::syscalls::fd_renumber_internal(ctx, ret_fd, fd); - if ret != Errno::Success { - bail!( - "journal restore error: failed renumber file descriptor after opening socket (from={}, to={}) - {}", - ret_fd, - fd, - ret - ); - } Ok(()) } } diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index 69837851f39..f80f81e1876 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -464,16 +464,20 @@ impl WasiThread { ); } } + let mut total_forgotten = 0usize; while let Some(disowned) = disown { - for hash in disowned.snapshots.keys() { - tracing::trace!( - "wasi[{}]::stack has been forgotten (hash={})", - self.pid(), - hash - ); + for _hash in disowned.snapshots.keys() { + total_forgotten += 1; } disown = disowned.next; } + if total_forgotten > 0 { + tracing::trace!( + "wasi[{}]::stack has been forgotten (cnt={})", + self.pid(), + total_forgotten + ); + } } else { memory_stack = &memory_stack[pstack.memory_stack.len()..]; memory_stack_corrected = diff --git a/lib/wasix/src/syscalls/wasi/fd_event.rs b/lib/wasix/src/syscalls/wasi/fd_event.rs index 6f3a01f2942..4c67857f5f7 100644 --- a/lib/wasix/src/syscalls/wasi/fd_event.rs +++ b/lib/wasix/src/syscalls/wasi/fd_event.rs @@ -10,7 +10,7 @@ pub fn fd_event( flags: EventFdFlags, ret_fd: WasmPtr, ) -> Result { - let fd = wasi_try_ok!(fd_event_internal(&mut ctx, initial_val, flags)?); + let fd = wasi_try_ok!(fd_event_internal(&mut ctx, initial_val, flags, None)?); let env = ctx.data(); let (memory, state, _) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; @@ -32,6 +32,7 @@ pub fn fd_event_internal( ctx: &mut FunctionEnvMut<'_, WasiEnv>, initial_val: u64, flags: EventFdFlags, + with_fd: Option, ) -> Result, WasiError> { let env = ctx.data(); let (memory, state, mut inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; @@ -49,9 +50,16 @@ pub fn fd_event_internal( | Rights::FD_WRITE | Rights::POLL_FD_READWRITE | Rights::FD_FDSTAT_SET_FLAGS; - let fd = wasi_try_ok_ok!(state - .fs - .create_fd(rights, rights, Fdflags::empty(), 0, inode)); + let fd = wasi_try_ok_ok!(if let Some(fd) = with_fd { + state + .fs + .with_fd(rights, rights, Fdflags::empty(), 0, inode, fd) + .map(|_| fd) + } else { + state + .fs + .create_fd(rights, rights, Fdflags::empty(), 0, inode) + }); Ok(Ok(fd)) } diff --git a/lib/wasix/src/syscalls/wasi/path_filestat_get.rs b/lib/wasix/src/syscalls/wasi/path_filestat_get.rs index 4941f62ee3a..293b9c86f9d 100644 --- a/lib/wasix/src/syscalls/wasi/path_filestat_get.rs +++ b/lib/wasix/src/syscalls/wasi/path_filestat_get.rs @@ -71,6 +71,7 @@ pub(crate) fn path_filestat_get_internal( path_string, flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )?; + let st_ino = file_inode.ino().as_u64(); let mut stat = if file_inode.is_preopened { *file_inode.stat.read().unwrap().deref() diff --git a/lib/wasix/src/syscalls/wasi/path_open.rs b/lib/wasix/src/syscalls/wasi/path_open.rs index 833dc3a3df7..0503bc10906 100644 --- a/lib/wasix/src/syscalls/wasi/path_open.rs +++ b/lib/wasix/src/syscalls/wasi/path_open.rs @@ -80,6 +80,7 @@ pub fn path_open( fs_rights_base, fs_rights_inheriting, fs_flags, + None, )?); let env = ctx.data(); @@ -123,6 +124,7 @@ pub(crate) fn path_open_internal( fs_rights_base: Rights, fs_rights_inheriting: Rights, fs_flags: Fdflags, + with_fd: Option, ) -> Result, WasiError> { let env = ctx.data(); let (memory, mut state, mut inodes) = @@ -252,7 +254,6 @@ pub(crate) fn path_open_internal( if let Some(handle) = handle { let handle = handle.read().unwrap(); - inode.stat.write().unwrap().st_mtim = handle.last_modified(); if let Some(fd) = handle.get_special_fd() { // We clone the file descriptor so that when its closed // nothing bad happens @@ -376,13 +377,27 @@ pub(crate) fn path_open_internal( // TODO: check and reduce these // TODO: ensure a mutable fd to root can never be opened - let out_fd = wasi_try_ok_ok!(state.fs.create_fd( - adjusted_rights, - fs_rights_inheriting, - fs_flags, - open_flags, - inode - )); + let out_fd = wasi_try_ok_ok!(if let Some(fd) = with_fd { + state + .fs + .with_fd( + adjusted_rights, + fs_rights_inheriting, + fs_flags, + open_flags, + inode, + fd, + ) + .map(|_| fd) + } else { + state.fs.create_fd( + adjusted_rights, + fs_rights_inheriting, + fs_flags, + open_flags, + inode, + ) + }); Ok(Ok(out_fd)) } diff --git a/lib/wasix/src/syscalls/wasi/poll_oneoff.rs b/lib/wasix/src/syscalls/wasi/poll_oneoff.rs index 98f3a501e81..dddf274c0a3 100644 --- a/lib/wasix/src/syscalls/wasi/poll_oneoff.rs +++ b/lib/wasix/src/syscalls/wasi/poll_oneoff.rs @@ -54,7 +54,7 @@ impl EventResult { /// Output: /// - `u32 nevents` /// The number of events seen -#[instrument(level = "trace", skip_all, fields(timeout_ms = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret)] +//#[instrument(level = "trace", skip_all, fields(timeout_ms = field::Empty, fd_guards = field::Empty, seen = field::Empty), ret)] pub fn poll_oneoff( mut ctx: FunctionEnvMut<'_, WasiEnv>, in_: WasmPtr, diff --git a/lib/wasix/src/syscalls/wasix/epoll_create.rs b/lib/wasix/src/syscalls/wasix/epoll_create.rs index cf9d942176f..91e63ea978d 100644 --- a/lib/wasix/src/syscalls/wasix/epoll_create.rs +++ b/lib/wasix/src/syscalls/wasix/epoll_create.rs @@ -18,7 +18,7 @@ pub fn epoll_create( mut ctx: FunctionEnvMut<'_, WasiEnv>, ret_fd: WasmPtr, ) -> Result { - let fd = wasi_try_ok!(epoll_create_internal(&mut ctx)?); + let fd = wasi_try_ok!(epoll_create_internal(&mut ctx, None)?); let env = ctx.data(); #[cfg(feature = "journal")] @@ -40,6 +40,7 @@ pub fn epoll_create( pub fn epoll_create_internal( ctx: &mut FunctionEnvMut<'_, WasiEnv>, + with_fd: Option, ) -> Result, WasiError> { wasi_try_ok_ok!(WasiEnv::process_signals_and_exit(ctx)?); @@ -60,9 +61,16 @@ pub fn epoll_create_internal( ); let rights = Rights::POLL_FD_READWRITE | Rights::FD_FDSTAT_SET_FLAGS; - let fd = wasi_try_ok_ok!(state - .fs - .create_fd(rights, rights, Fdflags::empty(), 0, inode)); + let fd = wasi_try_ok_ok!(if let Some(fd) = with_fd { + state + .fs + .with_fd(rights, rights, Fdflags::empty(), 0, inode, fd) + .map(|_| fd) + } else { + state + .fs + .create_fd(rights, rights, Fdflags::empty(), 0, inode) + }); Ok(Ok(fd)) } diff --git a/lib/wasix/src/syscalls/wasix/fd_pipe.rs b/lib/wasix/src/syscalls/wasix/fd_pipe.rs index 7752053d066..7faf4518707 100644 --- a/lib/wasix/src/syscalls/wasix/fd_pipe.rs +++ b/lib/wasix/src/syscalls/wasix/fd_pipe.rs @@ -16,7 +16,7 @@ pub fn fd_pipe( ro_fd1: WasmPtr, ro_fd2: WasmPtr, ) -> Result { - let (fd1, fd2) = wasi_try_ok!(fd_pipe_internal(&mut ctx)); + let (fd1, fd2) = wasi_try_ok!(fd_pipe_internal(&mut ctx, None, None)); let env = ctx.data(); #[cfg(feature = "journal")] @@ -38,7 +38,11 @@ pub fn fd_pipe( Ok(Errno::Success) } -pub fn fd_pipe_internal(ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> Result<(WasiFd, WasiFd), Errno> { +pub fn fd_pipe_internal( + ctx: &mut FunctionEnvMut<'_, WasiEnv>, + with_fd1: Option, + with_fd2: Option, +) -> Result<(WasiFd, WasiFd), Errno> { let env = ctx.data(); let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; let (pipe1, pipe2) = Pipe::channel(); @@ -63,12 +67,28 @@ pub fn fd_pipe_internal(ctx: &mut FunctionEnvMut<'_, WasiEnv>) -> Result<(WasiFd | Rights::POLL_FD_READWRITE | Rights::SOCK_SEND | Rights::FD_FDSTAT_SET_FLAGS; - let fd1 = state - .fs - .create_fd(rights, rights, Fdflags::empty(), 0, inode1)?; - let fd2 = state - .fs - .create_fd(rights, rights, Fdflags::empty(), 0, inode2)?; + + let fd1 = if let Some(fd) = with_fd1 { + state + .fs + .with_fd(rights, rights, Fdflags::empty(), 0, inode1, fd) + .map(|_| fd)? + } else { + state + .fs + .create_fd(rights, rights, Fdflags::empty(), 0, inode1)? + }; + + let fd2 = if let Some(fd) = with_fd2 { + state + .fs + .with_fd(rights, rights, Fdflags::empty(), 0, inode2, fd) + .map(|_| fd)? + } else { + state + .fs + .create_fd(rights, rights, Fdflags::empty(), 0, inode2)? + }; Ok((fd1, fd2)) } diff --git a/lib/wasix/src/syscalls/wasix/proc_spawn.rs b/lib/wasix/src/syscalls/wasix/proc_spawn.rs index bbec1b3aa40..10158c742ab 100644 --- a/lib/wasix/src/syscalls/wasix/proc_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/proc_spawn.rs @@ -178,6 +178,7 @@ pub fn proc_spawn_internal( 0, inode2, fd, + false, )?; trace!("fd_pipe (fd1={}, fd2={})", pipe, fd); diff --git a/lib/wasix/src/syscalls/wasix/sock_accept.rs b/lib/wasix/src/syscalls/wasix/sock_accept.rs index 727bf85bde3..0c317aec8d1 100644 --- a/lib/wasix/src/syscalls/wasix/sock_accept.rs +++ b/lib/wasix/src/syscalls/wasix/sock_accept.rs @@ -31,7 +31,13 @@ pub fn sock_accept( let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); - let (fd, _, _) = wasi_try_ok!(sock_accept_internal(env, sock, fd_flags, nonblocking)?); + let (fd, _, _) = wasi_try_ok!(sock_accept_internal( + env, + sock, + fd_flags, + nonblocking, + None + )?); wasi_try_mem_ok!(ro_fd.write(&memory, fd)); @@ -66,8 +72,13 @@ pub fn sock_accept_v2( let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); - let (fd, local_addr, peer_addr) = - wasi_try_ok!(sock_accept_internal(env, sock, fd_flags, nonblocking)?); + let (fd, local_addr, peer_addr) = wasi_try_ok!(sock_accept_internal( + env, + sock, + fd_flags, + nonblocking, + None + )?); #[cfg(feature = "journal")] if ctx.data().enable_journal { @@ -104,6 +115,7 @@ pub(crate) fn sock_accept_internal( sock: WasiFd, mut fd_flags: Fdflags, mut nonblocking: bool, + with_fd: Option, ) -> Result, WasiError> { let state = env.state(); let inodes = &state.inodes; @@ -153,7 +165,14 @@ pub(crate) fn sock_accept_internal( } let rights = Rights::all_socket(); - let fd = wasi_try_ok_ok!(state.fs.create_fd(rights, rights, new_flags, 0, inode)); + let fd = wasi_try_ok_ok!(if let Some(fd) = with_fd { + state + .fs + .with_fd(rights, rights, new_flags, 0, inode, fd) + .map(|_| fd) + } else { + state.fs.create_fd(rights, rights, new_flags, 0, inode) + }); Span::current().record("fd", fd); Ok(Ok((fd, local_addr, peer_addr))) diff --git a/lib/wasix/src/syscalls/wasix/sock_open.rs b/lib/wasix/src/syscalls/wasix/sock_open.rs index 02eb651c8de..da6de577780 100644 --- a/lib/wasix/src/syscalls/wasix/sock_open.rs +++ b/lib/wasix/src/syscalls/wasix/sock_open.rs @@ -43,7 +43,7 @@ pub fn sock_open( _ => {} } - let fd = wasi_try_ok!(sock_open_internal(&mut ctx, af, ty, pt)?); + let fd = wasi_try_ok!(sock_open_internal(&mut ctx, af, ty, pt, None)?); #[cfg(feature = "journal")] if ctx.data().enable_journal { @@ -65,6 +65,7 @@ pub(crate) fn sock_open_internal( af: Addressfamily, ty: Socktype, pt: SockProto, + with_fd: Option, ) -> Result, WasiError> { let env = ctx.data(); let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; @@ -101,9 +102,16 @@ pub(crate) fn sock_open_internal( .fs .create_inode_with_default_stat(inodes, kind, false, "socket".to_string().into()); let rights = Rights::all_socket(); - let fd = wasi_try_ok_ok!(state - .fs - .create_fd(rights, rights, Fdflags::empty(), 0, inode)); + let fd = wasi_try_ok_ok!(if let Some(fd) = with_fd { + state + .fs + .with_fd(rights, rights, Fdflags::empty(), 0, inode, fd) + .map(|_| fd) + } else { + state + .fs + .create_fd(rights, rights, Fdflags::empty(), 0, inode) + }); Span::current().record("sock", fd); Ok(Ok(fd))