Code change:
I noticed this bug while researching the same bug in Given , I expected: Instead, this happened: The related issue in the STL is microsoft/STL; the PR to fix this in the STL is microsoft/STL. The problem is that opens the file; this is not allowed for the following four files (not sure if there are additional files which act similarly): We can get around this by using instead of ; is that something that's desired, or is this considered a weird enough use case that we aren't interested in fixing it?
Documentation explicitly documents that is equivalent to accessing metadata of the file, doesn't it? Changing this behaviour would be a breaking change I'm afraid we wouldn't be able to afford.
true, the question was whether could use instead of followed by ; that's how we're solving this in the C++ standard library. (I'll note that this documentation is now wrong, since it now uses ) This would mean that the returned from would now have for but otherwise it'd be complete (although for reparse points, we'd still have to open the file). We could additionally do followed by , to fill out those fields. I'll note that we already have this behavior on UWP, and when getting metadata from a .
Hm, changing is not something I'd be minded to do lightly. At least not without further investigation. Some questions that spring to mind: What happens if the user does not have permission to read the directory but does have permission to read the file? Can we get all the metadata that's needed? Ah your edit answered that. Does this affect performance?
given that it's reading the directory, it should make performance better (assuming one doesn't do the double call thing). I'm personally not that invested, I just thought I'd share with y'all since I have to fix this bug for the STL
It's great that we have this report! I just want to make sure we investigate fully before making changes that may affect users of the standard library. I'll also note we do have the unstable whose behaviour can change.
Ok I tested with two users. For the sake of this example, I'll call them "Admin" and "User". As Admin I created a directory and denied User all access. Within the directory I then created a "" file and granted User access to just that file. As User, I tried using to test if the file exists. This failed with . I then tried using which succeeded. As does opening the file with , Thus can fail in situations where directly accessing the file would succeed.
FYI, has run into this in the wild: We'll work around it by falling back to if fails. Would be nice if could do something similar automatically, but I appreciate that there's a high bar for backwards compatibility and performance here.
To be clear, using FindFirstFile as a fallback (rather than replacement) answers my concerns and I think would be the best solution to this issue.

Related discussion:
text: } pub fn stat(path: &Path) -> io::Result<FileAttr> { <del> let mut opts = OpenOptions::new(); // No read or write permissions are necessary opts.access_mode(0); // This flag is so we can open directories too opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); let file = File::open(path, &opts)?; file.file_attr() </del> <ins> metadata(path, ReparsePoint::Follow) </ins> } pub fn lstat(path: &Path) -> io::Result<FileAttr> { <ins> metadata(path, ReparsePoint::Open) } #[repr(u32)] #[derive(Clone, Copy, PartialEq, Eq)] enum ReparsePoint { Follow = 0, Open = c::FILE_FLAG_OPEN_REPARSE_POINT, } impl ReparsePoint { fn as_flag(self) -> u32 { self as u32 } } fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> { </ins> let mut opts = OpenOptions::new(); // No read or write permissions are necessary opts.access_mode(0); <del> opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); let file = File::open(path, &opts)?; file.file_attr() </del> <ins> opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag()); // Attempt to open the file normally. // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`. // If the fallback fails for any reason we return the original error. match File::open(path, &opts) { Ok(file) => file.file_attr(), Err(e) if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _) => { // `ERROR_SHARING_VIOLATION` will almost never be returned. // Usually if a file is locked you can still read some metadata. // However, there are special system files, such as // `C:hiberfil.sys`, that are locked in a way that denies even that. unsafe { let path = maybe_verbatim(path)?; // `FindFirstFileW` accepts wildcard file names. // Fortunately wildcards are not valid file names and // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) // therefore it's safe to assume the file name given does not // include wildcards. let mut wfd = mem::zeroed(); let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); if handle == c::INVALID_HANDLE_VALUE { // This can fail if the user does not have read access to the // directory. Err(e) } else { // We no longer need the find handle. c::FindClose(handle); // `FindFirstFileW` reads the cached file information from the // directory. The downside is that this metadata may be outdated. let attrs = FileAttr::from(wfd); if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() { Err(e) } else { Ok(attrs) } } } } Err(e) => Err(e), } </ins> } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {