Fix symlink resolution on Linux and FreeBSD.

Linux's open returns ENOTDIR when used with O_DIRECTORY|O_NOFOLLOW and
the path is a symlink. Update the code to expect this.

FreeBSD's open returns EMLINK instead of ELOOP when using O_NOFOLLOW on
symlink. Update the code to expect this.
This commit is contained in:
Dan Gohman
2019-05-17 16:48:50 -07:00
committed by Jakub Konka
parent 9823bf6196
commit c5bda1f4e1
2 changed files with 21 additions and 1 deletions

View File

@@ -737,6 +737,23 @@ pub fn path_open(
return wasm32::__WASI_ENXIO;
}
}
// Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY
// on a symlink.
Some(Errno::ENOTDIR)
if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() =>
{
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_IFLNK) {
return wasm32::__WASI_ELOOP;
}
}
return wasm32::__WASI_ENOTDIR;
}
// FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
// a symlink.
Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => {
return wasm32::__WASI_ELOOP;
}
Some(e) => return wasm32::errno_from_nix(e),
None => return wasm32::__WASI_ENOSYS,
}

View File

@@ -158,8 +158,11 @@ pub fn path_get<P: AsRef<OsStr>>(
continue;
}
Err(e)
// Check to see if it was a symlink. Linux indicates
// this with ENOTDIR because of the O_DIRECTORY flag.
if e.as_errno() == Some(Errno::ELOOP)
|| e.as_errno() == Some(Errno::EMLINK) =>
|| e.as_errno() == Some(Errno::EMLINK)
|| e.as_errno() == Some(Errno::ENOTDIR) =>
{
// attempt symlink expansion
match readlinkat(