Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/walkdir.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/os/windows/mod.rs')
-rw-r--r--src/os/windows/mod.rs236
1 files changed, 104 insertions, 132 deletions
diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs
index d144f98..3f311f0 100644
--- a/src/os/windows/mod.rs
+++ b/src/os/windows/mod.rs
@@ -10,6 +10,7 @@ use std::io;
use std::mem;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::Path;
+use std::time::{self, SystemTime};
use winapi::shared::minwindef::{DWORD, FILETIME};
use winapi::shared::winerror::ERROR_NO_MORE_FILES;
@@ -19,6 +20,11 @@ use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::minwinbase::WIN32_FIND_DATAW;
use winapi::um::winnt::HANDLE;
+pub use crate::os::windows::stat::FileType;
+
+mod rawpath;
+mod stat;
+
/// A low-level Windows specific directory entry.
///
/// This type corresponds as closely as possible to the `WIN32_FIND_DATA`
@@ -93,53 +99,67 @@ impl DirEntry {
self.attr
}
- /// Return a 64-bit representation of the creation time of the underlying
- /// file.
- ///
- /// The 64-bit value returned is equivalent to winapi's `FILETIME`
- /// structure, which represents the number of 100-nanosecond intervals
- /// since January 1, 1601 (UTC).
- ///
- /// If the underlying file system does not support creation time, then
- /// `0` is returned.
+ /// Returns true if this file is marked as hidden via the
+ /// `FILE_ATTRIBUTE_HIDDEN` marker.
#[inline]
- pub fn creation_time(&self) -> u64 {
- self.creation_time
+ pub fn is_hidden(&self) -> bool {
+ use winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN;
+ self.file_attributes() & FILE_ATTRIBUTE_HIDDEN != 0
}
- /// Return a 64-bit representation of the last access time of the
- /// underlying file.
+ /// Return the creation time of the underlying file as a system time.
///
- /// The 64-bit value returned is equivalent to winapi's `FILETIME`
- /// structure, which represents the number of 100-nanosecond intervals
- /// since January 1, 1601 (UTC).
- ///
- /// If the underlying file system does not support last access time, then
- /// `0` is returned.
+ /// If the underlying file system does not support creation time, then an
+ /// error is returned.
#[inline]
- pub fn last_access_time(&self) -> u64 {
- self.last_access_time
+ pub fn created(&self) -> io::Result<SystemTime> {
+ if self.creation_time == 0 {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "creation time is not available on this platform currently",
+ ))
+ } else {
+ Ok(intervals_to_system_time(self.creation_time))
+ }
}
- /// Return a 64-bit representation of the last write time of the
- /// underlying file.
+ /// Return last access time of the underlying file as a system time.
///
- /// The 64-bit value returned is equivalent to winapi's `FILETIME`
- /// structure, which represents the number of 100-nanosecond intervals
- /// since January 1, 1601 (UTC).
+ /// If the underlying file system does not support creation time, then an
+ /// error is returned.
+ #[inline]
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ if self.last_access_time == 0 {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "last access time is not available on this platform currently",
+ ))
+ } else {
+ Ok(intervals_to_system_time(self.last_access_time))
+ }
+ }
+
+ /// Return the last modified time of the underlying file as a system time.
///
- /// If the underlying file system does not support last write time, then
- /// `0` is returned.
+ /// If the underlying file system does not support creation time, then an
+ /// error is returned.
#[inline]
- pub fn last_write_time(&self) -> u64 {
- self.last_write_time
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ if self.last_write_time == 0 {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "last write time is not available on this platform currently",
+ ))
+ } else {
+ Ok(intervals_to_system_time(self.last_write_time))
+ }
}
/// Return the file size, in bytes, of the corresponding file.
///
/// This value has no meaning if this entry corresponds to a directory.
#[inline]
- pub fn file_size(&self) -> u64 {
+ pub fn len(&self) -> u64 {
self.file_size
}
@@ -180,107 +200,6 @@ impl DirEntry {
}
}
-/// File type information discoverable from the `FindNextFile` winapi routines.
-///
-/// Note that this does not include all possible file types on Windows.
-/// Instead, this only differentiates between directories, regular files and
-/// symlinks. Additional file type information (such as whether a file handle
-/// is a socket) can only be retrieved via the `GetFileType` winapi routines.
-/// A safe wrapper for it is
-/// [available in the `winapi-util` crate](https://docs.rs/winapi-util/*/x86_64-pc-windows-msvc/winapi_util/file/fn.typ.html).
-#[derive(Clone, Copy)]
-pub struct FileType {
- attr: DWORD,
- reparse_tag: DWORD,
-}
-
-impl fmt::Debug for FileType {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let human = if self.is_file() {
- "File"
- } else if self.is_dir() {
- "Directory"
- } else if self.is_symlink_file() {
- "Symbolic Link (File)"
- } else if self.is_symlink_dir() {
- "Symbolic Link (Directory)"
- } else {
- "Unknown"
- };
- write!(f, "FileType({})", human)
- }
-}
-
-impl FileType {
- /// Create a file type from its raw winapi components.
- ///
- /// `attr` should be a file attribute bitset, corresponding to the
- /// `dwFileAttributes` member of file information structs.
- ///
- /// `reparse_tag` should be a valid reparse tag value when the
- /// `FILE_ATTRIBUTE_REPARSE_POINT` bit is set in `attr`. If the bit isn't
- /// set or if the tag is not available, then the tag can be any value.
- pub fn from_attr(attr: u32, reparse_tag: u32) -> FileType {
- FileType { attr: attr, reparse_tag: reparse_tag }
- }
-
- /// Returns true if this file type is a regular file.
- ///
- /// This corresponds to any file that is neither a symlink nor a directory.
- pub fn is_file(&self) -> bool {
- !self.is_dir() && !self.is_symlink()
- }
-
- /// Returns true if this file type is a directory.
- ///
- /// This corresponds to any file that has the `FILE_ATTRIBUTE_DIRECTORY`
- /// attribute and is not a symlink.
- pub fn is_dir(&self) -> bool {
- use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
-
- self.attr & FILE_ATTRIBUTE_DIRECTORY != 0 && !self.is_symlink()
- }
-
- /// Returns true if this file type is a symlink. This could be a symlink
- /// to a directory or to a file. To distinguish between them, use
- /// `is_symlink_file` and `is_symlink_dir`.
- ///
- /// This corresponds to any file that has a surrogate reparse point.
- pub fn is_symlink(&self) -> bool {
- use winapi::um::winnt::IsReparseTagNameSurrogate;
-
- self.reparse_tag().map_or(false, IsReparseTagNameSurrogate)
- }
-
- /// Returns true if this file type is a symlink to a file.
- ///
- /// This corresponds to any file that has a surrogate reparse point and
- /// is not a symlink to a directory.
- pub fn is_symlink_file(&self) -> bool {
- !self.is_symlink_dir() && self.is_symlink()
- }
-
- /// Returns true if this file type is a symlink to a file.
- ///
- /// This corresponds to any file that has a surrogate reparse point and has
- /// the `FILE_ATTRIBUTE_DIRECTORY` attribute.
- pub fn is_symlink_dir(&self) -> bool {
- use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
-
- self.attr & FILE_ATTRIBUTE_DIRECTORY != 0 && self.is_symlink()
- }
-
- fn reparse_tag(&self) -> Option<DWORD> {
- use winapi::um::winnt::FILE_ATTRIBUTE_REPARSE_POINT;
-
- if self.attr & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
- Some(self.reparse_tag)
- } else {
- None
- }
- }
-}
-
/// A handle to a directory stream.
///
/// The handle is automatically closed when it's dropped.
@@ -498,6 +417,17 @@ fn time_as_u64(time: &FILETIME) -> u64 {
(time.dwHighDateTime as u64) << 32 | time.dwLowDateTime as u64
}
+fn intervals_to_system_time(intervals: u64) -> SystemTime {
+ const NANOS_IN_SECOND: u64 = 1_000_000_000;
+ const NANOS_PER_INTERVAL: u64 = 100;
+ const SECONDS_TO_UNIX: u64 = 11_644_473_600;
+
+ let seconds_from_unix =
+ (intervals / (NANOS_IN_SECOND / NANOS_PER_INTERVAL)) - SECONDS_TO_UNIX;
+ let dur_from_unix = time::Duration::from_secs(seconds_from_unix);
+ SystemTime::UNIX_EPOCH + dur_from_unix
+}
+
fn to_utf16<T: AsRef<OsStr>>(t: T, buf: &mut Vec<u16>) -> io::Result<()> {
for cu16 in t.as_ref().encode_wide() {
if cu16 == 0 {
@@ -517,3 +447,45 @@ fn truncate_utf16(slice: &[u16]) -> &[u16] {
None => slice,
}
}
+
+pub(crate) fn escaped_u16s(slice: &[u16]) -> String {
+ use std::char;
+
+ let mut buf = String::with_capacity(slice.len());
+ for result in char::decode_utf16(slice.iter().cloned()) {
+ match result {
+ Ok(ch) => buf.push(ch),
+ Err(err) => {
+ let bad = err.unpaired_surrogate();
+ buf.push_str(&format!(r"\u{{{:X}}}", bad));
+ }
+ }
+ }
+ buf
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn escaping1() {
+ let x: Vec<u16> = "foo☃bar".encode_utf16().collect();
+ let escaped = escaped_u16s(&x);
+ assert_eq!("foo☃bar", escaped);
+ }
+
+ #[test]
+ fn escaping2() {
+ let mut x = vec![];
+ x.push(0xD800);
+ x.extend("a".encode_utf16());
+ x.push(0xDA02);
+ x.extend("b".encode_utf16());
+ x.push(0xDFFF);
+ x.extend("c".encode_utf16());
+
+ let escaped = escaped_u16s(&x);
+ assert_eq!(r"\u{D800}a\u{DA02}b\u{DFFF}c", escaped);
+ }
+}