use crate::fs::OpenOptionsExt;
use crate::fs::{errors, OpenOptions};
use crate::AmbientAuthority;
use std::ffi::OsString;
use std::ops::Deref;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::{Path, PathBuf};
use std::{fs, io};
use windows_sys::Win32::Storage::FileSystem::{
FILE_FLAG_BACKUP_SEMANTICS, FILE_SHARE_READ, FILE_SHARE_WRITE,
};
pub(crate) fn path_requires_dir(path: &Path) -> bool {
let wide: Vec<u16> = path.as_os_str().encode_wide().collect();
wide.ends_with(&['/' as u16])
|| wide.ends_with(&['/' as u16, '.' as _])
|| wide.ends_with(&['\\' as u16])
|| wide.ends_with(&['\\' as u16, '.' as _])
}
pub(crate) fn path_has_trailing_dot(_path: &Path) -> bool {
false
}
pub(crate) fn path_really_has_trailing_dot(path: &Path) -> bool {
let wide: Vec<u16> = path.as_os_str().encode_wide().collect();
wide.ends_with(&['/' as u16, '.' as u16]) || wide.ends_with(&['\\' as u16, '.' as u16])
}
pub(crate) fn path_has_trailing_slash(path: &Path) -> bool {
let wide: Vec<u16> = path.as_os_str().encode_wide().collect();
wide.ends_with(&['/' as u16]) || wide.ends_with(&['\\' as u16])
}
pub(crate) fn strip_dir_suffix(path: &Path) -> impl Deref<Target = Path> + '_ {
let mut wide: Vec<u16> = path.as_os_str().encode_wide().collect();
while wide.len() > 1
&& (*wide.last().unwrap() == '/' as u16 || *wide.last().unwrap() == '\\' as u16)
{
wide.pop();
}
PathBuf::from(OsString::from_wide(&wide))
}
pub(crate) fn dir_options() -> OpenOptions {
OpenOptions::new()
.read(true)
.dir_required(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
.share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE)
.clone()
}
pub(crate) fn readdir_options() -> OpenOptions {
dir_options().readdir_required(true).clone()
}
pub(crate) fn canonicalize_options() -> OpenOptions {
OpenOptions::new()
.read(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
.clone()
}
pub(crate) fn open_ambient_dir_impl(
path: &Path,
ambient_authority: AmbientAuthority,
) -> io::Result<fs::File> {
use std::os::windows::fs::OpenOptionsExt;
let _ = ambient_authority;
let dir = fs::OpenOptions::new()
.read(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
.share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE)
.open(path)?;
if !dir.metadata()?.is_dir() {
return Err(errors::is_not_directory());
}
Ok(dir)
}
#[test]
fn strip_dir_suffix_tests() {
assert_eq!(&*strip_dir_suffix(Path::new("/foo//")), Path::new("/foo"));
assert_eq!(&*strip_dir_suffix(Path::new("/foo/")), Path::new("/foo"));
assert_eq!(&*strip_dir_suffix(Path::new("foo/")), Path::new("foo"));
assert_eq!(&*strip_dir_suffix(Path::new("foo")), Path::new("foo"));
assert_eq!(&*strip_dir_suffix(Path::new("/")), Path::new("/"));
assert_eq!(&*strip_dir_suffix(Path::new("//")), Path::new("/"));
assert_eq!(
&*strip_dir_suffix(Path::new("\\foo\\\\")),
Path::new("\\foo")
);
assert_eq!(&*strip_dir_suffix(Path::new("\\foo\\")), Path::new("\\foo"));
assert_eq!(&*strip_dir_suffix(Path::new("foo\\")), Path::new("foo"));
assert_eq!(&*strip_dir_suffix(Path::new("foo")), Path::new("foo"));
assert_eq!(&*strip_dir_suffix(Path::new("\\")), Path::new("\\"));
assert_eq!(&*strip_dir_suffix(Path::new("\\\\")), Path::new("\\"));
}
#[test]
fn test_path_requires_dir() {
assert!(!path_requires_dir(Path::new(".")));
assert!(path_requires_dir(Path::new("/")));
assert!(path_requires_dir(Path::new("//")));
assert!(path_requires_dir(Path::new("/./.")));
assert!(path_requires_dir(Path::new("foo/")));
assert!(path_requires_dir(Path::new("foo//")));
assert!(path_requires_dir(Path::new("foo//.")));
assert!(path_requires_dir(Path::new("foo/./.")));
assert!(path_requires_dir(Path::new("foo/./")));
assert!(path_requires_dir(Path::new("foo/.//")));
assert!(path_requires_dir(Path::new("\\")));
assert!(path_requires_dir(Path::new("\\\\")));
assert!(path_requires_dir(Path::new("\\.\\.")));
assert!(path_requires_dir(Path::new("foo\\")));
assert!(path_requires_dir(Path::new("foo\\\\")));
assert!(path_requires_dir(Path::new("foo\\\\.")));
assert!(path_requires_dir(Path::new("foo\\.\\.")));
assert!(path_requires_dir(Path::new("foo\\.\\")));
assert!(path_requires_dir(Path::new("foo\\.\\\\")));
}
#[test]
fn test_path_has_trailing_slash() {
assert!(path_has_trailing_slash(Path::new("/")));
assert!(path_has_trailing_slash(Path::new("//")));
assert!(path_has_trailing_slash(Path::new("foo/")));
assert!(path_has_trailing_slash(Path::new("foo//")));
assert!(path_has_trailing_slash(Path::new("foo/./")));
assert!(path_has_trailing_slash(Path::new("foo/.//")));
assert!(path_has_trailing_slash(Path::new("\\")));
assert!(path_has_trailing_slash(Path::new("\\\\")));
assert!(path_has_trailing_slash(Path::new("foo\\")));
assert!(path_has_trailing_slash(Path::new("foo\\\\")));
assert!(path_has_trailing_slash(Path::new("foo\\.\\")));
assert!(path_has_trailing_slash(Path::new("foo\\.\\\\")));
assert!(!path_has_trailing_slash(Path::new("foo")));
assert!(!path_has_trailing_slash(Path::new("foo.")));
assert!(!path_has_trailing_slash(Path::new("/./foo")));
assert!(!path_has_trailing_slash(Path::new("..")));
assert!(!path_has_trailing_slash(Path::new("/..")));
assert!(!path_has_trailing_slash(Path::new("\\.\\foo")));
assert!(!path_has_trailing_slash(Path::new("..")));
assert!(!path_has_trailing_slash(Path::new("\\..")));
assert!(!path_has_trailing_slash(Path::new("/./.")));
assert!(!path_has_trailing_slash(Path::new("foo//.")));
assert!(!path_has_trailing_slash(Path::new("foo/./.")));
assert!(!path_has_trailing_slash(Path::new(".")));
assert!(!path_has_trailing_slash(Path::new("\\.\\.")));
assert!(!path_has_trailing_slash(Path::new("foo\\\\.")));
assert!(!path_has_trailing_slash(Path::new("foo\\.\\.")));
}