Auto merge of #47956 - retep998:is-nibbles, r=BurntSushi
This is the ideal FileType on Windows. You may not like it, but this is what peak performance looks like. Theoretically this would fix https://github.com/rust-lang/rust/issues/46484 The current iteration of this PR should not cause existing code to break, but instead merely improves handling around reparse points. Specifically... * Reparse points are considered to be symbolic links if they have the name surrogate bit set. Name surrogates are reparse points that effectively act like symbolic links, redirecting you to a different directory/file. By checking for this bit instead of specific tags, we become much more general in our handling of reparse points, including those added by third parties. * If something is a reparse point but does not have the name surrogate bit set, then we ignore the fact that it is a reparse point because it is actually a file or directory directly there, despite having additional handling by drivers due to the reparse point. * For everything which is not a symbolic link (including non-surrogate reparse points) we report whether it is a directory or a file based on the presence of the directory attribute bit. * Notably this still preserves invariant that when `is_symlink` returns `true`, both `is_dir` and `is_file` will return `false`. The potential for breakage was far too high. * Adds an unstable `FileTypeExt` to allow users to determine whether a symbolic link is a directory or a file, since `FileType` by design is incapable of reporting this information.
This commit is contained in:
commit
b298607864
@ -445,6 +445,24 @@ impl MetadataExt for Metadata {
|
||||
fn file_size(&self) -> u64 { self.as_inner().size() }
|
||||
}
|
||||
|
||||
/// Add support for the Windows specific fact that a symbolic link knows whether it is a file
|
||||
/// or directory.
|
||||
#[unstable(feature = "windows_file_type_ext", issue = "0")]
|
||||
pub trait FileTypeExt {
|
||||
/// Returns whether this file type is a symbolic link that is also a directory.
|
||||
#[unstable(feature = "windows_file_type_ext", issue = "0")]
|
||||
fn is_symlink_dir(&self) -> bool;
|
||||
/// Returns whether this file type is a symbolic link that is also a file.
|
||||
#[unstable(feature = "windows_file_type_ext", issue = "0")]
|
||||
fn is_symlink_file(&self) -> bool;
|
||||
}
|
||||
|
||||
#[unstable(feature = "windows_file_type_ext", issue = "0")]
|
||||
impl FileTypeExt for fs::FileType {
|
||||
fn is_symlink_dir(&self) -> bool { self.as_inner().is_symlink_dir() }
|
||||
fn is_symlink_file(&self) -> bool { self.as_inner().is_symlink_file() }
|
||||
}
|
||||
|
||||
/// Creates a new file symbolic link on the filesystem.
|
||||
///
|
||||
/// The `dst` path will be a file symbolic link pointing to the `src`
|
||||
|
@ -38,8 +38,9 @@ pub struct FileAttr {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum FileType {
|
||||
Dir, File, SymlinkFile, SymlinkDir, ReparsePoint, MountPoint,
|
||||
pub struct FileType {
|
||||
attributes: c::DWORD,
|
||||
reparse_tag: c::DWORD,
|
||||
}
|
||||
|
||||
pub struct ReadDir {
|
||||
@ -516,30 +517,34 @@ impl FilePermissions {
|
||||
|
||||
impl FileType {
|
||||
fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType {
|
||||
match (attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0,
|
||||
attrs & c::FILE_ATTRIBUTE_REPARSE_POINT != 0,
|
||||
reparse_tag) {
|
||||
(false, false, _) => FileType::File,
|
||||
(true, false, _) => FileType::Dir,
|
||||
(false, true, c::IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkFile,
|
||||
(true, true, c::IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkDir,
|
||||
(true, true, c::IO_REPARSE_TAG_MOUNT_POINT) => FileType::MountPoint,
|
||||
(_, true, _) => FileType::ReparsePoint,
|
||||
// Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is
|
||||
// invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint
|
||||
// to indicate it is something symlink-like, but not something you can follow.
|
||||
FileType {
|
||||
attributes: attrs,
|
||||
reparse_tag: reparse_tag,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool { *self == FileType::Dir }
|
||||
pub fn is_file(&self) -> bool { *self == FileType::File }
|
||||
pub fn is_dir(&self) -> bool {
|
||||
!self.is_symlink() && self.is_directory()
|
||||
}
|
||||
pub fn is_file(&self) -> bool {
|
||||
!self.is_symlink() && !self.is_directory()
|
||||
}
|
||||
pub fn is_symlink(&self) -> bool {
|
||||
*self == FileType::SymlinkFile ||
|
||||
*self == FileType::SymlinkDir ||
|
||||
*self == FileType::MountPoint
|
||||
self.is_reparse_point() && self.is_reparse_tag_name_surrogate()
|
||||
}
|
||||
pub fn is_symlink_dir(&self) -> bool {
|
||||
*self == FileType::SymlinkDir || *self == FileType::MountPoint
|
||||
self.is_symlink() && self.is_directory()
|
||||
}
|
||||
pub fn is_symlink_file(&self) -> bool {
|
||||
self.is_symlink() && !self.is_directory()
|
||||
}
|
||||
fn is_directory(&self) -> bool {
|
||||
self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
}
|
||||
fn is_reparse_point(&self) -> bool {
|
||||
self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
|
||||
}
|
||||
fn is_reparse_tag_name_surrogate(&self) -> bool {
|
||||
self.reparse_tag & 0x20000000 != 0
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user