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:
Jakub Konka
2019-10-17 11:33:56 +02:00
committed by GitHub
parent d458fb6815
commit c3bf04042e
7 changed files with 79 additions and 31 deletions

View File

@@ -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)?;