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).
There are several bugs here, but they all stem from the same problem -- the stdlib doesn't handle
O_PATHcorrectly in a few places.O_PATHrequires you to set an extra (unused) mode.O_PATHcauses most other flags to be ignored, so requiring a mode is a little bit weird. Really,O_PATHshould probably be handled as anotherOpenOptionsmode.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_PATHbecauseO_PATHfile descriptors haveempty_fopswhich means that allioctl(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
FIOCLEXgives a spurriousEBADF. The most obvious problem is withFile::try_clone()but I'm sure there are plenty of other examples:Rust really should use
fcntl(F_SETFD)becauseioctl(2)s are blocked on allO_PATHdescriptors (whilefcntlworks without issue).