Fixes path_symlink_trailing_slashes test case (#125)
* Fixes `path_symlink_trailing_slashes` test case This commit: * adds a couple `log::debug!` macro calls in and around `path_get` for easier future debugging * changes impl of `path_symlink` hostcall to actually *require* the final component (matching the impl of WASI in C) * ignores the error `__WASI_ENOTDIR` in `path_get`'s `readlinkat` call which is not meant to be an error at this stage (i.e., this potentially erroneous condition *will be* handled later, in one of the layers above) * Fixes `path_symlink_trailing` slashes on BSD-nixes This commit: * makes `path_symlink` host-specific (Linux and BSD-like nixes now have their own differing implementations) * on BSD-like nixes, when `ENOTDIR` is returned from `symlinkat` it checks whether the target path contains a trailing slash, strips it, and then checks if the target path without the trailing slash exists; if yes, then converts the error code to `EEXIST` to match Linux/POSIX spec
This commit is contained in:
@@ -49,6 +49,48 @@ pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
|
||||
use nix::{errno::Errno, fcntl::AtFlags, libc::symlinkat, sys::stat::fstatat};
|
||||
|
||||
let old_path_cstr = CString::new(old_path.as_bytes()).map_err(|_| Error::EILSEQ)?;
|
||||
let new_path_cstr = CString::new(resolved.path().as_bytes()).map_err(|_| Error::EILSEQ)?;
|
||||
|
||||
log::debug!("path_symlink old_path = {:?}", old_path);
|
||||
log::debug!("path_symlink resolved = {:?}", resolved);
|
||||
|
||||
let res = unsafe {
|
||||
symlinkat(
|
||||
old_path_cstr.as_ptr(),
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
new_path_cstr.as_ptr(),
|
||||
)
|
||||
};
|
||||
if res != 0 {
|
||||
match Errno::last() {
|
||||
Errno::ENOTDIR => {
|
||||
// On BSD, symlinkat returns ENOTDIR when it should in fact
|
||||
// return a EEXIST. It seems that it gets confused with by
|
||||
// the trailing slash in the target path. Thus, we strip
|
||||
// the trailing slash and check if the path exists, and
|
||||
// adjust the error code appropriately.
|
||||
let new_path = resolved.path().trim_end_matches('/');
|
||||
if let Ok(_) = fstatat(
|
||||
resolved.dirfd().as_raw_fd(),
|
||||
new_path,
|
||||
AtFlags::AT_SYMLINK_NOFOLLOW,
|
||||
) {
|
||||
Err(Error::EEXIST)
|
||||
} else {
|
||||
Err(Error::ENOTDIR)
|
||||
}
|
||||
}
|
||||
x => Err(host_impl::errno_from_nix(x)),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
|
||||
use nix::{errno::Errno, fcntl::AtFlags, libc::renameat, sys::stat::fstatat};
|
||||
let old_path_cstr = CString::new(resolved_old.path().as_bytes()).map_err(|_| Error::EILSEQ)?;
|
||||
|
||||
Reference in New Issue
Block a user