From c69f367bafb3a2f90d44fe54fc20d57996fa294a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 25 Jul 2019 09:44:04 -0700 Subject: [PATCH] std: Add more accessors for `Metadata` on Windows This commit adds accessors for more fields in `fs::Metadata` on Windows which weren't previously exposed. There's two sources of `fs::Metadata` on Windows currently, one from `DirEntry` and one from a file itself. These two sources of information don't actually have the same set of fields exposed in their stat information, however. To handle this the platform-specific accessors of Windows-specific information all return `Option` to return `None` in the case a metadata comes from a `DirEntry`, but they're guaranteed to return `Some` if it comes from a file itself. This is motivated by some changes in CraneStation/wasi-common#42, and I'm curious how others feel about this platform-specific functionality! --- src/libstd/sys/windows/ext/fs.rs | 30 +++++++++++++++++ src/libstd/sys/windows/fs.rs | 55 ++++++++++++++++++++++---------- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 268a14ff0aabb..23964dc5bd5df 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -437,6 +437,33 @@ pub trait MetadataExt { /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn file_size(&self) -> u64; + + /// Returns the value of the `dwVolumeSerialNumber` field of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn volume_serial_number(&self) -> Option; + + /// Returns the value of the `nNumberOfLinks` field of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn number_of_links(&self) -> Option; + + /// Returns the value of the `nFileIndex{Low,High}` fields of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn file_index(&self) -> Option; } #[stable(feature = "metadata_ext", since = "1.1.0")] @@ -446,6 +473,9 @@ impl MetadataExt for Metadata { fn last_access_time(&self) -> u64 { self.as_inner().accessed_u64() } fn last_write_time(&self) -> u64 { self.as_inner().modified_u64() } fn file_size(&self) -> u64 { self.as_inner().size() } + fn volume_serial_number(&self) -> Option { self.as_inner().volume_serial_number() } + fn number_of_links(&self) -> Option { self.as_inner().number_of_links() } + fn file_index(&self) -> Option { self.as_inner().file_index() } } /// Windows-specific extensions to [`FileType`]. diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 2f158c014060b..5bae6ba4749bd 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -25,6 +25,9 @@ pub struct FileAttr { last_write_time: c::FILETIME, file_size: u64, reparse_tag: c::DWORD, + volume_serial_number: Option, + number_of_links: Option, + file_index: Option, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -156,6 +159,9 @@ impl DirEntry { } else { 0 }, + volume_serial_number: None, + number_of_links: None, + file_index: None, }) } } @@ -291,23 +297,26 @@ impl File { pub fn file_attr(&self) -> io::Result { unsafe { let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(c::GetFileInformationByHandle(self.handle.raw(), - &mut info))?; - let mut attr = FileAttr { - attributes: info.dwFileAttributes, - creation_time: info.ftCreationTime, - last_access_time: info.ftLastAccessTime, - last_write_time: info.ftLastWriteTime, - file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64), - reparse_tag: 0, - }; - if attr.is_reparse_point() { + cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))?; + let mut reparse_tag = 0; + if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; if let Ok((_, buf)) = self.reparse_point(&mut b) { - attr.reparse_tag = buf.ReparseTag; + reparse_tag = buf.ReparseTag; } } - Ok(attr) + Ok(FileAttr { + attributes: info.dwFileAttributes, + creation_time: info.ftCreationTime, + last_access_time: info.ftLastAccessTime, + last_write_time: info.ftLastWriteTime, + file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32), + reparse_tag, + volume_serial_number: Some(info.dwVolumeSerialNumber), + number_of_links: Some(info.nNumberOfLinks), + file_index: Some((info.nFileIndexLow as u64) | + ((info.nFileIndexHigh as u64) << 32)), + }) } } @@ -336,6 +345,9 @@ impl File { }, file_size: 0, reparse_tag: 0, + volume_serial_number: None, + number_of_links: None, + file_index: None, }; let mut info: c::FILE_STANDARD_INFO = mem::zeroed(); let size = mem::size_of_val(&info); @@ -344,6 +356,7 @@ impl File { &mut info as *mut _ as *mut libc::c_void, size as c::DWORD))?; attr.file_size = info.AllocationSize as u64; + attr.number_of_links = Some(info.NumberOfLinks); if attr.is_reparse_point() { let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; if let Ok((_, buf)) = self.reparse_point(&mut b) { @@ -507,7 +520,9 @@ impl FileAttr { FilePermissions { attrs: self.attributes } } - pub fn attrs(&self) -> u32 { self.attributes as u32 } + pub fn attrs(&self) -> u32 { + self.attributes + } pub fn file_type(&self) -> FileType { FileType::new(self.attributes, self.reparse_tag) @@ -537,8 +552,16 @@ impl FileAttr { to_u64(&self.creation_time) } - fn is_reparse_point(&self) -> bool { - self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 + pub fn volume_serial_number(&self) -> Option { + self.volume_serial_number + } + + pub fn number_of_links(&self) -> Option { + self.number_of_links + } + + pub fn file_index(&self) -> Option { + self.file_index } }