fix(platform/redox): fstatat
This commit fixes multiple issues with `fstatat` implementation for Redox. 1. `flags` are now properly handled. The `flags` argument specifies AT_* flags not O_* flags. 2. Open with O_PATH to avoid actually *opening* the file which may block or cause permission issues. 3. Close the temporary file descriptor regardless of success of failure. Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
@@ -314,11 +314,47 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
fn fstatat(dirfd: c_int, path: Option<CStr>, buf: Out<stat>, 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<statvfs>) -> Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user