Description
There are several bugs here, but they all stem from the same problem -- the stdlib doesn't handle O_PATH
correctly in a few places.
O_PATH
requires you to set an extra (unused) mode.
O_PATH
causes most other flags to be ignored, so requiring a mode is a little bit weird. Really, O_PATH
should probably be handled as another OpenOptions
mode.
let file = OpenOptions::new().custom_flags(libc::O_PATH).open(".")?; // gives EINVAL
This one can be worked around pretty trivially (though it is a bit silly in my view), but unfortunately that's when you hit the next issue:
Rust uses ioctl(FIOCLEX)
to set close-on-exec.
This doesn't work with O_PATH
because O_PATH
file descriptors have empty_fops
which means that all ioctl(2)
s on them fail (this is a security feature).
Rust proactively sets close-on-exec in many different places, which means that any method that ends up triggering an FIOCLEX
gives a spurrious EBADF
. The most obvious problem is with File::try_clone()
but I'm sure there are plenty of other examples:
let file = OpenOptions::new().read(true).custom_flags(libc::O_PATH).open(".")?;
let new_file = file.try_clone()?; // gives EBADF
Rust really should use fcntl(F_SETFD)
because ioctl(2)
s are blocked on all O_PATH
descriptors (while fcntl
works without issue).