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.
This commit is contained in:
commit
f36b137074
@ -2208,3 +2208,29 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
|
|||||||
&mut self.inner
|
&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())
|
||||||
|
}
|
||||||
|
@ -2507,11 +2507,7 @@ impl Path {
|
|||||||
#[unstable(feature = "path_try_exists", issue = "83186")]
|
#[unstable(feature = "path_try_exists", issue = "83186")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_exists(&self) -> io::Result<bool> {
|
pub fn try_exists(&self) -> io::Result<bool> {
|
||||||
match fs::metadata(self) {
|
fs::try_exists(self)
|
||||||
Ok(_) => Ok(true),
|
|
||||||
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the path exists on disk and is pointing at a regular file.
|
/// Returns `true` if the path exists on disk and is pointing at a regular file.
|
||||||
|
@ -12,7 +12,7 @@ use crate::sys::time::SystemTime;
|
|||||||
use crate::sys::unsupported;
|
use crate::sys::unsupported;
|
||||||
use crate::sys_common::os_str_bytes::OsStrExt;
|
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;
|
//pub use crate::sys_common::fs::remove_dir_all;
|
||||||
|
|
||||||
fn cstr(path: &Path) -> io::Result<CString> {
|
fn cstr(path: &Path) -> io::Result<CString> {
|
||||||
|
@ -48,7 +48,7 @@ use libc::{
|
|||||||
dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
|
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);
|
pub struct File(FileDesc);
|
||||||
|
|
||||||
|
@ -275,6 +275,10 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
|
|||||||
unsupported()
|
unsupported()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_exists(_path: &Path) -> io::Result<bool> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
|
pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
|
||||||
unsupported()
|
unsupported()
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ use crate::sys::time::SystemTime;
|
|||||||
use crate::sys::unsupported;
|
use crate::sys::unsupported;
|
||||||
use crate::sys_common::FromInner;
|
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 {
|
pub struct File {
|
||||||
fd: WasiFd,
|
fd: WasiFd,
|
||||||
|
@ -173,6 +173,7 @@ pub const ERROR_INVALID_HANDLE: DWORD = 6;
|
|||||||
pub const ERROR_NOT_ENOUGH_MEMORY: DWORD = 8;
|
pub const ERROR_NOT_ENOUGH_MEMORY: DWORD = 8;
|
||||||
pub const ERROR_OUTOFMEMORY: DWORD = 14;
|
pub const ERROR_OUTOFMEMORY: DWORD = 14;
|
||||||
pub const ERROR_NO_MORE_FILES: DWORD = 18;
|
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_HANDLE_EOF: DWORD = 38;
|
||||||
pub const ERROR_FILE_EXISTS: DWORD = 80;
|
pub const ERROR_FILE_EXISTS: DWORD = 80;
|
||||||
pub const ERROR_INVALID_PARAMETER: DWORD = 87;
|
pub const ERROR_INVALID_PARAMETER: DWORD = 87;
|
||||||
|
@ -944,3 +944,32 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
|
|||||||
.map(drop)
|
.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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -41,3 +41,11 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
fs::remove_dir(path)
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user