diff --git a/src/hostcalls/fs.rs b/src/hostcalls/fs.rs index fed19d137a..684bd137d9 100644 --- a/src/hostcalls/fs.rs +++ b/src/hostcalls/fs.rs @@ -492,7 +492,7 @@ pub fn path_open( Err(e) => return enc_errno(e), }; - let fe = match hostcalls_impl::path_open( + match hostcalls_impl::path_open( wasi_ctx, dirfd, dirflags, @@ -504,18 +504,24 @@ pub fn path_open( needed_inheriting, fs_flags, ) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; + Ok(fe) => { + let guest_fd = match wasi_ctx.insert_fd_entry(fe) { + Ok(fd) => fd, + Err(e) => return enc_errno(e), + }; - let guest_fd = match wasi_ctx.insert_fd_entry(fe) { - Ok(fd) => fd, - Err(e) => return enc_errno(e), - }; + enc_fd_byref(memory, fd_out_ptr, guest_fd) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(enc_errno) + } + Err(e) => { + if let Err(e) = enc_fd_byref(memory, fd_out_ptr, wasm32::__wasi_fd_t::max_value()) { + return enc_errno(e); + } - enc_fd_byref(memory, fd_out_ptr, guest_fd) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(enc_errno) + enc_errno(e) + } + } } #[wasi_common_cbindgen] diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs index 25308e340b..0e480346a4 100644 --- a/src/sys/unix/hostcalls_impl/fs.rs +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -353,7 +353,7 @@ pub(crate) fn path_open( needed_base |= host::__WASI_RIGHT_PATH_CREATE_FILE; } if nix_all_oflags.contains(OFlag::O_TRUNC) { - needed_inheriting |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE; + needed_base |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE; } // convert file descriptor flags @@ -778,7 +778,34 @@ pub(crate) fn path_unlink_file( // nix doesn't expose unlinkat() yet match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } { 0 => Ok(()), - _ => Err(host_impl::errno_from_nix(errno::Errno::last())), + _ => { + let mut e = errno::Errno::last(); + + #[cfg(not(linux))] + { + // Non-Linux implementations may return EPERM when attempting to remove a + // directory without REMOVEDIR. While that's what POSIX specifies, it's + // less useful. Adjust this to EISDIR. It doesn't matter that this is not + // atomic with the unlinkat, because if the file is removed and a directory + // is created before fstatat sees it, we're racing with that change anyway + // and unlinkat could have legitimately seen the directory if the race had + // turned out differently. + use nix::fcntl::AtFlags; + use nix::sys::stat::{fstatat, SFlag}; + + if e == errno::Errno::EPERM { + if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) { + e = errno::Errno::EISDIR; + } + } else { + e = errno::Errno::last(); + } + } + } + + Err(host_impl::errno_from_nix(e)) + } } }