diff options
| author | bors <bors@rust-lang.org> | 2021-05-21 05:47:24 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-05-21 05:47:24 +0000 |
| commit | f36b137074407ce857e34337bc92f10e26bc3994 (patch) | |
| tree | 728ac4c791398ad6823f907277cff58f53b519cf | |
| parent | 6f5a198ffc0b624783a81e57e1d29c69283949c1 (diff) | |
| parent | 86dbc063ab15e67b2b7ed5411eb9fec9f7afbcb4 (diff) | |
| download | rust-f36b137074407ce857e34337bc92f10e26bc3994.tar.gz rust-f36b137074407ce857e34337bc92f10e26bc3994.zip | |
Auto merge of #85060 - ChrisDenton:win-file-exists, r=yaahc
Windows implementation of feature `path_try_exists` Draft of a Windows implementation of `try_exists` (#83186). The first commit reorganizes the code so I would be interested to get some feedback on if this is a good idea or not. It moves the `Path::try_exists` function to `fs::exists`. leaving the former as a wrapper for the latter. This makes it easier to provide platform specific implementations and matches the `fs::metadata` function. The other commit implements a Windows specific variant of `exists`. I'm still figuring out my approach so this is very much a first draft. Eventually this will need some more eyes from knowledgable Windows people.
| -rw-r--r-- | library/std/src/fs.rs | 26 | ||||
| -rw-r--r-- | library/std/src/path.rs | 6 | ||||
| -rw-r--r-- | library/std/src/sys/hermit/fs.rs | 2 | ||||
| -rw-r--r-- | library/std/src/sys/unix/fs.rs | 2 | ||||
| -rw-r--r-- | library/std/src/sys/unsupported/fs.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/wasi/fs.rs | 2 | ||||
| -rw-r--r-- | library/std/src/sys/windows/c.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys/windows/fs.rs | 29 | ||||
| -rw-r--r-- | library/std/src/sys_common/fs.rs | 8 |
9 files changed, 72 insertions, 8 deletions
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index e6120b8ee31..a1636e2f604 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -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()) +} diff --git a/library/std/src/path.rs b/library/std/src/path.rs index cbe14767bd3..9c5615f58c4 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -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. diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 5b3f2fa4e82..76ea70d997f 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -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> { diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 79617aa77b7..ef14865fbcd 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -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); diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs index cd533761e37..6b45e29c145 100644 --- a/library/std/src/sys/unsupported/fs.rs +++ b/library/std/src/sys/unsupported/fs.rs @@ -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() } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index ed0f03e4b71..45e38f68b8c 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -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, diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index e91c489361e..50d6e8cf27a 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -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; diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 8e6bd76f85f..2b6143de960 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -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), + } +} diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index 30908824dd6..309f5483874 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -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), + } +} |
