Skip to content

Commit

Permalink
Auto merge of #47956 - retep998:is-nibbles, r=BurntSushi
Browse files Browse the repository at this point in the history
This is the ideal FileType on Windows. You may not like it, but this is what peak performance looks like.

Theoretically this would fix #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.
  • Loading branch information
bors committed Feb 17, 2018
2 parents b85bd51 + 9269e83 commit b298607
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 21 deletions.
18 changes: 18 additions & 0 deletions src/libstd/sys/windows/ext/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
47 changes: 26 additions & 21 deletions src/libstd/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}

Expand Down

0 comments on commit b298607

Please sign in to comment.