openat2: fix internal assumption causing EINVAL
Inside of `openat2`, we are assuming that if we are passed `AT_SYMLINK_NOFOLLOW` that we are also passed a symlink. This is simply not the case, as POSIX defines the flag to only have a noticable effect if the resolved path is a symlink. Therefore, we cannot assume that we have a symlink if we see `AT_SYMLINK_NOFOLLOW`. The previous behavior caused an `EINVAL` in redoxfs because we do a consistency check to error out if we are passed `O_SYMLINK` (which was added because `AT_SYMLINK_NOFOLLOW` was observed) and aren't a symlink. Hmm, maybe a special errno like `ENOTLNK` should be deployed for this? It's specific enough that it could possibly be added to a future POSIX.
This commit is contained in:
@@ -485,6 +485,14 @@ pub(super) fn openat2_path(dirfd: c_int, path: &str, at_flags: c_int) -> Result<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn at_flags_to_open_flags(at_flags: c_int) -> c_int {
|
||||||
|
let mut out: c_int = 0;
|
||||||
|
if at_flags & fcntl::AT_SYMLINK_NOFOLLOW == fcntl::AT_SYMLINK_NOFOLLOW {
|
||||||
|
out |= fcntl::O_NOFOLLOW;
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
/// Canonicalize and open `path` with respect to `dirfd`.
|
/// Canonicalize and open `path` with respect to `dirfd`.
|
||||||
///
|
///
|
||||||
/// This unexported openat2 is similar to the Linux syscall but with a different interface. The
|
/// This unexported openat2 is similar to the Linux syscall but with a different interface. The
|
||||||
@@ -512,11 +520,7 @@ pub(super) fn openat2(
|
|||||||
oflags: c_int,
|
oflags: c_int,
|
||||||
) -> Result<File, Errno> {
|
) -> Result<File, Errno> {
|
||||||
// Translate at flags into open flags; openat will do this on its own most likely.
|
// Translate at flags into open flags; openat will do this on its own most likely.
|
||||||
let oflags = if at_flags & fcntl::AT_SYMLINK_NOFOLLOW == fcntl::AT_SYMLINK_NOFOLLOW {
|
let oflags = at_flags_to_open_flags(at_flags) | fcntl::O_CLOEXEC | oflags;
|
||||||
fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW | fcntl::O_PATH | fcntl::O_SYMLINK | oflags
|
|
||||||
} else {
|
|
||||||
fcntl::O_CLOEXEC | oflags
|
|
||||||
};
|
|
||||||
let c_path = CString::new(path).map_err(|_| Errno(EINVAL))?;
|
let c_path = CString::new(path).map_err(|_| Errno(EINVAL))?;
|
||||||
File::openat(dirfd, c_path.as_c_str().into(), oflags)
|
File::openat(dirfd, c_path.as_c_str().into(), oflags)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user