Skip to content

Windows implementation of feature path_try_exists #85060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2208,3 +2208,29 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
&mut self.inner
}
}

/// Returns `Ok(true)` if the path points at an existing entity.
///
/// This function will traverse symbolic links to query information about the
/// destination file. In case of broken symbolic links this will return `Ok(false)`.
///
/// As opposed to the `exists()` method, this one doesn't silently ignore errors
/// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission
/// denied on some of the parent directories.)
///
/// # Examples
///
/// ```no_run
/// #![feature(path_try_exists)]
/// use std::fs;
///
/// assert!(!fs::try_exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt"));
/// assert!(fs::try_exists("/root/secret_file.txt").is_err());
/// ```
// FIXME: stabilization should modify documentation of `exists()` to recommend this method
// instead.
#[unstable(feature = "path_try_exists", issue = "83186")]
#[inline]
pub fn try_exists<P: AsRef<Path>>(path: P) -> io::Result<bool> {
fs_imp::try_exists(path.as_ref())
}
6 changes: 1 addition & 5 deletions library/std/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2507,11 +2507,7 @@ impl Path {
#[unstable(feature = "path_try_exists", issue = "83186")]
#[inline]
pub fn try_exists(&self) -> io::Result<bool> {
match fs::metadata(self) {
Ok(_) => Ok(true),
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
Err(error) => Err(error),
}
fs::try_exists(self)
}

/// Returns `true` if the path exists on disk and is pointing at a regular file.
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/hermit/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::sys::time::SystemTime;
use crate::sys::unsupported;
use crate::sys_common::os_str_bytes::OsStrExt;

pub use crate::sys_common::fs::copy;
pub use crate::sys_common::fs::{copy, try_exists};
//pub use crate::sys_common::fs::remove_dir_all;

fn cstr(path: &Path) -> io::Result<CString> {
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use libc::{
dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
};

pub use crate::sys_common::fs::remove_dir_all;
pub use crate::sys_common::fs::{remove_dir_all, try_exists};

pub struct File(FileDesc);

Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/unsupported/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
unsupported()
}

pub fn try_exists(_path: &Path) -> io::Result<bool> {
unsupported()
}

pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
unsupported()
}
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/wasi/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::sys::time::SystemTime;
use crate::sys::unsupported;
use crate::sys_common::FromInner;

pub use crate::sys_common::fs::remove_dir_all;
pub use crate::sys_common::fs::{remove_dir_all, try_exists};

pub struct File {
fd: WasiFd,
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ pub const ERROR_INVALID_HANDLE: DWORD = 6;
pub const ERROR_NOT_ENOUGH_MEMORY: DWORD = 8;
pub const ERROR_OUTOFMEMORY: DWORD = 14;
pub const ERROR_NO_MORE_FILES: DWORD = 18;
pub const ERROR_SHARING_VIOLATION: u32 = 32;
pub const ERROR_HANDLE_EOF: DWORD = 38;
pub const ERROR_FILE_EXISTS: DWORD = 80;
pub const ERROR_INVALID_PARAMETER: DWORD = 87;
Expand Down
29 changes: 29 additions & 0 deletions library/std/src/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,3 +944,32 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
.map(drop)
}
}

// Try to see if a file exists but, unlike `exists`, report I/O errors.
pub fn try_exists(path: &Path) -> io::Result<bool> {
// Open the file to ensure any symlinks are followed to their target.
let mut opts = OpenOptions::new();
// No read, write, etc access rights are needed.
opts.access_mode(0);
// Backup semantics enables opening directories as well as files.
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
match File::open(path, &opts) {
Err(e) => match e.kind() {
// The file definitely does not exist
io::ErrorKind::NotFound => Ok(false),

// `ERROR_SHARING_VIOLATION` means that the file has been locked by
// another process. This is often temporary so we simply report it
// as the file existing.
io::ErrorKind::Other if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => {
Ok(true)
}
// Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
// file exists. However, these types of errors are usually more
// permanent so we report them here.
_ => Err(e),
},
// The file was opened successfully therefore it must exist,
Ok(_) => Ok(true),
}
}
8 changes: 8 additions & 0 deletions library/std/src/sys_common/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
}
fs::remove_dir(path)
}

pub fn try_exists(path: &Path) -> io::Result<bool> {
match fs::metadata(path) {
Ok(_) => Ok(true),
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
Err(error) => Err(error),
}
}