diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index e8a970337f..394d5282b5 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -314,11 +314,47 @@ impl Pal for Sys { } fn fstatat(dirfd: c_int, path: Option, buf: Out, flags: c_int) -> Result<()> { - let path = path - .and_then(|cs| str::from_utf8(cs.to_bytes()).ok()) - .ok_or(Errno(ENOENT))?; - let file = openat2(dirfd, path, flags, 0)?; - Sys::fstat(*file, buf) + // `path` should be non-null. + let path = path.ok_or(Errno(EFAULT))?; + let mut path = str::from_utf8(path.to_bytes()).ok().ok_or(Errno(EILSEQ))?; + + if path.is_empty() { + if flags & AT_EMPTY_PATH == AT_EMPTY_PATH { + if dirfd == AT_FDCWD { + path = "."; + } else { + return Sys::fstat(dirfd, buf); + } + } else { + // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out. + return Err(Errno(ENOENT)); + } + } + + // Use `O_PATH` to obtain a file descriptor without actually *opening* the file. This + // bypasses permission checks and avoids cases where opening a file is blocking operation + // (e.g., FIFOs). This gives a file descriptor where fstat(2) can be performed (and some + // other meta operations) but nothing else (e.g. read/write). + // + // `O_CLOEXEC` is used to avoid leaking file descriptors to child processes on exec(2). + // + // FIXME: Ideally we would want the file descriptor to not leak on fork(2) too because + // fstatat(2) should not have side effects. However, Redox does not currently support that, + // so we use `CLOEXEC` as a compromise. + let mut open_flags = fcntl::O_PATH | fcntl::O_CLOEXEC; + if flags & AT_SYMLINK_NOFOLLOW == AT_SYMLINK_NOFOLLOW { + open_flags |= fcntl::O_NOFOLLOW; + } + + let file = openat2(dirfd, path, open_flags, 0)?; + // Close the file descriptor after fstat(2) regardless of success or failure. + let fstat_res = Sys::fstat(*file, buf); + let close_res = syscall::close(file.fd as usize); + if let Err(err) = fstat_res { + return Err(err); + } + close_res?; + fstat_res } fn fstatvfs(fildes: c_int, mut buf: Out) -> Result<()> {