diff --git a/lib/wasix/src/syscalls/wasi/path_create_directory.rs b/lib/wasix/src/syscalls/wasi/path_create_directory.rs index b7263ff21c5..d26805d8bc6 100644 --- a/lib/wasix/src/syscalls/wasi/path_create_directory.rs +++ b/lib/wasix/src/syscalls/wasi/path_create_directory.rs @@ -85,6 +85,7 @@ pub(crate) fn path_create_directory_internal( } let mut cur_dir_inode = working_dir.inode; + let mut created_directory = false; for comp in &path_vec { let processing_cur_dir_inode = cur_dir_inode.clone(); let mut guard = processing_cur_dir_inode.write(); @@ -125,6 +126,7 @@ pub(crate) fn path_create_directory_internal( return Err(Errno::Notdir); } } else { + created_directory = true; state.fs_create_dir(&adjusted_path)?; } let kind = Kind::Dir { @@ -160,5 +162,9 @@ pub(crate) fn path_create_directory_internal( } } - Ok(()) + if created_directory { + Ok(()) + } else { + Err(Errno::Exist) + } } diff --git a/lib/wasix/src/syscalls/wasi/path_remove_directory.rs b/lib/wasix/src/syscalls/wasi/path_remove_directory.rs index d6ac9e67f76..2fc0fe2fecf 100644 --- a/lib/wasix/src/syscalls/wasi/path_remove_directory.rs +++ b/lib/wasix/src/syscalls/wasi/path_remove_directory.rs @@ -47,58 +47,125 @@ pub(crate) fn path_remove_directory_internal( path: &str, ) -> Result<(), Errno> { let env = ctx.data(); - let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; + let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; + let working_dir = state.fs.get_fd(fd)?; - let inode = state.fs.get_inode_at_path(inodes, fd, path, false)?; - let (parent_inode, childs_name) = - state - .fs - .get_parent_inode_at_path(inodes, fd, std::path::Path::new(&path), false)?; - - let host_path_to_remove = { - let guard = inode.read(); - match guard.deref() { - Kind::Dir { entries, path, .. } => { - if !entries.is_empty() || state.fs_read_dir(path)?.count() != 0 { - return Err(Errno::Notempty); - } - path.clone() - } - Kind::Root { .. } => return Err(Errno::Access), - _ => return Err(Errno::Notdir), - } - }; + 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) + }) + .collect::, Errno>>()?; + if path_vec.is_empty() { + trace!("path vector is invalid (its empty)"); + return Err(Errno::Inval); + } - { - let mut guard = parent_inode.write(); + let (child, parent) = path_vec.split_last().unwrap(); + + // if path only contains one component (the root), operation is not permitted + if child.is_empty() { + return Err(Errno::Access); + } + + let mut cur_dir_inode = working_dir.inode; + for comp in path_vec.iter() { + let processing_cur_dir_inode = cur_dir_inode.clone(); + let mut guard = processing_cur_dir_inode.write(); match guard.deref_mut() { Kind::Dir { - ref mut entries, .. + ref mut entries, + path, + parent, } => { - let removed_inode = entries.remove(&childs_name).ok_or(Errno::Inval)?; + match comp.borrow() { + ".." => { + if let Some(p) = parent.upgrade() { + cur_dir_inode = p; + continue; + } + } + "." => continue, + _ => (), + } + if let Some(child) = entries.get(comp) { + cur_dir_inode = child.clone(); + } else { + let parent_path = path.clone(); + let mut adjusted_path = path.clone(); + drop(guard); + + // TODO: double check this doesn't risk breaking the sandbox + adjusted_path.push(comp); + if let Ok(adjusted_path_stat) = path_filestat_get_internal( + &memory, + state, + inodes, + fd, + 0, + &adjusted_path.to_string_lossy(), + ) { + if adjusted_path_stat.st_filetype != Filetype::Directory { + trace!("path is not a directory"); + return Err(Errno::Notdir); + } + } else { + return Err(Errno::Noent); + } + let kind = Kind::Dir { + parent: cur_dir_inode.downgrade(), + path: adjusted_path, + entries: Default::default(), + }; + let new_inode = state + .fs + .create_inode(inodes, kind, false, comp.to_string())?; - // TODO: make this a debug assert in the future - assert!(inode.ino() == removed_inode.ino()); + // reborrow to insert + { + let mut guard = cur_dir_inode.write(); + if let Kind::Dir { + ref mut entries, .. + } = guard.deref_mut() + { + entries.insert(comp.to_string(), new_inode.clone()); + } + } + cur_dir_inode = new_inode; + } + } + Kind::Root { .. } => { + trace!("the root node can no create a directory"); + return Err(Errno::Access); + } + _ => { + trace!("path is not a directory"); + return Err(Errno::Notdir); } - Kind::Root { .. } => return Err(Errno::Access), - _ => unreachable!( - "Internal logic error in wasi::path_remove_directory, parent is not a directory" - ), } } - let env = ctx.data(); - let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) }; - if let Err(err) = state.fs_remove_dir(host_path_to_remove) { - // reinsert to prevent FS from being in bad state - let mut guard = parent_inode.write(); - if let Kind::Dir { - ref mut entries, .. - } = guard.deref_mut() - { - entries.insert(childs_name, inode); + if let Kind::Dir { + parent, + path: child_path, + entries, + } = cur_dir_inode.write().deref_mut() + { + let parent = parent.upgrade().ok_or(Errno::Noent)?; + + if let Kind::Dir { entries, .. } = parent.write().deref_mut() { + let child_inode = entries.remove(child).ok_or(Errno::Noent)?; + + if let Err(e) = state.fs_remove_dir(&child_path) { + tracing::warn!(path = ?child_path, error = ?e, "failed to remove directory"); + } } - return Err(err); + + drop(parent) } Ok(()) diff --git a/tests/integration/cli/resources/php/db/.ht.sqlite b/tests/integration/cli/resources/php/db/.ht.sqlite new file mode 100644 index 00000000000..cb821a6c547 Binary files /dev/null and b/tests/integration/cli/resources/php/db/.ht.sqlite differ diff --git a/tests/integration/cli/resources/php/test.php b/tests/integration/cli/resources/php/test.php new file mode 100644 index 00000000000..7302b54d23f --- /dev/null +++ b/tests/integration/cli/resources/php/test.php @@ -0,0 +1,16 @@ +query("SELECT name FROM sqlite_master WHERE type='table'"); + + if ($result) { + $sqlite->close(); + } else { + echo "1"; + exit(1); + }; +} + +echo "0" +?> diff --git a/tests/integration/cli/src/fixtures.rs b/tests/integration/cli/src/fixtures.rs index 7f047852b61..9beec7c73ef 100644 --- a/tests/integration/cli/src/fixtures.rs +++ b/tests/integration/cli/src/fixtures.rs @@ -4,6 +4,16 @@ use std::path::{Path, PathBuf}; use crate::{asset_path, c_asset_path}; +pub fn php() -> (PathBuf, PathBuf, PathBuf) { + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + let resources = root.join("resources").join("php"); + ( + root.join("tests").join("wasm").join("php.wasm"), + resources.clone(), + resources.join("db"), + ) +} + /// A WEBC file containing the Python interpreter, compiled to WASI. pub fn python() -> PathBuf { c_asset_path().join("python-0.1.0.wasmer") diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index e02d29a0aa1..d2b1f8c01b1 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -13,7 +13,11 @@ use predicates::str::contains; use rand::Rng; use reqwest::{blocking::Client, IntoUrl}; use tempfile::TempDir; -use wasmer_integration_tests_cli::{asset_path, fixtures, get_wasmer_path}; +use wasmer_integration_tests_cli::{ + asset_path, + fixtures::{self, php}, + get_wasmer_path, +}; const HTTP_GET_TIMEOUT: Duration = Duration::from_secs(5); @@ -43,6 +47,26 @@ static CACHE_RUST_LOG: Lazy = Lazy::new(|| { .join(",") }); +#[test] +fn run_php_with_sqlite() { + let (php_wasm, app_dir, db) = php(); + + let output = Command::new(get_wasmer_path()) + .arg("-q") + .arg("run") + .arg(php_wasm) + .arg("--mapdir") + .arg(format!("/db:{}", db.display())) + .arg("--mapdir") + .arg(format!("/app:{}", app_dir.display())) + .arg("--") + .arg("/app/test.php") + .output() + .unwrap(); + + assert_eq!(output.stdout, "0".as_bytes().to_vec()); +} + /// Ignored on Windows because running vendored packages does not work /// since Windows does not allow `::` characters in filenames (every other OS does) /// diff --git a/tests/integration/cli/tests/wasm/php.wasm b/tests/integration/cli/tests/wasm/php.wasm new file mode 100644 index 00000000000..4d7210e9201 Binary files /dev/null and b/tests/integration/cli/tests/wasm/php.wasm differ diff --git a/tests/wasi-fyi/test.sh b/tests/wasi-fyi/test.sh index 20f586139b0..5d5214b7b24 100755 --- a/tests/wasi-fyi/test.sh +++ b/tests/wasi-fyi/test.sh @@ -6,7 +6,7 @@ bash build.sh status=0 # Define skip list as an array -SKIP_LIST=("fs_create_dir-existing-directory.wasm") +SKIP_LIST=() # List and process .foo files for file in *.wasm; do