mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
Rollup merge of #98916 - ChrisDenton:hiberfil.sys, r=thomcc
Windows: Use `FindFirstFileW` for getting the metadata of locked system files Fixes #96980 Usually opening a file handle with access set to metadata only will always succeed, even if the file is locked. However some special system files, such as `C:\hiberfil.sys`, are locked by the system in a way that denies even that. So as a fallback we try reading the cached metadata from the directory. Note that the test is a bit iffy. I don't know if `hiberfil.sys` actually exists in the CI. r? rust-lang/libs
This commit is contained in:
commit
80395679cb
@ -1534,3 +1534,20 @@ fn read_large_dir() {
|
|||||||
entry.unwrap();
|
entry.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test the fallback for getting the metadata of files like hiberfil.sys that
|
||||||
|
/// Windows holds a special lock on, preventing normal means of querying
|
||||||
|
/// metadata. See #96980.
|
||||||
|
///
|
||||||
|
/// Note this fails in CI because `hiberfil.sys` does not actually exist there.
|
||||||
|
/// Therefore it's marked as ignored.
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn hiberfil_sys() {
|
||||||
|
let hiberfil = Path::new(r"C:\hiberfil.sys");
|
||||||
|
assert_eq!(true, hiberfil.try_exists().unwrap());
|
||||||
|
fs::symlink_metadata(hiberfil).unwrap();
|
||||||
|
fs::metadata(hiberfil).unwrap();
|
||||||
|
assert_eq!(true, hiberfil.exists());
|
||||||
|
}
|
||||||
|
@ -155,22 +155,7 @@ impl DirEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn metadata(&self) -> io::Result<FileAttr> {
|
pub fn metadata(&self) -> io::Result<FileAttr> {
|
||||||
Ok(FileAttr {
|
Ok(self.data.into())
|
||||||
attributes: self.data.dwFileAttributes,
|
|
||||||
creation_time: self.data.ftCreationTime,
|
|
||||||
last_access_time: self.data.ftLastAccessTime,
|
|
||||||
last_write_time: self.data.ftLastWriteTime,
|
|
||||||
file_size: ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64),
|
|
||||||
reparse_tag: if self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
|
||||||
// reserved unless this is a reparse point
|
|
||||||
self.data.dwReserved0
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
},
|
|
||||||
volume_serial_number: None,
|
|
||||||
number_of_links: None,
|
|
||||||
file_index: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,6 +864,26 @@ impl FileAttr {
|
|||||||
self.file_index
|
self.file_index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<c::WIN32_FIND_DATAW> for FileAttr {
|
||||||
|
fn from(wfd: c::WIN32_FIND_DATAW) -> Self {
|
||||||
|
FileAttr {
|
||||||
|
attributes: wfd.dwFileAttributes,
|
||||||
|
creation_time: wfd.ftCreationTime,
|
||||||
|
last_access_time: wfd.ftLastAccessTime,
|
||||||
|
last_write_time: wfd.ftLastWriteTime,
|
||||||
|
file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64),
|
||||||
|
reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||||
|
// reserved unless this is a reparse point
|
||||||
|
wfd.dwReserved0
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
volume_serial_number: None,
|
||||||
|
number_of_links: None,
|
||||||
|
file_index: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_u64(ft: &c::FILETIME) -> u64 {
|
fn to_u64(ft: &c::FILETIME) -> u64 {
|
||||||
(ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
|
(ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
|
||||||
@ -1145,22 +1150,73 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn stat(path: &Path) -> io::Result<FileAttr> {
|
pub fn stat(path: &Path) -> io::Result<FileAttr> {
|
||||||
let mut opts = OpenOptions::new();
|
metadata(path, ReparsePoint::Follow)
|
||||||
// No read or write permissions are necessary
|
|
||||||
opts.access_mode(0);
|
|
||||||
// This flag is so we can open directories too
|
|
||||||
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
|
|
||||||
let file = File::open(path, &opts)?;
|
|
||||||
file.file_attr()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
|
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
|
||||||
|
metadata(path, ReparsePoint::Open)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum ReparsePoint {
|
||||||
|
Follow = 0,
|
||||||
|
Open = c::FILE_FLAG_OPEN_REPARSE_POINT,
|
||||||
|
}
|
||||||
|
impl ReparsePoint {
|
||||||
|
fn as_flag(self) -> u32 {
|
||||||
|
self as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
|
||||||
let mut opts = OpenOptions::new();
|
let mut opts = OpenOptions::new();
|
||||||
// No read or write permissions are necessary
|
// No read or write permissions are necessary
|
||||||
opts.access_mode(0);
|
opts.access_mode(0);
|
||||||
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
|
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag());
|
||||||
let file = File::open(path, &opts)?;
|
|
||||||
file.file_attr()
|
// Attempt to open the file normally.
|
||||||
|
// If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
|
||||||
|
// If the fallback fails for any reason we return the original error.
|
||||||
|
match File::open(path, &opts) {
|
||||||
|
Ok(file) => file.file_attr(),
|
||||||
|
Err(e) if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _) => {
|
||||||
|
// `ERROR_SHARING_VIOLATION` will almost never be returned.
|
||||||
|
// Usually if a file is locked you can still read some metadata.
|
||||||
|
// However, there are special system files, such as
|
||||||
|
// `C:\hiberfil.sys`, that are locked in a way that denies even that.
|
||||||
|
unsafe {
|
||||||
|
let path = maybe_verbatim(path)?;
|
||||||
|
|
||||||
|
// `FindFirstFileW` accepts wildcard file names.
|
||||||
|
// Fortunately wildcards are not valid file names and
|
||||||
|
// `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
|
||||||
|
// therefore it's safe to assume the file name given does not
|
||||||
|
// include wildcards.
|
||||||
|
let mut wfd = mem::zeroed();
|
||||||
|
let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
|
||||||
|
|
||||||
|
if handle == c::INVALID_HANDLE_VALUE {
|
||||||
|
// This can fail if the user does not have read access to the
|
||||||
|
// directory.
|
||||||
|
Err(e)
|
||||||
|
} else {
|
||||||
|
// We no longer need the find handle.
|
||||||
|
c::FindClose(handle);
|
||||||
|
|
||||||
|
// `FindFirstFileW` reads the cached file information from the
|
||||||
|
// directory. The downside is that this metadata may be outdated.
|
||||||
|
let attrs = FileAttr::from(wfd);
|
||||||
|
if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {
|
||||||
|
Err(e)
|
||||||
|
} else {
|
||||||
|
Ok(attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
|
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
|
||||||
|
Loading…
Reference in New Issue
Block a user