diff --git a/Cargo.lock b/Cargo.lock index eaf3487e9ed..0085033cda1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2448,6 +2448,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "id-arena" version = "2.2.1" @@ -2473,12 +2591,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2828,6 +2957,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "litrs" version = "0.4.1" @@ -4916,6 +5051,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "tar" version = "0.4.42" @@ -5126,6 +5272,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -5832,12 +5988,12 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", "serde", ] @@ -5854,6 +6010,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -7550,6 +7718,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xattr" version = "1.3.1" @@ -7591,6 +7771,30 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -7612,6 +7816,27 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -7632,6 +7857,28 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "zip" version = "2.2.0" diff --git a/deny.toml b/deny.toml index 35a9d17436f..e15764eb402 100644 --- a/deny.toml +++ b/deny.toml @@ -103,6 +103,7 @@ allow = [ # BOOST source license # NOTE: not to be confused with business source license! "BSL-1.0", + "Unicode-3.0", ] # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the @@ -138,7 +139,7 @@ exceptions = [ [[licenses.clarify]] crate = "ring" expression = "MIT AND ISC AND OpenSSL" -license-files = [ { path = "LICENSE", hash = 0xbd0eed23 } ] +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] [licenses.private] # If true, ignores workspace crates that aren't published, or are only diff --git a/lib/config/rust-toolchain.toml b/lib/config/rust-toolchain.toml deleted file mode 100644 index 0538cafabc5..00000000000 --- a/lib/config/rust-toolchain.toml +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -channel = "1.70" -components = ["rustfmt", "clippy"] diff --git a/lib/virtual-io/src/selector.rs b/lib/virtual-io/src/selector.rs index 0f939b74a19..1c9693e13af 100644 --- a/lib/virtual-io/src/selector.rs +++ b/lib/virtual-io/src/selector.rs @@ -134,7 +134,14 @@ impl Selector { let mut events = mio::Events::with_capacity(128); loop { // Wait for an event to trigger - poll.poll(&mut events, None).unwrap(); + if let Err(e) = poll.poll(&mut events, None) { + // This can happen when a debugger is attached + #[cfg(debug_assertions)] + if e.kind() == std::io::ErrorKind::Interrupted { + continue; + } + panic!("Unexpected error in selector poll loop: {e:?}"); + } // Loop through all the events while under a guard lock let mut guard = engine.inner.lock().unwrap(); diff --git a/lib/wasix/src/fs/fd.rs b/lib/wasix/src/fs/fd.rs index 06909f0053f..cf133dcca85 100644 --- a/lib/wasix/src/fs/fd.rs +++ b/lib/wasix/src/fs/fd.rs @@ -62,7 +62,7 @@ impl Fd { pub struct InodeVal { pub stat: RwLock, pub is_preopened: bool, - pub name: Cow<'static, str>, + pub name: RwLock>, pub kind: RwLock, } diff --git a/lib/wasix/src/fs/fd_list.rs b/lib/wasix/src/fs/fd_list.rs index 927abdc8142..5ab3cb99339 100644 --- a/lib/wasix/src/fs/fd_list.rs +++ b/lib/wasix/src/fs/fd_list.rs @@ -218,7 +218,7 @@ mod tests { inner: Arc::new(InodeVal { is_preopened: false, kind: RwLock::new(Kind::Buffer { buffer: vec![] }), - name: Cow::Borrowed(""), + name: RwLock::new(Cow::Borrowed("")), stat: RwLock::new(Default::default()), }), }, diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index c183de77aa2..d19ac4035ef 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -545,7 +545,7 @@ impl WasiFs { /// Converts a relative path into an absolute path pub(crate) fn relative_path_to_absolute(&self, mut path: String) -> String { - if path.starts_with("./") { + if !path.starts_with("/") { let current_dir = self.current_dir.lock().unwrap(); path = format!("{}{}", current_dir.as_str(), &path[1..]); if path.contains("//") { @@ -575,7 +575,7 @@ impl WasiFs { let root_inode = inodes.add_inode_val(InodeVal { stat: RwLock::new(stat), is_preopened: true, - name: "/".into(), + name: RwLock::new("/".into()), kind: RwLock::new(root_kind), }); @@ -895,6 +895,13 @@ impl WasiFs { // TODO: rights checks 'path_iter: for (i, component) in path.components().enumerate() { + // Since we're resolving the path against the given inode, we want to + // assume '/a/b' to be the same as `a/b` relative to the inode, so + // we skip over the RootDir component. + if matches!(component, Component::RootDir) { + continue; + } + // used to terminate symlink resolution properly let last_component = i + 1 == n_components; // for each component traverse file structure @@ -1255,13 +1262,31 @@ impl WasiFs { follow_symlinks: bool, ) -> Result { let base_inode = self.get_fd_inode(base)?; - let start_inode = - if !base_inode.deref().name.starts_with('/') && self.is_wasix.load(Ordering::Acquire) { - let (cur_inode, _) = self.get_current_dir(inodes, base)?; - cur_inode + let name = base_inode.name.read().unwrap(); + + let start_inode; + if name.starts_with('/') { + drop(name); + start_inode = base_inode; + } else { + let kind = base_inode.kind.read().unwrap(); + + // HACK: We preopen '/' with an alias of '.' by default in `prepare_webc_env`. If wasix-libc + // picks that up as the base FD, we want to do the same thing we do when the base is '/' + if *name == "." + && matches!(kind.deref(), Kind::Dir { path, .. } if path.as_os_str().as_encoded_bytes() == b"/") + { + drop(name); + drop(kind); + start_inode = base_inode; } else { - self.get_fd_inode(base)? - }; + drop(name); + drop(kind); + let (cur_inode, _) = self.get_current_dir(inodes, base)?; + start_inode = cur_inode; + } + }; + self.get_inode_at_path_inner(inodes, start_inode, path, 0, follow_symlinks) } @@ -1416,7 +1441,7 @@ impl WasiFs { // REVIEW: // no need for +1, because there is no 0 end-of-string marker // john: removing the +1 seems cause regression issues - pr_name_len: inode_val.name.len() as u32 + 1, + pr_name_len: inode_val.name.read().unwrap().len() as u32 + 1, } .untagged(), } @@ -1527,7 +1552,7 @@ impl WasiFs { inodes.add_inode_val(InodeVal { stat: RwLock::new(stat), is_preopened, - name, + name: RwLock::new(name), kind: RwLock::new(kind), }) } @@ -1906,7 +1931,7 @@ impl WasiFs { inodes.add_inode_val(InodeVal { stat: RwLock::new(stat), is_preopened: true, - name: name.to_string().into(), + name: RwLock::new(name.to_string().into()), kind: RwLock::new(kind), }) }; diff --git a/lib/wasix/src/runners/wasi_common.rs b/lib/wasix/src/runners/wasi_common.rs index e50f2deffb2..9e0de3a0e03 100644 --- a/lib/wasix/src/runners/wasi_common.rs +++ b/lib/wasix/src/runners/wasi_common.rs @@ -66,14 +66,19 @@ impl CommonWasiOptions { }); let fs = prepare_filesystem(root_fs, &self.mounts, container_fs)?; - builder.add_preopen_dir("/")?; - + // TODO: What's a preopen for '.' supposed to mean anyway? Why do we need it? if self.mounts.iter().all(|m| m.guest != ".") { // The user hasn't mounted "." to anything, so let's map it to "/" let path = builder.get_current_dir().unwrap_or(PathBuf::from("/")); builder.add_map_dir(".", path)?; } + // wasix-libc favors later FDs, and we want it to pass in the preopen + // for '/' when opening things since that's faster, so do this after + // the '.' alias. Purely a performance thing, since we also account + // for the alias in `get_inode_at_path`. + builder.add_preopen_dir("/")?; + builder.set_fs(Box::new(fs)); for pkg in &self.injected_packages { diff --git a/lib/wasix/src/syscalls/wasi/fd_close.rs b/lib/wasix/src/syscalls/wasi/fd_close.rs index 435c0ebc873..5e90e821e47 100644 --- a/lib/wasix/src/syscalls/wasi/fd_close.rs +++ b/lib/wasix/src/syscalls/wasi/fd_close.rs @@ -19,25 +19,16 @@ pub fn fd_close(mut ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Result( let path_chars = wasi_try_mem!(path.slice(&memory, path_len)); let inode = wasi_try!(state.fs.get_fd_inode(fd)); - Span::current().record("path", inode.name.as_ref()); + let name = inode.name.read().unwrap(); + Span::current().record("path", name.as_ref()); // check inode-val.is_preopened? @@ -22,11 +23,11 @@ pub fn fd_prestat_dir_name( Kind::Dir { .. } | Kind::Root { .. } => { // TODO: verify this: null termination, etc let path_len: u64 = path_len.into(); - if (inode.name.len() as u64) < path_len { + if (name.len() as u64) < path_len { wasi_try_mem!(path_chars - .subslice(0..inode.name.len() as u64) - .write_slice(inode.name.as_bytes())); - wasi_try_mem!(path_chars.index(inode.name.len() as u64).write(0)); + .subslice(0..name.len() as u64) + .write_slice(name.as_bytes())); + wasi_try_mem!(path_chars.index(name.len() as u64).write(0)); //trace!("=> result: \"{}\"", inode_val.name); diff --git a/lib/wasix/src/syscalls/wasi/fd_readdir.rs b/lib/wasix/src/syscalls/wasi/fd_readdir.rs index 2595b1e689c..89ef2f3dd2c 100644 --- a/lib/wasix/src/syscalls/wasi/fd_readdir.rs +++ b/lib/wasix/src/syscalls/wasi/fd_readdir.rs @@ -64,7 +64,11 @@ pub fn fd_readdir( entry_vec.extend(entries.iter().filter(|(_, inode)| inode.is_preopened).map( |(name, inode)| { let stat = inode.stat.read().unwrap(); - (inode.name.to_string(), stat.st_filetype, stat.st_ino) + ( + inode.name.read().unwrap().to_string(), + stat.st_filetype, + stat.st_ino, + ) }, )); // adding . and .. special folders @@ -88,7 +92,11 @@ pub fn fd_readdir( .into_iter() .map(|(name, inode)| { let stat = inode.stat.read().unwrap(); - (format!("/{}", inode.name), stat.st_filetype, stat.st_ino) + ( + format!("/{}", inode.name.read().unwrap().as_ref()), + stat.st_filetype, + stat.st_ino, + ) }) .collect() } diff --git a/lib/wasix/src/syscalls/wasi/path_create_directory.rs b/lib/wasix/src/syscalls/wasi/path_create_directory.rs index 60343269fc2..a998a1f591a 100644 --- a/lib/wasix/src/syscalls/wasi/path_create_directory.rs +++ b/lib/wasix/src/syscalls/wasi/path_create_directory.rs @@ -1,4 +1,7 @@ -use std::{path::PathBuf, str::FromStr}; +use std::{ + path::{Component, PathBuf}, + str::FromStr, +}; use super::*; use crate::syscalls::*; @@ -29,14 +32,6 @@ pub fn path_create_directory( let mut path_string = unsafe { get_input_str_ok!(&memory, path, path_len) }; Span::current().record("path", path_string.as_str()); - // Convert relative paths into absolute paths - if path_string.starts_with("./") { - path_string = ctx.data().state.fs.relative_path_to_absolute(path_string); - trace!( - %path_string - ); - } - wasi_try_ok!(path_create_directory_internal(&mut ctx, fd, &path_string)); let env = ctx.data(); @@ -68,15 +63,18 @@ pub(crate) fn path_create_directory_internal( let path = std::path::PathBuf::from(path); let path_vec = path .components() - .map(|comp| { - comp.as_os_str() - .to_str() - .map(|inner_str| inner_str.to_string()) - .ok_or(Errno::Inval) + .filter_map(|comp| match comp { + Component::RootDir => None, + _ => Some( + comp.as_os_str() + .to_str() + .map(|inner_str| inner_str.to_string()) + .ok_or(Errno::Inval), + ), }) .collect::, Errno>>()?; if path_vec.is_empty() { - trace!("path vector is inva;id (its empty)"); + trace!("path vector is invalid (its empty)"); return Err(Errno::Inval); } @@ -86,6 +84,11 @@ pub(crate) fn path_create_directory_internal( let processing_cur_dir_inode = cur_dir_inode.clone(); let mut guard = processing_cur_dir_inode.write(); match guard.deref_mut() { + // TODO: this depends on entries already being filled in. This is the case when you go + // through wasix-libc (which is, realistically, what everybody does) because it stats + // the path before calling path_create_directory, at which point entries is filled in. + // However, an alternate implementation or a manually implemented module may not always + // do this. Kind::Dir { ref mut entries, path, diff --git a/lib/wasix/src/syscalls/wasi/path_filestat_get.rs b/lib/wasix/src/syscalls/wasi/path_filestat_get.rs index 293b9c86f9d..a8a1f09d14b 100644 --- a/lib/wasix/src/syscalls/wasi/path_filestat_get.rs +++ b/lib/wasix/src/syscalls/wasi/path_filestat_get.rs @@ -31,9 +31,16 @@ pub fn path_filestat_get( let mut path_string = unsafe { get_input_str!(&memory, path, path_len) }; // Convert relative paths into absolute paths - if path_string.starts_with("./") { - path_string = ctx.data().state.fs.relative_path_to_absolute(path_string); - } + // let path_str = if path_string == "." { + // &"/" + // } else if path_string.starts_with("./") { + // &path_string[1..] + // } else { + // &path_string + // }; + // if path_string.starts_with("./") || path_string == "." { + // path_string = ctx.data().state.fs.relative_path_to_absolute(path_string); + // } tracing::trace!(path = path_string.as_str()); let stat = wasi_try!(path_filestat_get_internal( @@ -109,13 +116,8 @@ pub fn path_filestat_get_old( let env = ctx.data(); let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; - let mut path_string = unsafe { get_input_str!(&memory, path, path_len) }; - - // Convert relative paths into absolute paths - if path_string.starts_with("./") { - path_string = ctx.data().state.fs.relative_path_to_absolute(path_string); - } - tracing::trace!(path = path_string.as_str()); + let path_string = unsafe { get_input_str!(&memory, path, path_len) }; + Span::current().record("path", path_string.as_str()); let stat = wasi_try!(path_filestat_get_internal( &memory, diff --git a/lib/wasix/src/syscalls/wasi/path_filestat_set_times.rs b/lib/wasix/src/syscalls/wasi/path_filestat_set_times.rs index 328ceb995ef..e70da3bde14 100644 --- a/lib/wasix/src/syscalls/wasi/path_filestat_set_times.rs +++ b/lib/wasix/src/syscalls/wasi/path_filestat_set_times.rs @@ -32,17 +32,9 @@ pub fn path_filestat_set_times( let env = ctx.data(); let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; - let mut path_string = unsafe { get_input_str_ok!(&memory, path, path_len) }; + let path_string = unsafe { get_input_str_ok!(&memory, path, path_len) }; Span::current().record("path", path_string.as_str()); - // Convert relative paths into absolute paths - if path_string.starts_with("./") { - path_string = ctx.data().state.fs.relative_path_to_absolute(path_string); - trace!( - %path_string - ); - } - wasi_try_ok!(path_filestat_set_times_internal( &mut ctx, fd, diff --git a/lib/wasix/src/syscalls/wasi/path_link.rs b/lib/wasix/src/syscalls/wasi/path_link.rs index aa7253c1485..63243d1e13a 100644 --- a/lib/wasix/src/syscalls/wasi/path_link.rs +++ b/lib/wasix/src/syscalls/wasi/path_link.rs @@ -87,25 +87,16 @@ pub(crate) fn path_link_internal( return Err(Errno::Access); } - // Convert relative paths into absolute paths - let old_path_str = ctx - .data() - .state - .fs - .relative_path_to_absolute(old_path.to_string()); - let new_path_str = ctx - .data() - .state - .fs - .relative_path_to_absolute(new_path.to_string()); + Span::current().record("old_path", old_path); + Span::current().record("new_path", new_path); let source_inode = state.fs.get_inode_at_path( inodes, old_fd, - &old_path_str, + old_path, old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, )?; - let target_path_arg = std::path::PathBuf::from(&new_path_str); + let target_path_arg = std::path::PathBuf::from(new_path); let (target_parent_inode, new_entry_name) = state .fs diff --git a/lib/wasix/src/syscalls/wasi/path_open.rs b/lib/wasix/src/syscalls/wasi/path_open.rs index 9dfb9fc9fb8..2223254275b 100644 --- a/lib/wasix/src/syscalls/wasi/path_open.rs +++ b/lib/wasix/src/syscalls/wasi/path_open.rs @@ -60,17 +60,9 @@ pub fn path_open( // - __WASI_O_EXCL (fail if file exists) // - __WASI_O_TRUNC (truncate size to 0) - let mut path_string = unsafe { get_input_str_ok!(&memory, path, path_len) }; + let path_string = unsafe { get_input_str_ok!(&memory, path, path_len) }; Span::current().record("path", path_string.as_str()); - // Convert relative paths into absolute paths - if path_string.starts_with("./") { - path_string = ctx.data().state.fs.relative_path_to_absolute(path_string); - trace!( - %path_string - ); - } - let out_fd = wasi_try_ok!(path_open_internal( &mut ctx, dirfd, diff --git a/lib/wasix/src/syscalls/wasi/path_readlink.rs b/lib/wasix/src/syscalls/wasi/path_readlink.rs index 194212ac950..4f7f8cffa25 100644 --- a/lib/wasix/src/syscalls/wasi/path_readlink.rs +++ b/lib/wasix/src/syscalls/wasi/path_readlink.rs @@ -37,14 +37,6 @@ pub fn path_readlink( let mut path_str = unsafe { get_input_str!(&memory, path, path_len) }; Span::current().record("path", path_str.as_str()); - // Convert relative paths into absolute paths - if path_str.starts_with("./") { - path_str = ctx.data().state.fs.relative_path_to_absolute(path_str); - trace!( - %path_str - ); - } - let inode = wasi_try!(state.fs.get_inode_at_path(inodes, dir_fd, &path_str, false)); { diff --git a/lib/wasix/src/syscalls/wasi/path_remove_directory.rs b/lib/wasix/src/syscalls/wasi/path_remove_directory.rs index a8d335e403c..d254b48838c 100644 --- a/lib/wasix/src/syscalls/wasi/path_remove_directory.rs +++ b/lib/wasix/src/syscalls/wasi/path_remove_directory.rs @@ -14,17 +14,9 @@ pub fn path_remove_directory( let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; let base_dir = wasi_try!(state.fs.get_fd(fd)); - let mut path_str = unsafe { get_input_str!(&memory, path, path_len) }; + let path_str = unsafe { get_input_str!(&memory, path, path_len) }; Span::current().record("path", path_str.as_str()); - // Convert relative paths into absolute paths - if path_str.starts_with("./") { - path_str = ctx.data().state.fs.relative_path_to_absolute(path_str); - trace!( - %path_str - ); - } - wasi_try!(path_remove_directory_internal(&mut ctx, fd, &path_str)); let env = ctx.data(); diff --git a/lib/wasix/src/syscalls/wasi/path_rename.rs b/lib/wasix/src/syscalls/wasi/path_rename.rs index 6e117c4dc70..7f1fe0b51cb 100644 --- a/lib/wasix/src/syscalls/wasi/path_rename.rs +++ b/lib/wasix/src/syscalls/wasi/path_rename.rs @@ -28,12 +28,10 @@ pub fn path_rename( ) -> Result { let env = ctx.data(); let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; - let mut source_str = unsafe { get_input_str_ok!(&memory, old_path, old_path_len) }; + let source_str = unsafe { get_input_str_ok!(&memory, old_path, old_path_len) }; Span::current().record("old_path", source_str.as_str()); - source_str = ctx.data().state.fs.relative_path_to_absolute(source_str); - let mut target_str = unsafe { get_input_str_ok!(&memory, new_path, new_path_len) }; + let target_str = unsafe { get_input_str_ok!(&memory, new_path, new_path_len) }; Span::current().record("new_path", target_str.as_str()); - target_str = ctx.data().state.fs.relative_path_to_absolute(target_str); let ret = path_rename_internal(&mut ctx, old_fd, &source_str, new_fd, &target_str)?; let env = ctx.data(); @@ -72,7 +70,7 @@ pub fn path_rename_internal( } } - // this is to be sure the source file is fetch from filesystem if needed + // this is to be sure the source file is fetched from the filesystem if needed wasi_try_ok!(state .fs .get_inode_at_path(inodes, source_fd, source_path, true)); @@ -206,7 +204,7 @@ pub fn path_rename_internal( if need_create { let mut guard = target_parent_inode.write(); if let Kind::Dir { entries, .. } = guard.deref_mut() { - let result = entries.insert(target_entry_name, source_entry); + let result = entries.insert(target_entry_name.clone(), source_entry); assert!( result.is_none(), "fatal error: race condition on filesystem detected or internal logic error" @@ -219,6 +217,7 @@ pub fn path_rename_internal( wasi_try_ok!(state .fs .get_inode_at_path(inodes, target_fd, target_path, true)); + *target_inode.name.write().unwrap() = target_entry_name.into(); target_inode.stat.write().unwrap().st_size = source_size; Ok(Errno::Success) diff --git a/lib/wasix/src/syscalls/wasi/path_symlink.rs b/lib/wasix/src/syscalls/wasi/path_symlink.rs index dd995511d95..55875b5879f 100644 --- a/lib/wasix/src/syscalls/wasi/path_symlink.rs +++ b/lib/wasix/src/syscalls/wasi/path_symlink.rs @@ -25,12 +25,10 @@ pub fn path_symlink( ) -> Result { let env = ctx.data(); let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; - let mut old_path_str = unsafe { get_input_str_ok!(&memory, old_path, old_path_len) }; + let old_path_str = unsafe { get_input_str_ok!(&memory, old_path, old_path_len) }; Span::current().record("old_path", old_path_str.as_str()); - let mut new_path_str = unsafe { get_input_str_ok!(&memory, new_path, new_path_len) }; + let new_path_str = unsafe { get_input_str_ok!(&memory, new_path, new_path_len) }; Span::current().record("new_path", new_path_str.as_str()); - old_path_str = ctx.data().state.fs.relative_path_to_absolute(old_path_str); - new_path_str = ctx.data().state.fs.relative_path_to_absolute(new_path_str); wasi_try_ok!(path_symlink_internal( &mut ctx, diff --git a/lib/wasix/src/syscalls/wasi/path_unlink_file.rs b/lib/wasix/src/syscalls/wasi/path_unlink_file.rs index 16a78c5daf2..7860396bfdf 100644 --- a/lib/wasix/src/syscalls/wasi/path_unlink_file.rs +++ b/lib/wasix/src/syscalls/wasi/path_unlink_file.rs @@ -24,14 +24,9 @@ pub fn path_unlink_file( if !base_dir.rights.contains(Rights::PATH_UNLINK_FILE) { return Ok(Errno::Access); } - let mut path_str = unsafe { get_input_str_ok!(&memory, path, path_len) }; + let path_str = unsafe { get_input_str_ok!(&memory, path, path_len) }; Span::current().record("path", path_str.as_str()); - // Convert relative paths into absolute paths - if path_str.starts_with("./") { - path_str = ctx.data().state.fs.relative_path_to_absolute(path_str); - } - let ret = path_unlink_file_internal(&mut ctx, fd, &path_str)?; let env = ctx.data(); diff --git a/tests/integration/cli/tests/snapshot.rs b/tests/integration/cli/tests/snapshot.rs index 5f6c845254b..4273b90cbbd 100644 --- a/tests/integration/cli/tests/snapshot.rs +++ b/tests/integration/cli/tests/snapshot.rs @@ -1362,3 +1362,12 @@ fn test_snapshot_worker_panicking() { )); assert_json_snapshot!(snapshot); } + +#[cfg_attr(any(target_env = "musl", target_os = "windows"), ignore)] +#[test] +fn test_snapshot_mkdir_rename() { + let snapshot = TestBuilder::new() + .with_name(function!()) + .run_wasm(include_bytes!("./wasm/mkdir-rename.wasm")); + assert_json_snapshot!(snapshot); +} diff --git a/tests/integration/cli/tests/snapshots/snapshot__snapshot_mkdir_rename.snap b/tests/integration/cli/tests/snapshots/snapshot__snapshot_mkdir_rename.snap new file mode 100644 index 00000000000..ec4c07b5e94 --- /dev/null +++ b/tests/integration/cli/tests/snapshots/snapshot__snapshot_mkdir_rename.snap @@ -0,0 +1,22 @@ +--- +source: tests/integration/cli/tests/snapshot.rs +assertion_line: 1369 +expression: snapshot +--- +{ + "spec": { + "name": "snapshot::test_snapshot_mkdir_rename", + "use_packages": [], + "include_webcs": [], + "cli_args": [], + "enable_threads": true, + "enable_network": false + }, + "result": { + "Success": { + "stdout": "", + "stderr": "", + "exit_code": 0 + } + } +} diff --git a/tests/integration/cli/tests/wasm/mkdir-rename.wasm b/tests/integration/cli/tests/wasm/mkdir-rename.wasm new file mode 100644 index 00000000000..ad160ef9f99 Binary files /dev/null and b/tests/integration/cli/tests/wasm/mkdir-rename.wasm differ diff --git a/tests/rust-wasi-tests/src/bin/mkdir-rename.rs b/tests/rust-wasi-tests/src/bin/mkdir-rename.rs new file mode 100644 index 00000000000..9a20a4e2f0d --- /dev/null +++ b/tests/rust-wasi-tests/src/bin/mkdir-rename.rs @@ -0,0 +1,12 @@ +fn main() { + std::fs::create_dir("/a").unwrap(); + assert!(std::fs::metadata("/a").unwrap().is_dir()); + + std::fs::rename("/a", "/b").unwrap(); + assert!(matches!(std::fs::metadata("/a"), Err(e) if e.kind() == std::io::ErrorKind::NotFound)); + assert!(std::fs::metadata("/b").unwrap().is_dir()); + + std::fs::create_dir("/a").unwrap(); + assert!(std::fs::metadata("/a").unwrap().is_dir()); + assert!(std::fs::metadata("/b").unwrap().is_dir()); +} diff --git a/tests/wasi-fyi/ported_fd_close.stdout b/tests/wasi-fyi/ported_fd_close.stdout index ce04f5f9424..53c9c272713 100644 --- a/tests/wasi-fyi/ported_fd_close.stdout +++ b/tests/wasi-fyi/ported_fd_close.stdout @@ -1,4 +1,3 @@ Successfully closed file! Successfully closed stderr! Successfully closed stdin! -Successfully closed stdout! diff --git a/tests/wasix/closing-pre-opened-dirs/main.c b/tests/wasix/closing-pre-opened-dirs/main.c index f9a9695406c..89f084ecd89 100644 --- a/tests/wasix/closing-pre-opened-dirs/main.c +++ b/tests/wasix/closing-pre-opened-dirs/main.c @@ -5,45 +5,78 @@ #include #include -int main() { - const char *expected_entries[] = {".", "..", "main.c", "main.wasm", "output", "run.sh", NULL}; +typedef unsigned char bool; +#define false (0) +#define true (1) - for (int fd = 0; fd <= 5; fd++) { +int main() +{ + const size_t expected_count = 6; + const char *expected_entries[] = {".", "..", "main.c", "main.wasm", "output", "run.sh"}; + bool entries_observed[6] = {false}; + + for (int fd = 3; fd <= 5; fd++) + { close(fd); } DIR *dir = opendir("."); - if (!dir) { + if (!dir) + { printf("opendir"); exit(EXIT_FAILURE); } struct dirent *entry; - int i = 0; - while ((entry = readdir(dir)) != NULL) { - if (expected_entries[i] == NULL) { - fprintf(stdout, "Unexpected extra entry: %s\n", entry->d_name); - closedir(dir); - exit(EXIT_FAILURE); + size_t total_count = 0; + while ((entry = readdir(dir)) != NULL) + { + bool found = false; + + for (int i = 0; i < expected_count; ++i) + { + if (strcmp(expected_entries[i], entry->d_name) == 0 && entries_observed[i] == false) + { + entries_observed[i] = true; + found = true; + break; + } } - int cmp_result = strcmp(entry->d_name, expected_entries[i]); - if (cmp_result != 0) { - printf("1"); - return 1; + if (!found) + { + printf("Expected file name: %s\n", entry->d_name); } - i++; + total_count++; + } + + for (int i = 0; i < expected_count; ++i) + { + if (!entries_observed[i]) + { + printf("Unobserved entry: %s\n", expected_entries[i]); + exit(EXIT_FAILURE); + } } - if (expected_entries[i] != NULL) { - printf("1"); - return 1; + if (total_count != expected_count) + { + printf("Mismatch in number of entries\n"); + exit(EXIT_FAILURE); } closedir(dir); printf("0"); + + // Use fclose instead of close to make sure the write above is flushed + fclose(stdin); + fclose(stdout); + fclose(stderr); + + // If this prints, it'll be caught in the output diff and fail the test. + printf("Expected stdout to be closed\n"); + return 0; } - diff --git a/tests/wasix/create-dir-at-cwd-with-chdir/main.c b/tests/wasix/create-dir-at-cwd-with-chdir/main.c index e98c7133e89..e91f2fbafac 100644 --- a/tests/wasix/create-dir-at-cwd-with-chdir/main.c +++ b/tests/wasix/create-dir-at-cwd-with-chdir/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,28 +8,71 @@ // the difference between this test and the one in create-dir-at-cwd is the // presence of chdir. -// this will force chdir to be linked with this binary which in turn will change -// the behavior of rel_path logic the libc. +// this will force chdir to be linked with this binary which in turn will change +// the behavior of rel_path logic in some versions of wasix-libc. // // for more info see: libc-find-relpath.h in wasix-libc int (*dummy_chdir_ref)(const char *path) = chdir; -int main() { - int status = EXIT_FAILURE; +int ensure_dir_accessible(const char *dir_name) +{ + struct stat st; + char buf[256]; - const char *dirName1 = "test1"; - if (mkdir(dirName1, 0755) != 0) { - goto end; + if (stat(dir_name, &st) != 0 || !S_ISDIR(st.st_mode)) + { + return -1; } - const char *dirName2 = "./test2"; - if (mkdir(dirName2, 0755) != 0) { - goto end; + sprintf(buf, "./%s", dir_name); + if (stat(buf, &st) != 0 || !S_ISDIR(st.st_mode)) + { + return -1; } - status = EXIT_SUCCESS; + sprintf(buf, "/home/%s", dir_name); + if (stat(buf, &st) != 0 || !S_ISDIR(st.st_mode)) + { + return -1; + } + + return 0; +} + +void error(const char *message) +{ + perror(message); + exit(-1); +} + +int main() +{ + if (mkdir("test1", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test1") != 0) + { + error("test1"); + } + + if (mkdir("./test2", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test2") != 0) + { + error("test2"); + } + + int cwd_fd = open(".", O_RDONLY | O_DIRECTORY); + if (cwd_fd < 0) + { + error("open cwd"); + } + + if (mkdirat(cwd_fd, "test3", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test3") != 0) + { + error("test3"); + } + + if (mkdirat(cwd_fd, "./test4", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test4") != 0) + { + error("test4"); + } -end: - printf("%d", status); + printf("0"); return 0; } diff --git a/tests/wasix/create-dir-at-cwd-with-chdir/run.sh b/tests/wasix/create-dir-at-cwd-with-chdir/run.sh index 66be3793d6b..eb6e2800474 100755 --- a/tests/wasix/create-dir-at-cwd-with-chdir/run.sh +++ b/tests/wasix/create-dir-at-cwd-with-chdir/run.sh @@ -2,4 +2,4 @@ $WASMER -q run main.wasm --dir . > output -rmdir test1 test2 2>/dev/null && printf "0" | diff -u output - 1>/dev/null +rmdir test1 test2 test3 test4 2>/dev/null && printf "0" | diff -u output - 1>/dev/null diff --git a/tests/wasix/create-dir-at-cwd/main.c b/tests/wasix/create-dir-at-cwd/main.c index 5e36441ef2e..eba61784af3 100644 --- a/tests/wasix/create-dir-at-cwd/main.c +++ b/tests/wasix/create-dir-at-cwd/main.c @@ -1,24 +1,68 @@ +#include #include #include #include #include -int main() { - int status = EXIT_FAILURE; +int ensure_dir_accessible(const char *dir_name) +{ + struct stat st; + char buf[256]; - const char *dirName1 = "test1"; - if (mkdir(dirName1, 0755) != 0) { - goto end; + if (stat(dir_name, &st) != 0 || !S_ISDIR(st.st_mode)) + { + return -1; } - const char *dirName2 = "./test2"; - if (mkdir(dirName2, 0755) != 0) { - goto end; + sprintf(buf, "./%s", dir_name); + if (stat(buf, &st) != 0 || !S_ISDIR(st.st_mode)) + { + return -1; } - status = EXIT_SUCCESS; + sprintf(buf, "/home/%s", dir_name); + if (stat(buf, &st) != 0 || !S_ISDIR(st.st_mode)) + { + return -1; + } + + return 0; +} + +void error(const char *message) +{ + perror(message); + exit(-1); +} + +int main() +{ + if (mkdir("test1", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test1") != 0) + { + error("test1"); + } + + if (mkdir("./test2", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test2") != 0) + { + error("test2"); + } + + int cwd_fd = open(".", O_RDONLY | O_DIRECTORY); + if (cwd_fd < 0) + { + error("open cwd"); + } + + if (mkdirat(cwd_fd, "test3", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test3") != 0) + { + error("test3"); + } + + if (mkdirat(cwd_fd, "./test4", S_IRWXU | S_IRWXG | S_IRWXO) != 0 || ensure_dir_accessible("test4") != 0) + { + error("test4"); + } -end: - printf("%d", status); + printf("0"); return 0; } diff --git a/tests/wasix/create-dir-at-cwd/run.sh b/tests/wasix/create-dir-at-cwd/run.sh index 66be3793d6b..eb6e2800474 100755 --- a/tests/wasix/create-dir-at-cwd/run.sh +++ b/tests/wasix/create-dir-at-cwd/run.sh @@ -2,4 +2,4 @@ $WASMER -q run main.wasm --dir . > output -rmdir test1 test2 2>/dev/null && printf "0" | diff -u output - 1>/dev/null +rmdir test1 test2 test3 test4 2>/dev/null && printf "0" | diff -u output - 1>/dev/null